views:

419

answers:

3

Hi all-

I have a user class, where I am trying to attach a profile created by a factory. Here is the class:

class User < ActiveRecord::Base
  acts_as_authentic
  has_one :profile

  after_create {self.profile = ProfileFactory.create_profile(self.role)}

end

and the factory looks like this

class ProfileFactory
    def self.create_profile(role)
      String s = "#{role}#{"Profile"}"
      Object.const_get(s).new
    end
end

For some reason it doesnt recognize self as a User. This is the error I get on making the ProfileFactory.create_profile call

undefined method 'role' for #<Class:0x2304218>

The user object has a role: String declared in its migration.

Any help is appreciated thanks.

+1  A: 

Why not do something more simplistic? Something like:

class User < ActiveRecord::Base
  has_one :profile
  after_create :add_profile

  protected

    def add_profile
      self.create_profile(:role => self.role)
    end
end

class Profile < ActiveRecord::Base
  belongs_to :user

end

Have you come from a Java background perchance?

Matt Darby
Matt-You hit the nail on the head. Hilarious. Thanks.
I thought this would work, but it is still throwing a unknown attribute: role error.In the migration, a role column is created in it. I just double checked the users table and it also has a role column. Is these something I need to do to make self.role work? I tried with self.email and it failed also. Any ideas?
The self.role isn't needed. Just role will do. The problem here, is that Matt doesn't get your database structure. the create_profile(:role => self.role) is attempting to create a new profile record with the role field set to the user's role. The user has the role field not the profile.
EmFi
+1  A: 

The User object in question is passed to the after_create block as a parameter.

class User < ActiveRecord::Base
  after_create do |user|
    user.profile = ProfileFactory.create_profile(user.role)
    user.save
  end
end
Duncan Beevers
Duncan-Thanks this worked well.
+1  A: 

Duncan's got the correct answer in terms of using your factory as a callback. But it might help you to understand what was going wrong.

Class methods receive the class as self, instance methods receive the instance as self. When you provide any method a block, the scope of the calling method is used for the block.

after_create is a class method that adds a call back to the block provided or methods listed as arguments. Blocks provided to callbacks (after_create, before_save, etc) are interpreted in the context of class methods. So self refers not, to the object being created, but the Class of the object being created.

In this snippet:

  after_create {self.profile = ProfileFactory.create_profile(self.role)}

self is the User class, not an instance of the User class as you expect.

Compared to the more traditional after_create syntax that Matt was hinting at, where an instance method is added to the callback chain. In this context self refers to the instance.

class User < ActiveRecord::Base
  has_one :profile
  after_create :add_profile

  protected

    def add_profile
      self.profile = ProfileFactory.create_profile(role)
    end
end

EmFi, This makes a lot of sense. So just to clarify, when invoking methods that are in the class from the callback methods but NOT actually in one of the callback methods, allows us to get around this class method problem, and use the current instance?

Yes, but not for the reasons you think. Callbacks only looks for instance methods when passed a symbol.

Instead what you've found a way to get around the instance method problem. You can't give a callback a class method, but you can provide it a block in which it calls one. I guess you could also define an instance method that calls the class method, but that seems a little backwards.

EmFi
EmFi,This makes a lot of sense. So just to clarify, when invoking methods that are in the class from the callback methods but NOT actually in one of the callback methods, allows us to get around this class method problem, and use the current instance?
My explanation required more formatting than a comment could provide. Answer has been updated to address your new question.
EmFi