------art_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 = 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 = [arg]
      arg = { arg => block }
    end

    props.each do |p|
      instance_var, init_meth = "@#{p}".to_sym, "#{p}_init".to_sym

      if (val = 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 and
        # 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 = instance_variable_get(instance_var) || val.nil?
            instance_variable_set(instance_var, x = 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 <ruby-talk / lethalcode.net> 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    = (arg.is_a?(Hash) ? arg.keys[0] : arg).to_s
>    b  ||= 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
>
>

------art_11337_28330408.1140453827652--