在 Ruby on Rails 中重写 setter 方法的正确方式是什么?

我使用Ruby on Rails 3.2.2,我想知道以下是一个“正确的”/“正确的”/“确定的”方法来覆盖我的类属性的setter方法。

attr_accessible :attribute_name


def attribute_name=(value)
... # Some custom operation.


self[:attribute_name] = value
end

上面的代码似乎可以正常工作。然而,我想知道,通过使用上面的代码,将来我是否会遇到问题,或者,至少,在Ruby on Rails中“我应该期望”/“可能发生”什么问题。如果这不是重写setter方法的正确方法,那么什么是正确的方法呢?


请注意:如果我使用代码

attr_accessible :attribute_name


def attribute_name=(value)
... # Some custom operation.


self.attribute_name = value
end

我得到以下错误:

SystemStackError (stack level too deep):
actionpack (3.2.2) lib/action_dispatch/middleware/reloader.rb:70
93708 次浏览

<强 >=========================================================================== 更新:2017年7月19日

现在Rails文档也建议像这样使用super:

class Model < ActiveRecord::Base


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


end

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

原来的答案

如果你想在通过模型访问时覆盖表列的setter方法,这是一种方法。

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

参见Rails文档中的覆盖默认访问器

因此,第一个方法是在Models of Ruby on Rails中覆盖列设置的正确方法。Rails已经提供了这些访问器,以作为模型的属性访问表的列。这就是我们所说的ActiveRecord ORM映射。

还要记住模型顶部的attr_accessible与访问器无关。它有一个完全不同的功能(参见这个问题)。

但是在纯Ruby中,如果你为一个类定义了访问器并且想要重写setter,你必须像这样使用实例变量:

class Person
attr_accessor :name
end


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

一旦你知道attr_accessor是做什么的,这将更容易理解。代码attr_accessor :name等价于这两个方法(getter和setter)

def name # getter
@name
end


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

你的第二个方法也会失败,因为它会导致一个无限循环,因为你在该方法中调用相同的方法attribute_name=

我发现(至少对于ActiveRecord关系集合)以下模式是有效的:

has_many :specialties


def specialty_ids=(values)
super values.uniq.first(3)
end

(这将获取传递数组中的前3个非重复项。)

使用super关键字:

def attribute_name=(value)
super(value.some_custom_encode)
end

相反,要覆盖读者:

def attribute_name
super.some_custom_decode
end

在铁轨上

假设你的表中有年龄属性

def age=(dob)
now = Time.now.utc.to_date
age = now.year - dob.year - ((now.month > dob.month || (now.month == dob.month && now.day >= dob.day)) ? 0 : 1)
super(age) #must add this otherwise you need to add this thing and place the value which you want to save.
end
< p >注意: 对于rails 4的新手,你不需要在model中指定attr_accessible。相反,你必须在控制器级别使用许可证方法来白名单你的属性

使用attr_writer覆盖setter attr_writer: attribute_name < / p >

  def attribute_name=(value)
# manipulate value
# then send result to the default setter
super(result)
end