You define the method initialize
to call old_initialize
, but then you alias_method
it to old_initialize
, so old_initialize
will call old_initialize
will call old_initialize
will call old_initialize
will call old_initialize
will call old_initialize
will call old_initialize
will call old_initialize
will call old_initialize
will call old_initialize
will call old_initialize
will call old_initialize
will call old_initialize
will call old_initialize
will call old_initialize
will call old_initialize
will call old_initialize
will call old_initialize
will call old_initialize
will call old_initialize
will call old_initialize
will call old_initialize
will call old_initialize
will call old_initialize
will call old_initialize
will call old_initialize
will call old_initialize
will call old_initialize
will call old_initialize
will call old_initialize
will call old_initialize
will call old_initialize
will call old_initialize
…
I wrote a rather lengthy article about the "right" way to call an old version of an overwritten method here:
The best way to do this is to simply not do it at all. Don't overwrite methods, override them. Ruby has inheritance, use it:
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]
We can make this a little nicer by assigning the mixins to constants so they get proper names, and amending the API so that one can create multiple accessors in one go:
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]