在Ruby on Rails中覆盖setter方法的正确方法是什么?

Bac*_*cko 172 ruby setter overriding ruby-on-rails ruby-on-rails-3

我正在使用Ruby on Rails 3.2.2,我想知道以下是否是一个"正确"/"正确"/"确定"的方式来覆盖我的类属性的setter方法.

attr_accessible :attribute_name

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

  self[:attribute_name] = value
end
Run Code Online (Sandbox Code Playgroud)

上面的代码似乎按预期工作.但是,我想知道,通过使用上面的代码,将来我会遇到问题,或者至少,我应该在Ruby on Rails上遇到"我应该期待"/"可能发生"的问题.如果这不是覆盖setter方法的正确方法,那么正确的方法是什么?


注意:如果我使用代码

attr_accessible :attribute_name

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

  self.attribute_name = value
end
Run Code Online (Sandbox Code Playgroud)

我收到以下错误:

SystemStackError (stack level too deep):
  actionpack (3.2.2) lib/action_dispatch/middleware/reloader.rb:70
Run Code Online (Sandbox Code Playgroud)

rub*_*nce 281

================================================== ========================= 更新:2017年7月19日

现在Rails文档也建议使用super如下:

class Model < ActiveRecord::Base

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

end
Run Code Online (Sandbox Code Playgroud)

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

原始答案

如果要在通过模型访问时覆盖表的列的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
Run Code Online (Sandbox Code Playgroud)

请参阅Rails文档中的覆盖默认访问器.

因此,您的第一个方法是在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
Run Code Online (Sandbox Code Playgroud)

一旦你知道什么attr_accessor,这将更容易理解.代码attr_accessor :name相当于这两个方法(getter和setter)

def name # getter
  @name
end

def name=(value) #  setter
  @name = value
end
Run Code Online (Sandbox Code Playgroud)

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

  • 为什么不叫`super`? (10认同)
  • 对于Rails 4,只需跳过`attr_accessible`,因为它不再存在,它应该可以工作 (9认同)
  • 有一个巨大的问题与```write_attribute```.转换将被跳过.请注意,```write_attribute```将跳过带有日期的时区转换,这几乎总是不受欢迎的. (4认同)
  • 超级也会工作但是有一些原因你可能不想要我们.例如,在mongoid gem中有一个错误,如果超级getter方法,则无法推送到数组.因为有办法在内存中管理数组是bug.此外,@ name还将返回值集,而不是调用覆盖的方法.但是在上面的解决方案中,两者都可以正常工作. (2认同)

Rob*_*jic 42

使用super关键字:

def attribute_name=(value)
  super(value.some_custom_encode)
end
Run Code Online (Sandbox Code Playgroud)

相反,要覆盖读者:

def attribute_name
  super.some_custom_decode
end
Run Code Online (Sandbox Code Playgroud)


Tai*_*aiz 16

在铁轨4

假设你的表中有年龄属性

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
Run Code Online (Sandbox Code Playgroud)

注意:对于rails 4中的新角色,您无需在模型中指定attr_accessible.相反,您必须使用permit方法在控制器级别列出您的属性.