<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>DogBiscuit</title>
    <description>... mmm, crunchy!</description>
    <link>http://dogbiscuit.org/mdub/weblog</link>
    <language>en-us</language>
    <generator>EvenYetAnotherWeblog</generator>
    <item>
      <title>method_missing magic - emulating Groovy's "it" in Ruby</title>
      <guid>http://dogbiscuit.org/mdub/weblog/Tech/Programming/Ruby/MethodMissingMagic</guid>
      <pubDate>Mon, 02 Oct 2006 13:55:00 +1000</pubDate>
      <description><![CDATA[<p>
Inspired variously by:
<ul>
<li>
<a href='http://blogs.pragprog.com/cgi-bin/pragdave.cgi/Tech/Ruby/ToProc.rdoc'>Symbol#to_proc</a>
</li>
<li>
Nat Pryce's articles on <a href='http://nat.truemesh.com/archives/000535.html'>Higher-Order Messaging in Ruby</a>
</li>
<li>
Groovy's <a href='http://groovy.codehaus.org/Closures'>implied "<tt>it</tt>" closure-parameter</a>
</li>
</ul>
</p>
<p>
I've cooked up a shortcut for generating simple blocks, meaning that rather
than 
</p>
<pre>
people.select { |x| x.name.length &gt; 10 }
</pre>
<p>
I can write such things as:
</p>
<pre>
people.select(&amp;its.name.length &gt; 10)
</pre>
<p>
<em> Disclaimer: I think this is more "cool hack" than useful tool; it's
probably too much of an <a href='http://www.eaves.org/blog-archive/000071.html'>alien
artifact</a> to be useful in
real life.  And it's not generally applicable, like "<tt>it</tt>" in Groovy.  And
really, it's not <strong>that</strong> much more verbose to use a block. Aaaaaanyway
...</em>
</p>
<p>
The trick is that the above is parsed as
</p>
<pre>
people.select(&amp;(its.name.length.&gt;(10)))
</pre>
<p>
The "<tt>its</tt>" method creates a <tt>MessageBuffer</tt> object, which records the
messages (method invocations) sent it's way:
</p>
<pre>
irb(main):001:0&gt; require 'message_buffer'
=&gt; true
irb(main):002:0&gt; its
=&gt; #&lt;MessageBuffer:0x6b40b44 @messages=[]&gt;
irb(main):003:0&gt; its.name.length &lt; 10
=&gt; #&lt;MessageBuffer:0x6b3e678 @messages=[[:name], [:length], [:&lt;, 10]]&gt;
</pre>
<p>
Now, the "&" operator coerces it's argument to a <tt>Proc</tt>, and
<tt>MessageBuffer#to_proc</tt> generates a <tt>Proc</tt> that replays all the recorded
messages.  Q.E.D.  
</p>
<p>
The full source-code is fairly short, so I'll include it inline:
</p>
<pre>
class MessageBuffer 

  instance_methods.each do |m|
    undef_method m unless m =~ /^(__|respond_to|inspect)/ 
  end
  
  def initialize
    @messages = []
  end

  def method_missing(*message)
    @messages &lt;&lt; message        # record the message
    self                        # return self so we can keep recording
  end
  
  def __replay_all_messages__(obj)
    @messages.inject(obj) do |obj, message|
      obj.__send__(*message)
    end 
  end
  
  def to_proc
    proc { |x| __replay_all_messages__(x) }
  end

end

def its
  MessageBuffer.new
end
</pre>
<p>
<hr/>
</p>
<p>
<em>Update: Florian Gross suggested a better way to replay recorded
messages, using <code>inject</code>, and I've updated the code accordingly.
</em>
</p>
]]></description>
    </item>
  </channel>
</rss>
