How to Override a Setter Method in Ruby on Rails

What is the right way to override a setter method in Ruby on Rails?

===========================================================================
Update: July 19, 2017

Now the Rails documentation is also suggesting to use super like this:

class Model < ActiveRecord::Base

def attribute_name=(value)
# custom actions
###
super(value)
end

end

===========================================================================

Original Answer

If you want to override the setter methods for columns of a table while accessing through models, this is the way to do it.

class Model < ActiveRecord::Base
attr_accessible :attribute_name

def attribute_name=(value)
# custom actions
###
write_attribute(:attribute_name, value)
# this is same as self[:attribute_name] = value
end

end

See Overriding default accessors in the Rails documentation.

So, your first method is the correct way to override column setters in Models of Ruby on Rails. These accessors are already provided by Rails to access the columns of the table as attributes of the model. This is what we call ActiveRecord ORM mapping.

Also keep in mind that the attr_accessible at the top of the model has nothing to do with accessors. It has a completely different functionlity (see this question)

But in pure Ruby, if you have defined accessors for a class and want to override the setter, you have to make use of instance variable like this:

class Person
attr_accessor :name
end

class NewPerson < Person
def name=(value)
# do something
@name = value
end
end

This will be easier to understand once you know what attr_accessor does. The code attr_accessor :name is equivalent to these two methods (getter and setter)

def name # getter
@name
end

def name=(value) # setter
@name = value
end

Also your second method fails because it will cause an infinite loop as you are calling the same method attribute_name= inside that method.

Override setter doesn't work with update_attributes

Your setter is passed the values as they are passed to update_attributes. In particular when this is triggered by a form submission (and assuming you are using the regular rails form helpers) f will actually be "0" or "1", so the comparison with true will always be false.

The easiest thing would be to check the value of finished? after the first call to write_attribute, so that rails can convert the submitted value to true/false. It's also unrubyish to do == true - this will break if the thing you are testing returns a truthy value rather than actually true (for example =~ on strings returns an integer when there is a match)

How to override a dynamically created setter method at runtime in Ruby?

Instead of using the default getter and setter:

singleton_class.send(:attr_accessor, name)

I suggest using a custom setter:

singleton_class.send(:attr_reader, name)
define_singleton_method("#{name}=") do |value|
if value.is_a?(Hash)
instance_variable_set("@#{name}", Config.new(value))
else
instance_variable_set("@#{name}", value)
end
end
public_send("#{name}=", value)

Ruby, how to overwrite the setter (=) method to allow 2 parameters?

As @eugen correctly says, you cannot pass two arguments to a setter like

self.my_vector = 1, 2

The nearest thing you can achieve is to unpack the argument using pattern matching:

def myvector=(arg)
arg => [x, y]
@my_vector = Vector2d.new(x, y)
end

foo.my_vector = [1, 2]

An alternative is to define a simple helper method v so you can write

foo.my_vector = v 1, 2


Related Topics



Leave a reply



Submit