views:

216

answers:

2

As this question is hard to describe, that's the best title i could come up with, so here is some code.

Given three Models Parent, Child && Grandchild.

Parent <  ActiveRecord::Base
  has_many :children
  has_many :grandchildren
  accepts_nested_attributes_for :child
end

Child <  ActiveRecord::Base
  belongs_to :parent
  has_many :kids, :as => :grandchildren #this is just an example
  accepts_nested_attributes_for :grandchild
end

Grandchild <  ActiveRecord::Base
  belongs_to :parent
  belongs_to :child
end

I'd like to add the current_user.id to both the child record and Grandchild record that gets created in during Parent#new. I've used hidden fields for now, because i couldn't find a good way to add them.

Maybe someone can help by creating a callback to add the current_user.id on create? I've never had much luck getting that into a model anyways, but you are smart.

Thoughts?

+2  A: 

Well, for one thing, I'd recommend a has_many :through relationship from parent to grandchild (through child), and vice versa. See the "Association Join Models" section in the ActiveRecord Association Class Methods API for more details.

As to your main question, like you say, a callback is probably what you want. I think something like this should do it (though this is untested code):

class Parent
  # ...somewhere at the top...
  before_create :set_current_user_on_descendants

  # ...somewhere in the main class body...
  # (I assume parent['current_user'] is passed in as a typical 
  # parameter, and thus self.current_user is already set.)
  def set_current_user_on_descendants
    children.each { |c| c.current_user = self.current_user }
    grandchildren.each { |gc| gc.current_user = self.current_user }
  end
end

There are a few stylistic points that could be done differently. You could define a "descendants" method that returned children + grandchildren and iterate over that, for example, or you could implement the callback on the child and grandchild classes (in which case you might want to pull it out in to a module for maximum DRYness, though for a one-line method in only two classes that might be overkill). Depending on exactly when you want to update current_user, you might want to use before_save or some other callback instead of before_create - you can find a complete list of the available callbacks in the ActiveRecord callbacks API.

John Hyland
i'll check this out for sure. thanks for being thorough, although i am familiar with the callbacks and my example was off the top of my head, this information will help others down the road. when i test i'll mark as complete cause it looks like it should work.
pjammer
well passing the @current_user didn't do anything, however i just stole the user_id column from `self.user_id` for each of the descendants. I'd like to mark your answer as complete if you want to add that to it.
pjammer
A: 

I suppose its also possible to override the default save! method

class Parent < ActiveRecord::Base
   def save! 
      children.each { |c| c.current_user = @current_user }
      grandchildren.each { |gc| gc.current_user = @current_user }

      super
   end
end

Untested as well. Not really sure this would work...

Lukas