From: Justin Bailey Date: 2006-02-21T01:44:06+09:00 Subject: Re: [SOLUTION] metakoans.rb (#67) ------=_Part_11337_28330408.1140453827652 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable Content-Disposition: inline I came up with a solution that involved creating a default initializer method for each attribute set. Great quiz! class Object def attribute(arg, &block) # Determine if we have an initial value for the attribute or not, and put # them all into a common form. if arg.is_a?(Hash) props =3D arg.collect { |k, v| k } else # Note this sets the initial value to nil, which is a no-op below, if # no block was specified. props =3D [arg] arg =3D { arg =3D> block } end props.each do |p| instance_var, init_meth =3D "@#{p}".to_sym, "#{p}_init".to_sym if (val =3D arg[p]) # set up initializer methods for block or given value. Note a # method is created for each attribute given that has a value associated self.instance_eval do if val.is_a?(Proc) define_method init_meth, val else define_method(init_meth) { val } end # set visibility private init_meth end end # define attribute accessor methods class_eval do attr_writer p.to_sym # for first time access, look to appropriate init method, if any an= d # get value. In either case, the instance_variable will be defined after # this method if it wasn't before. define_method(p.to_sym) do unless x =3D instance_variable_get(instance_var) || val.nil? instance_variable_set(instance_var, x =3D self.send(init_meth)) end x end # Define query accessor. Only returns true if the instance variable is defined, # regardless of its value. define_method("#{p}?") do ! instance_variable_get(instance_var).nil? end end end end end On 2/20/06, Greg Millam wrote: > > As with everyone, it seems, some more twiddling and playing with this > yielded me a better, faster, and smaller solution. > > This also beats my prior solution in that a? returns only true or false, > and variables are kept with the object rather than in a hash with the > object as a key. The reader methods also won't take an argument now. > (They did before.) > > 9 lines with a little golfing: (8 if you ditch the line for multiple > definitions, which isn't needed for passing the koans). > > It's pretty much the same as most of the other solutions out there, now. > > def attribute(arg,*rest,&b) > attribute(*rest,&b) if rest.any? # Allow multiple definitions. > n =3D (arg.is_a?(Hash) ? arg.keys[0] : arg).to_s > b ||=3D lambda { arg.is_a?(Hash) ? arg[n] : nil } > attr_writer n > define_method(n) { instance_variables.include?('@'+n) ? \ > instance_variable_get('@'+n) : instance_eval(&b) } > define_method(n+'?') { ! [nil,false].include?(send(n)) } > end > > Blast it, Ara, how do I stop myself from doing more and more tweaking? ;) > > - Greg > > ------=_Part_11337_28330408.1140453827652--