Forum: Ruby metakoans.rb (#67)

Announcement (2017-05-07): is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see and for other Rails- und Ruby-related community platforms.
4299e35bacef054df40583da2d51edea?d=identicon&s=25 James Gray (bbazzarrakk)
on 2006-02-23 14:55
(Received via mailing list)
This was a wildly popular quiz with solutions all over the board.  I
will do my
best to hit the highlights below...

	Where to Define

Probably the first question that needs answering when meta-programming
is, where
do I stick this method?  Some defined it at the top-level (creating a
method on Object), others reopened Object to stick it inside, and a
third group
defined a method in Module.  All three would satisfy the tests.  The
question is, how do we know which one is best?

I used the extremely unscientific method of copy Ruby.  The core method
is defined in Module, so it seemed like attribute() belonged there too.
Bamford had much more intelligent reasoning.  Here is Ross's response to
Shelly's question about why people were using Module when Object works:

	Well, you're extending Object with a method that relies on methods
	defined in Module (class_eval), and your method is available in places
	where it really didn't ought to be.

	[snip quoted solution]

	attribute :oops

	-:7:in `attribute': undefined method `class_eval' for main:Object
	        from -:3:in `attribute'
	        from -:15

	By defining an instance method on Module (from which Class inherits)
	ensure that a) class_eval is there, and b) attribute can be called only
	in a module/class definition context.

That convinced me.  Now that we know where it goes, let's break down a

	A Common Approach

Here's a solution, similar to many submitted, by Christian Neukirchen:

	class Module
	  def attribute(a, &block)
	    if a.kind_of? Hash
	      a, default = a.to_a.first
	      default = nil

	    a = a.to_sym
	    ivar = "@#{a}"

	    define_method(a) {
	      if instance_variables.include? ivar
	        instance_variable_get ivar
	        block ? instance_eval(&block) : default
	    define_method("#{a}=") { |v| instance_variable_set ivar, v }
	    define_method("#{a}?") { !!__send__(a) }

The if statement at the beginning of the method handles the case where a
argument is, or isn't, provided.  If the first argument to attribute()
was a
Hash, a default was given and it is extracted here.  Note that this
just pulls a single pair from the Hash.  That's all the tests required,
but many
chose to support declaring multiple arguments at once as we will see in
a bit.
If a default was not provided, it is set to nil.

The next two lines place the attribute method and instance variable
names in
local variables for future use.

The last chunk of code defines the three methods attribute() is expected
create.  Note that they are built using define_method(), which takes the
name of
the method to define and a block of code to execute as the method body.
remember, Ruby's blocks are closures and thus have access to the local
defined in the scope where they are created.  That's the key to this
kind of
solution, as the code needs to access things like the default argument
the provided block.

The first method, the attribute getter, checks to see if the
instance_variables() of the class include?() our new attribute.  In
other words,
this is a check to see if the variable has been set yet.  If it has, the
value is retrieved and returned with a call to instance_variable_get().
If the
variable is not set, the code executes a block, if provided, or uses the
(which may have been set to nil earlier).  The tests required that the
have access to the instance though, so the block is instance_eval()ed

The second method, the setter, is trivial.  Just stuff the new value in
variable with instance_variable_set().

The third and final method, for querying, uses a clever trick.
Technically, the
tests only required that the query method return the stored value and
implemented it that way.  Here's Florian Gross's implementation (name
holds the
attribute's name):

	alias_method(:"#{name}?", name)

The real purpose of the method though is to find out if the attribute
holds a true or false value.  Christian made sure only this true/false
was returned.  The code works by fetching the value with __send__()
(calling the
getter) and hitting it with Ruby's ! operator.  That will convert the
value to
it's opposite, but the key to the conversion is that it will only return
true or
false.  We really don't want the opposite though, so the ! operator is
again.  That takes us back to the original truth of the argument, but
now in
true/false form.

	Using def Instead of define_method()

You can solve this challenge by defining methods normally, but you have
approach it differently.  First, def is a keyword and not a method in
This has two effects:  you can't just inline the definitions and the
bodies are not closures.  Neither of these is a deal-breaker, they just
an additional tool.  Here's a solution introducing class_eval(), by
Bilkovich (minus the clever poems that made it exactly 42 lines):

	class Module
	  def attribute(name, &block)
	    return {|k,v| attribute(k) {v}} if name.is_a?(Hash)
	    define_method("__#{name}__", block || proc{nil})
	    class_eval <<-ZEN
	      attr_writer :#{name}
	      def #{name}
	        defined?(@#{name}) ? @#{name} : @#{name} = __#{name}__
	      def #{name}?
	        true unless #{name}.nil?

Here you can see support for multiple attributes right off the bat.
Notice that
it works just by recursively calling attribute(), passing a block that
the default argument.  That makes it so you only have one special case,
block.  I like the simplification there.

Next you can see that this code uses define_method() to set a
__attribute_name__() method, which will return the default value for
attribute.  It uses the block as a method body, if one was provided.
eliminates the need for instance_eval() we saw in the last solution,
since the
method body naturally works on the object instance.  In the event we
don't have
a block for the body, one is manufactured to return a default nil.

Now we get to defining the three methods.  Because we are using def here
have lost the closure trick, a new trick is needed to pass down the
name.  Wilson wraps the definitions in class_eval(), which just executes
code in the context of the class.  The String argument version of
is used here (with a heredoc for the String) so that the attribute name
can be
interpolated as needed.

This time the setter is generated with attr_writer(), which should be
enough to everyone I hope.

The getter uses some new techniques though.  First, it checks for the
variable using Ruby's defined? keyword.  This should be faster for
classes that
have a bunch of instance variables because the Array doesn't need to be
constructed and traversed.  Just as with the first solution, the
variable is
returned if set.  If it's not, the default method created earlier is
invoked to
set the variable (efficient, since it will only happen once) and that
value is

We will skip the query method this time, since I don't believe it
functions as
Ara intended.  Some people, including myself, misunderstood the
intentions of
this method.  No big deal.

	The Inner Meaning Approach

This week I am going to reverse my normal strategy of suggesting
everyone read
all the solutions and recommend that people spend some time studying the
itself.  It's really quite clever if you stop and think about it.

Ara couldn't use Test::Unit in this case, because the koans needed to be
in a set order and the process halted as soon as one failed.  Given that
rolled up a linear fail-fast test framework just for the purposes of
this quiz.

Well that and you simply have to respect a framework with lines like:

	MetaGuru.enlighten student

Once you have studied the quiz a bit, one of the solutions will take on
a whole
new meaning for you.  Ilmari Heikkinen sent in a solution that passes
quiz tests
not by properly implementing the attribute() method, but instead by
parts of the quiz framework.  When you think about it, this kind of
solution is
also a form of meta-programming and thus was probably equally

Ilmari's code is actually eight solutions in one and it randomly selects
one it will bypass the tests with when run.  Even more fun, these
solutions come
with great sayings in the spirit of the quiz itself.

Here are a few of my favorite hacks used by Ilmari:

	# ...

	puts "sometimes thinking about a problem makes it worse"
	class MetaStudent; def ponder(a) 0 end end

	# ...

	puts "don't send a student to do a guru's job"
	class MetaStudent; def send(a) 0 end end

	# ...

	puts "question what you have been taught"
	module MetaKoans; 9.times{|i| eval("def koan_#{i+1};0 end")} end

	# ...

The first one replaces the method the MetaStudent uses to ponder() each
with a trivial implementation.  The second works in a similar way
send(), which is used to call the koans themselves, with a trivial
implementation.  The third redefines the problems themselves with
implementations.  Don't miss the other five!

	Wrap Up

My thanks to the quiz creator and numerous solvers for what turned out
to be a
fun little diversion.  We must do more problems like this!

Tomorrows problem is the first from Caleb Tennis, after I killed two
suggestions.  (Yes, I'm very, very mean.)  Luckily Caleb didn't give up
on me
and this problem is a great one...
This topic is locked and can not be replied to.