Nick Sieger: Tag metaprogramming http://blog.nicksieger.com/articles/tag/metaprogramming?tag=metaprogramming en-us 40 Customizing RSpec <p><em>Update/Disclaimer: I refer to parts of RSpec that are not blessed as an extension API. Redefining <code>before_context_eval</code> and using the <code>@context_eval_module</code> variable directly may change in the future. I&#8217;ll keep this article updated to coincide with the changes. For now, these techniques should work fine with RSpec versions up to 0.7.5.</em></p> <p>RSpec seems to be getting more attention lately as a viable, nay, <em>preferred</em>, alternative to Test::Unit. It&#8217;s possible that it&#8217;s just my personal feed-reader-echo-chamber, but consider this: <a href="http://blog.fallingsnow.net/rubinius/">Rubinius</a> has started using RSpec alongside Test::Unit as an another way to test the alternate Ruby implementation. They&#8217;re even in the midst of building some snazzy extensions to allow the same specs to be run under a Ruby implementation of your choice. (Perhaps this will point the way to a new round of executable specs to accompany the <a href="http://www.headius.com/rubyspec/index.php/Main_Page">fledgling community spec</a>? Let&#8217;s wait and see how they do and leave that topic for another day.)</p> <p>But extending and customizing RSpec to add a DSL on top of RSpec&#8217;s <code>context/specify</code> framework doesn&#8217;t have to be the realm of experts. Here are some templates for how you can DRY up your specs by adding your own helper methods in such a way that they will be available to all your specs. But first, a little background.</p> <h3>Spec Helper</h3> <p>Most usages of RSpec that I&#8217;ve seen in the wild use a &#8220;spec helper&#8221; (<code>spec_helper.rb</code>). This file, following the pattern of Rails&#8217; <code>test_helper.rb</code>, minimally contains require statements to pull in the RSpec code and any supporting code for running specs. By requiring the spec helper via a path relative to your spec (usually with <code>require File.dirname(__FILE__) + '/spec_helper'</code> or similar), it also allows you the convenience of running your specs one at a time from anywhere (say, by launching from your editor) or with <code>rake</code> or <code>spec</code>. This file is where your shared helper methods will go, and where they&#8217;ll get registered to be pulled into the contexts.</p> <h3>What Context in <code>context</code>?</h3> <div class="typocode"><pre><code class="typocode_ruby "><span class="ident">context</span> <span class="punct">&quot;</span><span class="string">A new stack</span><span class="punct">&quot;</span> <span class="keyword">do</span> <span class="comment"># &lt;== What is the value of &quot;self&quot; here?</span> <span class="ident">specify</span> <span class="punct">&quot;</span><span class="string">should be empty</span><span class="punct">&quot;</span> <span class="keyword">do</span> <span class="keyword">end</span> <span class="keyword">end</span></code></pre></div> <p>How do those contexts work anyway? The <code>context</code> method that defines a context in which specs can be defined and run takes a block to define the individual specs, but what can really go in that block?</p> <p>It turns out that RSpec jumps through metaprogramming hoops (using <code>class_eval</code>) to make the block behave like a class definition. This means you can do things like put method definitions inside your context:</p> <div class="typocode"><pre><code class="typocode_ruby "><span class="ident">context</span> <span class="punct">&quot;</span><span class="string">A new stack</span><span class="punct">&quot;</span> <span class="keyword">do</span> <span class="keyword">def </span><span class="method">a_new_stack</span> <span class="constant">Stack</span><span class="punct">.</span><span class="ident">new</span> <span class="keyword">end</span> <span class="ident">specify</span> <span class="punct">&quot;</span><span class="string">should be empty</span><span class="punct">&quot;</span> <span class="keyword">do</span> <span class="ident">a_new_stack</span><span class="punct">.</span><span class="ident">should_be_empty</span> <span class="keyword">end</span> <span class="keyword">end</span></code></pre></div> <p>Which is nice, but the reason we&#8217;re here is to hide that away in <code>spec_helper.rb</code>. So, to get back to the point of the comment in the first example above, the <code>self</code> inside the context block is an anonymous <code>Module</code> object. It&#8217;s constructed in the <code>initialize</code> method of a <code>Context</code> (condensed from <code>spec/runner/context.rb</code> in the RSpec codebase):</p> <div class="typocode"><pre><code class="typocode_ruby "><span class="keyword">class </span><span class="class">Spec::Runner::Context</span> <span class="keyword">def </span><span class="method">initialize</span><span class="punct">(</span><span class="ident">name</span><span class="punct">,</span> <span class="punct">&amp;</span><span class="ident">context_block</span><span class="punct">)</span> <span class="attribute">@name</span> <span class="punct">=</span> <span class="ident">name</span> <span class="attribute">@context_eval_module</span> <span class="punct">=</span> <span class="constant">Module</span><span class="punct">.</span><span class="ident">new</span> <span class="attribute">@context_eval_module</span><span class="punct">.</span><span class="ident">extend</span> <span class="constant">ContextEval</span><span class="punct">::</span><span class="constant">ModuleMethods</span> <span class="attribute">@context_eval_module</span><span class="punct">.</span><span class="ident">include</span> <span class="constant">ContextEval</span><span class="punct">::</span><span class="constant">InstanceMethods</span> <span class="ident">before_context_eval</span> <span class="attribute">@context_eval_module</span><span class="punct">.</span><span class="ident">class_eval</span><span class="punct">(&amp;</span><span class="ident">context_block</span><span class="punct">)</span> <span class="keyword">end</span> <span class="keyword">def </span><span class="method">before_context_eval</span> <span class="keyword">end</span> <span class="keyword">end</span></code></pre></div> <p>(Take note of that empty <code>before_context_eval</code> method and the fact that it&#8217;s invoked during context initialization; that&#8217;s where we can plug in our custom extensions.)</p> <p>The object held by the <code>@context_eval_module</code> instance variable is being augmented in two ways: extension and inclusion. The object is <em>extended</em> with the <code>ContextEval::ModuleMethods</code> module; these methods are being added to the object&#8217;s singleton class. This has the effect of making these methods visible within the <code>context</code> block, functioning similar to &#8220;class&#8221; methods.</p> <p>The object also has the <code>ContextEval::InstanceMethods</code> module <em>included</em>. This has the effect of adding these as instance methods, making them visible from within <code>specify</code> blocks, which are made to behave like instance methods on the same object.</p> <h3>Putting it together</h3> <table style="text-align: left;" summary=""> <thead> <th>Technique</th> <th>Visibility</th> <th>Use</th> </thead> <tbody> <tr><td>@context_eval_module.extend</td><td>Context block</td><td>Custom setup, shared state declaration</td></tr> <tr><td>@context_eval_module.include</td><td>Specify block</td><td>Shared actions/functions, stub/expectation modification, encapsulate instance variables</td></tr> </tbody> </table> <h4>Adding specialized setup methods</h4> <p><code>spec_helper.rb</code> snippet:</p> <div class="typocode"><pre><code class="typocode_ruby "><span class="keyword">module </span><span class="module">SharedSetupMethods</span> <span class="keyword">def </span><span class="method">setup_new_stack</span> <span class="ident">setup</span> <span class="keyword">do</span> <span class="attribute">@stack</span> <span class="punct">=</span> <span class="constant">Stack</span><span class="punct">.</span><span class="ident">new</span> <span class="keyword">end</span> <span class="keyword">end</span> <span class="keyword">end</span> <span class="keyword">class </span><span class="class">Spec::Runner::Context</span> <span class="keyword">def </span><span class="method">before_context_eval</span> <span class="attribute">@context_eval_module</span><span class="punct">.</span><span class="ident">extend</span> <span class="constant">SharedSetupMethods</span> <span class="keyword">end</span> <span class="keyword">end</span></code></pre></div> <p>Example spec:</p> <div class="typocode"><pre><code class="typocode_ruby "><span class="ident">context</span> <span class="punct">&quot;</span><span class="string">A new stack</span><span class="punct">&quot;</span> <span class="keyword">do</span> <span class="ident">setup_new_stack</span> <span class="ident">specify</span> <span class="punct">&quot;</span><span class="string">should be empty</span><span class="punct">&quot;</span> <span class="keyword">do</span> <span class="attribute">@stack</span><span class="punct">.</span><span class="ident">should_be_empty</span> <span class="keyword">end</span> <span class="keyword">end</span> </code></pre></div> <h4>Adding shared accessors</h4> <p><code>spec_helper.rb</code> snippet:</p> <div class="typocode"><pre><code class="typocode_ruby "><span class="keyword">module </span><span class="module">StackMethods</span> <span class="ident">attr_accessor</span> <span class="symbol">:stack</span> <span class="keyword">def </span><span class="method">push_an_object</span> <span class="ident">stack</span> <span class="punct">&lt;&lt;</span> <span class="ident">mock</span><span class="punct">(&quot;</span><span class="string">some object</span><span class="punct">&quot;)</span> <span class="keyword">end</span> <span class="keyword">end</span> <span class="keyword">class </span><span class="class">Spec::Runner::Context</span> <span class="keyword">def </span><span class="method">before_context_eval</span> <span class="attribute">@context_eval_module</span><span class="punct">.</span><span class="ident">include</span> <span class="constant">StackMethods</span> <span class="keyword">end</span> <span class="keyword">end</span></code></pre></div> <p>Example spec:</p> <div class="typocode"><pre><code class="typocode_ruby "><span class="ident">context</span> <span class="punct">&quot;</span><span class="string">A stack with an object pushed</span><span class="punct">&quot;</span> <span class="keyword">do</span> <span class="ident">setup</span> <span class="keyword">do</span> <span class="attribute">@stack</span> <span class="punct">=</span> <span class="constant">Stack</span><span class="punct">.</span><span class="ident">new</span> <span class="keyword">end</span> <span class="ident">specify</span> <span class="punct">&quot;</span><span class="string">should not be empty</span><span class="punct">&quot;</span> <span class="keyword">do</span> <span class="ident">stack</span><span class="punct">.</span><span class="ident">should_be_empty</span> <span class="ident">push_an_object</span> <span class="ident">stack</span><span class="punct">.</span><span class="ident">should_not_be_empty</span> <span class="keyword">end</span> <span class="keyword">end</span> </code></pre></div> <p>The examples are simple, but hopefully illustrate the techniques. For an example of some code that&#8217;s actually useful, check out my sample <a href="http://svn.caldersphere.net/svn/main/rspec_selenium_rc/trunk/">RSpec Selenium RC integration project</a>, in particular the <a href="http://svn.caldersphere.net/svn/main/rspec_selenium_rc/trunk/lib/spec_helper.rb">spec helper</a> and the <a href="http://svn.caldersphere.net/svn/main/rspec_selenium_rc/trunk/spec/example_spec.rb">example spec</a>. (More on this in the future if it proves useful, but for now if you check it out and run <code>rake</code> on it, it should launch <a href="http://www.openqa.org/selenium-rc/">Selenium RC</a> and run the example spec in a Firefox browser.)</p> <p>By mixing and matching these techniques, you can layer a mini-DSL on top of RSpec and achieve DRY-er and even more readable and intention-revealing specs. Let me know if you&#8217;re able to find uses for these tips!</p> Tue, 02 Jan 2007 05:32:00 +0000 urn:uuid:21755a2d-0ffa-4f0c-be7d-a2e2e01d99b4 Nick Sieger http://blog.nicksieger.com/articles/2007/01/02/customizing-rspec rspec ruby metaprogramming http://blog.nicksieger.com/articles/trackback/178 Metaprogramming Pattern No. 1: Self-specialize <p>One of the biggest aspects of Ruby that I&#8217;ve been digging are the metaprogramming facilities, many of which draw from the &#8220;code as data&#8221; philosophy that comes from Lisp. Metaprogramming has become somewhat of a buzzword in the Ruby community, about as popular as &#8220;domain specific language&#8221; in terms of its presence in the titles of conference presentations and the like.</p> <p>So it seems to me that, while a good many smart people are talking and writing about metaprogramming, that we haven&#8217;t yet started cataloguing all the different techniques in a shareable way. Is it time to start writing a catalog of metaprogramming patterns? Or do I risk being <a href="http://www.c2.com/cgi/wiki?ShowTrialOfTheGangOfFour">taken to trial</a> for attempting to unveil the rubyist&#8217;s magic tricks?</p> <p>No, what I am really looking for is the metaprogramming <a href="http://domaindrivendesign.org/discussion/messageboardarchive/UbiquitousLanguage.html">ubiquitous language</a>. Ruby has a lot of language-level features that facilitate metaprogramming, but without a strong jargon to describe what&#8217;s going on. Sorry, but <code>module_eval</code>, <code>instance_eval</code>, metaclass vs. singleton class vs. eigenclass, <code>method_missing</code> etc. just don&#8217;t cut it. So here&#8217;s a first shot at re-invigorating the conversation and taking it to the &#8220;HNL&#8221; (&#8216;hole nuther level). So herewith begins Metaprogramming Pattern No. 1: Self-specialize.</p> <p><em>Why is this one number 1? No reason, pretty arbitrary. I haven&#8217;t taken the time to document any more yet. This will be an ongoing project for me.</em></p> <h2>Self-specialize</h2> <p>You have an algorithm or a &#8220;method object&#8221; that you wish to make flexible by parameterizing with additional code and/or data that isn&#8217;t known at the time you wrote the original class definition.</p> <p><em>Therefore</em>, write a method that redefines itself in the singleton class of the currently instantiated object, then re-sends the message to the customized method. In a way, this is simply <em>memoizing</em> the method body itself as a speed-up.</p> <p>Trivial example:</p> <div class="typocode"><pre><code class="typocode_ruby "><span class="keyword">class </span><span class="class">Foo</span> <span class="keyword">def </span><span class="method">initialize</span><span class="punct">(</span><span class="ident">p</span><span class="punct">)</span> <span class="attribute">@prefix</span> <span class="punct">=</span> <span class="ident">p</span> <span class="keyword">end</span> <span class="keyword">def </span><span class="method">result</span><span class="punct">(</span><span class="ident">val</span><span class="punct">)</span> <span class="ident">specialize_result</span> <span class="ident">result</span> <span class="ident">val</span> <span class="comment"># send the message again</span> <span class="keyword">end</span> <span class="ident">private</span> <span class="keyword">def </span><span class="method">specialize_result</span> <span class="ident">method_decl</span> <span class="punct">=</span> <span class="punct">&quot;</span><span class="string">def result(val); </span><span class="punct">&quot;</span><span class="comment">#{@prefix}: #{val}&quot;; end&quot;</span> <span class="ident">instance_eval</span> <span class="ident">method_decl</span><span class="punct">,</span> <span class="punct">&quot;</span><span class="string">generated code (<span class="expr">#{__FILE__}</span>:<span class="expr">#{__LINE__}</span>)</span><span class="punct">&quot;</span> <span class="keyword">end</span> <span class="keyword">end</span> <span class="ident">f</span> <span class="punct">=</span> <span class="constant">Foo</span><span class="punct">.</span><span class="ident">new</span><span class="punct">(&quot;</span><span class="string">foo</span><span class="punct">&quot;)</span> <span class="ident">f</span><span class="punct">.</span><span class="ident">result</span><span class="punct">(&quot;</span><span class="string">hi</span><span class="punct">&quot;)</span> <span class="comment"># =&gt; &quot;foo: hi&quot;</span> <span class="ident">g</span> <span class="punct">=</span> <span class="constant">Foo</span><span class="punct">.</span><span class="ident">new</span><span class="punct">(&quot;</span><span class="string">bar</span><span class="punct">&quot;)</span> <span class="ident">g</span><span class="punct">.</span><span class="ident">result</span><span class="punct">(&quot;</span><span class="string">hi</span><span class="punct">&quot;)</span> <span class="comment"># =&gt; &quot;bar: hi&quot;</span></code></pre></div> <p>Why would you do this rather than writing the logic into the original method? It&#8217;s hard to justify use of the technique in the example above. But it could be used to DRY up tedious, redundant code that for performance reasons you would prefer to have inlined rather than invoking an additional instance method.</p> <p>OK, so honestly I can&#8217;t come up with a decent reason to do this yet; I haven&#8217;t done enough metaprogramming to have had the need for it. Still, it seems like a nifty enough trick and maybe it will come in handy for you. This does show you just how dynamic Ruby&#8217;s method resolution is, that you can suddenly define and call a different method implementation inside of the method itself!</p> <p>This technique was spotted in the rewritten <a href="http://dev.rubyonrails.org/svn/rails/trunk/actionpack/lib/action_controller/routing.rb">routes</a> implementation for Rails 1.2 (currently on the <a href="http://dev.rubyonrails.org/svn/rails/trunk/">trunk</a>) &#8211; see the <code>#write_generate</code> and <code>#write_recognize</code> methods.</p> <p><em>Update</em>: Good timing. _why just posted <a href="http://redhanded.hobix.com/inspect/methodsThatSelfDestruct.html">much more succinct description</a> of the same phenomenon, with better examples.</p> Tue, 11 Jul 2006 03:48:00 +0000 urn:uuid:dc66b0b8-def5-4be6-9b1e-1a2e883c6106 Nick Sieger http://blog.nicksieger.com/articles/2006/07/11/metaprogramming-pattern-1-self-specialize ruby rails metaprogramming pattern http://blog.nicksieger.com/articles/trackback/33