Overriding default ActiveRecord getter and setter

Hi,

I have two ActiveRecord classes, Player and User. Both underlying
tables have a column called email.

class User < ActiveRecord::Base
end

class Player < ActiveRecord::Base
belongs_to :user
end

If the player instance’s user attribute is not nil I want any reference
to player.email get or set player.user.email - so I tried this…

class Player < ActiveRecord::Base
belongs_to :user
def email
return user.email if !(user.nil? || user.empty?)
end
end

… but it doesn’t work. When I retreive a relevant player object and
try player.email I still get the email address from the player rather
than the user.

Any ideas where I am going wrong?

Martin

On 12/1/06, linus1412 [email protected] wrote:

belongs_to :user
end

Martin,

Try this:

def email
user ? user.email : self[:email]
end


Zack C.
http://depixelate.com
http://trackplace.com

Zack,

I added…

def email
user ? user.email : self[:email]
end

def email=(email)
(user ? user : self).email = email
end

This still doesn’t work, it still brings back the player.email value.

Strangely it does work in console.

Martin

On 12/1/06, linus1412 [email protected] wrote:

is concerned.

    user ? user.email : self[:email]

Martin

class User < ActiveRecord::Base
belongs_to :user

Martin,

p.email => “[email protected]” # what i want

This is good.

p[:email] => “[email protected]” # not what i want

This is not good because it bypasses the accessor that you just wrote.
The reason is ActiveRecord::Base overrides [] like this:

  def [](attr_name)
    read_attribute(attr_name)
  end

… so it reads the actual column data. Use the first approach.

Hope this helps.


Zack C.
http://depixelate.com
http://trackplace.com

I can reproduce the same behaviour in console by doing

p = Player.find 4
p.email => “[email protected]” # what i want
p[:email] => “[email protected]” # not what i want

So are forms populated using second approach?

If so this seems to negate accessor overloading as far as form handling
is concerned.

Martin

I think I may have found an answer to this, but it doesn’t seem write.

If I override the player_before_type_cast and player_before_type_cast=
methods it works, as this is what the form calls to get the values to
display?

This works, but its it ‘right’?

Martin

On 4 December 2006 03:27, linus1412 wrote:

I think I may have found an answer to this, but it doesn’t seem write.

If I override the player_before_type_cast and player_before_type_cast=
methods it works, as this is what the form calls to get the values to
display?

This works, but its it ‘right’?
The problem is that text_field uses *_before_type_cast accessors to get
data.
I see two solutions:

  1. overriding *_before_type_cast accessor (as you described)
  2. adding virtual field (that has no underlying column). E.g. in your
    case
    this could be done like this:

class Player
def user_or_player_email
return user.email if user
self.email
end
def user_or_player_email=(value)
self.email = value
end
end

The trick here is that text_field will use plain value (not
_before_type_cast
variant) if attribute doesn’t support _before_type_cast accessor.

Although, I would prefer using second variant.

You could rename players.email column to something like “custom_email”
or “player_email” and use “email” as the name of virtual attribute.

Zack,

In console I have the choice to use p.email, but when my form displays
it is still showing the data from the player object, bypassing my
overridden method.

I assume that <%= text_field ‘player’, ‘email’ %> must effectively
still call read_attribute(attr_name) rather than use the accessor.

Martin

The other thing is that the various accessor methods are added
dynamically when they are first called, which probably means they
overwrite the email method you have defined.

Fred

On 4 December 2006 13:44, Frederick C. wrote:

The other thing is that the various accessor methods are added
dynamically when they are first called, which probably means they
overwrite the email method you have defined.

No, dynamically generated accessors for DB columns do not overwrite
existing
accessors. Dynamic accessor don’t get generated if there is method with
the
same name already.