<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">
  <title>myronmars.to/n</title>
  
  <link href="http://myronmars.to/n" />
  <updated>2013-04-07T16:38:22-07:00</updated>
  <id>http://myronmars.to/n</id>
  <author>
   <name>Myron Marston</name>
   <email>myron.marston@gmail.com</email>
  </author>

  
  <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/myronmarsto/n" /><feedburner:info uri="myronmarsto/n" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry>
    <title>RSpec Team Changes</title>
    <link href="http://feedproxy.google.com/~r/myronmarsto/n/~3/NXjt_95b2f0/rspec-team-changes" />
    <updated>2013-04-08T00:00:00-07:00</updated>
    <id>http://myronmars.to/n/dev-blog/2013/04/rspec-team-changes</id>
    <content type="html">&lt;p&gt;The RSpec core team recently expanded to include three new members:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href='http://www.soulcutter.com/'&gt;Bradley Schaefer&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href='http://jonrowe.co.uk/'&gt;Jon Rowe&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href='http://samphippen.com/'&gt;Sam Phippen&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bradley, Jon and Sam have all been doing great work reviewing pull requests, triaging issues, fixing bugs, improving RSpec&amp;#8217;s documentation and coding new features. We decided it was time to add them to the team.&lt;/p&gt;

&lt;p&gt;Meanwhile, &lt;a href='http://spicycode.com/'&gt;Chad Humphries&lt;/a&gt;, &lt;a href='http://patmaddox.com/'&gt;Pat Maddox&lt;/a&gt; and &lt;a href='https://github.com/justinko'&gt;Justin Ko&lt;/a&gt; have all chosen to retire from the team and move on to other things.&lt;/p&gt;

&lt;p&gt;Please join me in welcoming Bradley, Jon and Sam, and thanking Chad, Pat and Justin for all of their contributions to RSpec!&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/myronmarsto/n/~4/NXjt_95b2f0" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://myronmars.to/n/dev-blog/2013/04/rspec-team-changes</feedburner:origLink></entry>
  
  <entry>
    <title>RSpec 2.13 is released!</title>
    <link href="http://feedproxy.google.com/~r/myronmarsto/n/~3/JyMP9CUPqbA/rspec-2-13-is-released" />
    <updated>2013-02-23T00:00:00-08:00</updated>
    <id>http://myronmars.to/n/dev-blog/2013/02/rspec-2-13-is-released</id>
    <content type="html">&lt;p&gt;I&amp;#8217;ve just released RSpec 2.13. It&amp;#8217;s a minor release containing a few backward-compatible enhancements and lots of bug fixes. It is a recommended upgrade for all users.&lt;/p&gt;

&lt;p&gt;Thanks to all the contributors who helped make this RSpec release happen.&lt;/p&gt;

&lt;h2 id='notable_new_features'&gt;Notable New Features&lt;/h2&gt;

&lt;h3 id='profile_more_than_10_examples'&gt;Profile more than 10 examples&lt;/h3&gt;

&lt;p&gt;RSpec has featured a &lt;code&gt;--profile&lt;/code&gt; option for a long time. It dumps a report of the 10 slowest examples. Now you can pass a numeric option to have it print more than 10 examples.&lt;/p&gt;

&lt;p&gt;To print off the 15 slowest examples, you could use:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class=''&gt;&lt;span class='line'&gt;rspec --profile 15&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;h3 id='_and__declarations_can_use_'&gt;&lt;code&gt;let&lt;/code&gt; and &lt;code&gt;subject&lt;/code&gt; declarations can use &lt;code&gt;super&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;Users have requested this for a while. This allows to override a &lt;code&gt;let&lt;/code&gt; or &lt;code&gt;subject&lt;/code&gt; declaration in a nested group while delegating to the original definition from the parent group. Just use &lt;code&gt;super()&lt;/code&gt;:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;array_spec.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;describe&lt;/span&gt; &lt;span class='nb'&gt;Array&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;let&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:numbers&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;2&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;3&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;4&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;context&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;when evens are filtered out&amp;quot;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;let&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:numbers&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='k'&gt;super&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;reject&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;&amp;amp;&lt;/span&gt;&lt;span class='ss'&gt;:even?&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;Note that to use this feature, you &lt;em&gt;must&lt;/em&gt; use explicit parens in the call to &lt;code&gt;super&lt;/code&gt;; otherwise ruby will give you an ugly &lt;code&gt;implicit argument passing of super from method defined by
define_method() is not supported&lt;/code&gt; error.&lt;/p&gt;

&lt;h3 id='_matcher_supports_percent_deltas'&gt;&lt;code&gt;be_within&lt;/code&gt; matcher supports percent deltas&lt;/h3&gt;

&lt;p&gt;This is best illustrated by an example:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;account_spec.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='c1'&gt;# The existing `be_within` matcher (which still works):&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;expect&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;account&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;balance&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;to&lt;/span&gt; &lt;span class='n'&gt;be_within&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;10&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;of&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;500&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='c1'&gt;# Now you can do this, too:&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;expect&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;account&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;balance&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;to&lt;/span&gt; &lt;span class='n'&gt;be_within&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;2&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;percent_of&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;500&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;h3 id='_matcher_can_accept_a_list_of_matchers'&gt;&lt;code&gt;include&lt;/code&gt; matcher can accept a list of matchers&lt;/h3&gt;

&lt;p&gt;This is handy when you want to verify something about the items in a list rather that simply verifying the items&amp;#8217; identity.&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;user_spec.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='no'&gt;RSpec&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;Matchers&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;define&lt;/span&gt; &lt;span class='ss'&gt;:a_user_named&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='nb'&gt;name&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;match&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;user&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;user&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;name&lt;/span&gt; &lt;span class='o'&gt;==&lt;/span&gt; &lt;span class='nb'&gt;name&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;expect&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;users&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;to&lt;/span&gt; &lt;span class='kp'&gt;include&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;a_user_named&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;Coen&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;),&lt;/span&gt; &lt;span class='n'&gt;a_user_named&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;Daphne&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;h2 id='docs'&gt;Docs&lt;/h2&gt;

&lt;h3 id='rdoc'&gt;RDoc&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href='http://rubydoc.info/gems/rspec-core'&gt;http://rubydoc.info/gems/rspec-core&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href='http://rubydoc.info/gems/rspec-expectations'&gt;http://rubydoc.info/gems/rspec-expectations&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href='http://rubydoc.info/gems/rspec-mocks'&gt;http://rubydoc.info/gems/rspec-mocks&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href='http://rubydoc.info/gems/rspec-rails'&gt;http://rubydoc.info/gems/rspec-rails&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id='cucumber_features'&gt;Cucumber Features&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href='http://relishapp.com/rspec/rspec-core'&gt;http://relishapp.com/rspec/rspec-core&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href='http://relishapp.com/rspec/rspec-expectations'&gt;http://relishapp.com/rspec/rspec-expectations&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href='http://relishapp.com/rspec/rspec-mocks'&gt;http://relishapp.com/rspec/rspec-mocks&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href='http://relishapp.com/rspec/rspec-rails'&gt;http://relishapp.com/rspec/rspec-rails&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id='release_notes'&gt;Release Notes&lt;/h2&gt;

&lt;h3 id='rspeccore_2130'&gt;rspec-core 2.13.0&lt;/h3&gt;

&lt;p&gt;&lt;a href='http://github.com/rspec/rspec-core/compare/v2.12.2...v2.13.0'&gt;Full Changelog&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enhancements&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Allow &lt;code&gt;--profile&lt;/code&gt; option to take a count argument that determines the number of slow examples to dump (Greggory Rothmeier).&lt;/li&gt;

&lt;li&gt;Add &lt;code&gt;subject!&lt;/code&gt; that is the analog to &lt;code&gt;let!&lt;/code&gt;. It defines an explicit subject and sets a &lt;code&gt;before&lt;/code&gt; hook that will invoke the subject (Zubin Henner).&lt;/li&gt;

&lt;li&gt;Fix &lt;code&gt;let&lt;/code&gt; and &lt;code&gt;subject&lt;/code&gt; declaration so that &lt;code&gt;super&lt;/code&gt; and &lt;code&gt;return&lt;/code&gt; can be used in them, just like in a normal method. (Myron Marston)&lt;/li&gt;

&lt;li&gt;Allow output colors to be configured individually. (Charlie Maffitt)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bug fixes&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Don&amp;#8217;t blow up when dumping error output for instances of anonymous error classes (Myron Marston).&lt;/li&gt;

&lt;li&gt;Fix default backtrace filters so lines from projects containing &amp;#8220;gems&amp;#8221; in the name are not filtered, but lines from installed gems still are (Myron Marston).&lt;/li&gt;

&lt;li&gt;Fix autotest command so that is uses double quotes rather than single quotes for windows compatibility (Jonas Tingeborn).&lt;/li&gt;

&lt;li&gt;Fix &lt;code&gt;its&lt;/code&gt; so that uses of &lt;code&gt;subject&lt;/code&gt; in a &lt;code&gt;before&lt;/code&gt; or &lt;code&gt;let&lt;/code&gt; declaration in the parent group continue to reference the parent group&amp;#8217;s subject. (Olek Janiszewski)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id='rspecexpectations_2130'&gt;rspec-expectations 2.13.0&lt;/h3&gt;

&lt;p&gt;&lt;a href='http://github.com/rspec/rspec-expectations/compare/v2.12.1...v2.13.0'&gt;Full Changelog&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enhancements&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add support for percent deltas to &lt;code&gt;be_within&lt;/code&gt; matcher: &lt;code&gt;expect(value).to be_within(10).percent_of(expected)&lt;/code&gt; (Myron Marston).&lt;/li&gt;

&lt;li&gt;Add support to &lt;code&gt;include&lt;/code&gt; matcher to allow it to be given a list of matchers as the expecteds to match against (Luke Redpath).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bug fixes&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fix &lt;code&gt;change&lt;/code&gt; matcher so that it dups strings in order to handle mutated strings (Myron Marston).&lt;/li&gt;

&lt;li&gt;Fix &lt;code&gt;should be =~ /some regex/&lt;/code&gt; / &lt;code&gt;expect(...).to be =~ /some regex/&lt;/code&gt;. Previously, these either failed with a confusing &lt;code&gt;undefined method
matches?&amp;#39; for false:FalseClass&lt;/code&gt; error or were no-ops that didn&amp;#8217;t actually verify anything (Myron Marston).&lt;/li&gt;

&lt;li&gt;Add compatibility for diff-lcs 1.2 and relax the version constraint (Peter Goldstein).&lt;/li&gt;

&lt;li&gt;Fix DSL-generated matchers to allow multiple instances of the same matcher in the same example to have different description and failure messages based on the expected value (Myron Marston).&lt;/li&gt;

&lt;li&gt;Prevent &lt;code&gt;undefined method #split for Array&lt;/code&gt; error when dumping the diff of an array of multiline strings (Myron Marston).&lt;/li&gt;

&lt;li&gt;Don&amp;#8217;t blow up when comparing strings that are in an encoding that is not ASCII compatible (Myron Marston).&lt;/li&gt;

&lt;li&gt;Remove confusing &amp;#8220;Check the implementation of #==&amp;#8221; message printed for empty diffs (Myron Marston).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id='rspecmocks_2130'&gt;rspec-mocks 2.13.0&lt;/h3&gt;

&lt;p&gt;&lt;a href='http://github.com/rspec/rspec-mocks/compare/v2.12.2...v2.13.0'&gt;Full Changelog&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bug fixes&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fix bug that caused weird behavior when a method that had previously been stubbed with multiple return values (e.g. &lt;code&gt;obj.stub(:foo).and_return(1, 2)&lt;/code&gt;) was later mocked with a single return value (e.g. &lt;code&gt;obj.should_receive(:foo).once.and_return(1)&lt;/code&gt;). (Myron Marston)&lt;/li&gt;

&lt;li&gt;Fix bug related to a mock expectation for a method that already had multiple stubs with different &lt;code&gt;with&lt;/code&gt; constraints. Previously, the first stub was used, even though it may not have matched the passed args. The fix defers this decision until the message is received so that the proper stub response can be chosen based on the passed arguments (Myron Marston).&lt;/li&gt;

&lt;li&gt;Do not call &lt;code&gt;nil?&lt;/code&gt; extra times on a mocked object, in case &lt;code&gt;nil?&lt;/code&gt; itself is expected a set number of times (Myron Marston).&lt;/li&gt;

&lt;li&gt;Fix &lt;code&gt;missing_default_stub_error&lt;/code&gt; message so array args are handled properly (Myron Marston).&lt;/li&gt;

&lt;li&gt;Explicitly disallow &lt;code&gt;any_instance.unstub!&lt;/code&gt; (Ryan Jones).&lt;/li&gt;

&lt;li&gt;Fix &lt;code&gt;any_instance&lt;/code&gt; stubbing so that it works with &lt;code&gt;Delegator&lt;/code&gt; subclasses (Myron Marston).&lt;/li&gt;

&lt;li&gt;Fix &lt;code&gt;and_call_original&lt;/code&gt; so that it works with &lt;code&gt;Delegator&lt;/code&gt; subclasses (Myron Marston).&lt;/li&gt;

&lt;li&gt;Fix &lt;code&gt;any_instance.should_not_receive&lt;/code&gt; when &lt;code&gt;any_instance.should_receive&lt;/code&gt; is used on the same class in the same example. Previously it would wrongly report a failure even when the message was not received (Myron Marston).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id='rspecrails_2130'&gt;rspec-rails 2.13.0&lt;/h3&gt;

&lt;p&gt;&lt;a href='http://github.com/rspec/rspec-rails/compare/v2.12.2...v2.13.0'&gt;Full Changelog&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enhancements&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;be_valid&lt;/code&gt; matcher includes validation error messages. (Tom Scott)&lt;/li&gt;

&lt;li&gt;Adds cucumber scenario showing how to invoke an anonymous controller&amp;#8217;s non-resourceful actions. (Paulo Luis Franchini Casaretto)&lt;/li&gt;

&lt;li&gt;Null template handler is used when views are stubbed. (Daniel Schierbeck)&lt;/li&gt;

&lt;li&gt;The generated &lt;code&gt;spec_helper.rb&lt;/code&gt; in Rails 4 includes a check for pending migrations. (Andy Lindeman)&lt;/li&gt;

&lt;li&gt;Adds &lt;code&gt;rake spec:features&lt;/code&gt; task. (itzki)&lt;/li&gt;

&lt;li&gt;Rake tasks are automatically generated for each spec/ directory. (Rudolf Schmidt)&lt;/li&gt;
&lt;/ul&gt;&lt;img src="http://feeds.feedburner.com/~r/myronmarsto/n/~4/JyMP9CUPqbA" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://myronmars.to/n/dev-blog/2013/02/rspec-2-13-is-released</feedburner:origLink></entry>
  
  <entry>
    <title>5 Reasons to Avoid Bundler.require</title>
    <link href="http://feedproxy.google.com/~r/myronmarsto/n/~3/Pt5L8QtUtD4/5-reasons-to-avoid-bundler-require" />
    <updated>2012-12-17T00:00:00-08:00</updated>
    <id>http://myronmars.to/n/dev-blog/2012/12/5-reasons-to-avoid-bundler-require</id>
    <content type="html">&lt;p&gt;The application skeleton generated by &lt;code&gt;rails new&lt;/code&gt; contains a bit of code that I think you&amp;#8217;re better off removing:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;config/application.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='n'&gt;defined?&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='no'&gt;Bundler&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='c1'&gt;# If you precompile assets before deploying to production, use this line&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='no'&gt;Bundler&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;require&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='no'&gt;Rails&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;groups&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:assets&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='sx'&gt;%w(development test)&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='c1'&gt;# If you want your assets lazily compiled in production, use this line&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='c1'&gt;# Bundler.require(:default, :assets, Rails.env)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;For the uninitiated, &lt;code&gt;Bundler.require&lt;/code&gt; loops over each gem in your &lt;code&gt;Gemfile&lt;/code&gt; and requires all gem entries that have not explicitly disabled it with an option like &lt;code&gt;:require =&amp;gt; false&lt;/code&gt;. &lt;code&gt;Bundler.require&lt;/code&gt; is certainly convenient (you can use any gem from anywhere in your application, without having to think about it!), but it has a handful of significant downsides.&lt;/p&gt;

&lt;p&gt;Here are 5 reasons to avoid &lt;code&gt;Bundler.require&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id='1_it_slows_down_your_application_startup_time'&gt;1. It slows down your application start-up time&lt;/h2&gt;

&lt;p&gt;When you use &lt;code&gt;Bundler.require&lt;/code&gt;, it slows down the start-up time for anything that boots your application environment. Requiring files is not a free operation. Listing rake tasks with &lt;code&gt;rake -T&lt;/code&gt; gets slow. It takes much longer to get test feedback from the very first test, even if the test itself is very fast.&lt;/p&gt;

&lt;p&gt;The worst part is that your application&amp;#8217;s start-up time will grow linearly with the number of gems you are using. Each time you add a gem to your &lt;code&gt;Gemfile&lt;/code&gt;, it adds to the start-up time for &lt;em&gt;each&lt;/em&gt; and &lt;em&gt;every&lt;/em&gt; command you run that boots your environment. Do you really want to pay that kind of tax?&lt;/p&gt;

&lt;p&gt;Compare how long it takes to list the rake tasks in an application that uses &lt;code&gt;Bundler.require&lt;/code&gt; compared to one that doesn&amp;#8217;t. This is real output from two ruby applications at &lt;a href='http://www.seomoz.org/'&gt;SEOmoz&lt;/a&gt;. One uses &lt;code&gt;Bundler.require&lt;/code&gt; and one doesn&amp;#8217;t:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;app_with_bundler_require &lt;/span&gt;&lt;/figcaption&gt;
&lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class=''&gt;&lt;span class='line'&gt;➜  time rake -T &gt; /dev/null
&lt;/span&gt;&lt;span class='line'&gt;rake -T &gt; /dev/null  9.44s user 2.75s system 98% cpu 12.335 total&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;app_without_bundler_require &lt;/span&gt;&lt;/figcaption&gt;
&lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class=''&gt;&lt;span class='line'&gt;➜  time rake -T &gt; /dev/null
&lt;/span&gt;&lt;span class='line'&gt;rake -T &gt; /dev/null  0.22s user 0.03s system 98% cpu 0.255 total&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;That&amp;#8217;s nearly &lt;strong&gt;50 times&lt;/strong&gt; faster.&lt;/p&gt;

&lt;p&gt;And really, if you think about it, it&amp;#8217;s kind of ridiculous: why should a command that lists all available tasks (but runs none) waste time loading every gem your application uses? Likewise, why should running a single test file for an &lt;code&gt;ActiveRecord&lt;/code&gt; model load gems that are only used by controllers?&lt;/p&gt;

&lt;h2 id='2_it_removes__as_a_source_of_design_feedback'&gt;2. It removes &lt;code&gt;require&lt;/code&gt; as a source of design feedback&lt;/h2&gt;

&lt;p&gt;Once you stop using &lt;code&gt;Bundler.require&lt;/code&gt;, you have to start explicitly requiring your gems. I think that many ruby programmers dislike doing so because Rails itself tends to discourage explicit requires. Rails&amp;#8217; constant autoloading allows you to use any class in your application from anywhere without needing to require it, and the generated &lt;code&gt;config/application.rb&lt;/code&gt; file uses &lt;code&gt;Bundler.require&lt;/code&gt; so that you do not have to require any gems.&lt;/p&gt;

&lt;p&gt;However, in an application that uses explicit requires at the top of each file, they serve a useful purpose: you can tell at a glance how many dependencies a class has. They are a great source of design feedback. When a class needs 20 &lt;code&gt;require&lt;/code&gt; statements to work, it suggests that it&amp;#8217;s probably coupled to too many things. You may want to split out some classes from the monolith.&lt;/p&gt;

&lt;h2 id='3_it_makes_your_code_harder_to_understand'&gt;3. It makes your code harder to understand&lt;/h2&gt;

&lt;p&gt;Having a list of requires at the top of your files makes your code easier to understand, too.&lt;/p&gt;

&lt;p&gt;Consider this code:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;list_files_in.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;list_files_in&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;dir&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='sb'&gt;`ls &lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='n'&gt;dir&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;shellescape&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='sb'&gt;`&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;split&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;&lt;/span&gt;&lt;span class='se'&gt;\n&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='nb'&gt;puts&lt;/span&gt; &lt;span class='n'&gt;list_files_in&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;/usr/bin&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;You come across this bit of code, and notice the use of the &lt;code&gt;String#shellescape&lt;/code&gt; method. Having never seen it before, you&amp;#8217;re curious about it and visit the &lt;a href='http://www.ruby-doc.org/core-1.9.3/String.html'&gt;ruby string docs&lt;/a&gt; but can&amp;#8217;t find any mention of it.&lt;/p&gt;

&lt;p&gt;Imagine if this was the code instead:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;list_files_in.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;shellwords&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;list_files_in&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;dir&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='sb'&gt;`ls &lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='n'&gt;dir&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;shellescape&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='sb'&gt;`&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;split&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;&lt;/span&gt;&lt;span class='se'&gt;\n&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='nb'&gt;puts&lt;/span&gt; &lt;span class='n'&gt;list_files_in&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;/usr/bin&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;The presence of a &lt;code&gt;require&lt;/code&gt; statement gives you a great hint where to go look for the source of a method. &lt;code&gt;shellwords&lt;/code&gt; in the ruby standard library defines &lt;code&gt;String#shellescape&lt;/code&gt;, but without the &lt;code&gt;require&lt;/code&gt; statement, there&amp;#8217;s no easy way to guess this.&lt;/p&gt;

&lt;h2 id='4_it_will_continue_to_load_code_long_after_it_is_no_longer_used'&gt;4. It will continue to load code long after it is no longer used&lt;/h2&gt;

&lt;p&gt;There&amp;#8217;s another nice benefit of explicit &lt;code&gt;require&lt;/code&gt; statements: when you remove the last file that requires a particular gem, the gem will no longer be loaded at runtime. This will reduce your application&amp;#8217;s memory footprint.&lt;/p&gt;

&lt;p&gt;In contrast, consider what happens when you use &lt;code&gt;Bundler.require&lt;/code&gt; and you remove the last file that relies upon a certain gem: nothing. The gem will continue to be loaded at runtime, adding bloat to your application processes. You can of course remove the gem&amp;#8217;s entry from your Gemfile, but in a large application, you may not know if a particular gem is still being used&amp;#8230;especially since the lack of explicit &lt;code&gt;require&lt;/code&gt; statements gives you no easy way to tell.&lt;/p&gt;

&lt;h2 id='id37'&gt;&lt;del&gt;5. It prevents you from controlling the gem load order&lt;/del&gt;&lt;/h2&gt;

&lt;p&gt;(Update: I was wrong on this point, actually. Bundler maintainer Andre Arko was kind enough to correct me &lt;a href='#comment-741237808'&gt;below&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;&lt;del&gt;There's one final downside to &lt;code&gt;Bundler.require&lt;/code&gt;: it
gives you no way to control the order the gems are loaded.
Usually this doesn't matter...but occasionally it does, and
it can lead to surprising, hard-to-track-down bugs.&lt;/del&gt;&lt;/p&gt;&lt;p&gt;&lt;del&gt;Consider a gem like
&lt;a href='https://github.com/bblimke/webmock'&gt;WebMock&lt;/a&gt;.
It hooks into many different HTTP client libraries and provides
a consistent API to stub HTTP requests. Before version 1.7.0,
it would use constant detection to figure out whether or not
it should hook into a particular HTTP client library. For example,
it would hook into curb if and only if the &lt;code&gt;Curb&lt;/code&gt; constant was
defined when WebMock got loaded. Usually, things worked just
fine, but occasionally curb/webmock users would find that it wasn't
working as expected, due to the fact that WebMock got loaded
first.&lt;/del&gt;&lt;/p&gt;&lt;p&gt;&lt;del&gt;Yehuda Katz has rightfully
&lt;a href='http://yehudakatz.com/2010/04/17/ruby-require-order-problems/'&gt;recommended&lt;/a&gt;
that gem authors not use constant detection to decide whether or not to
activate some functionality, and I fully agree with that advice.
However, as a gem user, you may wind up using a gem that uses this
technique, and if you rely on &lt;code&gt;Bundler.require&lt;/code&gt; to load your gems,
you have no way to ensure the gems are loaded in the correct order.
If you use explicit &lt;code&gt;require&lt;/code&gt; statements, it is trivial to
solve.&lt;/del&gt;&lt;/p&gt;
&lt;h2 id='using_bundler_without_bundlerrequire'&gt;Using Bundler without Bundler.require&lt;/h2&gt;

&lt;p&gt;It may sound like I dislike bundler, but I think bundler is a fantastic tool, and I use it in all my projects. I just don&amp;#8217;t use &lt;code&gt;Bundler.require&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Instead, you can use &lt;a href='http://gembundler.com/v1.2/bundler_setup.html'&gt;Bundler.setup&lt;/a&gt; so it sets up your load paths. Or, consider using &lt;a href='/n/dev-blog/2012/03/faster-test-boot-times-with-bundler-standalone'&gt;bundler&amp;#8217;s &amp;#8211;standalone option&lt;/a&gt; for even faster startup times.&lt;/p&gt;

&lt;p&gt;In addition, I recommend you explicitly require each gem you rely on in each file that requires it, rather than loading them all from some file that is run at boot-time. This avoids the downsides of &lt;code&gt;Bundler.require&lt;/code&gt; while gaining the benefits I&amp;#8217;ve outlined above from using explicit &lt;code&gt;require&lt;/code&gt; statements.&lt;/p&gt;

&lt;p&gt;In effect, this uses bundler to solve the hard problem it is really good at (i.e. resolving and locking dependencies) but avoids using bundler for the really easy problem that you&amp;#8217;re better off managing yourself (i.e. requiring each gem).&lt;/p&gt;

&lt;h2 id='building_rails_apps_without_bundlerrequire'&gt;Building Rails apps without Bundler.require&lt;/h2&gt;

&lt;p&gt;If I&amp;#8217;ve convinced you to avoid using &lt;code&gt;Bundler.require&lt;/code&gt;, you may be excited to go remove the &lt;code&gt;Bundler.require&lt;/code&gt; line in your rails app&amp;#8217;s &lt;code&gt;config/application.rb&lt;/code&gt; file. Unfortunately, it&amp;#8217;s not that simple :(.&lt;/p&gt;

&lt;p&gt;For one, in an existing rails application, this is almost certainly going to break things. For another, many gems (particularly those that provide a railtie) assume that users will be using &lt;code&gt;Bundler.require&lt;/code&gt;. Many gems don&amp;#8217;t provide clear instructions for how to use their gem if you do not use &lt;code&gt;Bundler.require&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let&amp;#8217;s tackle the first problem first. If you&amp;#8217;re starting a new rails application, I recommend you remove &lt;code&gt;Bundler.require&lt;/code&gt; from the start. Then there&amp;#8217;s no migration pain :). If you&amp;#8217;re working on an existing application, you&amp;#8217;ll need to replace your &lt;code&gt;Bundler.require&lt;/code&gt; statement with a list of &lt;code&gt;require&lt;/code&gt; statements, one for each gem. Over time, you&amp;#8217;ll want to slim this list down to just the gems that are actually needed at boot time (hopefully a small list), but in the meantime, listing them all should (hopefully, at least) keep your application working.&lt;/p&gt;

&lt;p&gt;As for the second problem, there isn&amp;#8217;t a &amp;#8220;one size fits all&amp;#8221; solution. You may have to dig into the source code for the gems you use to understand how they work a bit better&amp;#8211;but this is probably a good thing, anyway! I can give you an example of the sort of thing you&amp;#8217;ll need to do, though.&lt;/p&gt;

&lt;p&gt;Many gems provide rake tasks. If the gem is no longer being loaded at environment boot time, these tasks may not be available. Ripple, for example, provides &lt;a href='https://github.com/basho/ripple/blob/v1.0.0.beta2/lib/ripple/railties/ripple.rake'&gt;a handful of rake tasks&lt;/a&gt;. To make these tasks available in your application, you&amp;#8217;ll need to add &lt;code&gt;load &amp;quot;ripple/railties/ripple.rake&amp;quot;&lt;/code&gt; to your &lt;code&gt;Rakefile&lt;/code&gt;, which is essentially what the &lt;code&gt;rake_tasks&lt;/code&gt; block in &lt;a href='https://github.com/basho/ripple/blob/v1.0.0.beta2/lib/ripple/railtie.rb#L7-L9'&gt;Ripple&amp;#8217;s railtie&lt;/a&gt; does.&lt;/p&gt;

&lt;h2 id='conclusion'&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;When you use &lt;code&gt;Bundler.require&lt;/code&gt;, you are opting for convenience over sustainable application development. It takes a bit more work (and greater understanding of how your application&amp;#8217;s gems plug into your application framework) to use explicit &lt;code&gt;require&lt;/code&gt; statements, but I&amp;#8217;ve found that doing so pays off in spades.&lt;/p&gt;

&lt;p&gt;For your next or current application, consider using explicit &lt;code&gt;require&lt;/code&gt; statements rather than &lt;code&gt;Bundler.require&lt;/code&gt;.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/myronmarsto/n/~4/Pt5L8QtUtD4" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://myronmars.to/n/dev-blog/2012/12/5-reasons-to-avoid-bundler-require</feedburner:origLink></entry>
  
  <entry>
    <title>Using Closures to Avoid Leaking State</title>
    <link href="http://feedproxy.google.com/~r/myronmarsto/n/~3/hXVEA5hlWtA/using-closures-to-avoid-leaking-state" />
    <updated>2012-10-15T00:00:00-07:00</updated>
    <id>http://myronmars.to/n/dev-blog/2012/10/using-closures-to-avoid-leaking-state</id>
    <content type="html">&lt;p&gt;I&amp;#8217;ve often found myself sandboxing a state change like so:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;spec_helper.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;shared_context&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;with isolated current dir&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:isolated_dir&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='kp'&gt;true&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;before&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='vi'&gt;@dir&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;Dir&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;pwd&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='no'&gt;Dir&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;chdir&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='no'&gt;Dir&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;mktmpdir&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;after&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='no'&gt;Dir&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;chdir&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='vi'&gt;@dir&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;my_spec.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;describe&lt;/span&gt; &lt;span class='no'&gt;MyClass&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:isolated_dir&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='kp'&gt;true&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='c1'&gt;# some examples that operate on the current dir&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;This works fine, but the host example group may also have a &lt;code&gt;@dir&lt;/code&gt; instance variable, which would lead to surprising bugs. In the past, I&amp;#8217;ve dealt with this possibility by naming the instance variable with something the host group is unlikely to use (such as &lt;code&gt;@__dir&lt;/code&gt;), but that&amp;#8217;s just putting a band aid on the underlying problem: our &lt;code&gt;shared_context&lt;/code&gt; declaration is leaking state.&lt;/p&gt;

&lt;p&gt;I realized recently that I can use the fact that ruby blocks are closures to avoid the problem entirely:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;spec_helper.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;shared_context&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;with isolated current dir&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:isolated_dir&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='kp'&gt;true&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;dir&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='kp'&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;before&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;dir&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;Dir&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;pwd&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='no'&gt;Dir&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;chdir&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='no'&gt;Dir&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;mktmpdir&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;after&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='no'&gt;Dir&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;chdir&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;dir&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;Ruby blocks retain bindings to the local variables present at their point of declaration, so we can create a local &lt;code&gt;dir&lt;/code&gt; variable in the parent context, and both the &lt;code&gt;before&lt;/code&gt; and &lt;code&gt;after&lt;/code&gt; blocks will have access to it.&lt;/p&gt;

&lt;p&gt;I think this is a superior way to write this code. It&amp;#8217;s easier to reason about (there&amp;#8217;s no way the host example group can accidentally modify the &lt;code&gt;dir&lt;/code&gt; variable) and state held by the host example group (such as &lt;code&gt;@dir&lt;/code&gt;) can&amp;#8217;t possibly be affected by including this shared context.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/myronmarsto/n/~4/hXVEA5hlWtA" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://myronmars.to/n/dev-blog/2012/10/using-closures-to-avoid-leaking-state</feedburner:origLink></entry>
  
  <entry>
    <title>Test Driving Database Indexes</title>
    <link href="http://feedproxy.google.com/~r/myronmarsto/n/~3/CzJYVKFkSPw/test-driving-database-indexes" />
    <updated>2012-09-24T00:00:00-07:00</updated>
    <id>http://myronmars.to/n/dev-blog/2012/09/test-driving-database-indexes</id>
    <content type="html">&lt;p&gt;Database indexes are conceptually very simple, but in practice, I&amp;#8217;ve found that it&amp;#8217;s hard to predict when they&amp;#8217;ll get used and what indexes a given table needs. On a project at work I came up with the idea to test-drive my database indexes, just like I test-drive the rest of my code. I&amp;#8217;d like to share the approach I came up with.&lt;/p&gt;

&lt;h2 id='the_goal'&gt;The Goal&lt;/h2&gt;

&lt;p&gt;Conceptually, this is how I wanted to write a test:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;my_model_spec.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;describe&lt;/span&gt; &lt;span class='no'&gt;MyModel&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='kp'&gt;include&lt;/span&gt; &lt;span class='no'&gt;CheckForFullTableScans&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;performs a complex query and returns data&amp;#39;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;check_for_full_table_scans&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='no'&gt;MyModel&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;db&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='n'&gt;results&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;MyModel&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;for_timeframe&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;current_year&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;widget_details&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='n'&gt;results&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='n'&gt;eq&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;whatever&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;Essentially, just wrap a block of code in &lt;code&gt;check_for_full_table_scans&lt;/code&gt;. Besides running the provided block, I wanted &lt;code&gt;check_for_full_table_scans&lt;/code&gt; to run an explain plan on each underlying query, keep track of all of them, and, at the completion of the block, raise an error if any of the explain plans included full table scans. In this way, I could start with a failing test, add an index, and watch the test pass after the change&amp;#8211;just like with normal TDD. In addition, the tests would provide regression coverage to protect against future schema changes introducing full table scans.&lt;/p&gt;

&lt;h2 id='the_code'&gt;The Code&lt;/h2&gt;

&lt;p&gt;Here&amp;#8217;s what I came up with:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;check_for_full_table_scans.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;module&lt;/span&gt; &lt;span class='nn'&gt;CheckForFullTableScans&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='no'&gt;FullTableScanError&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;Class&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='no'&gt;StandardError&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;check_for_full_table_scans&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;db&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;extension&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;FullTableScanChecker&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;DatabaseExtension&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;unless&lt;/span&gt; &lt;span class='n'&gt;db&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;singleton_class&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;ancestors&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;include?&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;extension&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='n'&gt;db&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;extend&lt;/span&gt; &lt;span class='n'&gt;extension&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='no'&gt;FullTableScanChecker&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;check&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='k'&gt;yield&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;FullTableScanChecker&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='kp'&gt;attr_reader&lt;/span&gt; &lt;span class='ss'&gt;:explain_plans&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='no'&gt;ExplainPlan&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;Struct&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:sql&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:results&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:backtrace&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;formatted_backtrace&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='n'&gt;backtrace&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;join&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;&lt;/span&gt;&lt;span class='se'&gt;\n&lt;/span&gt;&lt;span class='s2'&gt;       &amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;initialize&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='vi'&gt;@explain_plans&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='o'&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nc'&gt;self&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='nf'&gt;current_instance&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='no'&gt;Thread&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;current&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:__full_table_scan_checker&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nc'&gt;self&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='nf'&gt;check&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='n'&gt;instance&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='kp'&gt;new&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='no'&gt;Thread&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;current&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:__full_table_scan_checker&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;instance&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='k'&gt;yield&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;tap&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;instance&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;verify_no_full_table_scans&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;ensure&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='no'&gt;Thread&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;current&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:__full_table_scan_checker&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='kp'&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;explain_sql&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;sql&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='n'&gt;sql&lt;/span&gt; &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='n'&gt;sql&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;start_with?&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;EXPLAIN &amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='s2'&gt;&amp;quot;EXPLAIN &amp;quot;&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt; &lt;span class='n'&gt;sql&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;verify_no_full_table_scans&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='n'&gt;bad_plans&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;explain_plans&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;select&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;ep&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt; &lt;span class='n'&gt;has_full_table_scan?&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;ep&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='n'&gt;bad_plans&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;empty?&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='n'&gt;msg_parts&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;expected the executed queries would have no full table scans, but had &lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='n'&gt;bad_plans&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;size&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;:&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class='n'&gt;bad_plans&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;map&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;plan&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;          &lt;span class='s2'&gt;&amp;quot;  - SQL: &lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='n'&gt;plan&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;sql&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='se'&gt;\n&lt;/span&gt;&lt;span class='s2'&gt;    Explain Results: &lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='n'&gt;plan&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;results&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;inspect&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;          &lt;span class='s2'&gt;&amp;quot;&lt;/span&gt;&lt;span class='se'&gt;\n&lt;/span&gt;&lt;span class='s2'&gt;    Called From:&lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='n'&gt;plan&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;formatted_backtrace&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='k'&gt;raise&lt;/span&gt; &lt;span class='no'&gt;FullTableScanError&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;msg_parts&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;join&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;&lt;/span&gt;&lt;span class='se'&gt;\n\n&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;has_full_table_scan?&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;explain_plan&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='n'&gt;explain_plan&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;results&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;any?&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;result&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class='c1'&gt;# http://dev.mysql.com/doc/refman/5.0/en/explain-output.html#jointype_all&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class='c1'&gt;# http://dev.mysql.com/doc/refman/5.0/en/explain-output.html#explain-extra-information&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class='n'&gt;result&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;fetch&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:type&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='o'&gt;==&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;ALL&amp;quot;&lt;/span&gt; &lt;span class='o'&gt;||&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class='n'&gt;result&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;fetch&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:Extra&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='o'&gt;=~&lt;/span&gt; &lt;span class='sr'&gt;/full scan/i&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;module&lt;/span&gt; &lt;span class='nn'&gt;DatabaseExtension&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;_execute&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;connection&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;sql&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;opts&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;&amp;amp;&lt;/span&gt;&lt;span class='n'&gt;block&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class='n'&gt;full_table_scan_checker&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;FullTableScanChecker&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;current_instance&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='n'&gt;full_table_scan_checker&lt;/span&gt; &lt;span class='o'&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class='n'&gt;opts&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:type&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;==&lt;/span&gt; &lt;span class='ss'&gt;:select&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;          &lt;span class='n'&gt;explain_sql&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;full_table_scan_checker&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;explain_sql&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;sql&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;          &lt;span class='n'&gt;backtrace&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nb'&gt;caller&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;          &lt;span class='k'&gt;super&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;connection&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;explain_sql&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;opts&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;result&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;            &lt;span class='n'&gt;ep&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;ExplainPlan&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;sql&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;result&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;enum_for&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:each&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;to_a&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;backtrace&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;            &lt;span class='n'&gt;full_table_scan_checker&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;explain_plans&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class='n'&gt;ep&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;          &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class='k'&gt;super&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;connection&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;sql&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;opts&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;dup&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;&amp;amp;&lt;/span&gt;&lt;span class='n'&gt;block&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;Let&amp;#8217;s take it a piece at a time:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;check_for_full_table_scans.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;module&lt;/span&gt; &lt;span class='nn'&gt;DatabaseExtension&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;_execute&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;connection&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;sql&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;opts&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;&amp;amp;&lt;/span&gt;&lt;span class='n'&gt;block&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;full_table_scan_checker&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;FullTableScanChecker&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;current_instance&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='n'&gt;full_table_scan_checker&lt;/span&gt; &lt;span class='o'&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class='n'&gt;opts&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:type&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;==&lt;/span&gt; &lt;span class='ss'&gt;:select&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='n'&gt;explain_sql&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;full_table_scan_checker&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;explain_sql&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;sql&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='n'&gt;backtrace&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nb'&gt;caller&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='k'&gt;super&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;connection&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;explain_sql&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;opts&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;result&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class='n'&gt;ep&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;ExplainPlan&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;sql&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;result&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;enum_for&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:each&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;to_a&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;backtrace&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class='n'&gt;full_table_scan_checker&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;explain_plans&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class='n'&gt;ep&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;super&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;connection&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;sql&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;opts&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;dup&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;&amp;amp;&lt;/span&gt;&lt;span class='n'&gt;block&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;DatabaseExtension&lt;/code&gt; is the piece that hooks into each query to run the explain plan. On this project we&amp;#8217;re using &lt;a href='http://sequel.rubyforge.org/'&gt;Sequel&lt;/a&gt;, which runs every query through an &lt;a href='https://github.com/jeremyevans/sequel/blob/3.39.0/lib/sequel/adapters/mysql2.rb#L85-99'&gt;_execute&lt;/a&gt; method, so that&amp;#8217;s where we hook in. You&amp;#8217;ll notice this method has two calls to &lt;code&gt;super&lt;/code&gt;; that&amp;#8217;s because when we have a current &lt;code&gt;FullTableScanChecker&lt;/code&gt; instance (which indicates we&amp;#8217;re in a &lt;code&gt;check_for_full_table_scans&lt;/code&gt; block) and it is a SELECT query, we want to run the query twice: once prepended with &lt;code&gt;EXPLAIN&lt;/code&gt; to get the explain plan, and once as normal to get the real results. We store the SQL statement, the explain plan results, and the backtrace in a list of explain plans on the current &lt;code&gt;FullTableScanChecker&lt;/code&gt; instance.&lt;/p&gt;

&lt;p&gt;This module gets extended onto the database instance in &lt;code&gt;check_for_full_table_scans&lt;/code&gt; if it has not already been extended with the module:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;check_for_full_table_scans.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;check_for_full_table_scans&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;db&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;extension&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;FullTableScanChecker&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;DatabaseExtension&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;unless&lt;/span&gt; &lt;span class='n'&gt;db&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;singleton_class&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;ancestors&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;include?&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;extension&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;db&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;extend&lt;/span&gt; &lt;span class='n'&gt;extension&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='no'&gt;FullTableScanChecker&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;check&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='k'&gt;yield&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;This also delegates to &lt;code&gt;FullTableScanChecker&lt;/code&gt; to do the actual checking for full table scans. That class uses a thread local to store the current instance:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;check_for_full_table_scans.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nc'&gt;self&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='nf'&gt;current_instance&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='no'&gt;Thread&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;current&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:__full_table_scan_checker&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nc'&gt;self&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='nf'&gt;check&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;instance&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='kp'&gt;new&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='no'&gt;Thread&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;current&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:__full_table_scan_checker&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;instance&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;yield&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;tap&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;instance&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;verify_no_full_table_scans&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;ensure&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='no'&gt;Thread&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;current&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:__full_table_scan_checker&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='kp'&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;As you can see, &lt;code&gt;check&lt;/code&gt; yields, then checks all of the collected explain plans using &lt;code&gt;verify_no_full_table_scans&lt;/code&gt;:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;check_for_full_table_scans.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;verify_no_full_table_scans&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;bad_plans&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;explain_plans&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;select&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;ep&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt; &lt;span class='n'&gt;has_full_table_scan?&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;ep&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='n'&gt;bad_plans&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;empty?&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;msg_parts&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;expected the executed queries would have no full table scans, but had &lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='n'&gt;bad_plans&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;size&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;:&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;bad_plans&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;map&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;plan&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='s2'&gt;&amp;quot;  - SQL: &lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='n'&gt;plan&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;sql&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='se'&gt;\n&lt;/span&gt;&lt;span class='s2'&gt;    Explain Results: &lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='n'&gt;plan&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;results&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;inspect&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;&lt;/span&gt; &lt;span class='o'&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='s2'&gt;&amp;quot;&lt;/span&gt;&lt;span class='se'&gt;\n&lt;/span&gt;&lt;span class='s2'&gt;    Called From:&lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='n'&gt;plan&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;formatted_backtrace&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;raise&lt;/span&gt; &lt;span class='no'&gt;FullTableScanError&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;msg_parts&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;join&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;&lt;/span&gt;&lt;span class='se'&gt;\n\n&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;has_full_table_scan?&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;explain_plan&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;explain_plan&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;results&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;any?&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;result&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='c1'&gt;# http://dev.mysql.com/doc/refman/5.0/en/explain-output.html#jointype_all&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='c1'&gt;# http://dev.mysql.com/doc/refman/5.0/en/explain-output.html#explain-extra-information&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;result&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;fetch&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:type&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='o'&gt;==&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;ALL&amp;quot;&lt;/span&gt; &lt;span class='o'&gt;||&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;result&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;fetch&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:Extra&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='o'&gt;=~&lt;/span&gt; &lt;span class='sr'&gt;/full scan/i&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;This looks for full table scans based on some things I found mentioned in the MySQL docs. There are probably other things I could check for as well.&lt;/p&gt;

&lt;h2 id='the_output'&gt;The Output&lt;/h2&gt;

&lt;p&gt;Here&amp;#8217;s what the output from this code looks like:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class=''&gt;&lt;span class='line'&gt;CheckForFullTableScans::FullTableScanError:
&lt;/span&gt;&lt;span class='line'&gt;   expected the executed queries would have no full table scans, but had 1:
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;     - SQL: SELECT * FROM `top_rankings` INNER JOIN `ranking_deltas` ON ((`ranking_deltas`.`top_ranking_id` = `top_rankings`.`id`) AND (`ranking_deltas`.`type` = 'previous_week')) WHERE (`top_rankings`.`competitor_name` = 'Some Competitor') ORDER BY `ranking_deltas`.`sort_order` DESC
&lt;/span&gt;&lt;span class='line'&gt;       Explain Results: [{:id=&gt;1, :select_type=&gt;"SIMPLE", :table=&gt;"top_rankings", :type=&gt;"ALL", :possible_keys=&gt;"PRIMARY", :key=&gt;nil, :key_len=&gt;nil, :ref=&gt;nil, :rows=&gt;4, :Extra=&gt;"Using where; Using temporary; Using filesort"}, {:id=&gt;1, :select_type=&gt;"SIMPLE", :table=&gt;"ranking_deltas", :type=&gt;"eq_ref", :possible_keys=&gt;"ranking_deltas_top_ranking_id_type_index", :key=&gt;"ranking_deltas_top_ranking_id_type_index", :key_len=&gt;"771", :ref=&gt;"vanguard_test.top_rankings.id,const", :rows=&gt;1, :Extra=&gt;"Using where"}]
&lt;/span&gt;&lt;span class='line'&gt;       Called From:
&lt;/span&gt;&lt;span class='line'&gt;          &amp;lt;full backtrace&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;h2 id='conclusion'&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;We&amp;#8217;ve been using this for a couple of months, and it&amp;#8217;s working really well for us so far. As I mentioned above, we&amp;#8217;re using Sequel; I&amp;#8217;m sure you could use a similar technique for ActiveRecord, but you&amp;#8217;d have to hook into each query in a different manner (I suspect most of &lt;code&gt;FullTableScanChecker&lt;/code&gt; could remain the same, though).&lt;/p&gt;

&lt;p&gt;I&amp;#8217;d be curious to hear if anyone has tried something like this before!&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/myronmarsto/n/~4/CzJYVKFkSPw" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://myronmars.to/n/dev-blog/2012/09/test-driving-database-indexes</feedburner:origLink></entry>
  
  <entry>
    <title>Mixing and Matching Parts of RSpec</title>
    <link href="http://feedproxy.google.com/~r/myronmarsto/n/~3/vmI7QEF9Kic/mixing-and-matching-parts-of-rspec" />
    <updated>2012-07-23T00:00:00-07:00</updated>
    <id>http://myronmars.to/n/dev-blog/2012/07/mixing-and-matching-parts-of-rspec</id>
    <content type="html">&lt;p&gt;RSpec was split into three subprojects for the last major release (2.0):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;rspec-core: The test runner and main DSL (&lt;code&gt;describe&lt;/code&gt;, &lt;code&gt;it&lt;/code&gt;, &lt;code&gt;before&lt;/code&gt;, &lt;code&gt;after&lt;/code&gt;, &lt;code&gt;let&lt;/code&gt;, &lt;code&gt;shared_examples&lt;/code&gt;, etc).&lt;/li&gt;

&lt;li&gt;rspec-expectations: Provides a readable syntax for specifying the expected outcomes of your tests using matchers.&lt;/li&gt;

&lt;li&gt;rspec-mocks: RSpec&amp;#8217;s test-double framework.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One of the cool things about this is that it allows you to mix and match parts of RSpec with other testing libraries. Unfortunately, even though RSpec 2 has been out for over 18 months, there&amp;#8217;s not a lot of good information out there about how to do that.&lt;/p&gt;

&lt;p&gt;I want to correct that by showing a few examples of the possibilities.&lt;/p&gt;

&lt;p&gt;All of the examples below have some setup/configuration code at the top that you would probably want to extract into &lt;code&gt;test_helper.rb&lt;/code&gt; or &lt;code&gt;spec_helper.rb&lt;/code&gt; in a real project.&lt;/p&gt;

&lt;p&gt;I made a &lt;a href='https://github.com/myronmarston/mix_and_match_rspec'&gt;github project&lt;/a&gt; with all these examples, so check that out if you want some code you can play with.&lt;/p&gt;

&lt;h2 id='using_rspeccore_with_another_assertion_library'&gt;Using rspec-core with another assertion library&lt;/h2&gt;

&lt;p&gt;If you like RSpec&amp;#8217;s test runner, but don&amp;#8217;t like the syntax and failure output provided by rspec-expectations, you can use the assertions from the standard library provided by &lt;a href='http://ruby-doc.org/stdlib-1.9.3/libdoc/minitest/unit/rdoc/MiniTest/Assertions.html'&gt;MiniTest&lt;/a&gt;:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;rspec_and_minitest_assertions.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;set&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='no'&gt;RSpec&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;configure&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;rspec&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;rspec&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;expect_with&lt;/span&gt; &lt;span class='ss'&gt;:stdlib&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;describe&lt;/span&gt; &lt;span class='no'&gt;Set&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;specify&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;a passing example&amp;quot;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;set&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;Set&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;set&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class='mi'&gt;3&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class='mi'&gt;4&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;assert_include&lt;/span&gt; &lt;span class='n'&gt;set&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;specify&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;a failing example&amp;quot;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;set&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;Set&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;set&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class='mi'&gt;3&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class='mi'&gt;4&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;assert_include&lt;/span&gt; &lt;span class='n'&gt;set&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;5&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;The output:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class=''&gt;&lt;span class='line'&gt;$ rspec rspec_and_minitest_assertions.rb 
&lt;/span&gt;&lt;span class='line'&gt;.F
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;Failures:
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  1) Set a failing example
&lt;/span&gt;&lt;span class='line'&gt;     Failure/Error: assert_include set, 5
&lt;/span&gt;&lt;span class='line'&gt;     MiniTest::Assertion:
&lt;/span&gt;&lt;span class='line'&gt;       Expected #&amp;lt;Set: {3, 4}&gt; to include 5.
&lt;/span&gt;&lt;span class='line'&gt;     # ./rspec_and_minitest_assertions.rb:17:in `block (2 levels) in &amp;lt;top (required)&gt;'
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;Finished in 0.00093 seconds
&lt;/span&gt;&lt;span class='line'&gt;2 examples, 1 failure
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;Failed examples:
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;rspec ./rspec_and_minitest_assertions.rb:14 # Set a failing example&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href='https://github.com/sconover/wrong/'&gt;Wrong&lt;/a&gt; is an interesting alternative that uses a single method (&lt;code&gt;assert&lt;/code&gt; with a block) to provide detailed failure output:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;rspec_and_wrong.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;set&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;wrong&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='no'&gt;RSpec&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;configure&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;rspec&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;rspec&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;expect_with&lt;/span&gt; &lt;span class='no'&gt;Wrong&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;describe&lt;/span&gt; &lt;span class='no'&gt;Set&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;specify&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;a passing example&amp;quot;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;set&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;Set&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;set&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class='mi'&gt;3&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class='mi'&gt;4&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;assert&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;set&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;include?&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;3&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;specify&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;a failing example&amp;quot;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;set&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;Set&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;set&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class='mi'&gt;3&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class='mi'&gt;4&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;assert&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;set&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;include?&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;5&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;The output:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class=''&gt;&lt;span class='line'&gt;$ rspec rspec_and_wrong.rb
&lt;/span&gt;&lt;span class='line'&gt;.F
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;Failures:
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  1) Set a failing example
&lt;/span&gt;&lt;span class='line'&gt;     Failure/Error: assert { set.include?(5) }
&lt;/span&gt;&lt;span class='line'&gt;     Wrong::Assert::AssertionFailedError:
&lt;/span&gt;&lt;span class='line'&gt;       Expected set.include?(5), but
&lt;/span&gt;&lt;span class='line'&gt;           set is #&amp;lt;Set: {3, 4}&gt;
&lt;/span&gt;&lt;span class='line'&gt;     # ./rspec_and_wrong.rb:18:in `block (2 levels) in &amp;lt;top (required)&gt;'
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;Finished in 0.04012 seconds
&lt;/span&gt;&lt;span class='line'&gt;2 examples, 1 failure
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;Failed examples:
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;rspec ./rspec_and_wrong.rb:15 # Set a failing example&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;As demonstrated by these examples, you simply configure &lt;code&gt;expect_with&lt;/code&gt; to use an alternate library. You can specify &lt;code&gt;:stdlib&lt;/code&gt;, &lt;code&gt;:rspec&lt;/code&gt; (to be explicit about using rspec-expectations) or any module; the module will be mixed in to the example context.&lt;/p&gt;

&lt;h2 id='using_minitest_and_rspecexpectations'&gt;Using minitest and rspec-expectations&lt;/h2&gt;

&lt;p&gt;If you like running your tests with MiniTest but prefer the syntax and failure output of rspec-expectations, you can combine them:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;minitest_and_rspec_expectations.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;minitest/autorun&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;rspec/expectations&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;set&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='no'&gt;RSpec&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;Matchers&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;configuration&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;syntax&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='ss'&gt;:expect&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;module&lt;/span&gt; &lt;span class='nn'&gt;MiniTest&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;remove_const&lt;/span&gt; &lt;span class='ss'&gt;:Assertion&lt;/span&gt; &lt;span class='c1'&gt;# so we can re-assign it w/o a ruby warning&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='c1'&gt;# So expectation failures are considered failures, not errors.&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='no'&gt;Assertion&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;RSpec&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;Expectations&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;ExpectationNotMetError&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;Unit&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;TestCase&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='kp'&gt;include&lt;/span&gt; &lt;span class='no'&gt;RSpec&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;Matchers&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='c1'&gt;# So each use of `expect` is counted as an assertion...&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;expect&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='n'&gt;a&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;&amp;amp;&lt;/span&gt;&lt;span class='n'&gt;b&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='n'&gt;assert&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='kp'&gt;true&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='k'&gt;super&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;TestSet&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='no'&gt;MiniTest&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;Unit&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;TestCase&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;test_passing_expectation&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;set&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;Set&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;set&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class='mi'&gt;3&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class='mi'&gt;4&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;expect&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;set&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;to&lt;/span&gt; &lt;span class='kp'&gt;include&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;3&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;test_failing_expectation&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;set&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;Set&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;set&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class='mi'&gt;3&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class='mi'&gt;4&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;expect&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;set&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;to&lt;/span&gt; &lt;span class='kp'&gt;include&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;5&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;The output:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class=''&gt;&lt;span class='line'&gt;$ ruby minitest_and_rspec_expectations.rb
&lt;/span&gt;&lt;span class='line'&gt;Run options: --seed 12759
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;# Running tests:
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;.F
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;Finished tests in 0.001991s, 1004.5203 tests/s, 1004.5203 assertions/s.
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  1) Failure:
&lt;/span&gt;&lt;span class='line'&gt;test_failing_expectation(TestSet)
&lt;/span&gt;&lt;span class='line'&gt;[/Users/myron/.rvm/gems/ruby-1.9.3-p194/gems/rspec-expectations-2.11.1/lib/rspec/expectations/handler.rb:17]:
&lt;/span&gt;&lt;span class='line'&gt;expected #&amp;lt;Set: {3, 4}&gt; to include 5
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;2 tests, 2 assertions, 1 failures, 0 errors, 0 skips&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;Let&amp;#8217;s take the integration code bit-by-bit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;RSpec::Matchers.configuration.syntax = :expect&lt;/code&gt; configures the desired rspec-expectations syntax to just the &lt;a href='/n/dev-blog/2012/06/rspecs-new-expectation-syntax'&gt;new expect syntax&lt;/a&gt;. By default, both the old &lt;code&gt;should&lt;/code&gt; and the new &lt;code&gt;expect&lt;/code&gt; syntaxes are available.&lt;/li&gt;

&lt;li&gt;MiniTest treats &lt;code&gt;MiniTest::Assertion&lt;/code&gt; exceptions differently from other errors, counting them as test failures rather than errors in the output. We re-assign the constant to &lt;code&gt;RSpec::Expectations::ExpectationNotMetError&lt;/code&gt; in order to have our expectation failures counted as failures, not errors.&lt;/li&gt;

&lt;li&gt;MiniTest&amp;#8217;s output includes a count of the total number of assertions and the number of assertions per second. We add a call to &lt;code&gt;assert(true)&lt;/code&gt; every time &lt;code&gt;expect&lt;/code&gt; is called so the counts are correct.&lt;/li&gt;

&lt;li&gt;Finally, we include &lt;code&gt;RSpec::Matchers&lt;/code&gt; into the test context to make &lt;code&gt;expect&lt;/code&gt; and the matchers available.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For other test runners, you could probably get by with just mixing &lt;code&gt;RSpec::Matchers&lt;/code&gt; into your test context&amp;#8211;most of the rest of this is MiniTest-specific.&lt;/p&gt;

&lt;h2 id='using_minitest_and_rspecmocks'&gt;Using minitest and rspec-mocks&lt;/h2&gt;

&lt;p&gt;MiniTest features a mock object framework, that, in &lt;a href='https://github.com/seattlerb/minitest/blob/master/lib/minitest/mock.rb'&gt;the words of the README&lt;/a&gt;, &amp;#8220;is a beautifully tiny mock (and stub) object framework&amp;#8221;. It is indeed beautiful and tiny. However, rspec-mocks has many more features, and if you like those features, you can easily use it with MiniTest:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;minitest_and_rspec_mocks.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;minitest/autorun&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;rspec/mocks&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='no'&gt;MiniTest&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;Unit&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;TestCase&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;add_setup_hook&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;test_case&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='no'&gt;RSpec&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;Mocks&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;setup&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;test_case&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='no'&gt;MiniTest&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;Unit&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;TestCase&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;add_teardown_hook&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;test_case&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;begin&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='no'&gt;RSpec&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;Mocks&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;verify&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;ensure&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='no'&gt;RSpec&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;Mocks&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;teardown&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;TestSet&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='no'&gt;MiniTest&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;Unit&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;TestCase&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;test_passing_mock&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;foo&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;mock&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;foo&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;should_receive&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:bar&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;foo&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;bar&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;test_failing_mock&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;foo&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;mock&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;foo&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;should_receive&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:bar&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;test_stub_real_object&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='no'&gt;Object&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;stub&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;foo&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;bar&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;assert_equal&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;bar&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='no'&gt;Object&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;foo&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;The output:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class=''&gt;&lt;span class='line'&gt;$ ruby minitest_and_rspec_mocks.rb 
&lt;/span&gt;&lt;span class='line'&gt;Run options: --seed 27480
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;# Running tests:
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;..E
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;Finished tests in 0.002546s, 1178.3189 tests/s, 392.7730 assertions/s.
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  1) Error:
&lt;/span&gt;&lt;span class='line'&gt;test_failing_mock(TestSet):
&lt;/span&gt;&lt;span class='line'&gt;RSpec::Mocks::MockExpectationError: (Mock).bar(any args)
&lt;/span&gt;&lt;span class='line'&gt;    expected: 1 time
&lt;/span&gt;&lt;span class='line'&gt;    received: 0 times
&lt;/span&gt;&lt;span class='line'&gt;    minitest_and_rspec_mocks.rb:25:in `test_failing_mock'
&lt;/span&gt;&lt;span class='line'&gt;    /Users/myron/.rvm/gems/ruby-1.9.3-p194/gems/rspec-mocks-2.11.1/lib/rspec/mocks/error_generator.rb:87:in `__raise'
&lt;/span&gt;&lt;span class='line'&gt;    /Users/myron/.rvm/gems/ruby-1.9.3-p194/gems/rspec-mocks-2.11.1/lib/rspec/mocks/error_generator.rb:46:in `raise_expectation_error'
&lt;/span&gt;&lt;span class='line'&gt;    /Users/myron/.rvm/gems/ruby-1.9.3-p194/gems/rspec-mocks-2.11.1/lib/rspec/mocks/message_expectation.rb:259:in `generate_error'
&lt;/span&gt;&lt;span class='line'&gt;    /Users/myron/.rvm/gems/ruby-1.9.3-p194/gems/rspec-mocks-2.11.1/lib/rspec/mocks/message_expectation.rb:215:in `verify_messages_received'
&lt;/span&gt;&lt;span class='line'&gt;    /Users/myron/.rvm/gems/ruby-1.9.3-p194/gems/rspec-mocks-2.11.1/lib/rspec/mocks/method_double.rb:117:in `block in verify'
&lt;/span&gt;&lt;span class='line'&gt;    /Users/myron/.rvm/gems/ruby-1.9.3-p194/gems/rspec-mocks-2.11.1/lib/rspec/mocks/method_double.rb:117:in `each'
&lt;/span&gt;&lt;span class='line'&gt;    /Users/myron/.rvm/gems/ruby-1.9.3-p194/gems/rspec-mocks-2.11.1/lib/rspec/mocks/method_double.rb:117:in `verify'
&lt;/span&gt;&lt;span class='line'&gt;    /Users/myron/.rvm/gems/ruby-1.9.3-p194/gems/rspec-mocks-2.11.1/lib/rspec/mocks/proxy.rb:96:in `block in verify'
&lt;/span&gt;&lt;span class='line'&gt;    /Users/myron/.rvm/gems/ruby-1.9.3-p194/gems/rspec-mocks-2.11.1/lib/rspec/mocks/proxy.rb:96:in `each'
&lt;/span&gt;&lt;span class='line'&gt;    /Users/myron/.rvm/gems/ruby-1.9.3-p194/gems/rspec-mocks-2.11.1/lib/rspec/mocks/proxy.rb:96:in `verify'
&lt;/span&gt;&lt;span class='line'&gt;    /Users/myron/.rvm/gems/ruby-1.9.3-p194/gems/rspec-mocks-2.11.1/lib/rspec/mocks/methods.rb:116:in `rspec_verify'
&lt;/span&gt;&lt;span class='line'&gt;    /Users/myron/.rvm/gems/ruby-1.9.3-p194/gems/rspec-mocks-2.11.1/lib/rspec/mocks/space.rb:11:in `block in verify_all'
&lt;/span&gt;&lt;span class='line'&gt;    /Users/myron/.rvm/gems/ruby-1.9.3-p194/gems/rspec-mocks-2.11.1/lib/rspec/mocks/space.rb:10:in `each'
&lt;/span&gt;&lt;span class='line'&gt;    /Users/myron/.rvm/gems/ruby-1.9.3-p194/gems/rspec-mocks-2.11.1/lib/rspec/mocks/space.rb:10:in `verify_all'
&lt;/span&gt;&lt;span class='line'&gt;    /Users/myron/.rvm/gems/ruby-1.9.3-p194/gems/rspec-mocks-2.11.1/lib/rspec/mocks.rb:19:in `verify'
&lt;/span&gt;&lt;span class='line'&gt;    minitest_and_rspec_mocks.rb:10:in `block in &amp;lt;main&gt;'
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;3 tests, 1 assertions, 0 failures, 1 errors, 0 skips&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;The integration here is a little more manual, but it&amp;#8217;s not bad:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Call &lt;code&gt;RSpec::Mocks.setup(test)&lt;/code&gt; before each test or example to ensure things are setup properly.&lt;/li&gt;

&lt;li&gt;Call &lt;code&gt;RSpec::Mocks.verify&lt;/code&gt; after each test or example so that mock expectations are checked.&lt;/li&gt;

&lt;li&gt;Call &lt;code&gt;RSpec::Mocks.teardown&lt;/code&gt; at the very end to ensure that any modifications to real objects (for a stub, for example) are cleaned up. Note: this &lt;em&gt;must&lt;/em&gt; be called after every test (even failing ones) to prevent stubs from &amp;#8220;leaking&amp;#8221; outside of a given test. That&amp;#8217;s why I put this in the &lt;code&gt;ensure&lt;/code&gt; clause above.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id='using_rspeccore_with_another_mocking_library'&gt;Using rspec-core with another mocking library&lt;/h2&gt;

&lt;p&gt;RSpec can easily be used with an alternate mocking library. In fact, many RSpec users prefer &lt;a href='http://gofreerange.com/mocha/docs/'&gt;Mocha&lt;/a&gt; to rspec-mocks, and the two can integrate just fine:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;rspec_and_mocha.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='no'&gt;RSpec&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;configure&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;rspec&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;rspec&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;mock_with&lt;/span&gt; &lt;span class='ss'&gt;:mocha&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;describe&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;RSpec and Mocha&amp;quot;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;specify&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;a passing mock&amp;quot;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;foo&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;mock&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;foo&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;expects&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:bar&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;foo&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;bar&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;specify&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;a failing mock&amp;quot;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;foo&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;mock&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;foo&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;expects&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:bar&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;specify&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;stubbing a real object&amp;quot;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;foo&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;Object&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;foo&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;stubs&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;bar&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt; &lt;span class='mi'&gt;3&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;expect&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;foo&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;bar&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;to&lt;/span&gt; &lt;span class='n'&gt;eq&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;3&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;The output:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class=''&gt;&lt;span class='line'&gt;rspec rspec_and_mocha.rb 
&lt;/span&gt;&lt;span class='line'&gt;.F.
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;Failures:
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  1) RSpec and Mocha a failing mock
&lt;/span&gt;&lt;span class='line'&gt;     Failure/Error: foo.expects(:bar)
&lt;/span&gt;&lt;span class='line'&gt;     Mocha::ExpectationError:
&lt;/span&gt;&lt;span class='line'&gt;       not all expectations were satisfied
&lt;/span&gt;&lt;span class='line'&gt;       unsatisfied expectations:
&lt;/span&gt;&lt;span class='line'&gt;       - expected exactly once, not yet invoked: #&amp;lt;Mock:0x7fcc01844840&gt;.bar(any_parameters)
&lt;/span&gt;&lt;span class='line'&gt;     # ./rspec_and_mocha.rb:14:in `block (2 levels) in &amp;lt;top (required)&gt;'
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;Finished in 0.00388 seconds
&lt;/span&gt;&lt;span class='line'&gt;3 examples, 1 failure
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;Failed examples:
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;rspec ./rspec_and_mocha.rb:12 # RSpec and Mocha a failing mock&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;You can similarly configure &lt;code&gt;mock_with :flexmock&lt;/code&gt; or &lt;code&gt;mock_with :rr&lt;/code&gt; to use one of those mocking libraries. I didn&amp;#8217;t include those examples here because they&amp;#8217;re configured in the exact same way, but &lt;a href='https://github.com/myronmarston/mix_and_match_rspec'&gt;the github repo&lt;/a&gt; containing all the examples in this blog post has examples for them, too.&lt;/p&gt;

&lt;h2 id='conclusion'&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;It takes some extra leg work, but most of ruby&amp;#8217;s testing tools can integrate with each other without any problems, so don&amp;#8217;t feel constrained to use all of MiniTest or all of RSpec if there&amp;#8217;s parts you love and parts you dislike. Use a testing stack that best meets your needs.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/myronmarsto/n/~4/vmI7QEF9Kic" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://myronmars.to/n/dev-blog/2012/07/mixing-and-matching-parts-of-rspec</feedburner:origLink></entry>
  
  <entry>
    <title>Constant Stubbing in RSpec 2.11</title>
    <link href="http://feedproxy.google.com/~r/myronmarsto/n/~3/vqznsw_kpsM/constant-stubbing-in-rspec-2-11" />
    <updated>2012-06-29T00:00:00-07:00</updated>
    <id>http://myronmars.to/n/dev-blog/2012/06/constant-stubbing-in-rspec-2-11</id>
    <content type="html">&lt;p&gt;In the 2.11 release, rspec-mocks is gaining a significant new capability that, as far as I know, isn&amp;#8217;t provided by any other ruby mocking library: constant stubbing&lt;sup id='fnref:1'&gt;&lt;a href='#fn:1' rel='footnote'&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;Let&amp;#8217;s look at the API, and then talk a bit about some of the use cases for it.&lt;/p&gt;

&lt;h2 id='the_api'&gt;The API&lt;/h2&gt;

&lt;p&gt;The main API is &lt;code&gt;stub_const&lt;/code&gt;:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;stub_const.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;describe&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;stub_const&amp;quot;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;changes the constant value for the duration of the example&amp;quot;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;stub_const&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;Foo::SIZE&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;10&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;expect&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='no'&gt;Foo&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;SIZE&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;to&lt;/span&gt; &lt;span class='n'&gt;eq&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;10&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;This works for both defined and undefined constants; you could stub &lt;code&gt;A::B::C::D::E::F&lt;/code&gt; even if none of the intermediary constants exist. When the example completes, the constants will be restored to their original states: any newly defined constants will be undefined, and any modified constants will be restored to their original values.&lt;/p&gt;

&lt;p&gt;Note that constant names must be fully qualified; the current module nesting is not considered:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;stub_const.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;module&lt;/span&gt; &lt;span class='nn'&gt;MyGem&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;SomeClass&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt; &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;module&lt;/span&gt; &lt;span class='nn'&gt;MyGem&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;describe&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;Something&amp;quot;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;let&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:fake_class&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='no'&gt;Class&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;accidentally stubs the wrong constant&amp;quot;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='c1'&gt;# this stubs ::SomeClass (in the top-level namespace),&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='c1'&gt;# not MyGem::SomeClass like you probably mean.&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='n'&gt;stub_const&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;SomeClass&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;fake_class&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;stubs the right constant&amp;quot;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='n'&gt;stub_const&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;MyGem::SomeClass&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;fake_class&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;stub_const&lt;/code&gt; also supports a &lt;code&gt;:transfer_nested_constants&lt;/code&gt; option. Consider a case where you have nested constants:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;card_deck.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;CardDeck&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='no'&gt;SUITS&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:spades&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:diamonds&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:clubs&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:hearts&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='no'&gt;NUM_CARDS&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='mi'&gt;52&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;stub_const(&amp;quot;CardDeck&amp;quot;, fake_class)&lt;/code&gt; cuts off access to the nested constants (&lt;code&gt;CardDeck::SUITS&lt;/code&gt; and &lt;code&gt;CardDeck::NUM_CARDS&lt;/code&gt;), unless you manually assign &lt;code&gt;fake_class::SUITS&lt;/code&gt; and &lt;code&gt;fake_class::NUM_CARDS&lt;/code&gt;. The &lt;code&gt;:transfer_nested_constants&lt;/code&gt; option is provided to take care of this for you:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;transfer_nested_constants.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='c1'&gt;# Default behavior:&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;fake_class&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;Class&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;stub_const&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;CardDeck&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;fake_class&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='no'&gt;CardDeck&lt;/span&gt; &lt;span class='c1'&gt;# =&amp;gt; fake_class&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='no'&gt;CardDeck&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;SUITS&lt;/span&gt; &lt;span class='c1'&gt;# =&amp;gt; raises uninitialized constant error&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='no'&gt;CardDeck&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;NUM_CARDS&lt;/span&gt; &lt;span class='c1'&gt;# =&amp;gt; raises uninitialized constant error&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='c1'&gt;# `:transfer_nested_constants =&amp;gt; true` transfers all nested constants:&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;stub_const&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;CardDeck&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;fake_class&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:transfer_nested_constants&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='kp'&gt;true&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='no'&gt;CardDeck&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;SUITS&lt;/span&gt; &lt;span class='c1'&gt;# =&amp;gt; [:spades, :diamonds, :clubs, :hearts]&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='no'&gt;CardDeck&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;NUM_CARDS&lt;/span&gt; &lt;span class='c1'&gt;# =&amp;gt; 52&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='c1'&gt;# Or you can specify a list of constants to transfer:&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;stub_const&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;CardDeck&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;fake_class&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:transfer_nested_constants&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:SUITS&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='no'&gt;CardDeck&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;SUITS&lt;/span&gt; &lt;span class='c1'&gt;# =&amp;gt; [:spades, :diamonds, :clubs, :hearts]&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='no'&gt;CardDeck&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;NUM_CARDS&lt;/span&gt; &lt;span class='c1'&gt;# =&amp;gt; raises uninitialized constant error&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;h2 id='use_cases'&gt;Use Cases&lt;/h2&gt;

&lt;p&gt;I&amp;#8217;ve found this useful in a few different situations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It provides a simple way to change a class setting expressed as a constant for one test. In the past, I&amp;#8217;ve often defined static class methods just so they could be stubbed, even though it made more sense to use a constant. Now you can just use a constant!&lt;/li&gt;

&lt;li&gt;It makes dependency injection easy when the class-under-test depends on a collaborator&amp;#8217;s class method (e.g. when the collaborator is stateless). You can easily stub the collaborator&amp;#8217;s class constant with a test double.&lt;/li&gt;

&lt;li&gt;It makes stubbing unloaded dependencies dead-simple. Gary Bernhardt discussed this situation at length in &lt;a href='https://www.destroyallsoftware.com/screencasts/catalog/stubbing-unloaded-dependencies'&gt;Destroy all Software #46&lt;/a&gt;. He mentioned mutating constants as a possible way of stubbing unloaded dependencies, but recommended against it because of the complexity of safely managing this. Now that rspec-mocks can do it for you, it&amp;#8217;s far less complex, and much, much safer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you&amp;#8217;re curious how it all works, check out the &lt;a href='https://github.com/rspec/rspec-mocks/blob/master/lib/rspec/mocks/stub_const.rb'&gt;source on github&lt;/a&gt;.&lt;/p&gt;
&lt;div class='footnotes'&gt;&lt;hr /&gt;&lt;ol&gt;&lt;li id='fn:1'&gt;
&lt;p&gt;Actually, this has been available in &lt;a href='https://github.com/xaviershay/rspec-fire'&gt;rspec-fire&lt;/a&gt; for a good four months or so. But it&amp;#8217;s not really a full mocking library&amp;#8230;it builds on top of rspec-mocks, and now this functionality has been ported over to rspec-mocks.&lt;/p&gt;
&lt;a href='#fnref:1' rev='footnote'&gt;&amp;#8617;&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/myronmarsto/n/~4/vqznsw_kpsM" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://myronmars.to/n/dev-blog/2012/06/constant-stubbing-in-rspec-2-11</feedburner:origLink></entry>
  
  <entry>
    <title>Thoughts on Mocking</title>
    <link href="http://feedproxy.google.com/~r/myronmarsto/n/~3/L96suAgEzLw/thoughts-on-mocking" />
    <updated>2012-06-20T00:00:00-07:00</updated>
    <id>http://myronmars.to/n/dev-blog/2012/06/thoughts-on-mocking</id>
    <content type="html">&lt;p&gt;I&amp;#8217;ve been thinking a lot about different styles of TDD recently, particularly the mockist vs. classical schools identified by Martin Fowler in his classic article, &lt;a href='http://martinfowler.com/articles/mocksArentStubs.html'&gt;Mocks Aren&amp;#8217;t Stubs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I personally tend to favor a more mockist style of testing, but I&amp;#8217;ve come to realize that mockist testing needs to go hand-in-hand with a particular school of OO design, and on many projects a classical testing approach would work far better.&lt;/p&gt;

&lt;p&gt;My friend &lt;a href='http://cohitre.com/'&gt;Carlos&lt;/a&gt; recently came to me with some questions about how mocking should be used, and expressed frustration at how brittle the mock-based tests on his current project are. As he describe it, the mocks caused the tests to be coupled to the implementation, and thus made refactoring very difficult. Martin Fowler also &lt;a href='http://martinfowler.com/articles/mocksArentStubs.html#CouplingTestsToImplementations'&gt;discusses this problem&lt;/a&gt; in his article.&lt;/p&gt;

&lt;p&gt;However, I don&amp;#8217;t think it&amp;#8217;s that simple. Mocking has often made it easier for me to refactor. And really, all tests have some level of coupling to the system-under-test, since it makes certain assumptions about how the code works or what side effects result from a particular action. I&amp;#8217;ve developed a theory recently:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Mock-based tests are more coupled to the &lt;strong&gt;interfaces&lt;/strong&gt; in your system, while classical tests are more coupled to the &lt;strong&gt;implementation&lt;/strong&gt; of an object&amp;#8217;s collaborators.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A mock-based test will verify the message the object-under-test sends to its collaborator (i.e. the interface). In contrast, in a classical test, one would typically allow the object-under-test to call a real method on a real collaborator, and then make an assertion about how that changes the state of the collaborator or one of the collaborator&amp;#8217;s collaborators. Thus, this kind of classical test contains knowledge of (and is coupled to) the implementation of the collaborator&amp;#8211;it has to know what the collaborator does in order to make an assertion about how it affects the state of the world.&lt;/p&gt;

&lt;p&gt;These different kinds of coupling lend themselves to different kinds of change. With a classical test, you can refactor the interface between the object-under-test and a collaborator, and the test will continue to pass just fine. However, if you change how the collaborator is implemented&amp;#8211;and in particular, how the state change is exposed&amp;#8211;then your test will likely break. In constrast, with a mockist test, you can completely change the collaborator all you want without breaking the test as long as you keep the interface the same.&lt;/p&gt;

&lt;h2 id='an_example'&gt;An Example&lt;/h2&gt;

&lt;p&gt;Let&amp;#8217;s look at a simple example to make this more concrete. We&amp;#8217;ve got a &lt;code&gt;User&lt;/code&gt; entity that delegates persistence to a collaborator and provides an &lt;code&gt;#archive!&lt;/code&gt; method:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;user.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;User&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;initialize&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;id&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;persistence&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;Persistence&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='vi'&gt;@id&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='vi'&gt;@persistence&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nb'&gt;id&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;persistence&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='kp'&gt;attr_reader&lt;/span&gt;   &lt;span class='ss'&gt;:id&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:persistence&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='kp'&gt;attr_accessor&lt;/span&gt; &lt;span class='ss'&gt;:archived_at&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;archive!&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='nb'&gt;self&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;archived_at&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;Time&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;now&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;persistence&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;save&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;self&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;attributes&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;archived_at&amp;quot;&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='n'&gt;archived_at&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;Persistence&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;redis&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='vi'&gt;@redis&lt;/span&gt; &lt;span class='o'&gt;||=&lt;/span&gt; &lt;span class='no'&gt;Redis&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;save&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;user&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;redis&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;set&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;users:&lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='n'&gt;user&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;id&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='no'&gt;JSON&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;dump&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;user&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;attributes&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;Here&amp;#8217;s how we might test the &lt;code&gt;#archive!&lt;/code&gt; method using a classical test and a mockist test:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;user_spec.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;describe&lt;/span&gt; &lt;span class='no'&gt;User&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;#archive!&amp;quot;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;context&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;using a mockist test&amp;#39;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;saves the user with the archived_at timestamp set&amp;#39;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='n'&gt;persistence&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;mock&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;Persistence&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='n'&gt;user&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;User&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;23&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;persistence&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='n'&gt;persistence&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;should_receive&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:save&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;u&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt; &lt;span class='n'&gt;u&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;archived_at&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;should_not&lt;/span&gt; &lt;span class='n'&gt;be_nil&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='n'&gt;user&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;archive!&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;context&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;using a classical test&amp;#39;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;saves the user with the archived_at timestamp set&amp;#39;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='n'&gt;user&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;User&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;23&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='n'&gt;user&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;archive!&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='n'&gt;attributes_json&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;user&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;persistence&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;redis&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;get&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;users:23&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='no'&gt;JSON&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;load&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;attributes_json&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;fetch&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;archived_at&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;should_not&lt;/span&gt; &lt;span class='n'&gt;be_nil&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;Which test is less brittle in this highly contrived example? It depends. If you decide to change the persistence interface (e.g. so that &lt;code&gt;#save&lt;/code&gt; is renamed as &lt;code&gt;#persist&lt;/code&gt;, or whatever), then the mockist test will break, but the classical test can continue to pass unchanged. On the other hand, if you decide to change the persistence implementation (e.g. so that it&amp;#8217;s saving to a relational database, or whatever), then the classical test will break, but the mockist test will continue to pass unchanged, as long as you preserve the &lt;code&gt;#save&lt;/code&gt; interface.&lt;/p&gt;

&lt;h2 id='conclusion'&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;With these observations in hand, I think we can draw conclusions about what sorts of codebases work well with mockist tests. Mocking works really well when the interfaces between objects in your system are stable and rarely change. From experience, I&amp;#8217;ve seen two primary factors affecting the stability of interfaces:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The size of the interfaces. Large interfaces become unwieldy over time while small, focused interfaces serve a single purpose well and rarely need to change.&lt;/li&gt;

&lt;li&gt;The extent to which your interfaces speak in the language of your application domain rather than the language of your technical infrastructure. This allows you to change large portions of your technical infrastructure without needing many changes to the interfaces between your domain objects.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If your codebase doesn&amp;#8217;t feature these kinds of interfaces, mocking is unlikely to work well for you and you&amp;#8217;re probably better off using classical testing techniques. Of course, this isn&amp;#8217;t an all-or-nothing choice; you can use mock-based testing for some parts of your system, and classical testing for other parts.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/myronmarsto/n/~4/L96suAgEzLw" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://myronmars.to/n/dev-blog/2012/06/thoughts-on-mocking</feedburner:origLink></entry>
  
  <entry>
    <title>RSpec's New Expectation Syntax</title>
    <link href="http://feedproxy.google.com/~r/myronmarsto/n/~3/CE2dIX6A9gA/rspecs-new-expectation-syntax" />
    <updated>2012-06-15T00:00:00-07:00</updated>
    <id>http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax</id>
    <content type="html">&lt;p&gt;RSpec has featured a readable english-like syntax for setting expectations for a long time:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;some_spec.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;foo&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='n'&gt;eq&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;bar&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;foo&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;should_not&lt;/span&gt; &lt;span class='n'&gt;eq&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;bar&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;RSpec 2.11 will include a new variant to this syntax:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;some_spec.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;expect&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;foo&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;to&lt;/span&gt; &lt;span class='n'&gt;eq&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;bar&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;expect&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;foo&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;not_to&lt;/span&gt; &lt;span class='n'&gt;eq&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;bar&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;There are a few things motivating this new syntax, and I wanted to blog about it to spread awareness.&lt;/p&gt;

&lt;h2 id='delegation_issues'&gt;Delegation Issues&lt;/h2&gt;

&lt;p&gt;Between &lt;code&gt;method_missing&lt;/code&gt;, &lt;code&gt;BasicObject&lt;/code&gt; and the standard library&amp;#8217;s &lt;code&gt;delegate&lt;/code&gt;, ruby has very rich tools for building delegate or proxy objects. Unfortunately, RSpec&amp;#8217;s &lt;code&gt;should&lt;/code&gt; syntax, as elegantly as it reads, is prone to producing weird, confusing failures when testing delegate/proxy objects.&lt;/p&gt;

&lt;p&gt;Consider a simple proxy object that subclasses &lt;code&gt;BasicObject&lt;/code&gt;:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;fuzzy_proxy.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;FuzzyProxy&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='no'&gt;BasicObject&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;initialize&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;target&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='vi'&gt;@target&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;target&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;fuzzy?&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='kp'&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;method_missing&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='n'&gt;args&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;&amp;amp;&lt;/span&gt;&lt;span class='n'&gt;block&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='vi'&gt;@target&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;__send__&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;*&lt;/span&gt;&lt;span class='n'&gt;args&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;&amp;amp;&lt;/span&gt;&lt;span class='n'&gt;block&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;Simple enough; it defines a &lt;code&gt;#fuzzy?&lt;/code&gt; predicate, and delegates all other method calls to the target object.&lt;/p&gt;

&lt;p&gt;Here&amp;#8217;s a simple spec to test its fuzziness:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;fuzzy_proxy_spec.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;describe&lt;/span&gt; &lt;span class='no'&gt;FuzzyProxy&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;is fuzzy&amp;#39;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;instance&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;FuzzyProxy&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:some_object&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;instance&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='n'&gt;be_fuzzy&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;Surprisingly, this fails:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;result &lt;/span&gt;&lt;/figcaption&gt;
&lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class=''&gt;&lt;span class='line'&gt;1) FuzzyProxy is a fuzzy proxy
&lt;/span&gt;&lt;span class='line'&gt;     Failure/Error: instance.should be_fuzzy
&lt;/span&gt;&lt;span class='line'&gt;     NoMethodError:
&lt;/span&gt;&lt;span class='line'&gt;       undefined method `fuzzy?' for :some_object:Symbol
&lt;/span&gt;&lt;span class='line'&gt;     # ./fuzzy_proxy.rb:11:in `method_missing'
&lt;/span&gt;&lt;span class='line'&gt;     # ./fuzzy_proxy_spec.rb:6:in `block (2 levels) in &amp;lt;top (required)&gt;'
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;Finished in 0.01152 seconds
&lt;/span&gt;&lt;span class='line'&gt;1 example, 1 failure&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;The problem is that rspec-expectations defines &lt;code&gt;should&lt;/code&gt; on &lt;code&gt;Kernel&lt;/code&gt;, and &lt;code&gt;BasicObject&lt;/code&gt; does not include &lt;code&gt;Kernel&lt;/code&gt;&amp;#8230;so &lt;code&gt;instance.should&lt;/code&gt; triggers &lt;code&gt;method_missing&lt;/code&gt; and gets delegated to the target object. The result is actually &lt;code&gt;:some_object.should be_fuzzy&lt;/code&gt; which is clearly false (or rather, a &lt;code&gt;NoMethodError&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;It gets even more confusing when using &lt;code&gt;delegate&lt;/code&gt; in the standard library. It &lt;a href='https://github.com/ruby/ruby/blob/v1_9_3_194/lib/delegate.rb#L43-50'&gt;selectively includes&lt;/a&gt; some of &lt;code&gt;Kernel&lt;/code&gt;&amp;#8217;s methods&amp;#8230;which means that if rspec-expectations gets loaded before &lt;code&gt;delegate&lt;/code&gt;, &lt;code&gt;should&lt;/code&gt; will work properly on delegate objects, but if &lt;code&gt;delegate&lt;/code&gt; is loaded first, it will proxy the &lt;code&gt;should&lt;/code&gt; calls just like in our &lt;code&gt;FuzzyProxy&lt;/code&gt; example above.&lt;/p&gt;

&lt;p&gt;The underlying problem is RSpec&amp;#8217;s &lt;code&gt;should&lt;/code&gt; syntax: for &lt;code&gt;should&lt;/code&gt; to work properly, it must be defined on every object in the system&amp;#8230; but RSpec does not own every object in the system and cannot ensure that it always works consistently. As we&amp;#8217;ve seen, it doesn&amp;#8217;t work as RSpec expects on proxy objects. Note that this isn&amp;#8217;t just a problem with RSpec; it&amp;#8217;s a problem with minitest/spec&amp;#8217;s &lt;code&gt;must_xxx&lt;/code&gt; syntax as well.&lt;/p&gt;

&lt;p&gt;The solution we came up with is the new &lt;code&gt;expect&lt;/code&gt; syntax:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;fuzzy_proxy_spec.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;describe&lt;/span&gt; &lt;span class='no'&gt;FuzzyProxy&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;is fuzzy&amp;#39;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;instance&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;FuzzyProxy&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:some_object&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;expect&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;instance&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;to&lt;/span&gt; &lt;span class='n'&gt;be_fuzzy&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;This does not rely on any methods being present on all objects in the system, and thus avoids the underlying problem altogether.&lt;/p&gt;

&lt;h2 id='almost_all_matchers_are_supported'&gt;(Almost) All Matchers Are Supported&lt;/h2&gt;

&lt;p&gt;The new &lt;code&gt;expect&lt;/code&gt; syntax looks different from the old &lt;code&gt;should&lt;/code&gt; syntax, but under the covers, it&amp;#8217;s essentially the same. You pass a matcher to the &lt;code&gt;#to&lt;/code&gt; method, and it fails the example if it does not match.&lt;/p&gt;

&lt;p&gt;All matchers are supported, with an important exception: the &lt;code&gt;expect&lt;/code&gt; syntax does not directly support the operator matchers.&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;eq_spec.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='c1'&gt;# rather than:&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;foo&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='o'&gt;==&lt;/span&gt; &lt;span class='n'&gt;bar&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='c1'&gt;# ...use:&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;expect&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;foo&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;to&lt;/span&gt; &lt;span class='n'&gt;eq&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;bar&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;While operator matchers are intuitive to use, they require special handling in RSpec for them to work right, due to Ruby&amp;#8217;s precedence rules. Furthermore, &lt;code&gt;should ==&lt;/code&gt; generates a ruby warning&lt;sup id='fnref:1'&gt;&lt;a href='#fn:1' rel='footnote'&gt;1&lt;/a&gt;&lt;/sup&gt;, and people have been occasionally surprised by the fact that &lt;code&gt;should !=&lt;/code&gt; does not work as they might expect&lt;sup id='fnref:2'&gt;&lt;a href='#fn:2' rel='footnote'&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;The new syntax affords us the chance to make a clean break from the inconsistencies of the operator matchers without the risk of breaking existing test suites, so we decided not to support operator matchers with the new syntax. Here&amp;#8217;s a listing of each of the old operator matchers (used with &lt;code&gt;should&lt;/code&gt;), and their &lt;code&gt;expect&lt;/code&gt; equivalent:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;expectations.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;foo&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='o'&gt;==&lt;/span&gt; &lt;span class='n'&gt;bar&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;expect&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;foo&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;to&lt;/span&gt; &lt;span class='n'&gt;eq&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;bar&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='s2'&gt;&amp;quot;a string&amp;quot;&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;should_not&lt;/span&gt; &lt;span class='o'&gt;=~&lt;/span&gt; &lt;span class='sr'&gt;/a regex/&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;expect&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;a string&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;not_to&lt;/span&gt; &lt;span class='n'&gt;match&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='sr'&gt;/a regex/&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;2&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;3&lt;/span&gt;&lt;span class='o'&gt;].&lt;/span&gt;&lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='o'&gt;=~&lt;/span&gt; &lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='mi'&gt;2&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;3&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;expect&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;2&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;3&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;to&lt;/span&gt; &lt;span class='n'&gt;match_array&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='mi'&gt;2&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;3&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;You may have noticed I didn&amp;#8217;t list the comparison matchers (e.g. &lt;code&gt;x.should &amp;lt; 10&lt;/code&gt;)&amp;#8211;that&amp;#8217;s because they work but have never been recommended. Who says &amp;#8220;x should less than 10&amp;#8221;? They were always intended to be used with &lt;code&gt;be&lt;/code&gt;, which both reads better and continues to work:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;comparison_matchers.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;foo&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='n'&gt;be&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='mi'&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;foo&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='n'&gt;be&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;=&lt;/span&gt; &lt;span class='mi'&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;foo&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='n'&gt;be&lt;/span&gt; &lt;span class='o'&gt;&amp;gt;&lt;/span&gt; &lt;span class='mi'&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;foo&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='n'&gt;be&lt;/span&gt; &lt;span class='o'&gt;&amp;gt;=&lt;/span&gt; &lt;span class='mi'&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;expect&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;foo&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;to&lt;/span&gt; &lt;span class='n'&gt;be&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='mi'&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;expect&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;foo&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;to&lt;/span&gt; &lt;span class='n'&gt;be&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;=&lt;/span&gt; &lt;span class='mi'&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;expect&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;foo&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;to&lt;/span&gt; &lt;span class='n'&gt;be&lt;/span&gt; &lt;span class='o'&gt;&amp;gt;&lt;/span&gt; &lt;span class='mi'&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;expect&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;foo&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;to&lt;/span&gt; &lt;span class='n'&gt;be&lt;/span&gt; &lt;span class='o'&gt;&amp;gt;=&lt;/span&gt; &lt;span class='mi'&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;h2 id='unification_of_block_vs_value_syntaxes'&gt;Unification of Block vs. Value Syntaxes&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;expect&lt;/code&gt; has actually been available in RSpec for a long time&lt;sup id='fnref:3'&gt;&lt;a href='#fn:3' rel='footnote'&gt;3&lt;/a&gt;&lt;/sup&gt; in a limited form, as a more-readable alternative for block expectations:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;block_expectations.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='c1'&gt;# rather than:&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='nb'&gt;lambda&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;do_something&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='n'&gt;raise_error&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='no'&gt;SomeError&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='c1'&gt;# ...you can do:&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;expect&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;something&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;to&lt;/span&gt; &lt;span class='n'&gt;raise_error&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='no'&gt;SomeError&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;Before RSpec 2.11, &lt;code&gt;expect&lt;/code&gt; would not accept any normal arguments, and could not be used for value expectations. With the changes in 2.11, it&amp;#8217;s nice to have the unity of the same syntax for both kinds of expectations.&lt;/p&gt;

&lt;h2 id='configuration_options'&gt;Configuration Options&lt;/h2&gt;

&lt;p&gt;By default, both the &lt;code&gt;should&lt;/code&gt; and &lt;code&gt;expect&lt;/code&gt; syntaxes are available. However, if you want to use only one syntax or the other, you can configure RSpec:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;spec_helper.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='no'&gt;RSpec&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;configure&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;config&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;config&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;expect_with&lt;/span&gt; &lt;span class='ss'&gt;:rspec&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;c&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='c1'&gt;# Disable the `expect` sytax...&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;c&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;syntax&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='ss'&gt;:should&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='c1'&gt;# ...or disable the `should` syntax...&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;c&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;syntax&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='ss'&gt;:expect&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='c1'&gt;# ...or explicitly enable both&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;c&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;syntax&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:should&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:expect&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;For example, if you&amp;#8217;re starting a new project, and you want to ensure only &lt;code&gt;expect&lt;/code&gt; is used for consistency, you can disable &lt;code&gt;should&lt;/code&gt; entirely. When one of the syntaxes is disabled, the corresponding method will simply be undefined.&lt;/p&gt;

&lt;p&gt;In the future, we plan to change the defaults so that only &lt;code&gt;expect&lt;/code&gt; is available unless you explicitly enable &lt;code&gt;should&lt;/code&gt;. We may do this as soon as RSpec 3.0, but we want to give users plenty of time to get acquianted with it.&lt;/p&gt;

&lt;p&gt;Let us know what you think!&lt;/p&gt;
&lt;div class='footnotes'&gt;&lt;hr /&gt;&lt;ol&gt;&lt;li id='fn:1'&gt;
&lt;p&gt;As &lt;a href='http://mislav.uniqpath.com/2011/06/ruby-verbose-mode/'&gt;Mislav reports&lt;/a&gt;, when warnings are turned on, you can get a &amp;#8220;Useless use of == in void context&amp;#8221; warning.&lt;/p&gt;
&lt;a href='#fnref:1' rev='footnote'&gt;&amp;#8617;&lt;/a&gt;&lt;/li&gt;&lt;li id='fn:2'&gt;
&lt;p&gt;On ruby 1.8, &lt;code&gt;x.should != y&lt;/code&gt; is syntactic sugar for &lt;code&gt;!(x.should == y)&lt;/code&gt; and RSpec has no way to distinguish &lt;code&gt;should ==&lt;/code&gt; from &lt;code&gt;should !=&lt;/code&gt;. On 1.9, we can distinguish between them (since &lt;code&gt;!=&lt;/code&gt; can now be defined as a separate method), but it would be confusing to support it on 1.9 but not on 1.8, so we &lt;a href='https://github.com/rspec/rspec-expectations/issues/33'&gt;decided to just raise an error instead&lt;/a&gt;.&lt;/p&gt;
&lt;a href='#fnref:2' rev='footnote'&gt;&amp;#8617;&lt;/a&gt;&lt;/li&gt;&lt;li id='fn:3'&gt;
&lt;p&gt;It was originally added &lt;a href='https://github.com/dchelimsky/rspec/commit/7e4f872b4becbd41588da95c0e5d954a6e770293'&gt;over 3 years ago!&lt;/a&gt;&lt;/p&gt;
&lt;a href='#fnref:3' rev='footnote'&gt;&amp;#8617;&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/myronmarsto/n/~4/CE2dIX6A9gA" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax</feedburner:origLink></entry>
  
  <entry>
    <title>In Defense of 100% Test Coverage</title>
    <link href="http://feedproxy.google.com/~r/myronmarsto/n/~3/g_IbZToyhSY/in-defense-of-100-test-coverage" />
    <updated>2012-05-14T00:00:00-07:00</updated>
    <id>http://myronmars.to/n/dev-blog/2012/05/in-defense-of-100-test-coverage</id>
    <content type="html">&lt;p&gt;I&amp;#8217;d like to talk a bit about something that seems to be almost universally unpopular these days: 100% test coverage. A few people like &lt;a href='https://twitter.com/#!/unclebobmartin/statuses/190125475543261184'&gt;Uncle Bob Martin&lt;/a&gt; have said positive things about it, but the more popular sentiment is closer to what &lt;a href='http://37signals.com/svn/posts/3159-testing-like-the-tsa'&gt;DHH&lt;/a&gt; says about it: don&amp;#8217;t bother aiming for it.&lt;/p&gt;

&lt;h2 id='tldr'&gt;TL;DR&lt;/h2&gt;

&lt;p&gt;You should not make 100% test coverage the focus of your testing practice, but when applied carefully, I&amp;#8217;ve found it useful on some kinds of projects, particularly as an automated &amp;#8220;dead code&amp;#8221; finder.&lt;/p&gt;

&lt;h2 id='why_100_test_coverage_can_be_a_waste'&gt;Why 100% Test Coverage Can be a Waste&lt;/h2&gt;

&lt;p&gt;Let&amp;#8217;s address some common criticisms to start with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When dev managers mandate 100% test coverage on their developers, it encourages the devs to write terrible tests that are written just to meet the goal.&lt;/li&gt;

&lt;li&gt;In a high-level language like Ruby, you commonly have one-liner class-level macros that are &amp;#8220;covered&amp;#8221; simply by loading the class in the test environment. For example, a &lt;code&gt;validates_presence_of :name&lt;/code&gt; declaration in your ActiveRecord model will be reported as covered by your code coverage tool as soon as the model is loaded in your test environment&amp;#8230;but yet, there is very real behavior here that isn&amp;#8217;t being tested at all. Thus, the code coverage number can be deceiving.&lt;/li&gt;

&lt;li&gt;Having 100% test coverage on a project gives a false sense of confidence. A project can be littered with bugs and yet still have 100% test coverage.&lt;/li&gt;

&lt;li&gt;There is a cost associated with your test suite. The longer it takes to run, the longer your red-green-refactor cycle takes. Excess tests that were written to satisfy a code coverage number may be brittle and drive up the cost of change on a project (as you have to fix numerous tests for each change you make).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I absolutely agree with every single one of these criticisms. Test coverage should never be the goal, and making it the focus of your testing practice can lead to very poor tests.&lt;/p&gt;

&lt;p&gt;That said, even though it should never be the &lt;em&gt;focus&lt;/em&gt; of how write your tests, I have 100% test coverage as reported by &lt;a href='https://github.com/colszowka/simplecov'&gt;SimpleCov&lt;/a&gt; on multiple projects, and on these projects, my CI builds will fail if the test coverage drops below 100%. I find 100% test coverage to be useful and worthwhile on certain kinds of projects. I&amp;#8217;d like to explain why I find it useful and how I generally approach test coverage.&lt;/p&gt;

&lt;h2 id='100_test_coverage_as_an_automated_dead_code_finder'&gt;100% Test Coverage as an Automated &amp;#8220;Dead Code&amp;#8221; Finder&lt;/h2&gt;

&lt;p&gt;A common problem on older projects is crufty code: code that may not be used anymore, but no one&amp;#8217;s really sure, so the code gets left in the code base, &amp;#8220;just in case&amp;#8221;. Over time, this build up of cruft can incur significant cost in a project. Wouldn&amp;#8217;t it be great if there was an automated tool that helped identify dead code that can safely be removed?&lt;/p&gt;

&lt;p&gt;It turns out there is. Your code coverage tool, paired with making it a part of your continuous integration builds, can serve this purpose. It&amp;#8217;s a key benefit my projects have gotten out of maintaining 100% test coverage, and, surprisingly, I&amp;#8217;ve never heard any one mention it before.&lt;/p&gt;

&lt;p&gt;The high level of test coverage I keep makes it possible for me to refactor my code ruthlessly. I often wind up with helper classes or methods that are not unit tested directly; they are not part of the public interface of the project, and serve to support some other method or class that is part of the public interface. Later on, when I refactor yet again, these helper methods or classes may no longer be used, and, if they still remain in the code base, the build will fail and tell me. This has helped me many times.&lt;/p&gt;

&lt;h2 id='what_100_test_coverage_does_and_does_not_guarantee'&gt;What 100% Test Coverage does (and does not) Guarantee&lt;/h2&gt;

&lt;p&gt;As I&amp;#8217;ve already mentioned, I refactor my code ruthlessly, and it&amp;#8217;s a huge benefit I get out of practicing TDD. Maintaining a high level of test coverage increases my confidence in the safety of my refactorings.&lt;/p&gt;

&lt;p&gt;It&amp;#8217;s true that 100% test coverage can give a false sense of the &amp;#8220;robustness&amp;#8221; of a project, and I know that having 100% test coverage guarantees me nothing. However, having less than 100% test coverage does guarantee something: it guarantees that a project&amp;#8217;s test suite will be unable to detect when a refactoring breaks the uncovered code. Every bit of a uncovered code is a potential source of undetectable breakage, and I like to minimize these by keeping my test coverage up.&lt;/p&gt;

&lt;h2 id='how_i_use_test_coverage'&gt;How I Use Test Coverage&lt;/h2&gt;

&lt;p&gt;The most important thing is to use your brain&amp;#8211;don&amp;#8217;t aim for 100% test coverage because I&amp;#8217;ve found it useful, and don&amp;#8217;t heap scorn on the practice because of what others say. Still, here are some guidelines that I&amp;#8217;ve found useful:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Not every project needs or warrants 100% test coverage. I&amp;#8217;ve never maintained 100% test coverage in a rails app and probably never will. In general, the more UI a project has, the less need for high test coverage number. Most of my projects these days are backend services (which often expose an HTTP API) or gems we use internally, and it makes much more sense to maintain test coverage on these sorts of projects.&lt;/li&gt;

&lt;li&gt;If you manage developers, don&amp;#8217;t ever mandate a test coverage threshold. It&amp;#8217;s a bad idea for a team to focus their testing practice on a test coverage threshold. A team can (and should) come to a consensus about what test coverage threshold they want (if any) for themselves.&lt;/li&gt;

&lt;li&gt;If you want to use your code coverage tool as an automated &amp;#8220;dead code&amp;#8221; finder, it&amp;#8217;s helpful to start the project at 100% test coverage. Using a lower threshold makes it harder to detect dead code since the coverage number will vary as you write and refactor code.&lt;/li&gt;

&lt;li&gt;In my minute-by-minute TDD cycle, I don&amp;#8217;t think about test coverage &lt;em&gt;at all&lt;/em&gt;, and I usually run my tests without SimpleCov being loaded. I have a rake task that runs the tests with SimpleCov loaded and verifies the coverage threshold. I use this right before pushing commits and as part of my CI build. The majority of the time, the tests I&amp;#8217;ve naturally written have 100% coverage.&lt;/li&gt;

&lt;li&gt;Occasionally when there are holes in the coverage, it doesn&amp;#8217;t really make sense to add tests for it. Generally, I&amp;#8217;ll extract this code into another file and add a SimpleCov &lt;a href='http://rubydoc.info/gems/simplecov/0.6.3/frames'&gt;filter&lt;/a&gt; so that it ignores the file and does not count it when calculating the coverage percent. This allows me to continue using SimpleCov as a dead code finder, without having to add silly tests that would exist only to satisfy the coverage tool.&lt;/li&gt;
&lt;/ul&gt;&lt;img src="http://feeds.feedburner.com/~r/myronmarsto/n/~4/g_IbZToyhSY" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://myronmars.to/n/dev-blog/2012/05/in-defense-of-100-test-coverage</feedburner:origLink></entry>
  
  <entry>
    <title>The New NullDB Team</title>
    <link href="http://feedproxy.google.com/~r/myronmarsto/n/~3/5BnGwKuP0nA/the-new-nulldb-team" />
    <updated>2012-05-09T00:00:00-07:00</updated>
    <id>http://myronmars.to/n/dev-blog/2012/05/the-new-nulldb-team</id>
    <content type="html">&lt;p&gt;A few weeks ago, I &lt;a href='/n/dev-blog/2012/04/new-nulldb-maintainer-needed'&gt;announced&lt;/a&gt; that I was looking for a new NullDB maintainer. Since then, multiple people have stepped forward and I&amp;#8217;m happy to announce the formation of a &lt;a href='https://github.com/nulldb'&gt;NullDB core team&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Arvind Laxminarayan (&lt;a href='https://github.com/ardsrk'&gt;github&lt;/a&gt; | &lt;a href='https://twitter.com/ardsrk'&gt;twitter&lt;/a&gt;)&lt;/li&gt;

&lt;li&gt;Isaac Sanders (&lt;a href='https://github.com/isaacsanders'&gt;github&lt;/a&gt; | &lt;a href='https://twitter.com/isaacsanders'&gt;twitter&lt;/a&gt;)&lt;/li&gt;

&lt;li&gt;Matt Van Horn (&lt;a href='https://github.com/mattvanhorn'&gt;github&lt;/a&gt; | &lt;a href='https://twitter.com/nycplayer'&gt;twitter&lt;/a&gt;)&lt;/li&gt;

&lt;li&gt;Woody Peterson (&lt;a href='https://github.com/woahdae'&gt;github&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Please join me in thanking Arvind, Isaac, Matt and Woody for stepping forward and taking over maintainance on this project. I&amp;#8217;m confident the project can move forward with a much better direction now that I won&amp;#8217;t be the bottleneck anymore!&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/myronmarsto/n/~4/5BnGwKuP0nA" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://myronmars.to/n/dev-blog/2012/05/the-new-nulldb-team</feedburner:origLink></entry>
  
  <entry>
    <title>New NullDB Maintainer Needed</title>
    <link href="http://feedproxy.google.com/~r/myronmarsto/n/~3/h80hWPtbzyc/new-nulldb-maintainer-needed" />
    <updated>2012-04-19T00:00:00-07:00</updated>
    <id>http://myronmars.to/n/dev-blog/2012/04/new-nulldb-maintainer-needed</id>
    <content type="html">&lt;p&gt;&lt;a href='https://github.com/nulldb/nulldb'&gt;NullDB&lt;/a&gt; is a handy project that makes it easy to write database-decoupled unit tests for your ActiveRecord models. The project was originally started by &lt;a href='http://avdi.org/'&gt;Avdi Grimm&lt;/a&gt;; two years ago, &lt;a href='http://devblog.avdi.org/2010/04/06/nulldb-has-a-new-maintainer/'&gt;I took over as maintainer&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Since that time, I&amp;#8217;ve focused my open source efforts on &lt;a href='https://github.com/myronmarston/vcr'&gt;several&lt;/a&gt; &lt;a href='https://github.com/rspec/rspec-core/commits/master?author=myronmarston'&gt;other&lt;/a&gt; &lt;a href='https://github.com/seancribbs/ripple/commits?author=myronmarston'&gt;projects&lt;/a&gt; and I no longer have the time to maintain NullDB. I&amp;#8217;m also not using ActiveRecord much these days so I&amp;#8217;m not using NullDB as regularly as I used to. At this point there&amp;#8217;s a &lt;a href='https://github.com/nulldb/nulldb/issues'&gt;backlog of issues and pull requests&lt;/a&gt; that needs someone with more time and interest than I to deal with.&lt;/p&gt;

&lt;p&gt;I also have some ideas for some &amp;#8220;next steps&amp;#8221; I&amp;#8217;d like to see for NullDB:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It&amp;#8217;d be great to get some CI builds going on &lt;a href='http://travis-ci.org/'&gt;Travis&lt;/a&gt;.&lt;/li&gt;

&lt;li&gt;NullDB should ideally be fully de-coupled from Rails, so that it can easily be used in non-Rails ActiveRecord projects. A Rails 3 RailTie can be used for deeper NullDB/Rails integration without coupling them.&lt;/li&gt;

&lt;li&gt;RSpec 2 provides a great API for testing libraries to easily integrate with examples and example groups using metadata. It&amp;#8217;d be great if NullDB took advantage of this (e.g. so that all specs except those tagged with &lt;code&gt;:realdb&lt;/code&gt; used the NullDB adapter by default).&lt;/li&gt;

&lt;li&gt;NullDB still &amp;#8220;officially&amp;#8221; supports Ruby 1.8.6 and Rails 2.x. I think it&amp;#8217;s time to drop support for those.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you&amp;#8217;re interested in helping maintain NullDB, let me know, via a comment below, a tweet or an email. If you&amp;#8217;re interested in helping out but don&amp;#8217;t think you have the time to take over as the main maintainer, that&amp;#8217;s fine&amp;#8211;it&amp;#8217;d be great to get multiple people who can help out!&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/myronmarsto/n/~4/h80hWPtbzyc" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://myronmars.to/n/dev-blog/2012/04/new-nulldb-maintainer-needed</feedburner:origLink></entry>
  
  <entry>
    <title>Faster Test Boot Times with Bundler Standalone</title>
    <link href="http://feedproxy.google.com/~r/myronmarsto/n/~3/61uCDtoJ0jM/faster-test-boot-times-with-bundler-standalone" />
    <updated>2012-03-20T00:00:00-07:00</updated>
    <id>http://myronmars.to/n/dev-blog/2012/03/faster-test-boot-times-with-bundler-standalone</id>
    <content type="html">&lt;h2 id='tldr'&gt;TL;DR&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;bundle install --standalone&lt;/code&gt; generates a file that allows you to avoid loading bundler at runtime (while still having your load path set up) when booting your test environment. With a fast test suite, this can make a noticeable difference in how long it takes to run.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;I recently started a new project, and inspired by what I&amp;#8217;ve been learning from &lt;a href='https://www.destroyallsoftware.com/'&gt;Destroy All Software&lt;/a&gt;, I want the tests to run as fast as possible. Getting the wall clock times of my test runs down under a second makes me so much more productive!&lt;/p&gt;

&lt;p&gt;One of the keys to achieving this, as people like &lt;a href='http://www.confreaks.com/videos/641-gogaruco2011-fast-rails-tests'&gt;Corey Haines&lt;/a&gt; talk about, is loading as few things as possible. This has an important design benefit&amp;#8211;it forces you to think carefully everytime you couple your code-under-test to a new dependency&amp;#8211;and is essential for achieving fast tests.&lt;/p&gt;

&lt;p&gt;If you use bundler for your project (as you should; it&amp;#8217;s awesome), it&amp;#8217;s an additional thing that you may not have to load when you run your tests. On this project, the tests are fast enough that running the tests through bundler makes a significant, noticeable difference.&lt;/p&gt;

&lt;p&gt;Without &lt;code&gt;bundle exec&lt;/code&gt;:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;no_bundler.txt  &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='sh'&gt;&lt;span class='line'&gt;➜  &lt;span class='nb'&gt;time &lt;/span&gt;rspec spec/unit/models
&lt;/span&gt;&lt;span class='line'&gt;................................
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;Finished in 0.05011 seconds
&lt;/span&gt;&lt;span class='line'&gt;32 examples, 0 failures
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;rspec spec/unit/models  0.52s user 0.06s system 97% cpu 0.595 total
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;With &lt;code&gt;bundle exec&lt;/code&gt;:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;bundler.txt  &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='sh'&gt;&lt;span class='line'&gt;➜  &lt;span class='nb'&gt;time &lt;/span&gt;bundle &lt;span class='nb'&gt;exec &lt;/span&gt;rspec spec/unit/models
&lt;/span&gt;&lt;span class='line'&gt;................................
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;Finished in 0.05819 seconds
&lt;/span&gt;&lt;span class='line'&gt;32 examples, 0 failures
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;bundle &lt;span class='nb'&gt;exec &lt;/span&gt;rspec spec/unit/models  1.20s user 0.18s system 98% cpu 1.398 total
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;That&amp;#8217;s 0.6 seconds vs. 1.4 seconds&amp;#8211;more than twice as slow to run the tests through &lt;code&gt;bundle exec&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When I noticed this, I tried running other specs without &lt;code&gt;bundle exec&lt;/code&gt;&amp;#8230;and immediately ran into a problem:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;load_error.txt  &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='sh'&gt;&lt;span class='line'&gt;➜  rspec spec/unit/apps
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;.../rubygems/custom_require.rb:36:in &lt;span class='sb'&gt;`&lt;/span&gt;require&lt;span class='err'&gt;&amp;#39;&lt;/span&gt;: cannot load such file -- shardonnay &lt;span class='o'&gt;(&lt;/span&gt;LoadError&lt;span class='o'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;...the full backtrace here...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;Lame. I had forgotten that I had a few &lt;code&gt;:git&lt;/code&gt; gems in my Gemfile (such as shardonnay, a private gem we&amp;#8217;re actively developing internally). As the &lt;a href='http://gembundler.com/v1.1/git.html'&gt;bundler documentation&lt;/a&gt; states, &lt;code&gt;:git&lt;/code&gt; gems are not available to rubygems. You have to use &lt;code&gt;Bundler.setup&lt;/code&gt; to make them available on the load path. But &lt;code&gt;Bundler.setup&lt;/code&gt; is equivalent to &lt;code&gt;bundle exec&lt;/code&gt;&lt;sup id='fnref:1'&gt;&lt;a href='#fn:1' rel='footnote'&gt;1&lt;/a&gt;&lt;/sup&gt;, and I still wanted to skip it if I could find way.&lt;/p&gt;

&lt;h2 id='the_solution'&gt;The Solution&lt;/h2&gt;

&lt;p&gt;One of my co-workers, Evan Battaglia, suggested there might be a way to make a record of all the load paths that need to be setup (including the paths for &lt;code&gt;:git&lt;/code&gt; gems) and re-use that. I started looking into doing this when I remembered a new bundler 1.1 option I&amp;#8217;ve been meaning to try out: &lt;code&gt;bundle install --standalone&lt;/code&gt;. Running this generates a handy file at &lt;code&gt;bundle/bundler/setup.rb&lt;/code&gt;:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;bundle/bundler/setup.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;path&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;File&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;expand_path&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;..&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='bp'&gt;__FILE__&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='vg'&gt;$:&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;unshift&lt;/span&gt; &lt;span class='no'&gt;File&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;expand_path&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;&lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='n'&gt;path&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;/../ruby/1.9.1/gems/rake-0.9.2.2/lib&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='vg'&gt;$:&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;unshift&lt;/span&gt; &lt;span class='no'&gt;File&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;expand_path&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;&lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='n'&gt;path&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;/../ruby/1.9.1/gems/addressable-2.2.7/lib&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='vg'&gt;$:&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;unshift&lt;/span&gt; &lt;span class='no'&gt;File&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;expand_path&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;&lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='n'&gt;path&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;/../ruby/1.9.1/bundler/gems/shardonnay-f2cf0d585593/lib&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;The real file in my project is much longer, with many more gems, but that should give you the idea. Notice that it sets up the load path both for installed released gems and &lt;code&gt;:git&lt;/code&gt; gems. This makes it perfect for what I want to do.&lt;/p&gt;

&lt;p&gt;To use this, I&amp;#8217;ve git-ignored the &lt;code&gt;bundle&lt;/code&gt; directory (since it contains artifacts generated by bundler) and I have this bit of code that runs at the start of booting my test environment:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;setup_load_paths.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;begin&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='c1'&gt;# use `bundle install --standalone&amp;#39; to get this...&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;require_relative&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;../bundle/bundler/setup&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;rescue&lt;/span&gt; &lt;span class='no'&gt;LoadError&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='c1'&gt;# fall back to regular bundler if the developer hasn&amp;#39;t bundled standalone&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;bundler&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='no'&gt;Bundler&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;setup&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;With this in place, I&amp;#8217;m able to run my specs without &lt;code&gt;bundle exec&lt;/code&gt;, with no slowdown from bundler at runtime (since bundler isn&amp;#8217;t even being loaded at runtime!).&lt;/p&gt;

&lt;h2 id='caveats'&gt;Caveats&lt;/h2&gt;

&lt;p&gt;This is working great for me, and I plan to keep using this strategy, but it does have a few caveats:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There&amp;#8217;s no guarantee that the right version of rspec will be used (since it is the binary I invoke from my shell, before the load paths get setup). I&amp;#8217;m using an RVM gemset for this project so it&amp;#8217;s not much of an issue.&lt;/li&gt;

&lt;li&gt;Bundler doesn&amp;#8217;t keep any persistent state about the fact that I&amp;#8217;m using &lt;code&gt;--standalone&lt;/code&gt;. If I forget the &lt;code&gt;--standalone&lt;/code&gt; flag the next time I run &lt;code&gt;bundle install&lt;/code&gt;, the generated setup file will not be updated with the new load paths&amp;#8211;which means the wrong version of a gem may be used the next time I run my tests.&lt;/li&gt;

&lt;li&gt;A full &lt;code&gt;Bundler.setup&lt;/code&gt; gives you a sandbox guarantee: the only gems that can be loaded are those that included in the locked bundle. There&amp;#8217;s no such guarantee when using &lt;code&gt;--standalone&lt;/code&gt;. I can install a gem and immediately require it. I&amp;#8217;m not too concerned about this since the normal way I install gems now is via Bundler; plus, our CI server will catch any problems here since it is running &lt;code&gt;bundle install&lt;/code&gt; (with no &lt;code&gt;--standalone&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I&amp;#8217;m certainly willing to live with these tradeoffs in exchange for faster test environment boot times, but you may not be.&lt;/p&gt;
&lt;div class='footnotes'&gt;&lt;hr /&gt;&lt;ol&gt;&lt;li id='fn:1'&gt;
&lt;p&gt;Technically, &lt;code&gt;Bundler.setup&lt;/code&gt; and &lt;code&gt;bundle exec&lt;/code&gt; aren&amp;#8217;t quite equivalent. &lt;code&gt;bundle exec&lt;/code&gt; essentially runs &lt;code&gt;Bundler.setup&lt;/code&gt; before the specified binary (rspec, in this case), ensuring that the version of rspec specified in your &lt;code&gt;Gemfile.lock&lt;/code&gt; is used. Putting &lt;code&gt;Bundler.setup&lt;/code&gt; in a file loaded by your tests (say, &lt;code&gt;spec_helper.rb&lt;/code&gt;) will ensure that all gems loaded after that point are the correct versions, but can&amp;#8217;t make any guarantee about gems that have already been loaded (such as rspec itself).&lt;/p&gt;
&lt;a href='#fnref:1' rev='footnote'&gt;&amp;#8617;&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/myronmarsto/n/~4/61uCDtoJ0jM" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://myronmars.to/n/dev-blog/2012/03/faster-test-boot-times-with-bundler-standalone</feedburner:origLink></entry>
  
  <entry>
    <title>Building an Around Hook Using Fibers</title>
    <link href="http://feedproxy.google.com/~r/myronmarsto/n/~3/0JkbtAugItI/building-an-around-hook-using-fibers" />
    <updated>2012-03-09T00:00:00-08:00</updated>
    <id>http://myronmars.to/n/dev-blog/2012/03/building-an-around-hook-using-fibers</id>
    <content type="html">&lt;p&gt;Ruby 1.9&amp;#8217;s &lt;a href='http://ruby-doc.org/core-1.9.3/Fiber.html'&gt;fibers&lt;/a&gt; have lots of interesting uses. Recently, I realized that they can be used to build an around hook out of separate before and after hooks.&lt;/p&gt;

&lt;p&gt;RSpec provides an &lt;code&gt;around(:each)&lt;/code&gt; hook but has no equivalent &lt;code&gt;around(:all)&lt;/code&gt; hook. Let&amp;#8217;s build one!&lt;/p&gt;

&lt;p&gt;First, we&amp;#8217;ll start with a contrived example. We have some examples in a group that all need to run with a different working directory. Here&amp;#8217;s how we can manage this with the existing &lt;code&gt;before(:all)&lt;/code&gt; and &lt;code&gt;after(:all)&lt;/code&gt; hooks:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;chdir_spec.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;describe&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;Changing to the tmp/foo directory&amp;quot;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;before&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:all&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='vi'&gt;@orig_dir&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;Dir&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;pwd&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='no'&gt;Dir&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;chdir&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;tmp/foo&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;after&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:all&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='no'&gt;Dir&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;chdir&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='vi'&gt;@orig_dir&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;changes the directory for all examples in this group&amp;quot;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='no'&gt;Dir&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;pwd&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='n'&gt;eq&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='no'&gt;File&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;expand_path&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;../tmp/foo&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='bp'&gt;__FILE__&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;Dir.chdir&lt;/code&gt; has two forms. The form above changes the directory for all subsequent code. The other form takes a block, and changes the directory for the duration of the block. This makes it an ideal candidate for an around hook. Here&amp;#8217;s what we&amp;#8217;d like to do instead:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;chdir_spec.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;describe&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;Changing to the tmp/foo directory&amp;quot;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;around&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:all&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;group&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='no'&gt;Dir&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;chdir&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;tmp/foo&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;&amp;amp;&lt;/span&gt;&lt;span class='n'&gt;group&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;changes the directory for all examples in this group&amp;quot;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='no'&gt;Dir&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;pwd&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='n'&gt;eq&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='no'&gt;File&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;expand_path&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;../tmp/foo&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='bp'&gt;__FILE__&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;Conceptually, you can think of a fiber as a thread, but rather than the OS or VM scheduling it, you manually switch back and forth. Unlike with a thread, you don&amp;#8217;t have to worry about race conditions when modifying shared process memory, since all context switches are manual.&lt;/p&gt;

&lt;p&gt;Our around hook needs to have its own sequence of execution that gets interrupted in the middle and then resumed; thus, the entire thing needs to run in its own fiber. Here&amp;#8217;s a first pass at that:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;chdir_spec.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;fiber&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;module&lt;/span&gt; &lt;span class='nn'&gt;AroundAllHook&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;around&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;scope&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;&amp;amp;&lt;/span&gt;&lt;span class='n'&gt;block&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='c1'&gt;# let RSpec handle around(:each) hooks...&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='k'&gt;super&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;scope&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;&amp;amp;&lt;/span&gt;&lt;span class='n'&gt;block&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='k'&gt;unless&lt;/span&gt; &lt;span class='n'&gt;scope&lt;/span&gt; &lt;span class='o'&gt;==&lt;/span&gt; &lt;span class='ss'&gt;:all&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;group&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='nb'&gt;self&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;fiber&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='kp'&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;before&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:all&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='n'&gt;fiber&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;Fiber&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;&amp;amp;&lt;/span&gt;&lt;span class='n'&gt;block&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='n'&gt;fiber&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;resume&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;group&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;after&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:all&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='n'&gt;fiber&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;resume&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;describe&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;Changing to the tmp/foo directory&amp;quot;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='kp'&gt;extend&lt;/span&gt; &lt;span class='no'&gt;AroundAllHook&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;around&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:all&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;group&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='no'&gt;Dir&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;chdir&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;tmp/foo&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='no'&gt;Fiber&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;yield&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;changes the directory for all examples in this group&amp;quot;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='no'&gt;Dir&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;pwd&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='n'&gt;eq&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='no'&gt;File&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;expand_path&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;../tmp/foo&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='bp'&gt;__FILE__&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;So how does this work? Taking it line by line:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;fiber = nil&lt;/code&gt; creates a local variable. This is important so that the same fiber instance is in scope in both the before hook and the after hook. This is possible because ruby blocks are a kind of &lt;a href='http://en.wikipedia.org/wiki/Closure_(computer_science)'&gt;closure&lt;/a&gt; and thus retain a binding to that variable. It&amp;#8217;s important that we use a local variable here and not an instance variable. The local variable causes the fiber to be shared between just this pair of hooks. If you used an instance variable, it would not work when someone defined multiple &lt;code&gt;around(:all)&lt;/code&gt; hooks in the same group, because the fiber would be shared between multiple before/after pairs. We need one fiber per pair.&lt;/li&gt;

&lt;li&gt;&lt;code&gt;before(:all)&lt;/code&gt; and &lt;code&gt;after(:all)&lt;/code&gt; use the existing RSpec hooks. We can assume they&amp;#8217;ll be available in this context because this module is intended to be extended onto an example group.&lt;/li&gt;

&lt;li&gt;Within the before hook, we create a new fiber: &lt;code&gt;fiber =
Fiber.new(&amp;amp;block)&lt;/code&gt;. The block is passed to the fiber so that it runs entirely within it.&lt;/li&gt;

&lt;li&gt;&lt;code&gt;fiber.resume(group)&lt;/code&gt; starts the fiber, and passes the example group as an argument. That will cause it to be the yielded argument in the block.&lt;/li&gt;

&lt;li&gt;Within the declared &lt;code&gt;around(:all)&lt;/code&gt; hook, we have &lt;code&gt;Dir.chdir(&amp;quot;tmp/foo&amp;quot;)
{ Fiber.yield }&lt;/code&gt;. This changes the directory, then uses &lt;code&gt;Fiber.yield&lt;/code&gt; to return control to the root fiber&lt;sup id='fnref:1'&gt;&lt;a href='#fn:1' rel='footnote'&gt;1&lt;/a&gt;&lt;/sup&gt;. This allows RSpec to do what it normally does immediately after a &lt;code&gt;before(:all)&lt;/code&gt; hook: it runs the examples in the group.&lt;/li&gt;

&lt;li&gt;Once the examples finish, RSpec invokes our &lt;code&gt;after(:all)&lt;/code&gt; hook. In our hook, we simply resume the fiber: &lt;code&gt;fiber.resume&lt;/code&gt;. This returns execution to the point immediately after the &lt;code&gt;Fiber.yield&lt;/code&gt; in the &lt;code&gt;Dir.chdir&lt;/code&gt; block. Thus, it allows the block to complete and &lt;code&gt;Dir.chdir&lt;/code&gt; returns the working directory to its original value when the block completes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you&amp;#8217;re not following what &lt;code&gt;Fiber#resume&lt;/code&gt; and &lt;code&gt;Fiber.yield&lt;/code&gt; do, check out the &lt;a href='http://ruby-doc.org/core-1.9.3/Fiber.html'&gt;fiber ruby docs&lt;/a&gt;. They have a good explanation and a simpler example.&lt;/p&gt;

&lt;p&gt;At this point, we have a working &lt;code&gt;around(:all)&lt;/code&gt; hook, but it&amp;#8217;s a pretty leaky abstraction. The user of this hook has to know to call &lt;code&gt;Fiber.yield&lt;/code&gt; in the middle of his hook; otherwise they&amp;#8217;ll get a &lt;code&gt;FiberError&lt;/code&gt; (&amp;#8220;dead fiber called&amp;#8221;) when RSpec runs the &lt;code&gt;after(:all)&lt;/code&gt; hook.&lt;/p&gt;

&lt;p&gt;Let&amp;#8217;s improve this API. First, we&amp;#8217;ll implement a &lt;code&gt;#run_examples&lt;/code&gt; method that simply delegates to &lt;code&gt;Fiber.yield&lt;/code&gt;:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;chdir_spec.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;delegate&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;module&lt;/span&gt; &lt;span class='nn'&gt;AroundAllHook&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;FiberAwareGroup&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='no'&gt;SimpleDelegator&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;run_examples&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='no'&gt;Fiber&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;yield&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;Now we can pass a &lt;code&gt;FiberAwareGroup&lt;/code&gt; instance to &lt;code&gt;fiber.resume&lt;/code&gt;:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;chdir_spec.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;before&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:all&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;fiber&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;Fiber&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='o'&gt;&amp;amp;&lt;/span&gt;&lt;span class='n'&gt;block&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;fiber&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;resume&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='no'&gt;FiberAwareGroup&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;group&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;Finally, we can use this in the &lt;code&gt;around(:all)&lt;/code&gt; hook:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;chdir_spec.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;around&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:all&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;group&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='no'&gt;Dir&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;chdir&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;tmp/foo&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;group&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;run_examples&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;Note that we could have defined &lt;code&gt;run_examples&lt;/code&gt; as a singleton method on &lt;code&gt;group&lt;/code&gt;, or defined the method in a module that we extend onto &lt;code&gt;group&lt;/code&gt;, but I prefer delegation for cases like these. Both of the other techniques permanently modify the object and thus leak the &lt;code&gt;#run_examples&lt;/code&gt; method into other contexts where it no longer makes sense.&lt;/p&gt;

&lt;p&gt;This is an improvement from requiring users to call &lt;code&gt;Fiber.yield&lt;/code&gt;, but we can take it a step further. We&amp;#8217;d like to be able to treat the yielded group as a proc, like so:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;chdir_spec.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;around&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:all&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;group&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='no'&gt;Dir&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;chdir&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;tmp/foo&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;&amp;amp;&lt;/span&gt;&lt;span class='n'&gt;group&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;We simply need to define an appropriate &lt;code&gt;#to_proc&lt;/code&gt;:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;chdir_spec.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;FiberAwareGroup&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='no'&gt;SimpleDelegator&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;run_examples&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='no'&gt;Fiber&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;yield&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;to_proc&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='nb'&gt;proc&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='n'&gt;run_examples&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;To see the final code, check out the little &lt;a href='https://gist.github.com/2005175'&gt;microgem&lt;/a&gt; I put together.&lt;/p&gt;
&lt;div class='footnotes'&gt;&lt;hr /&gt;&lt;ol&gt;&lt;li id='fn:1'&gt;
&lt;p&gt;I was going use the term &amp;#8220;main fiber&amp;#8221; instead (since &amp;#8220;main thread&amp;#8221; is a standard term), but the &lt;a href='http://ruby-doc.org/core-1.9.3/Fiber.html#method-c-current'&gt;ruby docs&lt;/a&gt; use the term &amp;#8220;root fiber&amp;#8221; so I figured I would too :).&lt;/p&gt;
&lt;a href='#fnref:1' rev='footnote'&gt;&amp;#8617;&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/myronmarsto/n/~4/0JkbtAugItI" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://myronmars.to/n/dev-blog/2012/03/building-an-around-hook-using-fibers</feedburner:origLink></entry>
  
  <entry>
    <title>VCR 2.0.0 Released!</title>
    <link href="http://feedproxy.google.com/~r/myronmarsto/n/~3/WDzhifd60pw/vcr-2-0-0-released" />
    <updated>2012-03-02T00:00:00-08:00</updated>
    <id>http://myronmars.to/n/dev-blog/2012/03/vcr-2-0-0-released</id>
    <content type="html">&lt;p&gt;I&amp;#8217;ve just released VCR 2.0.0 final. This marks the culmination of 6 months of work, and involved re-writing large portions of VCR.&lt;/p&gt;

&lt;p&gt;VCR 2 is far more flexible than VCR 1.x ever was. The &lt;a href='https://github.com/myronmarston/vcr/blob/v2.0.0/Upgrade.md'&gt;Upgrade&lt;/a&gt; doc has a high-level listing of most of the new features. I&amp;#8217;ve also blogged about the new stuff:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href='/n/dev-blog/2011/10/custom-request-matchers-in-vcr-2-0'&gt;Custom Request Matchers in VCR 2.0&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href='/n/dev-blog/2011/11/cassettes-in-vcr-2-0'&gt;Cassettes in VCR 2.0&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href='/n/dev-blog/2011/12/vcr-2-0-0-rc-released'&gt;VCR 2.0.0 RC Released!&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href='/n/dev-blog/2012/02/vcr-2-0-0-rc2-released'&gt;VCR 2.0.0 RC2 Released!&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id='thanks'&gt;Thanks!&lt;/h2&gt;

&lt;p&gt;I couldn&amp;#8217;t have developed VCR 2 on my own. Thanks to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;David Balatero, for help with Typhoeus integration.&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;Bartosz Blimke, for help with WebMock integration.&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;Wesley Beary, for help with Excon integration.&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;Ryan Bates, for the great idea to improve VCR&amp;#8217;s integration with RSpec using metadata RSpec metadata.&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;Loren Segal, for patiently answering all my noob YARD questions.&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;Jeff Pollard, for reviewing code and giving great feedback.&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;Avdi Grimm, for several ideas for new features.&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;Everyone who submitted patches for VCR: 2.0:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Benjamin Oakes&lt;/li&gt;

&lt;li&gt;Flaviu Simihaian&lt;/li&gt;

&lt;li&gt;Michael Lavrisha&lt;/li&gt;

&lt;li&gt;Paco Guzmán&lt;/li&gt;

&lt;li&gt;Ryan Bates&lt;/li&gt;

&lt;li&gt;Sathya Sekaran&lt;/li&gt;

&lt;li&gt;Carlos Kirkconnell&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;The Travis CI crew. Travis has made it so easy to keep VCR&amp;#8217;s tests passing against multiple ruby interpreters!&lt;/p&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;Everyone who tried one of the VCR 2.0 preleases and gave me feedback.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;img src="http://feeds.feedburner.com/~r/myronmarsto/n/~4/WDzhifd60pw" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://myronmars.to/n/dev-blog/2012/03/vcr-2-0-0-released</feedburner:origLink></entry>
  
  <entry>
    <title>VCR 2.0.0 RC2 Released!</title>
    <link href="http://feedproxy.google.com/~r/myronmarsto/n/~3/Ea7HUZgyZ6Q/vcr-2-0-0-rc2-released" />
    <updated>2012-02-24T00:00:00-08:00</updated>
    <id>http://myronmars.to/n/dev-blog/2012/02/vcr-2-0-0-rc2-released</id>
    <content type="html">&lt;p&gt;I&amp;#8217;ve just released VCR 2.0.0.rc2. This is (hopefully) the last stop before the final 2.0.0 relesae. If there are no major issues, I plan to release 2.0.0 next week some time. Please give 2.0.0.rc2 a try with your test suite!&lt;/p&gt;

&lt;p&gt;On to what&amp;#8217;s been changed since RC1&amp;#8230;&lt;/p&gt;

&lt;h2 id='yard_api_docs'&gt;Yard API Docs&lt;/h2&gt;

&lt;p&gt;VCR has used its cucumber suite, hosted on &lt;a href='http://relishapp.com/myronmarston/vcr'&gt;relish&lt;/a&gt;, as documentation for a good year or so. I&amp;#8217;ve received many positive comments about the docs, and how useful all the executable examples are.&lt;/p&gt;

&lt;p&gt;The relish docs are great at demonstrating each high-level feature through an exectuble example, but they&amp;#8217;re not so great at documenting the interface for particular objects. VCR now has many hooks, and it&amp;#8217;s hard to document the full interface of the objects yielded to those hooks on relish.&lt;/p&gt;

&lt;p&gt;So, with the help of &lt;a href='http://www.benjaminoakes.com/'&gt;Benjamin Oakes&lt;/a&gt; (and many patient answers to my questions from &lt;a href='http://gnuu.org/'&gt;Loren Segal&lt;/a&gt;), VCR now has proper &lt;a href='http://rubydoc.info/github/myronmarston/vcr/frames'&gt;YARD API docs&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id='hook_filters'&gt;Hook Filters&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;before_http_request&lt;/code&gt;, &lt;code&gt;after_http_request&lt;/code&gt;, and &lt;code&gt;around_http_request&lt;/code&gt; hooks all now accept zero or more filters as an argument. &amp;#8220;Filters&amp;#8221; are simply objects that respond to &lt;code&gt;#to_proc&lt;/code&gt;. Before invoking the hook, VCR will call the filter procs, and only continue invoking the hook if all of the filter procs return a truthy value.&lt;/p&gt;

&lt;p&gt;On top of that, the request object has gained some additional predicate methods in the context of these hooks: &lt;code&gt;#real?&lt;/code&gt;, &lt;code&gt;#ignored?&lt;/code&gt;, &lt;code&gt;#stubbed?&lt;/code&gt;, &lt;code&gt;#recordable?&lt;/code&gt; and &lt;code&gt;#unhandled?&lt;/code&gt;. You can use these in a hook to conditionally run logic for only certain types of requests. For example, to do something before all real requests:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;request_hook.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='no'&gt;VCR&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;configure&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;c&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;c&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;before_http_request&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;request&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='n'&gt;request&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;real?&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='n'&gt;do_something_with&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;request&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;Or, if you prefer (and I certainly do!), using a filter:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;request_hook.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='no'&gt;VCR&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;configure&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;c&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;c&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;before_http_request&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:real?&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;request&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;do_something_with&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;request&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;This feature is also quite useful when used in conjunction with an &lt;code&gt;around_http_request&lt;/code&gt; hook to globally handle requests to a particular host. Here&amp;#8217;s the code example I put in the &lt;a href='/n/dev-blog/2011/12/vcr-2-0-0-rc-released'&gt;2.0.0 RC1 release announcement&lt;/a&gt;:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;handle_geocoding_requests.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='no'&gt;VCR&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;configure&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;c&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;c&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;around_http_request&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;request&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;uri&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;URI&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;request&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;uri&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='n'&gt;uri&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;host&lt;/span&gt; &lt;span class='o'&gt;==&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;api.geocoder.com&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='c1'&gt;# extract an address like &amp;quot;1700 E Pine St, Seattle, WA&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='c1'&gt;# from a query like &amp;quot;address=1700+E+Pine+St%2C+Seattle%2C+WA&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='n'&gt;address&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;CGI&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;unescape&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;uri&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;query&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;split&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;=&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;last&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='no'&gt;VCR&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;use_cassette&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;geocoding/&lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='n'&gt;address&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;&amp;amp;&lt;/span&gt;&lt;span class='n'&gt;request&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='n'&gt;request&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;proceed&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;Here&amp;#8217;s how you could clean this up a bit with a hook filter:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;handle_geocoding_requests.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='no'&gt;VCR&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;configure&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;c&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;c&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;around_http_request&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;lambda&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;r&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt; &lt;span class='n'&gt;r&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;uri&lt;/span&gt; &lt;span class='o'&gt;=~&lt;/span&gt; &lt;span class='sr'&gt;/api.geocoder.com/&lt;/span&gt; &lt;span class='p'&gt;})&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;request&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='c1'&gt;# extract an address like &amp;quot;1700 E Pine St, Seattle, WA&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='c1'&gt;# from a query like &amp;quot;address=1700+E+Pine+St%2C+Seattle%2C+WA&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;address&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;CGI&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;unescape&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='no'&gt;URI&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;request&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;uri&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;query&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;split&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;=&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;last&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='no'&gt;VCR&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;use_cassette&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;geocoding/&lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='n'&gt;address&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;&amp;amp;&lt;/span&gt;&lt;span class='n'&gt;request&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;h2 id='improved_excon_support'&gt;Improved Excon Support&lt;/h2&gt;

&lt;p&gt;VCR has supported Excon for a while, but the integration has &lt;a href='http://groups.google.com/group/ruby-fog/browse_thread/thread/737295ebb42e67d1/df2effe4cceffc68'&gt;had a few issues&lt;/a&gt;. I myself have experienced some of this pain when I tried to use VCR to help test some code that uses Fog to communicate with S3.&lt;/p&gt;

&lt;p&gt;I finally dug in and worked through the issues. There were a couple bugs on the VCR side and one bug in Excon&amp;#8217;s stubbing support. I believe VCR should work seemlessly with any excon request now, including with Fog.&lt;/p&gt;

&lt;h2 id='debug_logging'&gt;Debug Logging&lt;/h2&gt;

&lt;p&gt;Sometimes VCR doesn&amp;#8217;t work the way you would expect, and prior to now, there hasn&amp;#8217;t been a good built-in way to get insight into what VCR was doing. VCR now has a new &lt;code&gt;debug_logger&lt;/code&gt; option. Set it to any object with a &lt;code&gt;puts&lt;/code&gt; method:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;debug_logging.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='no'&gt;VCR&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;configure&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;c&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;c&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;debug_logger&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='vg'&gt;$stdout&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='c1'&gt;# or&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;c&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;debug_logger&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;File&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;open&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;log/vcr.log&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;h2 id='new__option'&gt;New &lt;code&gt;preserve_exact_body_bytes&lt;/code&gt; option&lt;/h2&gt;

&lt;p&gt;Some HTTP servers are not well-behaved and respond with invalid data: the response body may not be encoded according to the encoding specified in the HTTP headers, or there may be bytes that are invalid for the given encoding. The YAML and JSON serializers are not generally designed to handle these cases gracefully, and you may get errors when the cassette is serialized or deserialized. Also, the encoding may not be preserved when round-tripped through the serializer.&lt;/p&gt;

&lt;p&gt;VCR now has a a &lt;code&gt;preserve_exact_body_bytes&lt;/code&gt; option to help deal with cases like these. You can set it globally:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;preserve_exact_body_bytes.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='no'&gt;VCR&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;configure&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;c&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;c&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;preserve_exact_body_bytes&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;http_message&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;http_message&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;body&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;encoding&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;name&lt;/span&gt; &lt;span class='o'&gt;==&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;ASCII-8BIT&amp;#39;&lt;/span&gt; &lt;span class='o'&gt;||&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='o'&gt;!&lt;/span&gt;&lt;span class='n'&gt;http_message&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;body&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;valid_encoding?&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;&amp;#8230;or you can force the option for a particular cassette:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;preserve_exact_body_bytes.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='no'&gt;VCR&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;use_cassette&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;example&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:preserve_exact_body_bytes&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='kp'&gt;true&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='c1'&gt;# ...&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;When you set this opion, VCR will base64 encode the body of the request or response during serialization, in order to preserve the bytes exactly.&lt;/p&gt;

&lt;h2 id='whats_next'&gt;What&amp;#8217;s next&lt;/h2&gt;

&lt;p&gt;I plan to release VCR 2.0.0 final in the next week or two. Please give VCR 2.0.0.rc2 a try and let me know if you have any issues!&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/myronmarsto/n/~4/Ea7HUZgyZ6Q" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://myronmars.to/n/dev-blog/2012/02/vcr-2-0-0-rc2-released</feedburner:origLink></entry>
  
  <entry>
    <title>Why Sinatra's Halt is Awesome</title>
    <link href="http://feedproxy.google.com/~r/myronmarsto/n/~3/jJqlrRyUd24/why-sinatras-halt-is-awesome" />
    <updated>2012-01-09T00:00:00-08:00</updated>
    <id>http://myronmars.to/n/dev-blog/2012/01/why-sinatras-halt-is-awesome</id>
    <content type="html">&lt;p&gt;Most of &lt;a href='http://sinatrarb.com/'&gt;Sinatra&amp;#8217;s&lt;/a&gt; well-deserved praise is directed toward its simple, elegant routing DSL. I want to draw attention to another feature that I love that doesn&amp;#8217;t get much attention: Sinatra&amp;#8217;s &lt;code&gt;halt&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;One of the biggest sources of complexity when developing robust applications is all of the error-handling code. Generally, code is easier to understand and is more maintainable when the error handling is not mixed together with the &amp;#8220;happy path&amp;#8221; code. This is one reason that virtually all modern languages have opted to provide exceptions rather than old C-style error return values. &lt;code&gt;halt&lt;/code&gt; provides similar benefits.&lt;/p&gt;

&lt;p&gt;Consider a simple sinatra route:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;application.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;get&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;/users/:user_id/projects/:project_id/tasks-due-on/:task_date&amp;#39;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;user&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;User&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;find&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;params&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:user_id&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;project&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;user&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;projects&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;find&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;params&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:project_id&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;tasks&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;project&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;tasks_due_on&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='no'&gt;Date&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;iso8601&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;params&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:task_date&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;tasks&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;to_json&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;This is all &amp;#8220;happy path&amp;#8221; code, and there are a few potential problems that aren&amp;#8217;t handled at all:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The user record for the given &lt;code&gt;:user_id&lt;/code&gt; may not exist.&lt;/li&gt;

&lt;li&gt;The project record for the given &lt;code&gt;:project_id&lt;/code&gt; may not exist, or may not belong to the given user.&lt;/li&gt;

&lt;li&gt;The &lt;code&gt;:task_date&lt;/code&gt; date string may not be in iso8601 format (i.e. YYYY-MM-DD).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let&amp;#8217;s handle each of these so that our API returns semantic HTTP status codes rather than responding with a 500 error:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;application.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;get&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;/users/:user_id/projects/:project_id/tasks-due-on/:task_date&amp;#39;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;user&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;User&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;find&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;params&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:user_id&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='n'&gt;user&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;project&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;user&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;projects&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;find&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;params&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:project_id&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='n'&gt;project&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='k'&gt;begin&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class='n'&gt;tasks&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;project&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;tasks_due_on&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='no'&gt;Date&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;iso8601&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;params&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:task_date&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;&lt;span class='p'&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='k'&gt;rescue&lt;/span&gt; &lt;span class='no'&gt;ArgumentError&lt;/span&gt; &lt;span class='c1'&gt;# for an invalid date string&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class='n'&gt;status&lt;/span&gt; &lt;span class='mi'&gt;400&lt;/span&gt; &lt;span class='c1'&gt;# bad request&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='k'&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class='n'&gt;tasks&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;to_json&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='n'&gt;status&lt;/span&gt; &lt;span class='mi'&gt;404&lt;/span&gt; &lt;span class='c1'&gt;# not found&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;status&lt;/span&gt; &lt;span class='mi'&gt;404&lt;/span&gt; &lt;span class='c1'&gt;# not found&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;This is certainly more robust, but I really, really hate this style of code. It makes my eyes bleed just looking at it. Let&amp;#8217;s clean it up a bit with some helper methods and &lt;code&gt;halt&lt;/code&gt;:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;application.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;helpers&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;user&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='vi'&gt;@user&lt;/span&gt; &lt;span class='o'&gt;||=&lt;/span&gt; &lt;span class='no'&gt;User&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;find&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;params&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:user_id&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='o'&gt;||&lt;/span&gt; &lt;span class='n'&gt;halt&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;404&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;project&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='vi'&gt;@project&lt;/span&gt; &lt;span class='o'&gt;||=&lt;/span&gt; &lt;span class='n'&gt;user&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;projects&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;find&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;params&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:project_id&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='o'&gt;||&lt;/span&gt; &lt;span class='n'&gt;halt&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='mi'&gt;404&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;task_date&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='vi'&gt;@task_date&lt;/span&gt; &lt;span class='o'&gt;||=&lt;/span&gt; &lt;span class='no'&gt;Date&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;iso8601&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;params&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:task_date&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;rescue&lt;/span&gt; &lt;span class='no'&gt;ArgumentError&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;halt&lt;/span&gt; &lt;span class='mi'&gt;400&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;tasks&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='vi'&gt;@tasks&lt;/span&gt; &lt;span class='o'&gt;||=&lt;/span&gt; &lt;span class='n'&gt;project&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;tasks_due_on&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;task_date&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;get&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;/users/:user_id/projects/:project_id/tasks-due-on/:task_date&amp;#39;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;tasks&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;to_json&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;Much, much better. It&amp;#8217;s more lines of code, but so much simpler. The error handling for each piece of data is handled directly in the helper method that is responsible for that piece of data. This has the added benefit of making it simpler to implement other routes that need some of these pieces of data (and the corresponding error handling):&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;application.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;helpers&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='c1'&gt;# all the helpers defined above...&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;get&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;/users/:user_id/projects/:project_id&amp;#39;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;project&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;to_json&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;get&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;/users/:user_id/projects/:project_id/tasks-due-on/:task_date&amp;#39;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;tasks&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;to_json&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;This is only possible through the magic of &lt;code&gt;halt&lt;/code&gt;. Under the covers, &lt;code&gt;halt&lt;/code&gt; uses &lt;code&gt;throw&lt;/code&gt;&lt;sup id='fnref:1'&gt;&lt;a href='#fn:1' rel='footnote'&gt;1&lt;/a&gt;&lt;/sup&gt; to immediately stop processing the route and return the response given to &lt;code&gt;halt&lt;/code&gt;. Some ruby developers have &lt;a href='http://m.onkey.org/ruby-i-don-t-like-2-catch-wtf-throw-wtf'&gt;gone on record as hating ruby&amp;#8217;s throw&lt;/a&gt;, and it can certainly be abused&amp;#8230;but Sinatra&amp;#8217;s &lt;code&gt;halt&lt;/code&gt; sure is useful and is only made possible by &lt;code&gt;throw&lt;/code&gt;&lt;sup id='fnref:2'&gt;&lt;a href='#fn:2' rel='footnote'&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;The next time you&amp;#8217;re building a Sinatra application, I encourage you to consider using &lt;code&gt;halt&lt;/code&gt; to simplify your error handling.&lt;/p&gt;
&lt;div class='footnotes'&gt;&lt;hr /&gt;&lt;ol&gt;&lt;li id='fn:1'&gt;
&lt;p&gt;If you&amp;#8217;re unfamiliar with &lt;code&gt;throw&lt;/code&gt;, check out Avdi Grimm&amp;#8217;s &lt;a href='http://rubylearning.com/blog/2011/07/12/throw-catch-raise-rescue-im-so-confused/'&gt;blog post&lt;/a&gt; on the topic.&lt;/p&gt;
&lt;a href='#fnref:1' rev='footnote'&gt;&amp;#8617;&lt;/a&gt;&lt;/li&gt;&lt;li id='fn:2'&gt;
&lt;p&gt;Technically, you could implement &lt;code&gt;halt&lt;/code&gt; using exceptions, but that seems semantically wrong to me. &lt;code&gt;throw&lt;/code&gt; is specifically intended for cases like these.&lt;/p&gt;
&lt;a href='#fnref:2' rev='footnote'&gt;&amp;#8617;&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/myronmarsto/n/~4/jJqlrRyUd24" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://myronmars.to/n/dev-blog/2012/01/why-sinatras-halt-is-awesome</feedburner:origLink></entry>
  
  <entry>
    <title>Deprecating a Legacy Subsystem in Rails</title>
    <link href="http://feedproxy.google.com/~r/myronmarsto/n/~3/siBDiy99g-Q/deprecating-a-legacy-subsystem-in-rails" />
    <updated>2011-12-28T00:00:00-08:00</updated>
    <id>http://myronmars.to/n/dev-blog/2011/12/deprecating-a-legacy-subsystem-in-rails</id>
    <content type="html">&lt;p&gt;A major part of my job in the past year at &lt;a href='http://www.seomoz.org/'&gt;SEOmoz&lt;/a&gt; was a re-write of the &lt;a href='http://www.seomoz.org/features'&gt;rank-tracking component&lt;/a&gt; of SEOmoz PRO. The existing system had major performance and scalability problems, and &lt;a href='http://bitfluxx.com/'&gt;Jeff Pollard&lt;/a&gt; and I were tasked with designing and and building an internal service to solve all these problems.&lt;/p&gt;

&lt;p&gt;Jeff previously &lt;a href='http://devblog.seomoz.org/2011/10/using-riak-for-ranking-collection/'&gt;blogged a bit&lt;/a&gt; about the design of the system, particularly our choice of Riak as the main data store. One of the other interesting bits to come out of this project was the way we migrated from the old system to the new system. We knew it would take at least a couple weeks (although, in the end it took 3 months!) and it was important that there was ZERO negative customer impact during the migration period. I came up with a technique that made it very easy to keep the existing system around in a deprecated state, and, once all data had been migrated to the new system, remove it entirely.&lt;/p&gt;

&lt;h2 id='the_goals'&gt;The Goals&lt;/h2&gt;

&lt;p&gt;We had several goals and constraints for the migration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The volume of data to import (billions of MySQL records) was such that a single atomic switch to the new system seemed impossible (or, at the very least, extremely risky). Thus, we knew we wanted to have a way to run the old and new systems side-by-side, and migrate customers over to the new system one-by-one.&lt;/li&gt;

&lt;li&gt;The old ranking system used a tangle of SQL views, ActiveRecord models, and controller and view logic. There was no value in re-using any of this code for the new system. Thus, we wanted to &amp;#8220;quarantine&amp;#8221; this old code to have a clean slate to build out the controller and views for the new system (the new system didn&amp;#8217;t have any ActiveRecord models since it relied upon the HTTP API of the service for its data). Once the migration was complete, we wanted it to be very easy to remove the old system (i.e. by simply deleting the source files from a few directories).&lt;/li&gt;

&lt;li&gt;The routes for the existing ranking system were fine, and we wanted to keep using them for the new system.&lt;/li&gt;

&lt;li&gt;Previously, we had replaced one of the other subsystems with an internal service. For that project, the rails app had a long-running branch (as in several months) that integrated with the new service. We frequently experienced merge pain for that project. This time, we knew we wanted to use a feature flag, and build out the rails side of the new system directly in the master branch, with no painful merges.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The solution I came up with worked very well, and I think it can apply to similar problems in other systems.&lt;/p&gt;

&lt;h2 id='step_1_move_the_models_into_deprecated_directory'&gt;Step 1: Move the models into deprecated directory&lt;/h2&gt;

&lt;p&gt;First, I made a new directory at &lt;code&gt;app/models/deprecated&lt;/code&gt; and moved all the models for the old ranking system there. This quarantined the legacy code and made it clear that the code was going away soon and no significant investment should go into refactoring it.&lt;/p&gt;

&lt;p&gt;We had to configure rails to recognize this new directory for auto-loading:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;config/application.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;module&lt;/span&gt; &lt;span class='nn'&gt;Cmoz&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;Application&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='no'&gt;Rails&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;Application&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='c1'&gt;# ...&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;config&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;autoload_paths&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;&lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='n'&gt;config&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;root&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;/app/models/deprecated&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='c1'&gt;# ...&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;h2 id='step_2_add_a_feature_flag_to_the_user_model'&gt;Step 2: Add a feature flag to the user model&lt;/h2&gt;

&lt;p&gt;The new service was named &amp;#8220;silo&amp;#8221;, so we added a &lt;code&gt;using_silo&lt;/code&gt; flag to the user model that defaulted to false.&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;db/migrate/20110509180019_add_using_silo_to_users.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;AddUsingSiloToUsers&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='no'&gt;ActiveRecord&lt;/span&gt;&lt;span class='o'&gt;::&lt;/span&gt;&lt;span class='no'&gt;Migration&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nc'&gt;self&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='nf'&gt;up&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;add_column&lt;/span&gt; &lt;span class='ss'&gt;:users&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:using_silo&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:boolean&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:null&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='kp'&gt;false&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:default&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='kp'&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nc'&gt;self&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='nf'&gt;down&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;remove_column&lt;/span&gt; &lt;span class='ss'&gt;:users&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:using_silo&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;h2 id='step_3_turn_the_old_controller_into_a_conditionally_extended_module'&gt;Step 3: Turn the old controller into a conditionally extended module&lt;/h2&gt;

&lt;p&gt;The controller and views were a bit more difficult to deprecate cleanly. As I mentioned above, we wanted to preserve the existing routes, and I don&amp;#8217;t know of a way in rails to route identical requests to different controllers based on a feature flag on the current user record. Instead, we kept a single controller, and turned the old controller into a mixin that we conditionally extended onto the controller instance.&lt;/p&gt;

&lt;p&gt;We started with a controller that was roughly like this:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;app/controllers/rankings_controller.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;RankingsController&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='no'&gt;ApplicationController&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;index&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='c1'&gt;# fetch rankings from the old database&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='vi'&gt;@rankings&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;fetch_rankings&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='c1'&gt;# ...and lots more stuff; this was not a skinny controller&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;show&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='c1'&gt;# fetch rankings from the old database for the given keyword&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='vi'&gt;@rankings&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;fetch_rankings_for_keyword&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;params&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:keyword_id&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='c1'&gt;# ...and lots more stuff; this was not a skinny controller&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;&amp;#8230;and we changed it like so:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;app/controllers/deprecated/legacy_rankings_controller.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;module&lt;/span&gt; &lt;span class='nn'&gt;LegacyRankingsController&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;index&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='c1'&gt;# fetch rankings from the old database&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='vi'&gt;@rankings&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;fetch_rankings&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='c1'&gt;# ...and lots more stuff; this was not a skinny controller&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;show&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='c1'&gt;# fetch rankings from the old database for the given keyword&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='vi'&gt;@rankings&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;fetch_rankings_for_keyword&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;params&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:keyword_id&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='c1'&gt;# ...and lots more stuff; this was not a skinny controller&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;app/controllers/rankings_controller.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;deprecated/legacy_rankings_controller&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;RankingsController&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='no'&gt;ApplicationController&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;before_filter&lt;/span&gt; &lt;span class='ss'&gt;:conditionally_use_legacy_controller&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;index&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;raise&lt;/span&gt; &lt;span class='no'&gt;NotImplementedError&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;The new code hasn&amp;#39;t been written yet&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;show&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;raise&lt;/span&gt; &lt;span class='no'&gt;NotImplementedError&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;The new code hasn&amp;#39;t been written yet&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='kp'&gt;private&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;conditionally_use_legacy_controller&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='kp'&gt;extend&lt;/span&gt; &lt;span class='no'&gt;LegacyRankingsController&lt;/span&gt; &lt;span class='k'&gt;unless&lt;/span&gt; &lt;span class='n'&gt;current_user&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;using_silo?&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;Hopefully it&amp;#8217;s clear what&amp;#8217;s going on here, but in case it&amp;#8217;s not:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rails constructs a new instance of the controller class for every request.&lt;/li&gt;

&lt;li&gt;&lt;code&gt;extend&lt;/code&gt; is standard ruby method that lets you apply a module to a single object instance. The methods in the module, like singleton methods, take precedence over the methods defined in the controller class.&lt;/li&gt;

&lt;li&gt;The before filter replaces the controller implementation with the logic in the module for all requests for users who are not yet using silo. The fact that rails uses a new instance of the controller for every request ensures that the module extension is performed (or not) for each request in isolation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This gives us a very simple, clean way to route the requests to different action implementations based on our feature flag.&lt;/p&gt;

&lt;h2 id='step_4_move_the_views_into_a_deprecated_directory'&gt;Step 4: Move the views into a deprecated directory&lt;/h2&gt;

&lt;p&gt;As with the models above, we moved the views from &lt;code&gt;app/views/rankings&lt;/code&gt; to &lt;code&gt;app/views/deprecated/rankings&lt;/code&gt; to quarantine the old code. Of course, this prevents rails from finding the views where it expects them&amp;#8230;but luckily, rails provides &lt;code&gt;prepend_view_paths&lt;/code&gt; (both as an &lt;a href='https://github.com/rails/rails/blob/v3.1.3/actionpack/lib/abstract_controller/view_paths.rb#L54-56'&gt;instance method&lt;/a&gt; and a &lt;a href='https://github.com/rails/rails/blob/v3.1.3/actionpack/lib/abstract_controller/view_paths.rb#L69-77'&gt;class method&lt;/a&gt;), which solves this problem perfectly. We just need to tweak &lt;code&gt;conditionally_use_legacy_controller&lt;/code&gt; a bit to use the instance method:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;app/controllers/rankings_controller.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;conditionally_use_legacy_controller&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;unless&lt;/span&gt; &lt;span class='n'&gt;current_user&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;using_silo?&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='kp'&gt;extend&lt;/span&gt; &lt;span class='no'&gt;LegacyRankingsController&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;prepend_view_path&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;app/views/deprecated&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;This forces Rails to render the views in &lt;code&gt;app/views/rankings&lt;/code&gt; for users on the new rankings system, and to render the views in &lt;code&gt;app/views/deprecated/rankings&lt;/code&gt; for users still on the old system. It only affects this one request because we are using the instance method &lt;code&gt;prepend_view_path&lt;/code&gt;, not the class method.&lt;/p&gt;

&lt;h2 id='wrapping_up'&gt;Wrapping Up&lt;/h2&gt;

&lt;p&gt;From here, I had a clean slate to implement the controller and views for the new rankings system. The &lt;code&gt;using_silo&lt;/code&gt; flag made it easy to let admins try-out and test the new system before turning it on for all new users. We migrated existing users over to the new system one-by-one, and it was easy to get rid of the code for the old system once all users were migrated.&lt;/p&gt;

&lt;p&gt;I imagine this pattern would be useful in plenty of other situations, too; it&amp;#8217;s a simple, clean way to route requests to different controller and view implementations.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/myronmarsto/n/~4/siBDiy99g-Q" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://myronmars.to/n/dev-blog/2011/12/deprecating-a-legacy-subsystem-in-rails</feedburner:origLink></entry>
  
  <entry>
    <title>VCR 2.0.0 RC Released!</title>
    <link href="http://feedproxy.google.com/~r/myronmarsto/n/~3/2OS4vwMFwGQ/vcr-2-0-0-rc-released" />
    <updated>2011-12-08T00:00:00-08:00</updated>
    <id>http://myronmars.to/n/dev-blog/2011/12/vcr-2-0-0-rc-released</id>
    <content type="html">&lt;p&gt;I&amp;#8217;ve just released the release candidate for VCR 2.0.0. There&amp;#8217;s lots of new goodness. This blog post is a bit of an &amp;#8220;extended&amp;#8221; change log detailing most of the interesting changes from VCR 1.x.&lt;/p&gt;

&lt;h2 id='whats_been_removed'&gt;What&amp;#8217;s been removed&lt;/h2&gt;

&lt;p&gt;Support for Ruby 1.8.6 and 1.9.1 has been dropped. It doesn&amp;#8217;t make sense to keep supporting these versions, especially since I use &lt;a href='http://travis-ci.org/#!/myronmarston/vcr'&gt;travis-ci&lt;/a&gt; for my CI builds and it no longer supports 1.8.6 and 1.9.1.&lt;/p&gt;

&lt;p&gt;There are several old deprecated APIs that have been removed. This is unlikely to affect anyone since I believe they have all been deprecated for over a year. I won&amp;#8217;t detail them here. If you&amp;#8217;re running a fairly recent 1.x release without any warnings then this shouldn&amp;#8217;t affect you at all.&lt;/p&gt;

&lt;p&gt;VCR 1.x supported regexes that were manually inserted in a cassette in place of a URI. This support has been dropped. VCR 2.0 supports &lt;a href='/n/dev-blog/2011/10/custom-request-matchers-in-vcr-2-0#request_matching_in_vcr_20'&gt;custom request matchers&lt;/a&gt; which are much more flexible then the regex support in VCR 1.x. You could even re-implement the regex support using a custom request matcher in a few lines of code if it is important for your test suite.&lt;/p&gt;

&lt;h2 id='whats_been_changed'&gt;What&amp;#8217;s been changed&lt;/h2&gt;

&lt;p&gt;The configuration API has changed slightly:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;configuration.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='c1'&gt;# VCR 1.x&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='no'&gt;VCR&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;config&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;c&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;c&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;cassette_library_dir&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;cassettes&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;c&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;stub_with&lt;/span&gt; &lt;span class='ss'&gt;:fakeweb&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:typhoeus&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='c1'&gt;# VCR 2.0&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='no'&gt;VCR&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;configure&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;c&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;c&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;cassette_library_dir&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;cassettes&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;c&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;hook_into&lt;/span&gt; &lt;span class='ss'&gt;:fakeweb&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:typhoeus&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;Your existing configuration will continue to work with a deprecation warning.&lt;/p&gt;

&lt;p&gt;The &lt;a href='/n/dev-blog/2011/11/cassettes-in-vcr-2-0'&gt;cassette format has changed significantly&lt;/a&gt;. VCR 1.x cassettes are not compatible with VCR 2.0. You&amp;#8217;ll need to either re-record them or &lt;a href='https://github.com/myronmarston/vcr/blob/master/Upgrade.md'&gt;migrate them to the new format&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Individual HTTP interactions are no longer replayed multiple times during the use of a single cassette. The new &lt;a href='https://www.relishapp.com/myronmarston/vcr/docs/request-matching/playback-repeats'&gt;&lt;code&gt;:allow_playback_repeats&lt;/code&gt; cassette option&lt;/a&gt; can be used to restore the VCR 1.x behavior.&lt;/p&gt;

&lt;p&gt;The &lt;a href='https://github.com/technoweenie/faraday'&gt;Faraday&lt;/a&gt; integration has been rewritten. The Faraday integration in VCR 1.x was more than a little confusing; besides configuring &lt;code&gt;config.stub_with :faraday&lt;/code&gt;, you also had to insert &lt;code&gt;VCR::Middleware::Faraday&lt;/code&gt; in the Faraday middleware stack and provide a cassette configuration block. In VCR 2.0, you simply configure &lt;code&gt;config.hook_into :faraday&lt;/code&gt;, just like for FakeWeb, WebMock, Typhoeus or Excon. Under the covers, it takes care of inserting the middleware in the Faraday stack. Alternately, if you want control over where the VCR middleware goes in the stack, you can opt to insert it yourself.&lt;/p&gt;

&lt;h2 id='custom_request_matchers'&gt;Custom Request Matchers&lt;/h2&gt;

&lt;p&gt;This is one of the best new features of VCR 2.0. I &lt;a href='/n/dev-blog/2011/10/custom-request-matchers-in-vcr-2-0#request_matching_in_vcr_20'&gt;blogged about this previously&lt;/a&gt;, so I won&amp;#8217;t belabor it here.&lt;/p&gt;

&lt;h2 id='swappable_and_custom_serializers'&gt;Swappable (and Custom) Serializers&lt;/h2&gt;

&lt;p&gt;This is one of the other big new features of VCR 2.0. My &lt;a href='/n/dev-blog/2011/11/cassettes-in-vcr-2-0#new_serializers'&gt;recent blog post&lt;/a&gt; contains the pertinent details.&lt;/p&gt;

&lt;h2 id='request_hooks'&gt;Request Hooks&lt;/h2&gt;

&lt;p&gt;VCR now provides &lt;code&gt;before_http_request&lt;/code&gt;, &lt;code&gt;after_http_request&lt;/code&gt; and &lt;code&gt;around_http_request&lt;/code&gt; hooks. These can be used in many ways. Here&amp;#8217;s how you could use an &lt;code&gt;after_http_request&lt;/code&gt; hook to log all HTTP requests:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;log_request.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='no'&gt;VCR&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;configure&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;c&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;c&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;after_http_request&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;request&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;response&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='no'&gt;Logger&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;log_http_request&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;request&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;response&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;You can also use a request hook to globally handle all requests made to a specific API:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;handle_geocoding_requests.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='no'&gt;VCR&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;configure&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;c&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;c&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;around_http_request&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;request&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;uri&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;URI&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;request&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;uri&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;if&lt;/span&gt; &lt;span class='n'&gt;uri&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;host&lt;/span&gt; &lt;span class='o'&gt;==&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;api.geocoder.com&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='c1'&gt;# extract an address like &amp;quot;1700 E Pine St, Seattle, WA&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='c1'&gt;# from a query like &amp;quot;address=1700+E+Pine+St%2C+Seattle%2C+WA&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='n'&gt;address&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;CGI&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;unescape&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;uri&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;query&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;split&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;=&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;last&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='no'&gt;VCR&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;use_cassette&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;geocoding/&lt;/span&gt;&lt;span class='si'&gt;#{&lt;/span&gt;&lt;span class='n'&gt;address&lt;/span&gt;&lt;span class='si'&gt;}&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;&amp;amp;&lt;/span&gt;&lt;span class='n'&gt;request&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class='n'&gt;request&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;proceed&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;In an &lt;code&gt;around_http_request&lt;/code&gt;, you can either treat the request as a proc (and pass it on to a method that expects a block as &lt;code&gt;&amp;amp;request&lt;/code&gt;), or use &lt;code&gt;request.proceed&lt;/code&gt; to allow the request to continue.&lt;/p&gt;

&lt;p&gt;I certainly wouldn&amp;#8217;t recommend doing this for all requests&amp;#8211;you&amp;#8217;ll often want to test the same requests getting different responses in different tests&amp;#8211;but for truly stateless APIs that always return the same response for a given request (such as a geocoder) this can be very handy.&lt;/p&gt;

&lt;p&gt;On ruby 1.8 you won&amp;#8217;t be able to use an &lt;code&gt;around_http_request&lt;/code&gt; hook because it uses a fiber; instead you can use separate &lt;code&gt;before_http_request&lt;/code&gt; and &lt;code&gt;after_http_request&lt;/code&gt; to achieve the same behavior.&lt;/p&gt;

&lt;h2 id='ignore_a_request_based_on_anything'&gt;Ignore a Request Based on Anything&lt;/h2&gt;

&lt;p&gt;VCR 1.x made it simple to ignore a request based on the host:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;vcr_setup.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='no'&gt;VCR&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;configure&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;c&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;c&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;ignore_localhost&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='kp'&gt;true&lt;/span&gt; &lt;span class='c1'&gt;# to ignore 127.0.0.1 and localhost requests&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='c1'&gt;# or...&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;c&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;ignore_hosts&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;foo.com&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;bar.com&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;This worked great for most people, but some wanted to &lt;a href='https://github.com/myronmarston/vcr/issues/42'&gt;selectively ignore localhost requests based on port&lt;/a&gt;. VCR 2.0 now lets you ignore a request based on &lt;em&gt;anything&lt;/em&gt;, by providing a block that returns a truthy value if the given request should be ignored. Here&amp;#8217;s how you can ignore only localhost requests to port 7500:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;ignore_request.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='no'&gt;VCR&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;configure&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;c&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;c&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;ignore_request&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;request&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;uri&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;URI&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;request&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;uri&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='n'&gt;uri&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;host&lt;/span&gt; &lt;span class='o'&gt;==&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;localhost&amp;#39;&lt;/span&gt; &lt;span class='o'&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class='n'&gt;uri&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;port&lt;/span&gt; &lt;span class='o'&gt;==&lt;/span&gt; &lt;span class='mi'&gt;7500&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;h2 id='improved_unhandled_request_error_messages'&gt;Improved Unhandled Request Error Messages&lt;/h2&gt;

&lt;p&gt;If you&amp;#8217;ve used VCR 1.x, you&amp;#8217;ve undoubtedly gotten an error like this:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;vcr_1_x_error.txt  &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='sh'&gt;&lt;span class='line'&gt;Real HTTP connections are disabled. Unregistered request: GET
&lt;/span&gt;&lt;span class='line'&gt;http://api.somehost.com/widgets.  You can use VCR to automatically
&lt;/span&gt;&lt;span class='line'&gt;record this request and replay it later.  For more details, visit
&lt;/span&gt;&lt;span class='line'&gt;the VCR documentation at: http://relishapp.com/myronmarston/vcr/v/1-11-3
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='o'&gt;(&lt;/span&gt;FakeWeb::NetConnectNotAllowedError&lt;span class='o'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;You get this kind of error when a request is made that VCR does not know how to handle. There are a lot of different ways you can fix the error, but the message doesn&amp;#8217;t give you much help.&lt;/p&gt;

&lt;p&gt;In VCR 2.0, you&amp;#8217;ll get a more helpful error message:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;example_error.txt  &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='sh'&gt;&lt;span class='line'&gt;&lt;span class='o'&gt;================================================================================&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;An HTTP request has been made that VCR does not know how to handle:
&lt;/span&gt;&lt;span class='line'&gt;  GET http://api.somehost.com/widgets
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;VCR is currently using the following cassette:
&lt;/span&gt;&lt;span class='line'&gt;  - cassettes/widgets.yml
&lt;/span&gt;&lt;span class='line'&gt;  - :record &lt;span class='o'&gt;=&lt;/span&gt;&amp;gt; :once
&lt;/span&gt;&lt;span class='line'&gt;  - :match_requests_on &lt;span class='o'&gt;=&lt;/span&gt;&amp;gt; &lt;span class='o'&gt;[&lt;/span&gt;:method, :uri&lt;span class='o'&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;Under the current configuration VCR can not find a suitable HTTP interaction
&lt;/span&gt;&lt;span class='line'&gt;to replay and is prevented from recording new requests. There are a few ways
&lt;/span&gt;&lt;span class='line'&gt;you can deal with this:
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  * You can use the :new_episodes record mode to allow VCR to
&lt;/span&gt;&lt;span class='line'&gt;    record this new request to the existing cassette &lt;span class='o'&gt;[&lt;/span&gt;1&lt;span class='o'&gt;]&lt;/span&gt;.
&lt;/span&gt;&lt;span class='line'&gt;  * If you want VCR to ignore this request &lt;span class='o'&gt;(&lt;/span&gt;and others like it&lt;span class='o'&gt;)&lt;/span&gt;, you can
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='nb'&gt;set &lt;/span&gt;an &lt;span class='sb'&gt;`&lt;/span&gt;ignore_request&lt;span class='sb'&gt;`&lt;/span&gt; callback &lt;span class='o'&gt;[&lt;/span&gt;2&lt;span class='o'&gt;]&lt;/span&gt;.
&lt;/span&gt;&lt;span class='line'&gt;  * The current record mode &lt;span class='o'&gt;(&lt;/span&gt;:once&lt;span class='o'&gt;)&lt;/span&gt; does not allow new requests to be recorded
&lt;/span&gt;&lt;span class='line'&gt;    to a previously recorded cassette. You can delete the cassette file and re-run
&lt;/span&gt;&lt;span class='line'&gt;    your tests to allow the cassette to be recorded with this request &lt;span class='o'&gt;[&lt;/span&gt;3&lt;span class='o'&gt;]&lt;/span&gt;.
&lt;/span&gt;&lt;span class='line'&gt;  * The cassette contains 1 HTTP interaction that has not been
&lt;/span&gt;&lt;span class='line'&gt;    played back. If your request is non-deterministic, you may need to
&lt;/span&gt;&lt;span class='line'&gt;    change your :match_requests_on cassette option to be more lenient
&lt;/span&gt;&lt;span class='line'&gt;    or use a custom request matcher to allow it to match &lt;span class='o'&gt;[&lt;/span&gt;4&lt;span class='o'&gt;]&lt;/span&gt;.
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='o'&gt;[&lt;/span&gt;1&lt;span class='o'&gt;]&lt;/span&gt; https://www.relishapp.com/myronmarston/vcr/v/2-0-0-rc1/docs/record-modes/new-episodes
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='o'&gt;[&lt;/span&gt;2&lt;span class='o'&gt;]&lt;/span&gt; https://www.relishapp.com/myronmarston/vcr/v/2-0-0-rc1/docs/configuration/ignore-request
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='o'&gt;[&lt;/span&gt;3&lt;span class='o'&gt;]&lt;/span&gt; https://www.relishapp.com/myronmarston/vcr/v/2-0-0-rc1/docs/record-modes/once
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='o'&gt;[&lt;/span&gt;4&lt;span class='o'&gt;]&lt;/span&gt; https://www.relishapp.com/myronmarston/vcr/v/2-0-0-rc1/docs/request-matching
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='o'&gt;================================================================================&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;h2 id='integration_with_rspec_2_metadata'&gt;Integration with RSpec 2 Metadata&lt;/h2&gt;

&lt;p&gt;&lt;a href='http://twitter.com/rbates'&gt;Ryan Bates&lt;/a&gt; had this &lt;a href='https://gist.github.com/1212530'&gt;great idea&lt;/a&gt; to integrate VCR with RSpec 2 using metadata. VCR 2.0 now provides direct support for this:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;vcr_rspec_metadata.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='no'&gt;VCR&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;configure&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;c&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;c&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;configure_rspec_metadata!&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='no'&gt;RSpec&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;configure&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;c&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='c1'&gt;# so we can use `:vcr` rather than `:vcr =&amp;gt; true`;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='c1'&gt;# in RSpec 3 this will no longer be necessary.&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;c&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;treat_symbols_as_metadata_keys_with_true_values&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='kp'&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='c1'&gt;# apply it to an example group&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;describe&lt;/span&gt; &lt;span class='no'&gt;MyAPIWrapper&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:vcr&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;describe&lt;/span&gt; &lt;span class='no'&gt;MyAPIWrapper&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='c1'&gt;# apply it to an individual example&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;does something&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:vcr&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='c1'&gt;# set some cassette options&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;does something&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:vcr&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='ss'&gt;:record&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='ss'&gt;:new_episodes&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='c1'&gt;# override the cassette name&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;does something&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:vcr&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='ss'&gt;:cassette_name&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;something&amp;quot;&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;p&gt;The old &lt;a href='https://www.relishapp.com/myronmarston/vcr/docs/test-frameworks/usage-with-rspec-macro'&gt;&lt;code&gt;use_vcr_cassette&lt;/code&gt; macro&lt;/a&gt; still works. The primary difference is that the macro uses the same cassette for each example in an example group, while the metadata uses a different cassette for each individual example.&lt;/p&gt;

&lt;h2 id='exclusive_cassettes'&gt;Exclusive cassettes&lt;/h2&gt;

&lt;p&gt;VCR has always allowed you to &amp;#8220;nest&amp;#8221; cassettes; for example, you may use a cassette for an entire cucumber scenario and then also use a cassette in an individual step definition. When you do this, requests may be handled by an HTTP interaction from the outer cassette if there is not an HTTP interaction from the inner cassette that matches.&lt;/p&gt;

&lt;p&gt;If you do not want to allow the matching to &amp;#8220;fall through&amp;#8221; to the outer cassette you can use the new &lt;code&gt;:exclusive&lt;/code&gt; option:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;exclusive_cassette.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='no'&gt;VCR&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;use_cassette&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;my_cassette&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:exclusive&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='kp'&gt;true&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='c1'&gt;# ...&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;h2 id='vcr_200_final'&gt;VCR 2.0.0 Final&lt;/h2&gt;

&lt;p&gt;I plan to release VCR 2.0.0 final in a couple of weeks. Please give the RC a try and give me feedback!&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/myronmarsto/n/~4/2OS4vwMFwGQ" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://myronmars.to/n/dev-blog/2011/12/vcr-2-0-0-rc-released</feedburner:origLink></entry>
  
  <entry>
    <title>Cassettes in VCR 2.0</title>
    <link href="http://feedproxy.google.com/~r/myronmarsto/n/~3/o7LERILvtYc/cassettes-in-vcr-2-0" />
    <updated>2011-11-07T00:00:00-08:00</updated>
    <id>http://myronmars.to/n/dev-blog/2011/11/cassettes-in-vcr-2-0</id>
    <content type="html">&lt;p&gt;The first beta release of VCR 2.0 was focused on &lt;a href='/n/dev-blog/2011/10/custom-request-matchers-in-vcr-2-0'&gt;improving how the request matchers work&lt;/a&gt; so users can easily customize them to their needs.&lt;/p&gt;

&lt;p&gt;The second beta, released yesterday, includes a bunch of changes to the cassette format. Unfortunately, cassettes recorded with VCR 1.x are not compatible with VCR 2.0. I imagine this may make upgrading painful for some users (though, I&amp;#8217;m hopeful the pain will be minimal), and I thought it worthwhile to explain the reasons for all the changes.&lt;/p&gt;

&lt;h2 id='yaml_issues'&gt;YAML issues&lt;/h2&gt;

&lt;p&gt;&lt;a href='http://yaml.org/'&gt;YAML&lt;/a&gt; is a great serialization format. It&amp;#8217;s the most human-readable and human-editable format I know of (both of which I consider to be very important for VCR). It&amp;#8217;s been the only serialization format for VCR cassettes from the first release.&lt;/p&gt;

&lt;p&gt;However, it&amp;#8217;s not without issues. Syck, the YAML engine in ruby 1.8, has a number of unfortunate bugs. I&amp;#8217;ve had &lt;a href='https://github.com/myronmarston/vcr/issues/4'&gt;multiple&lt;/a&gt; &lt;a href='https://github.com/myronmarston/vcr/issues/39'&gt;reports&lt;/a&gt; of syck segfaulting due to particular data in the VCR cassette. In addition, it &lt;a href='https://gist.github.com/815754'&gt;removes spaces from whitespace-only lines&lt;/a&gt;. A string like &lt;code&gt;&amp;quot;1\n
\n2&amp;quot;&lt;/code&gt;, when round-tripped through syck, results in &lt;code&gt;&amp;quot;1\n\n2&amp;quot;&lt;/code&gt;&amp;#8211;a string of one less character. This can cause problems for HTTP clients that depend on the &amp;#8220;Content-Length&amp;#8221; header accurately matching the length of the response body. Mechanize, for example, will raise an error.&lt;/p&gt;

&lt;p&gt;Psych, the new YAML engine in ruby 1.9 written by &lt;a href='http://twitter.com/tenderlove'&gt;tenderlove&lt;/a&gt;, is much improved. Since 1.7.0, VCR has attempted to use psych when it is avaiable. However, psych also occasionally has &lt;a href='https://github.com/myronmarston/vcr/issues/74'&gt;segfaulting&lt;/a&gt;/&lt;a href='https://github.com/myronmarston/vcr/issues/83'&gt;memory corruption&lt;/a&gt; issues.&lt;/p&gt;

&lt;h2 id='new_serializers'&gt;New Serializers!&lt;/h2&gt;

&lt;p&gt;VCR 2.0 allows you to choose from several different serializers, or even provide your own. This can be useful to work around an issue with either sych or psych, or simply because you prefer another format (e.g. JSON). VCR 2.0 now has 4 built in serializers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;YAML&lt;/li&gt;

&lt;li&gt;Syck&lt;/li&gt;

&lt;li&gt;Psych&lt;/li&gt;

&lt;li&gt;JSON&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;YAML is still the default, and uses the standard library &lt;code&gt;YAML&lt;/code&gt; after requiring &amp;#8220;yaml&amp;#8221;. This could wind up using either syck or psych, depending upon your ruby interpreter. The Syck and Psych serializers are useful as a way to force those libraries to be used. Syck is particularly useful when you have a project that needs to run on 1.8 and 1.9, so that the same YAML library gets used regardless of the ruby interpreter version. The JSON serializer uses &lt;a href='https://github.com/intridea/multi_json'&gt;multi_json&lt;/a&gt; so that it supports a variety of JSON backends.&lt;/p&gt;

&lt;p&gt;Here&amp;#8217;s how to pick a serializer:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;serialize_with_json.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='c1'&gt;# use the JSON serializer for a particular cassette...&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='no'&gt;VCR&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;use_cassette&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s2'&gt;&amp;quot;example&amp;quot;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:serialize_with&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='ss'&gt;:json&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='c1'&gt;# make an HTTP request&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='c1'&gt;# ...or use the JSON serializer for all cassettes&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='no'&gt;VCR&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;configure&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;config&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;config&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;default_cassette_options&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='ss'&gt;:serialize_with&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='ss'&gt;:json&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;h2 id='providing_your_own_custom_serializer'&gt;Providing Your Own Custom Serializer&lt;/h2&gt;

&lt;p&gt;It&amp;#8217;s fairly trivial to provide your own serializer. You need to provide an object that implements three methods:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;file_extension&lt;/code&gt;&lt;/li&gt;

&lt;li&gt;&lt;code&gt;serialize(hash)&lt;/code&gt;&lt;/li&gt;

&lt;li&gt;&lt;code&gt;deserialize(string)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here&amp;#8217;s an example using ruby&amp;#8217;s marshal library:&lt;/p&gt;
&lt;div class='bogus-wrapper'&gt;&lt;notextile&gt;&lt;div class='code-wrap'&gt;&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;marshal_serializer.rb &lt;/span&gt;&lt;/figcaption&gt;
 &lt;div class='highlight'&gt;&lt;table&gt;&lt;tr&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='rb'&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;serializer&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;Object&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='n'&gt;serializer&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;instance_eval&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;file_extension&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='s2'&gt;&amp;quot;rb_marshal&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;serialize&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;hash&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='no'&gt;Marshal&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;dump&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nb'&gt;hash&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;deserialize&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;string&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class='no'&gt;Marshal&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;load&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;string&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='no'&gt;VCR&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;configure&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;config&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;config&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;cassette_serializers&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:marshal&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;serializer&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class='n'&gt;config&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;default_cassette_options&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt; &lt;span class='ss'&gt;:serialize_with&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='ss'&gt;:marshal&lt;/span&gt; &lt;span class='p'&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/notextile&gt;&lt;/div&gt;
&lt;h2 id='cassettes_are_more_portable'&gt;Cassettes are More Portable&lt;/h2&gt;

&lt;p&gt;VCR 1.x serialized some structs directly to YAML. This caused the classes (&lt;code&gt;VCR::HTTPInteraction&lt;/code&gt;, &lt;code&gt;VCR::Request&lt;/code&gt; and &lt;code&gt;VCR::Response&lt;/code&gt;) to be named directly in the cassette. In 2.0, VCR passes a simple hash to the serializer, so these class names no longer show up in the cassette file. Besides making it possible to use different serializers, this also makes the VCR cassette format much more portable. You can read and use a VCR cassette without loading VCR now! It opens up the possibility to use a VCR cassette from other languages as well.&lt;/p&gt;

&lt;h2 id='now_with_less_normalization'&gt;Now With Less Normalization&lt;/h2&gt;

&lt;p&gt;VCR 1.x extensively normalized each HTTP Interaction. VCR originally only worked with FakeWeb and Net::HTTP. As I expanded VCR to work with many other libraries, I tried to ensure that the cassette that resulted from a particular set of recorded HTTP interactions would be the same, regardless of the HTTP client library or stubbing library used. At the time, it made sense to me that since a VCR cassette is agnostic about which HTTP client library was used, it should be the same for the same set of HTTP interactions. Net::HTTP normalizes header keys to lower case, so VCR 1.x performed the same normalization on the headers stored in the cassette. On playback, it would de-normalize as necessary; if the header key was &lt;code&gt;content-type&lt;/code&gt;, it would be de-normalized as &lt;code&gt;Content-Type&lt;/code&gt;. Theoretically, this would have allowed you to change your implementation to use a different HTTP library, and the VCR cassette should continue to playback just fine.&lt;/p&gt;

&lt;p&gt;Unfortunately, the de-normalization doesn&amp;#8217;t always work properly. If a header is recorded as &lt;code&gt;etag&lt;/code&gt;, it gets de-normalized to &lt;code&gt;Etag&lt;/code&gt; even though it may originally have been &lt;code&gt;ETag&lt;/code&gt;. This is in fact an &lt;a href='https://github.com/fog/fog/issues/434'&gt;issue when using VCR with Fog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So, in VCR 2.0, I&amp;#8217;ve removed this normalization. You&amp;#8217;ll see more variance in the data that gets recorded by an identical HTTP request from different HTTP libraries.&lt;/p&gt;

&lt;h2 id='new__metadata'&gt;New &lt;code&gt;recorded_at&lt;/code&gt; metadata&lt;/h2&gt;

&lt;p&gt;VCR 2.0 now includes a &lt;code&gt;recorded_at&lt;/code&gt; timestamp for each HTTP Interaction. This allows the &lt;code&gt;re_record_interval&lt;/code&gt; cassette option to work much more accurately. Previously, VCR used the cassette file&amp;#8217;s modification time, but this may not be accurate, especially when the file on disk changes due to your source control (i.e. if you change git branches or whatever).&lt;/p&gt;

&lt;h2 id='new__metadata'&gt;New &lt;code&gt;recorded_with&lt;/code&gt; metadata&lt;/h2&gt;

&lt;p&gt;VCR 2.0 also includes a bit of metadata about the cassette as a whole: &lt;code&gt;recorded_with&lt;/code&gt; will be a string like &lt;code&gt;&amp;quot;VCR 2.0.0&amp;quot;&lt;/code&gt;. Theoretically, if any other tool like VCR comes along and wanted to adopt the same fixture format, it could use this to identify itself as well.&lt;/p&gt;

&lt;p&gt;I&amp;#8217;ve wanted to make changes to the VCR cassette format for awhile, but have held off for fear of making breaking changes. This bit of metadata should make future format changes much easier to make in a backwards-compatible way, as it will identify the format version that was used to record the cassette so it can be easily and automatically upgraded.&lt;/p&gt;

&lt;h2 id='upgrading'&gt;Upgrading&lt;/h2&gt;

&lt;p&gt;The &lt;a href='https://www.relishapp.com/myronmarston/vcr/docs/upgrade'&gt;upgrade notes&lt;/a&gt; contain instructions for how to upgrade from VCR 1.x. If you&amp;#8217;re curious to see examples of how the cassette format changed, take a look at a &lt;a href='https://github.com/myronmarston/vcr/blob/v1.11.3/spec/fixtures/not_1.9.1/cassette_spec/example.yml'&gt;1.x cassette&lt;/a&gt; compared to the same cassette in &lt;a href='https://github.com/myronmarston/vcr/blob/v2.0.0.beta2/spec/fixtures/cassette_spec/example.yml'&gt;2.0 format&lt;/a&gt;.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/myronmarsto/n/~4/o7LERILvtYc" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://myronmars.to/n/dev-blog/2011/11/cassettes-in-vcr-2-0</feedburner:origLink></entry>
  
</feed>
