I'm trying to do subj, but getting wrong results. Let's consider a
model Person with one attrubute "full_name". I wrote the following
code to compose full name of first name and last name:
class Person < ActiveRecord::Base
attr_accessor :first_name
attr_accessor :last_name
def full_name
unless first_name.nil? || last_name.nil?
"#{first_name} #{last_name}"
else
super
end
end
end
Such "overriding" does not work while persisting. Tests are better
than any words:
def test_full_name_through_first_and_last_names [SUCCESS]
person = Person.new(:first_name => "John", :last_name => "Smith")
assert_equal "John Smith", person.full_name
end
def test_full_name [SUCCESS]
person = Person.new(:full_name => "John Smith")
assert_equal "John Smith", person.full_name
end
def test_full_name_persisting [! FAILED !]
person = Person.create!(:first_name => "John", :last_name =>
"Smith")
assert_equal "John Smith", Person.find(person.id).full_name
end
Can somebody please explain mechanism of attributes getters and
setters? How and when they are injected into the model class? Why
don't Rails use them when getting data to persist? And how to override
them?
Sorry, I couldn't find correspondent code in Rails sources.
I revealed another interesting fact: database row contains just "
" (space) as the "full_name" attribute. It means that somehow
last_name and first_name are EMPTY when ActiveRecord calling full_name
method. WTF?!
Well the key thing is that when you set first_name or last_name all
that is happening is an instance variable is being set. Nowhere are
you propagating that to fullname.
You might also run into problems because attribute accessor methods
are only generated on the fly.
Fred
Thanks, Fred, you are right, I found place in the Rails code where
attributes are reading and they are using _instance_variable_, not a
"getter" method:
def read_attribute(attr_name)
attr_name = attr_name.to_s
if !(value = @attributes[attr_name]).nil? <--- that's it
......
as you see, applying here to instance variable avoids any attempts of
overriding attribute reading procedure.
Thanks, Fred, you are right, I found place in the Rails code where
attributes are reading and they are using _instance_variable_, not a
"getter" method:
def read_attribute(attr_name)
attr_name = attr_name.to_s
if !(value = @attributes[attr_name]).nil? <--- that's it
......
Yup that's it, attributes are stored in @attributes
as you see, applying here to instance variable avoids any attempts of
overriding attribute reading procedure.
Not sure what you mean there. It's still perfectly possible to overwrite accessors.
Fred
I solved problem by setting instance variable in before_save:
class Person < ActiveRecord::Base
attr_accessor :first_name
attr_accessor :last_name
def full_name
unless first_name.nil? || last_name.nil?
"#{first_name} #{last_name}"
else
super
end
end
before_save :set_full_name
def set_full_name
self.full_name = full_name
end
end