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&#46; Redefining <code>before_context_eval</code> and using the <code>@context_eval_module</code> variable directly may change in the future&#46; I&#8217;ll keep this article updated to coincide with the changes&#46; For now, these techniques should work fine with RSpec versions up to 0&#46;7&#46;5&#46;</em></p> <p>RSpec seems to be getting more attention lately as a viable, nay, <em>preferred</em>, alternative to Test::Unit&#46; It&#8217;s possible that it&#8217;s just my personal feed&#45;reader&#45;echo&#45;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&#46; 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&#46; (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&#46;)</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&#46; 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&#46; But first, a little background&#46;</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>)&#46; 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&#46; 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>&#46; This file is where your shared helper methods will go, and where they&#8217;ll get registered to be pulled into the contexts&#46;</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&#46; 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>&#46; 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&#46; 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&#46;)</p> <p>The object held by the <code>@context_eval_module</code> instance variable is being augmented in two ways: extension and inclusion&#46; 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&#46; This has the effect of making these methods visible within the <code>context</code> block, functioning similar to &#8220;class&#8221; methods&#46;</p> <p>The object also has the <code>ContextEval::InstanceMethods</code> module <em>included</em>&#46; 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&#46;</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&#46;extend</td><td>Context block</td><td>Custom setup, shared state declaration</td></tr> <tr><td>@context_eval_module&#46;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&#46; 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>&#46; (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&#46;)</p> <p>By mixing and matching these techniques, you can layer a mini&#45;DSL on top of RSpec and achieve DRY&#45;er and even more readable and intention&#45;revealing specs&#46; 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&#46; 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&#46;</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&#46; 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>&#46; Ruby has a lot of language&#45;level features that facilitate metaprogramming, but without a strong jargon to describe what&#8217;s going on&#46; Sorry, but <code>module_eval</code>, <code>instance_eval</code>, metaclass vs&#46; singleton class vs&#46; eigenclass, <code>method_missing</code> etc&#46; just don&#8217;t cut it&#46; So here&#8217;s a first shot at re&#45;invigorating the conversation and taking it to the &#8220;HNL&#8221; (&#8216;hole nuther level)&#46; So herewith begins Metaprogramming Pattern No&#46; 1: Self&#45;specialize&#46;</p> <p><em>Why is this one number 1? No reason, pretty arbitrary&#46; I haven&#8217;t taken the time to document any more yet&#46; This will be an ongoing project for me&#46;</em></p> <h2>Self&#45;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&#46;</p> <p><em>Therefore</em>, write a method that redefines itself in the singleton class of the currently instantiated object, then re&#45;sends the message to the customized method&#46; In a way, this is simply <em>memoizing</em> the method body itself as a speed&#45;up&#46;</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&#46; 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&#46;</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&#46; Still, it seems like a nifty enough trick and maybe it will come in handy for you&#46; 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&#46;2 (currently on the <a href="http://dev.rubyonrails.org/svn/rails/trunk/">trunk</a>) &#45;&#45; see the <code>#write_generate</code> and <code>#write_recognize</code> methods&#46;</p> <p><em>Update</em>: Good timing&#46; _why just posted <a href="http://redhanded.hobix.com/inspect/methodsThatSelfDestruct.html">much more succinct description</a> of the same phenomenon, with better examples&#46;</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