194

我正在使用 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
4

5 回答 5

314

==================================================== ========================= 更新: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 文档中的覆盖默认访问器。

因此,您的第一种方法是在 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=

于 2012-05-05T19:26:08.320 回答
46

使用super关键字:

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

相反,要覆盖阅读器:

def attribute_name
  super.some_custom_decode
end
于 2014-02-05T16:09:31.773 回答
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

注意:对于 rails 4 中的新成员,您不需要在模型中指定attr_accessible。相反,您必须使用permit方法在控制器级别将您的属性列入白名单。

于 2014-09-10T11:50:30.267 回答
3

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

has_many :specialties

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

(这会抓取传递的数组中的前 3 个非重复条目。)

于 2013-02-25T01:26:38.333 回答
0

用于覆盖 setter attr_writer attr_writer:attribute_name

  def attribute_name=(value)
    # manipulate value
    # then send result to the default setter
    super(result)
  end
于 2017-02-22T15:23:20.807 回答