Set attr_accessor of active record (indirectly)

I have an attr_accessor for :password set on my user model.

If I have a variable “key” whose value is “password” and another
variable “value” whose value is “secret”, can I use this to set
the :password? I can make it work several ways with regular model
attributes, but not with attr_accessor attributes, e.g.

u.login
=> “mylogin”

u.write_attribute(‘login’, ‘mylogin2’)
=> “mylogin2”

u.login
=> “mylogin2”

u.password
=> “foo”

u.write_attribute(‘password’, ‘mypass2’)
=> “mypass2”

u.password
=> “foo”

Thanks –

Tom

tharrison wrote:

I have an attr_accessor for :password set on my user model.

If I have a variable “key” whose value is “password” and another
variable “value” whose value is “secret”, can I use this to set
the :password? I can make it work several ways with regular model
attributes, but not with attr_accessor attributes, e.g.

Tom

Tom, it sounds like you are using acts_as_authenticated or some variant
of it… in which case you probably have attr_accessible() somewhere in
your user model. The reason the password field behaves differently than
the others is because of it’s omission from that list.

I strongly suggest you keep it out of that list but that is another
topic altogether…

hth

ilan

Tom, this problem could use some better explaining.

Ok. Let me try.

I have a form for the User model. Most of the fields (attributes) are
database columns, like first_name, login, etc. Password, however, is
a virtual attribute, defined as an “attr_accessor”. A person enters
values into the form, including password, and when the form is
submitted I have a hash of params[:user] containing attributes of the
user. This form does not contain all of the values for the user
model, and because in some cases the user model is new while in others
it is an existing record, I wish to selectively set attribute. I have
used various forms of write_attribute to accomplish this. All
database-backed model attributes work fine (as in the case of “login”
in the example). The plain-text password however is not stored in the
model, so is defined as a virtual attribute (attr_accessor) on the
model.

I can write values to the regular attributes of the model. I cannot
successfully write values to the password field. While I get no
error, the value is not updated in the model. Please see the example
I provided.

If I have the name of an attribute, like “password”, and a value
(perhaps a computed one), how can I set this value on the @user
instance variable?

Is that explained better?

Tom

2008/3/5, tharrison [email protected]:

u.write_attribute(‘login’, ‘mylogin2’)
=> “mylogin2”
u.login
=> “mylogin2”

key = "password"
value = "secret"

u.send("#{key}=", value)

Setting an attribute in Ruby is just sending a message.

HTH,
Stefan

Ok, firstly have you checked out the restful_authentication plugin? If
not, you can make your life a lot easier by using it, and, if you
can’t use it, it will definitely help you to make your own login
system work better.

In my login system based on restful_authentication i’ve got password
and password_confirmation as attr_accessors. You should be able to
send the #new or update_attribtes method the :password
and :password_confirmation keys and it will update them in the model

It sounds like you haven’t got any point where your model assigns your
password to a column in the db (where it can be saved)

I’ve got a before_save hook which calls ‘encrypt_password’ method.
This generates a hashed password and salt and assigns these to the
database columns for storing my password (in my case hashed_password)

You can see all this in action by starting a new rails project, adding
restful_authentication plugin and generating the models

Hope that helps a bit, though it doesn’t answer directly why you can’t
update using update_attribute (seems a bit overcomplex) .BTW if you
need to bypass parts of the validation code when you’re just updating
models, you can add :if => :method_name to the end of your validation
commands which will check the output of a method for boolean result so
you can selectively disable validation where necessary in certain
parts of your app.

Thanks everyone. The “send” magic worked, which in turn helped me
realized that rails has the “attributes” method (which uses send), and
also honors other model settings. So, to update a potentially
existing instance with only values that change:

Find a, existing user if an optional parameter is passed, otherwise

create a new one
@user = User.find_by_some_optional_parameter(params[:optional_param])
|| User.new

This will only update the attributes in from the user hash and not

overwrite other aspects of the @user instance
@user.attributes = params[:user]

Sometimes the hardest part of finding a solution is knowing the proper
question to ask :slight_smile:

Tom

Hi –

On Tue, 4 Mar 2008, tharrison wrote:

u.write_attribute(‘login’, ‘mylogin2’)
=> “mylogin2”
u.login
=> “mylogin2”

u.password
=> “foo”
u.write_attribute(‘password’, ‘mypass2’)
=> “mypass2”
u.password
=> “foo”

You could do this:

u.send(“#{key}=”, value)

attr_accessor :password just creates two methods for you:

def password
@password
end

def password=(password)
@password = password
end

so you have to proceed from there.

David


Upcoming Rails training from David A. Black and Ruby Power and Light:
ADVANCING WITH RAILS, April 14-17 2008, New York City
CORE RAILS, June 24-27 2008, London (Skills Matter)
See http://www.rubypal.com for details. Berlin dates coming soon!