我正在尝试编写自定义attr_accessor
。
这将收到一个块,并将其结果分配给稍后将在初始化中访问的变量。
class Object
def custom_attr_accessor(attr)
alias_method :old_initialize, :initialize
define_method "initialize" do
old_initialize
instance_variable_set "@#{attr}", yield
end
define_method "#{attr}" do
instance_variable_get "@#{attr}"
end
end
end
class Foo
custom_attr_accessor :foo do
"foo"
end
custom_attr_accessor :bar do
"bar"
end
end
# f = Foo.new
# puts f.foo
# => "foo"
# puts f.bar
# => "bar"
但我得到了
stack level too deep (SystemStackError)
无论如何,当班级使用一个custom_attr_accessor
时,它会按预期工作。
答案 0 :(得分:2)
您定义方法initialize
以调用old_initialize
,然后将alias_method
定义为old_initialize
,以便old_initialize
将old_initialize
调用old_initialize
致电old_initialize
将致电old_initialize
致电old_initialize
将致电old_initialize
致电old_initialize
致电old_initialize
将致电old_initialize
致电old_initialize
old_initialize
将致电old_initialize
致电old_initialize
致电old_initialize
致电old_initialize
致电old_initialize
致电old_initialize
致电old_initialize
{1}}将致电old_initialize
致电old_initialize
致电old_initialize
致电old_initialize
致电old_initialize
致电old_initialize
致电old_initialize
1}}将致电old_initialize
致电old_initialize
将致电old_initialize
致电old_initialize
致电old_initialize
将致电old_initialize
致电{{1}调用old_initialize
会调用class Module
def custom_attr_accessor(attr)
attr_reader attr
prepend(Module.new do
define_method(:initialize) do |*args|
super(*args)
instance_variable_set(:"@#{attr}", yield)
end
end)
end
end
class Foo
custom_attr_accessor :foo do 'foo' end
custom_attr_accessor :bar do 'bar' end
end
# It works:
Foo.new
# => #<Foo:0xdeadbeef081542 @foo='foo', @bar='bar'>
# How it works:
Foo.ancestors
# => [#<Module:0xdeadbeef081523>,
# #<Module:0xdeadbeef081524>,
# Foo,
# Object,
# Kernel,
# BasicObject]
...
我写了一篇相当冗长的关于&#34;权利&#34;调用旧版本的覆盖方法here:
的方法这样做的最好方法就是根本不做。不要覆盖方法,覆盖它们。 Ruby有继承,使用它:
class Module
def custom_attr_accessor(attr=(no_attr = true), **attr_specs, &blk)
attr_specs[attr] = blk unless no_attr
attr_specs.each do |attr, blk|
attr_reader attr
prepend CustomAttrAccessor.(attr, &blk)
end
end
end
module CustomAttrAccessor
def self.call(attr)
m = Module.new do
define_method(:initialize) do |*args|
super(*args)
instance_variable_set(:"@#{attr}", yield)
end
end
const_set(:"CustomAttrAccessor_#{attr}_#{m.object_id}", m)
end
end
class Foo
custom_attr_accessor :foo do 'foo' end
custom_attr_accessor :bar do 'bar' end
end
# It works:
Foo.new
# => #<Foo:0xdeadbeef081542 @foo='foo', @bar='bar'>
# How it works:
Foo.ancestors
# => [CustomAttrAccessor::CustomAttrAccessor_bar_48151623420020,
# CustomAttrAccessor::CustomAttrAccessor_foo_48151623420010,
# Foo,
# Object,
# Kernel,
# BasicObject]
class Bar
custom_attr_accessor :foo do 'FOO' end
custom_attr_accessor :bar do 'BAR' end
custom_attr_accessor baz: -> { 'BAZ' }, qux: -> { 'QUX' }
end
# It works:
Bar.new
# => #<Bar:0xdeadbeef081542 @foo='FOO', @bar='BAR' @baz='BAZ', @qux='QUX'>
# How it works:
Bar.ancestors
# => [CustomAttrAccessor::CustomAttrAccessor_qux_48151623420060,
# CustomAttrAccessor::CustomAttrAccessor_baz_48151623420050,
# CustomAttrAccessor::CustomAttrAccessor_bar_48151623420040,
# CustomAttrAccessor::CustomAttrAccessor_foo_48151623420030,
# Bar,
# Object,
# Kernel,
# BasicObject]
我们可以通过将mixins分配给常量以使它们获得正确的名称,并修改API以便可以一次创建多个访问者来使这更好一点:
{{1}}
答案 1 :(得分:0)
你是否寻找这样的东西:
class Object
def custom_attr_accessor(attr)
define_method "#{attr}=".to_sym do |val|
instance_variable_set("@#{attr}", val)
end
define_method attr do
instance_variable_get("@#{attr}") || yield
end
end
end
class Foo
custom_attr_accessor :foo do
"foo"
end
custom_attr_accessor :bar do
"bar"
end
end
f = Foo.new
puts f.foo #=> foo
f.foo = 1
puts f.foo #=> 1
puts f.bar #=> bar
f.bar = 2
puts f.bar #=> 2