[SUMMARY] metakoans.rb (#67)

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 private
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 real
question is, how do we know which one is best?

I used the extremely unscientific method of copy Ruby. The core method attr()
is defined in Module, so it seemed like attribute() belonged there too. Ross
Bamford had much more intelligent reasoning. Here is Ross's response to Adam
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
  (NoMethodError)
          from -:3:in `attribute'
          from -:15
  
  By defining an instance method on Module (from which Class inherits) you
  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 solution.

  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
      else
        default = nil
      end
  
      a = a.to_sym
      ivar = "@#{a}"
  
      define_method(a) {
        if instance_variables.include? ivar
          instance_variable_get ivar
        else
          block ? instance_eval(&block) : default
        end
      }
      define_method("#{a}=") { |v| instance_variable_set ivar, v }
      define_method("#{a}?") { !!__send__(a) }
    end
  end

The if statement at the beginning of the method handles the case where a default
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 solution
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 to
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. Now
remember, Ruby's blocks are closures and thus have access to the local variables
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 and/or
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 stored
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 default
(which may have been set to nil earlier). The tests required that the block
have access to the instance though, so the block is instance_eval()ed when
invoked.

The second method, the setter, is trivial. Just stuff the new value in the
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 some
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 currently
holds a true or false value. Christian made sure only this true/false answer
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 applied
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 to
approach it differently. First, def is a keyword and not a method in Ruby.
This has two effects: you can't just inline the definitions and the method
bodies are not closures. Neither of these is a deal-breaker, they just require
an additional tool. Here's a solution introducing class_eval(), by Wilson
Bilkovich (minus the clever poems that made it exactly 42 lines):

  class Module
    def attribute(name, &block)
      return name.map {|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}__
        end
        def #{name}?
          true unless #{name}.nil?
        end
      ZEN
    end
  end

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 returns
the default argument. That makes it so you only have one special case, the
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 that
attribute. It uses the block as a method body, if one was provided. This
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 and
have lost the closure trick, a new trick is needed to pass down the attribute
name. Wilson wraps the definitions in class_eval(), which just executes the
code in the context of the class. The String argument version of class_eval()
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 familiar
enough to everyone I hope.

The getter uses some new techniques though. First, it checks for the instance
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
returned.

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 quiz
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 tried
in a set order and the process halted as soon as one failed. Given that Ara
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 altering
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 enlightening.

Ilmari's code is actually eight solutions in one and it randomly selects which
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 koan
with a trivial implementation. The second works in a similar way replacing
send(), which is used to call the koans themselves, with a trivial
implementation. The third redefines the problems themselves with trivial
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 previous
suggestions. (Yes, I'm very, very mean.) Luckily Caleb didn't give up on me
and this problem is a great one...