Referencing a non-ActiveRecord attribute

244 views
Skip to first unread message

rapdup

unread,
Feb 15, 2009, 6:54:33 PM2/15/09
to Ruby on Rails: Talk
I am trying to access a model's attribute that does not have a column
in the model's corresponding table. It exists only through an
attr_accessor:

class Widget < ActiveRecord::Base
attr_accessor :attribute_not_in_db
end

I can access the attribute directly:

>> w = Widget.new(:attribute_not_in_db => "foo")
>> w.attribute_not_in_db
=> "foo"

However, I can't seem to access it other ways:

>> attr_symbol = :attribute_not_in_db
>> w[attr_symbol]
=> nil

>> attr_name = "attribute_not_in_db"
>> w[attr_name]
=> nil

>> w.read_attribute(attr_symbol)
=> nil

>> w.read_attribute(attr_name)
=> nil

This is probably rather basic, but my searching hasn't turned up the
answer. Any guidance would be apprecited.

thx.

Jeff Lewis

unread,
Feb 15, 2009, 10:50:41 PM2/15/09
to Ruby on Rails: Talk
One way to dynamically add attributes to some model object on the fly
is to just directly add the new key(s)/val(s) to the model instance's
attributes:

$ ./script/console
Loading development environment (Rails 2.2.2)

>> test = Test.find(:first)
=> #<Test id: 1, name: "Abe">

>> test.attributes.keys
=> ["id", "name"]

>> test["favorite_color"] = 'blue'
=> "blue"

>> test.attributes.keys
=> ["id", "favorite_color", "name"]

>> test.favorite_color
=> "blue"

Another way, assuming the purpose is to add additional attribs coming
from a db qry, ... Note that any additional attribs returned from a
db query will be automatically added to the returned model ob(s):

>> test2 = Test.find_by_sql("select *, 'blue' as favorite_color from tests order by id limit 1").first
=> #<Test id: 1, name: "Abe">

>> test2.attributes.keys
=> ["id", "favorite_color", "name"]

>> test2.favorite_color
=> "blue"

Note that neither of the above requires any accessor-related code in
your model ob (unless you want/need it for other purposes):

$ cat app/models/test.rb
class Test < ActiveRecord::Base
end

Jeff

rapdup

unread,
Feb 15, 2009, 11:39:15 PM2/15/09
to Ruby on Rails: Talk
Thanks for the response.
I'm afraid my initial post wasn't clear as it could be. I am trying to
find a specific syntax to access an accessor getter/setter using a
variable, in this example "x":

user = User.new(:login=> "foo", :password=>"bar") # :password is not
a column in the db, but defined in the model using
"attr_accessor :password" (see initial post)

[:login, :password].each do |x|
original_val = user[x]
user[x] = some_new_val
do_some_test(user)
user[x] = original_val
end

It works for :login which is an actual attribute of User. However,
since :password was created as an attribute accessor (def password &
def password=), the syntax "user[e]" doesn't work. I'm looking for a
way to make this work while keeping "attr_accessor :password"

I hope this is clearer.
Thanks for the assist.

rapdup

unread,
Feb 16, 2009, 12:14:34 AM2/16/09
to Ruby on Rails: Talk
Okay, I found a solution. It isn't very readable, but it works:

user = User.new(:login=> "foo", :password=>"bar") # :password is not
a column in the db, but defined in the model using
"attr_accessor :password" (see initial post)
[:login, :password].each do |x|
getter = x # => :password
setter = (x.to_s << "=").to_sym # => :password=
original_val = user.method(getter).call
user.method(setter).call(some_new_val)
do_some_test(user)
user.method(setter).call(original_val)
end

Any clean up suggestion?

Jeff Lewis

unread,
Feb 16, 2009, 12:43:54 AM2/16/09
to Ruby on Rails: Talk
The reason to add extra attributes as shown in my example is to allow
for the accessor goodies provided by ActiveRecord for the model ob:

$ cat app/models/hit.rb
class Test < ActiveRecord::Base
attr_accessor :foo
end

$ ./script/console
Loading development environment (Rails 2.2.2)

>> test = Test.new
=> #<Test id: nil, name: nil>

>> test.id = 987
=> 987

>> test[:id]
=> 987

>> test["id"]
=> 987

>> test.foo = 'bar'
=> "bar"

>> test[:foo]
=> nil #huh?

>> test["foo"]
=> nil #huh?

>> test.attributes.keys
=> ["id", "name"] #huh?

### add key/val via model ob's attributes:
>> test['foo'] = 'bar'
=> "bar" #cool.

>> test.foo
=> "bar" #cool.

>> test[:foo]
=> "bar" #cool.

>> test.attributes.keys
=> ["id", "foo", "name"] #cool.

So, for your User example, if you changed the adding of password
attrib as:

...
user["password"] = "bar"
...

then your orig code should work as you intended.

Hope that helps,

Jeff

jemminger

unread,
Feb 16, 2009, 8:37:54 PM2/16/09
to Ruby on Rails: Talk
This is marginally cleaner IMO:


[:login, :password].each do |getter|
setter = "#{getter}="
original_val = user.send(getter)
user.send(setter, some_new_val)
do_some_test(user)
user.send(setter, original_val)
end

Reply all
Reply to author
Forward
0 new messages