Этот код показывает, что я хотел бы сделать, но, конечно, не будет работать, потому что у Родителя еще нет идентификатора:
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 должна быть идемпотентной. Это решение не является строго идемпотентным, и мне трудно понять, как я могу гарантировать, что состояние сервера будет отменено, если пользователь уйдет от новой формы.
Я иду по болезненному пути здесь?