在 Ruby on Rails 中重写 setter 方法的正确方法是什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10464793/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What is the right way to override a setter method in Ruby on Rails?
提问by Backo
I am using Ruby on Rails 3.2.2 and I would like to know if the following is a "proper"/"correct"/"sure" way to override a setter method for a my class attribute.
我正在使用 Ruby on Rails 3.2.2,我想知道以下是否是覆盖我的类属性的 setter 方法的“正确”/“正确”/“确定”方法。
attr_accessible :attribute_name
def attribute_name=(value)
... # Some custom operation.
self[:attribute_name] = value
end
The above code seems to work as expected. However, I would like to know if, by using the above code, in future I will have problems or, at least, what problems "should I expect"/"could happen" with Ruby on Rails. If that isn't the right way to override a setter method, what is the right way?
上面的代码似乎按预期工作。但是,我想知道,通过使用上面的代码,将来我是否会遇到问题,或者至少,Ruby on Rails 会出现什么问题“我应该期待”/“可能会发生”。如果这不是覆盖 setter 方法的正确方法,那么正确的方法是什么?
Note: If I use the code
注意:如果我使用代码
attr_accessible :attribute_name
def attribute_name=(value)
... # Some custom operation.
self.attribute_name = value
end
I get the following error:
我收到以下错误:
SystemStackError (stack level too deep):
actionpack (3.2.2) lib/action_dispatch/middleware/reloader.rb:70
回答by rubyprince
===========================================================================Update: July 19, 2017
================================================== ==========================更新:2017 年 7 月 19 日
Now the Rails documentationis also suggesting to use superlike this:
现在Rails 文档也建议super像这样使用:
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.
如果您想在通过模型访问时覆盖表列的 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
See Overriding default accessorsin the Rails documentation.
请参阅Rails 文档中的覆盖默认访问器。
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.
因此,您的第一种方法是在 Ruby on Rails 模型中覆盖列设置器的正确方法。Rails 已经提供了这些访问器来访问表的列作为模型的属性。这就是我们所说的 ActiveRecord ORM 映射。
Also keep in mind that the attr_accessibleat the top of the model has nothing to do with accessors.It has a completely different functionlity (see this question)
还要记住,attr_accessible模型顶部的 与访问器无关。它具有完全不同的功能(请参阅此问题)
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:
但是在纯 Ruby 中,如果您为类定义了访问器并想要覆盖 setter,则必须像这样使用实例变量:
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_accessordoes. The code attr_accessor :nameis equivalent to these two methods (getter and setter)
一旦你知道这是什么attr_accessor,这将更容易理解。代码attr_accessor :name等价于这两个方法(getter和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.
您的第二种方法也失败了,因为当您attribute_name=在该方法中调用相同的方法时,它会导致无限循环。
回答by Robert Kajic
Use the superkeyword:
使用super关键字:
def attribute_name=(value)
super(value.some_custom_encode)
end
Conversely, to override the reader:
相反,要覆盖阅读器:
def attribute_name
super.some_custom_decode
end
回答by Taimoor Changaiz
In rails 4
在轨道 4
let say you have ageattribute in your table
假设您的表中有年龄属性
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
Note: For new comers in rails 4 you don't need to specify attr_accessiblein model. Instead you have to white-list your attributes at controller level using permitmethod.
注意:对于 rails 4 中的新人,您不需要在模型中指定attr_accessible。相反,您必须使用permit方法在控制器级别将您的属性列入白名单。
回答by Robin Daugherty
I have found that (at least for ActiveRecord relationship collections) the following pattern works:
我发现(至少对于 ActiveRecord 关系集合)以下模式有效:
has_many :specialties
def specialty_ids=(values)
super values.uniq.first(3)
end
(This grabs the first 3 non-duplicate entries in the array passed.)
(这会获取传递的数组中的前 3 个非重复条目。)
回答by bananaappletw
Using attr_writerto overwrite setter
attr_writer :attribute_name
利用attr_writer覆盖二传手attr_writer:ATTRIBUTE_NAME
def attribute_name=(value)
# manipulate value
# then send result to the default setter
super(result)
end

