views:

421

answers:

1

Updated

Appears to be a precedence error and nothing to do with the question I originally asked. See discussion below.

Original question

Is it possible to use active record associations in callbacks? I've tested this code in the console and it works fine as long as it isn't in a callback. I'm trying to create callbacks that pull attributes from other associated models and I keep getting errors of nil.attribute.

If callbacks are not the correct approach to take, how would one do a similar action in rails? If the associations are simple, you could use create_association(attributes => ), but as associations get more complex this starts to get messy.

For example...

class User < ActiveRecord::Base
  belongs_to :b
  before_validation_on_create {|user| user.create_b} #note, other logic prevents creating multiple b
end

class B < ActiveRecord::Base
  has_many :users, :dependent => destroy
  after_create{ |b| b.create_c }
  has_one :c
end

class C < ActiveRecord::Base
  belongs_to :b
  after_create :create_alert_email

  private
  def create_alert_email 
    self.alert_email = User.find_by_b_id(self.b_id).email  #error, looks for nil.email
  end
end
+2  A: 

Off course associations are available in your callbacks. After all, the create_after_email is simply a method. You can even call it alone, without using a callback. ActiveRecord doesn't apply any special flag to callback methods to prevent them from working as any other method.

Also notice you are running a User#find query directly without taking advantage of any association method. An other reason why ActiveRecord association feature should not be the guilty in this case.

The reason why you are getting the error should probably searched somewhere else. Be sure self.b_id is set and references a valid record. Perhaps it is nil or actually there's no User record with that value. In fact, you don't test whether the query returns a record or nil: you are assuming a record with that value always exists. Are you sure this assumption is always statisfied?

Simone Carletti
Thanks weppos. It does appear to be set. I am guessing my problem has to do with the timing of record creations. For instance if I comment out... #self.alert_email = User.find_by_b_id(self.b_id).email And run my tests which have: user = Factory(:user) c = user.b.c assert(c.valid?) # => true assert_equal(user.email, c.alert_email) # => <"[email protected]"> expected but was <nil> As to your question about not using associations, I tried and got the same result. And yes, I need nil checks. I cut them out for readability
Kevin Dewalt
My formatting isn't working so let me give a better response:<br><br>The create_alert_email method works as expected except when run as a callback. From the console I run Factory(:user) and the method works as I expect. So somehow it looks to me that model associations don't work as I would expect during callbacks.
Kevin Dewalt
It might be a problem of precedences. You should edit your post to include more details to check whether you miss something. Just looking at your example, it should work. But I have no way to understand whether the execution is run in the correct order so that the query doesn't return nil. You can also run an other test: comment the after_create callback and call create_alert_email as a method immediately after record creation. It should raise the same error.
Simone Carletti
You're right, I did forget some code that I think answers the question. Added another callback to Class B above. User record isn't saved when c is created. In any case...you answered my original question that associations can be used within callbacks when you don't make other stupid mistakes. ;-) Thanks again for the help.
Kevin Dewalt