我想给我的一个模型一个属性访问器,默认为八个零的数组。这是我尝试的第一个语法:
attr_accessor_with_default:weekly_magnitude_list, [0,0,0,0,0,0,0,0]
上面没有达到我的预期,因为模型的所有实例最终共享同一个Array对象。引用我的博客(http://barelyenough.org/blog/2007/09/things-to-be-suspicious-of-attr_accessor_with_default-with-a-collection/)提出了不同的语法,基本上将默认值包装在一个块中。
attr_accessor_with_default(:weekly_magnitude_list) {[0,0,0,0,0,0,0,0]}
这不起作用(对我来说,在Rails 3中)。每当我调用访问器时,我似乎都会得到一个全新的Array对象。这实际上意味着我无法写信。
有人知道这样做的正确方法吗?
为了您的荣幸,我已经包含了一个简单测试的输出,证明了这一点:
class Container
attr_accessor_with_default :naive_collection, [0,0]
attr_accessor_with_default(:block_collection) {[0,0]}
end
> c = Container.new
=> #<Container:0x7f3610f717a8>
> c.naive_collection[0] = "foo"
=> "foo"
> Container.new.naive_collection
=> ["foo", 0]
# expected [0,0]
> c.block_collection[0] = "foo"
=> "foo"
> c.block_collection
=> [0, 0]
# expected ["foo", 0]
答案 0 :(得分:1)
我在遇到同样的问题时偶然发现了这个问题。
作为参考,docs指定在实例范围内动态评估块形式。继续这个例子,它的用处非常有限,但至少按照你期望的方式工作:
class Container
attr_accessor_with_default(:block_collection) { name.underscore }
end
> c = Container.new(:name => "TestName")
> c.block_collection # => "test_name"
> c.block_collection = "something else" # => "something else"
> c.name => "TestName"
这是真正古怪的部分,但是......
class Container
attr_accessor_with_default :naive_collection, [0, 0]
end
# This works as expected
> c = Container.new
> c.naive_collection = ["foo", "bar"] # => ["foo", "bar"]
> Container.new.naive_collection # => [0, 0]
> c.naive_collection[0] = 0 # => [0, "bar"]
> Container.new.naive_collection # => [0, 0]
# But this doesn't
> c2 = Container.new
> c2.naive_collection # => [0, 0]
> c2.naive_collection[0] = "problem!" # => ["problem!", 0]
> Container.new.naive_collection # => ["problem!", 0]
深入挖掘源代码,我看到attr_accessor_with_default定义了一个返回默认值或执行块的实例方法。哪个没问题。
然后继续在module_eval中执行它:
def #{sym}=(value)
class << self;
attr_reader :#{sym}
end
@#{sym} = value
end
这简直令人费解。如果我无法弄清楚这种行为的动机,我最终可能会将此作为一个错误。
<强>更新强>
我设法弄清楚这里出了什么问题。
def attr_accessor_with_default(sym, default = Proc.new)
define_method(sym, block_given? ? default : Proc.new { default })
module_eval(<<-EVAL, __FILE__, __LINE__ + 1)
def #{sym}=(value)
class << self; attr_accessor :#{sym} end
@#{sym} = value
end
EVAL
end
最初,默认值作为proc存在。 一旦调用setter,getter和setter方法将被attr_accessor方法覆盖,并初始化实例变量。 问题是getter中的默认proc返回类级别的默认值。所以,当你做类似的事情时:
> c2.naive_collection[0] = "problem!" # => ["problem!", 0]
您实际上正在更改班级的默认值。
我认为这种方法可能应该实现为:
class Module
def attr_accessor_with_default(sym, default = Proc.new)
module_eval(<<-EVAL, __FILE__, __LINE__ + 1)
def #{sym}
class << self; attr_reader :#{sym} end
@#{sym} = #{ default.dup }
end
def #{sym}=(value)
class << self; attr_accessor :#{sym} end
@#{sym} = value
end
EVAL
end
end
我会打票并提供补丁。
再次更新 https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/6496