Rails - непрошеному родителю необходимо найти детей-кандидатов и назначить их себе. Затем отобразите «новую» форму - PullRequest
0 голосов
/ 12 августа 2011

Этот код показывает, что я хотел бы сделать, но, конечно, не будет работать, потому что у Родителя еще нет идентификатора:

class Parent < ActiveRecord::Base
  has_many :children

  after_initialize :find_children, :if => Proc.new {|parent| parent.new_record?}

  private
  def find_children
    Child.where("blah blah blah").each do |child|
      child.parent = self
      #etc, etc, etc
    end
  end
end

Это почти так, как будто «новому» действию моего контроллера нужно сохранить Родителя перед отображением новой формы. Это не правильно. Какой хороший подход к этой проблеме?

Обновление

Дочерними объектами в моем конкретном случае являются BillTransactions (думаю, сборы и кредиты), а родителями - Bills. В течение расчетного периода эти транзакции начисляются на счет. В конце расчетного периода пользователь создает счет за данный период, поэтому необходимо, чтобы счет находил своих детей при его создании.

Я думал об этом еще немного после того, как опубликовал вопрос. Поскольку Bill и BillTransactions могут существовать во многих различных состояниях (в ожидании, в черновике, в активном состоянии, по электронной почте и т. Д.), Я буду использовать конечный автомат для управления жизненным циклом объекта. Пока это то, что я придумал:

class Bill < ActiveRecord::Base                                                                        
  belongs_to :account                                                                                  
  has_many :bill_transactions                                                                          

  attr_accessible :account, :bill_period_start, :bill_period_end                                       

  after_initialize :find_fees, :if => Proc.new {|bill| bill.new_record?}                               

  validates_presence_of :account, :bill_period_start, :bill_period_end                                 

  state_machine :initial => :pending do                                                                
    after_transition :pending => :canceled, :do => :destroy_self                                       
    before_transition :active => :emailed, :do => :email_bill                                          

    event :save_draft do                                                                               
      transition :pending => :draft                                                                    
    end                                                                                                

    event :activate do                                                                                 
      transition [:pending, :draft] => :active                                                         
    end                                                                                                

    event :email do                                                                                    
      transition :active => :emailed                                                                   
    end                                                                                                

    event :apply_payment do                                                                            
      transition [:active, :emailed] => :partial                                                       
      transition [:active, :emailed, :partial] => :paid                                                
    end                                                                                                

    event :cancel do                                                                                   
      transition [:pending, :draft] => :canceled                                                       
    end                                                                                                
  end                                                                                                  

  private                                                                                              
  def find_fees                                                                                        
    self.save                                                                                          
    unless [account, bill_period_start, bill_period_end].any? {|attr| attr.nil? }                      
      BillTransaction.where(:account_id => account.id, :transaction_date => bill_period_start..bill_period_end, :transaction_type => BillTransaction::TRANS_TYPES['Fee']).each do |fee|                        
        fee.stage self
      end
    end
  end

  def destroy_self                                                                                     
    self.bill_transactions.each do |trans|                                                             
      trans.unstage
    end
    self.destroy                                                                                       
  end
end

Таким образом, после первой инициализации Билла он в основном сохраняет себя, находит все соответствующие транзакции и «ставит» их. Это означает, что состояние BillTransaction установлено на поэтапное (которое может перейти обратно к неоплаченному, если новый счет уничтожен), а его bill_id - к ​​идентификатору текущего счета. Вы можете видеть, что, если счет в состоянии ожидания отменяется, все транзакции не обрабатываются (возвращаются в состояние без выставления счета).

Проблема с этим решением заключается в том, что отправка запроса GET в BillsController # new должна быть идемпотентной. Это решение не является строго идемпотентным, и мне трудно понять, как я могу гарантировать, что состояние сервера будет отменено, если пользователь уйдет от новой формы.

Я иду по болезненному пути здесь?

1 Ответ

0 голосов
/ 12 августа 2011

Я бы создал новый метод "создатель" в Bill, который возвращает новый счет с присоединенными транзакциями. Что-то вроде:

def self.NewWithTransactions
    bill = Bill.new
    bill_transactions = find_candidate_transactions
    bill
end

Затем из нового действия вашего контроллера просто выполните:

bill = Bill.NewWithTransactions

Отбросьте это обратно, и вы должны иметь возможность создать новый счет с транзакциями, прикрепленными при отправке. Если это не сработает, вам, вероятно, придется поступить так, как предложил один из комментаторов, и отправить несвязанные транзакции на ваш взгляд и повторно связать их в действии create.

...