"Dave Baldwin" <dave.baldwin / 3dlabs.com> 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.

> so with this I can write
>
>     Box {width 10}
>
> which is acceptable from the DSL view point, but makes the getter 
> operations more expensive.  In the real version a getter operation  takes 
> about 7 statements to allow for evaluation of block if the ivar  had been 
> set to a Proc object and inheritance of values from its  parent object (in 
> a visual hierarchy).  The setter operation is often  only done when the 
> object is created but the getter is done very  frequently so I want to 
> 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]