创建具有可选默认值的属性访问器
Creating attribute accessors with optional defaults
我有一个 Rails 应用程序,它经常需要设置默认属性。有时用户会为将要遵守的属性提供值,但在其他情况下,模型或用户可能希望使用默认值覆盖这些属性,而忽略原始值。
我猜想这个问题需要一个 banged (!
) 和 non-banged 方法来设置默认值,允许用户和程序切换到适当的状态。 non-banged setter 只会在它们不为 nil 时设置默认值,而 banged 版本将始终使用默认值覆盖属性。差异很小:
class BangDiBang
attr_accessor :value
def set_default
self.value ||= do_some_suff_to_determine_default_value
end
def set_default!
self.value = do_some_suff_to_determine_default_value
end
...
end
这段代码的问题是,如果我要设置一堆变量,我最终会为每个变量重复相同的代码两次。
我的问题是如何把这段代码partial出来?将逻辑保存在一个方法中,并有两个方法 set_value
和 set_value!
调用具有不同赋值运算符的中央逻辑。
我想到了一个解决方案:将中心逻辑写成文本,替换 setter 方法中的赋值操作并求值(但这感觉不对)。我怎样才能不重复自己?
您的处理方式不适用于多个参数,因为您正在调用 set_default
但未指定哪个变量。理想情况下,您需要为每个变量定义行为。有一些库可以处理这类事情,但您可以很容易地自己动手:
class Example
def self.default_param(name, default_value)
define_method("default_#{name}") { default_value }
define_method(name) do
instance_variable_get("@#{name}") || default_value
end
attr_writer name
end
default_param :foo, 'foo default'
end
ex = Example.new
ex.foo #=> "foo default"
ex.foo = 'bar'
ex.foo #=> "bar"
ex.default_foo #=> "foo default"
我重命名了 set_default
和 set_default!
以便更清楚:对于每个具有默认值的变量,创建三个方法(例如使用 foo
作为变量名称) :
foo
— returns @foo
的值如果为真,则 default_foo
否则
foo=
— 设置 @foo
的值
default_foo
— returns 指定的默认值
您可以进一步划分和整理上面的一些代码,创建一个 default_params
(复数)方法来获取哈希,将 class 宏提取到关注点:
module DefaultParams
def default_param(name, default_value)
define_method("default_#{name}") { default_value }
define_method(name) do
instance_variable_get("@#{name}") || default_value
end
attr_writer name
end
def default_params(params)
params.each { |default| default_param(*default) }
end
end
class Example
extend DefaultParams
default_params foo: 'default foo', bar: 'my favorite bar'
end
我在 coreyward 的启发下实施了一个解决方案。起初我没有意识到,我需要将默认值分离到单个方法中,而不是使用两种方法来设置默认值数组。这为应用程序的设计提供了极大的灵活性。所以非常感谢你的回答。
对于 rails 设置,我已将其添加到 application_record.rb
def set_attr_from_defaults
default_attrs.each do |atr|
eval("self.#{atr[0..atr.length-9]} ||= self.#{atr}")
end
end
def set_attr_from_defaults!
default_attrs.each do |atr|
eval("self.#{atr[0..atr.length-9]} = self.#{atr}")
end
end
def set_default_attr params
params.each { |key, value| self.define_singleton_method(key){value} }
end
def default_attrs
self.attributes.keys.map{|i| (i+'_default').to_sym} & self.methods
end
default_attrs
生成一个不包含默认属性的符号列表。 set_default_attr
定义单例方法,预期使用像 attrname_default: 'default_value' ...
这样的散列中的解析。 set_attr_from_defaults
将从默认值设置属性,当属性具有像 var: var_default
这样的一对时。数字 9
是 _default
+ 1.
的长度
我有一个 Rails 应用程序,它经常需要设置默认属性。有时用户会为将要遵守的属性提供值,但在其他情况下,模型或用户可能希望使用默认值覆盖这些属性,而忽略原始值。
我猜想这个问题需要一个 banged (!
) 和 non-banged 方法来设置默认值,允许用户和程序切换到适当的状态。 non-banged setter 只会在它们不为 nil 时设置默认值,而 banged 版本将始终使用默认值覆盖属性。差异很小:
class BangDiBang
attr_accessor :value
def set_default
self.value ||= do_some_suff_to_determine_default_value
end
def set_default!
self.value = do_some_suff_to_determine_default_value
end
...
end
这段代码的问题是,如果我要设置一堆变量,我最终会为每个变量重复相同的代码两次。
我的问题是如何把这段代码partial出来?将逻辑保存在一个方法中,并有两个方法 set_value
和 set_value!
调用具有不同赋值运算符的中央逻辑。
我想到了一个解决方案:将中心逻辑写成文本,替换 setter 方法中的赋值操作并求值(但这感觉不对)。我怎样才能不重复自己?
您的处理方式不适用于多个参数,因为您正在调用 set_default
但未指定哪个变量。理想情况下,您需要为每个变量定义行为。有一些库可以处理这类事情,但您可以很容易地自己动手:
class Example
def self.default_param(name, default_value)
define_method("default_#{name}") { default_value }
define_method(name) do
instance_variable_get("@#{name}") || default_value
end
attr_writer name
end
default_param :foo, 'foo default'
end
ex = Example.new
ex.foo #=> "foo default"
ex.foo = 'bar'
ex.foo #=> "bar"
ex.default_foo #=> "foo default"
我重命名了 set_default
和 set_default!
以便更清楚:对于每个具有默认值的变量,创建三个方法(例如使用 foo
作为变量名称) :
foo
— returns@foo
的值如果为真,则default_foo
否则foo=
— 设置@foo
的值
default_foo
— returns 指定的默认值
您可以进一步划分和整理上面的一些代码,创建一个 default_params
(复数)方法来获取哈希,将 class 宏提取到关注点:
module DefaultParams
def default_param(name, default_value)
define_method("default_#{name}") { default_value }
define_method(name) do
instance_variable_get("@#{name}") || default_value
end
attr_writer name
end
def default_params(params)
params.each { |default| default_param(*default) }
end
end
class Example
extend DefaultParams
default_params foo: 'default foo', bar: 'my favorite bar'
end
我在 coreyward 的启发下实施了一个解决方案。起初我没有意识到,我需要将默认值分离到单个方法中,而不是使用两种方法来设置默认值数组。这为应用程序的设计提供了极大的灵活性。所以非常感谢你的回答。
对于 rails 设置,我已将其添加到 application_record.rb
def set_attr_from_defaults
default_attrs.each do |atr|
eval("self.#{atr[0..atr.length-9]} ||= self.#{atr}")
end
end
def set_attr_from_defaults!
default_attrs.each do |atr|
eval("self.#{atr[0..atr.length-9]} = self.#{atr}")
end
end
def set_default_attr params
params.each { |key, value| self.define_singleton_method(key){value} }
end
def default_attrs
self.attributes.keys.map{|i| (i+'_default').to_sym} & self.methods
end
default_attrs
生成一个不包含默认属性的符号列表。 set_default_attr
定义单例方法,预期使用像 attrname_default: 'default_value' ...
这样的散列中的解析。 set_attr_from_defaults
将从默认值设置属性,当属性具有像 var: var_default
这样的一对时。数字 9
是 _default
+ 1.