Forum: Ruby on Rails Referencing a non-ActiveRecord attribute

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
rapdup (Guest)
on 2009-02-16 01:54
(Received via mailing list)
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 B. (Guest)
on 2009-02-16 05:51
(Received via mailing list)
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 (Guest)
on 2009-02-16 06:39
(Received via mailing list)
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 (Guest)
on 2009-02-16 07:16
(Received via mailing list)
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 B. (Guest)
on 2009-02-16 07:44
(Received via mailing list)
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
Jeff E. (Guest)
on 2009-02-17 03:38
(Received via mailing list)
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
This topic is locked and can not be replied to.