Referencing a non-ActiveRecord attribute


#1

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.


#2

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


#3

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?


#4

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


#5

On Feb 16, 12:14 am, rapdup removed_email_address@domain.invalid wrote:

do_some_test(user)
user.method(setter).call(original_val)

end

Any clean up suggestion?

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


#6

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.