From: ara.t.howard@... Date: 2006-03-22T00:44:46+09:00 Subject: Re: Initializing ivars from block without prefixing with self? On Tue, 21 Mar 2006, Dave Baldwin wrote: > I am creating a DSL using Ruby. A cut down version is below: > > class _Box > attr_accessor :width > def initialize (&block) > self.width = 20 # some default value > instance_eval(&block) if block_given? > end > end > > # Helper function to avoid the DSL user having to use .new > def Box(&block) > _Box.new(&block) > end > > I want to do something like: > > Box {width = 10} > > but the only way I can get it to work is > > Box {self.width = 10} > > Having to prefix the ivar with self. makes the DSL look clunky and wouldn't > be acceptable to my users. > > Is there anyway to avoid the self.? Using {@width = 10} will work but > prevents some necessary validation from taking place. > > > I have an alternative where width is a method: > > class _Box > def width (val = :no_param) > if val != :no_param > @width = val > end > @width > end > end > > 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. here is one simple way: harp:~ > cat a.rb class OpenStruct alias_method "__eval__", "instance_eval" alias_method "__set__", "instance_variable_set" alias_method "__get__", "instance_variable_get" instance_methods.each{ |m| undef_method m unless m =~ /^__/ } def initialize(&block) __eval__ &block end def method_missing m, *a, &b m = m.to_s.delete "=?" a.size == 0 ? __get__("@#{ m }") : __set__("@#{ m }", a.shift) end end module Initializable def initialize &block os = OpenStruct.new &block attributes.each{|a| instance_variable_set "@#{ a }", os.__send__(a)} end def to_s require "yaml" attributes.inject({}){|h,a| h.update a => send(a)}.to_yaml end def attributes self.class::ATTRIBUTES end end class Box ATTRIBUTES = %w[ width height ] ATTRIBUTES.each{|a| attr a} include Initializable end def Box(*a, &b) Box.new(*a, &b) end puts Box { width 42 height 42 } harp:~ > ruby a.rb --- height: 42 width: 42 the idea is to initialize another object, the OpenStruct in this case, and then relay the properties. this object can have any 'slow' but convenient behaviour your desire, leaving the Box class to have normal 'fast' attributes. hth. -a -- share your knowledge. it's a way to achieve immortality. - h.h. the 14th dali lama