Overriding attribute getters in ActiveRecord models

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