views:

147

answers:

1

Hi all!

I'm having trouble with the following code:

User < AR
  acts_as_authentic

  belongs_to :owner, :polymorphic => true
end

Worker < AR
  has_one :user, :as => :owner
  accepts_nested_attributes_for :user
end

Employer < AR
  has_one :user, :as => :owner
  accepts_nested_attributes_for :user
end

I'd like to create registration forms based on user types, and to include authentication fields such as username and password. I currently do this:

UserRegistrationController < AC
  #i.e. a new Employer
  def new
    @employer = Employer.new
    @employer.build_user
  end
...
end

I then include User fields with fields_for. All views render fine, but here's the catch: I cannot build a User, it tells me :password is a wrong method, so I guess the authentication logic has been bypassed. What should I do? Am I doing it wrong altogether? Should I drop polymorphic associations in favor of Single Table Inheritance? Whatever I do, I have to make sure it plays nicely with Authlogic.

+1  A: 

I'd approach the building of new users of either type in the opposite direction. ie:

#controller 
@employer = Employer.new
@user = @employer.build_user

#view
form_for @user |f|
  f.text_field :login
  f.password_field :password
  fields_for :owner, @employer |f_e|
    f_e.some_field :some_value

#controller
def create
  @owner = params[:owner][:some_employer_field_or_virtual_attribute] ? Employer.new params[:owner] : Worker.new params[:owner]
  @owner.save
  @user = User.new(params[:user].merge!(:owner => @owner)
  if @user.save
    ...

re. mentioned virtual attribute - if there's no field in the model, and thus in the form, which distinguishes user type as employer or worker then set an virtual attribute within each which you can put as a hidden boolean field in the form

mark
Hey thanks! I'll try it out. What should I save in the create action? Should I save both separately?I'd rather stick with with the has_one association, it seems more scalable. Besides, I'm not that worried about space right now.
ferparra
I think you'll need to save both separately. Rails won't be able to determine which type of model is on the other end of the polymorphic relationship. Try | @employer = Employer.new(params[:employer]) | @employer.save | @user = User.new(params[:user].merge!(:owner => @employer)). You'll need to remove the accepts_nested_attributes_for from the model.
mark
Hey, also in your original form definition, are you sure you weren't defining the user attributes in the owner fields? I'm going to post another answer because perhaps this should work. ^ See edit to original answer.
mark
In the original form definition I have defined all user attributes. In that case, I get an error from activerecord stating that :password is not a valid method (it's true, it is not defined in the users table).
ferparra
You should have a crypted_password and password_salt columns. If you have them try in script/console instantiating and saving a new user.
mark
That's correct. I've always been able to create new users from console if I construct a user from the start. Could you provide me with an alternative code from the comment above? I have no way to know whether the :owner key in params hash is either a worker/employer.
ferparra
Have edited answer :)
mark