Announcement (2017-05-07): www.ruby-forum.com is now read-only since I
unfortunately do not have the time to support and maintain the forum any
more. Please see rubyonrails.org/community and ruby-lang.org/en/community
for other Rails- und Ruby-related community platforms.

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

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

The only interesting thing I have to say here that hasn't been said is
that this test seems like a variation of Pair Programming, where one
person writes the tests and the other writes the code. No better way to
ensure you only write code to make a failing test pass than to know
nothing about the problem space except the most recent test failure.
class Object
def attribute(arg, &block)
name = (arg.class == Hash ? arg.keys[0] : arg)
define_method(name) do
first_access_action(arg,&block) unless instance_eval("defined? " +
"@" + name)
instance_variable_get "@" + name
end
attr_writer name
alias_method name+"?",name
end
def first_access_action(arg,&block)
name = (arg.class == Hash ? arg.keys[0] : arg)
send(name+"=",instance_eval(&block)) if block_given?
send(name+"=",arg.values[0]) if arg.class == Hash
end
end