------ 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--