“Dave B.” [email protected] wrote
Having to prefix the ivar with self. makes the DSL look clunky
I agree, though some experienced Rubyists don’t see it that way.
move down the route of separate setters and getters rather than combining
them in the one method as an optimization.
If you have some guarantees about the ordering of getters and setters,
you
can dynamically (re)define the getter and setter at the instance level.
The
attached worked for the case of instance variables being initialized
only
once, but being read at any time (the context for this was some kind of
lazy
creation of an object graph including forward references). Perhaps some
bits
of it will apply to your case.
class Object
def singleton_class
class << self; self; end
end
end
module LazyForwardRef
UNDEFINED = nil
def forward_ref(attr)
getter = attr
setter = (attr.to_s + ‘=’).intern
ivar = (‘@’ + attr.to_s).intern
suspension = lambda { self.send getter }
self.singleton_class.send :define_method, getter, lambda {
v = instance_variable_get ivar
if v == UNDEFINED
suspension
elsif v.is_a?(Proc)
current = v.call
self.send(setter, current) if current != v
current
else v
end
}
self.singleton_class.send :define_method, setter, lambda {|val|
self.instance_variable_set ivar, val
}
end
end
require ‘pp’
x = Object.new
x.extend LazyForwardRef
begin
p x.foo
rescue NoMethodError => detail
pp detail
pp [LINE, x, x.methods.sort - Object.instance_methods]
end
x.forward_ref(:foo)
pp [LINE, x, x.methods.sort - Object.instance_methods]
x.foo
pp [LINE, x, x.foo]
x.foo = ‘Something else’
pp [LINE, x, x.foo]
y = Object.new
y.extend LazyForwardRef
y.forward_ref(:bar)
pp [LINE, y, y.methods.sort - Object.instance_methods]
z = Object.new
z.extend LazyForwardRef
z.forward_ref(:baz)
y.bar = z.baz
pp [LINE, x, y, z, x.foo, y.bar, z.baz, x.foo==z.baz]
x.foo = y.bar
pp [LINE, x, y, x.foo, y.bar]
z.baz= 10
pp [LINE, x, y, z]
pp [LINE, x.foo, y.bar, z.baz]
pp [LINE, x, y, z]