<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

 <title>Ben Mabey</title>
 <link href="http://benmabey.com/atom.xml" rel="self"/>
 <link href="http://benmabey.com/"/>
 <updated>2014-06-14T15:17:11-06:00</updated>
 <id>http://benmabey.com/</id>
 <author>
   <name>Ben Mabey</name>
   <email>ben+blog@benmabey.com</email>
 </author>

 
 <entry>
   <title>DatabaseCleaner and EmailSpec need new homes</title>
   <link href="http://benmabey.com/2013/09/11/databasecleaner-and-email-spec-need-new-homes.html"/>
   <updated>2013-09-11T00:00:00-06:00</updated>
   <id>http://benmabey.com/2013/09/11/databasecleaner-and-email-spec-need-new-homes</id>
   <content type="html">&lt;p&gt;Several years ago I scratched some itches I had while developing Ruby and Rails applications by creating
&lt;a href=&quot;http://rubygems.org/gems/database_cleaner&quot;&gt;DatabaseCleaner&lt;/a&gt; and &lt;a href=&quot;http://rubygems.org/gems/email_spec&quot;&gt;EmailSpec&lt;/a&gt;. These
were small gems which contain boilerplate code for testing that I found myself copying and pasting from project to project.
It turned out that a lot of other developers had the same problems. Shortly after pushing them to github
&lt;a href=&quot;http://benmabey.com/2008/12/18/github-rocks.html&quot;&gt;I quickly got contributors&lt;/a&gt; and the projects became moderately
popular.&lt;/p&gt;

&lt;p&gt;While the maintenance of these projects hasn't been large I'm finding myself less and less motivated to keep them
updated, handle bug reports, and improve them.  A large part of the lack of motivation is that I haven't developed an
application that requires them in a number of years.  So, instead of letting them continue to rot I'd like to hand
the projects over to people who actually use them and are motivated to maintain them properly and hopefully improve
them.  Here is some rambling of where each project is and what you should know before considering taking them over:&lt;/p&gt;

&lt;h2&gt;DatabaseCleaner&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/bmabey/database_cleaner&quot;&gt;DatabaseCleaner&lt;/a&gt; is by far the more popular gem and is found in quite a bit
of Rails applications these days. Sadly, there are quite a few
&lt;a href=&quot;https://github.com/bmabey/database_cleaner/issues&quot;&gt;open tickets&lt;/a&gt; for DatabaseCleaner on github. Many of them are old
and can be closed and others are duplicates all stemming from a few specific bugs.  Most of the maintenance of the gem
deals with ORM version changes.  The specs are all passing but the integration tests which require additional DBs to be
installed have fallen into disrepair (see below). :/&lt;/p&gt;

&lt;p&gt;The biggest problem with this gem is that it started out as a bunch of monkey patches and has
continued to grow that way. The vast majority of issues with the project has stemmed from this use of monkeypatching and
library version differences (e.g. ActiveRecord API and internal differences).  I've been wanting to take a different
approach but just haven't dedicated the necessary time to do so.  I think having a databasecleaner-core which just
provides the configuration API and let other people maintain ORM specific plugins would be a better way to distribute
the work among interested parties (much like how &lt;a href=&quot;https://github.com/intridea/omniauth&quot;&gt;omniauth&lt;/a&gt; is organized). Since
having all of the databases installed for tests is onerous I think this approach could help in that regard.  (Barring
that a vagrant box would go a long ways.)&lt;/p&gt;

&lt;p&gt;Another thing I would change is the use of Cucumber features for the integration tests. The features generate other
features to create sample applications.  Sound confusing?  It is, and is needless complexity that I think should be
removed in favor of some additional straightforward specs.&lt;/p&gt;

&lt;h2&gt;EmailSpec&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/bmabey/email-spec&quot;&gt;EmailSpec&lt;/a&gt; requires very little maintenance and I don't see any need to drastically
change the library.  There are also a good number of &lt;a href=&quot;https://github.com/bmabey/email-spec/issues&quot;&gt;open issues&lt;/a&gt; that
I've not addressed but they are all small and could be dealt with quickly.  The specs and coverage of the project is in
good shape and so merging in PRs is fast.&lt;/p&gt;

&lt;h2&gt;Let me know if you are interested!&lt;/h2&gt;

&lt;p&gt;Contact me via &lt;a href=&quot;http://twitter.com/bmabey&quot;&gt;twitter&lt;/a&gt; or &lt;a href=&quot;mailto:ben@benmabey.com&quot;&gt;email&lt;/a&gt; if you are willing to give one
or both of these gems a better home.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Presentation: Clojure, Plain and Simple</title>
   <link href="http://benmabey.com/2013/09/04/presentation-clojure-plain-and-simple.html"/>
   <updated>2013-09-04T00:00:00-06:00</updated>
   <id>http://benmabey.com/2013/09/04/presentation-clojure-plain-and-simple</id>
   <content type="html">&lt;p&gt;Last month I gave an &lt;a href=&quot;https://speakerdeck.com/bmabey/clojure-plain-and-simple&quot;&gt;introductory presentation on Clojure&lt;/a&gt; to
the &lt;a href=&quot;http://www.ujug.org&quot;&gt;local Java users group&lt;/a&gt;. It's main focus was on Clojure's time model and how the design of
Clojure separates (decomplects) many concepts which are all implemented onto Objects in Java, and other OO languages.
This is the original abstract for the presentation:&lt;/p&gt;

&lt;p&gt;Tony Hoare famously said &quot;There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies. The first method is far more difficult.&quot; Clojure is a functional Lisp that targets, among other platforms, the JVM and strives to enable the former approach to building software.&lt;/p&gt;

&lt;p&gt;In its pursuit of simplicity Clojure encourages the use of pure functions, sequence abstractions which allow for lazy and parallel processing of data, persistent (immutable) data structures, and a novel way of dealing with state as a succession of values. While these concepts may sound intimidating for those unfamiliar with functional programming, they are actually less complicated than many programming constructs that programmers use everyday.&lt;/p&gt;

&lt;p&gt;This talk will cover these concepts and the motivation behind them. You will learn the basics of Clojure programming and
will be given a taste of what developing an application in Clojure is like.&lt;/p&gt;

&lt;script async class=&quot;speakerdeck-embed&quot; data-id=&quot;4e727b40e8b60130abbb124255f208d5&quot; data-ratio=&quot;1.33333333333333&quot; src=&quot;//speakerdeck.com/assets/embed.js&quot;&gt;&lt;/script&gt;

</content>
 </entry>
 
 <entry>
   <title>Presentation on PCA</title>
   <link href="http://benmabey.com/2013/01/30/presentation-on-pca.html"/>
   <updated>2013-01-30T00:00:00-07:00</updated>
   <id>http://benmabey.com/2013/01/30/presentation-on-pca</id>
   <content type="html">&lt;p&gt;I gave a presentation at &lt;a href=&quot;http://rebdrainlabs.com&quot;&gt;work&lt;/a&gt; to introduce people to &lt;a href=&quot;http://en.wikipedia.org/wiki/Principal_component_analysis&quot;&gt;Principal component analysis (PCA)&lt;/a&gt;.  Basic linear algebra knowledge was assumed
and code examples were given in R.  The presentation is available to view
&lt;a href=&quot;http://benmabey.com/presentations/pca-tutorial&quot;&gt;here&lt;/a&gt; and you can see the source for the deck &lt;a href=&quot;http://github.com/bmabey/pca-tutorial/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The deck isn't completely standalone since I don't explain every step made as I did when actually presenting it.  That
said I think the deck should be useful for anyone who wants to get a quick idea of what PCA is and the math behind it (I
only take into account conventional PCA, not probabilistic interpretations).  I am inconsistent with some of my
equations to make some of the algebra easier (all legal though!) which I explained during the actual presentation.  For
people who want to go deeper and follow the math more closely I highly recommend the
&lt;a href=&quot;http://www.snl.salk.edu/~shlens/&quot;&gt;tutorial by Jonathan Shlens&lt;/a&gt; which is where I got most of my derivations.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>FAQ: What machine learning book should I start with?</title>
   <link href="http://benmabey.com/2011/10/07/faq-what-machine-learning-book-should-i-start-with.html"/>
   <updated>2011-10-07T00:00:00-06:00</updated>
   <id>http://benmabey.com/2011/10/07/faq-what-machine-learning-book-should-i-start-with</id>
   <content type="html">&lt;p&gt;I run across this question a lot and I am sometimes asked it directly.  It came up again on the discussion board for Stanford's new online introductory &lt;a href=&quot;http://www.ml-class.org&quot;&gt;Machine Learning course&lt;/a&gt;.  I gave the following answer and it was well received so I thought I'd post it here as well (since that forum is not publicly available).&lt;/p&gt;

&lt;p&gt;The answer, as in all things, is that &quot;it depends&quot;. What makes a good starting book is dependent on your background (specifically in math) and what your end goal is.&lt;/p&gt;

&lt;h2&gt;The Curious Programmer / Aspiring Practitioner&lt;/h2&gt;

&lt;p&gt;If you are a programmer that is either unsure of your mathematical foundation and/or just want to learn enough to apply ML to a problem here are some good practical books.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.amazon.com/Programming-Collective-Intelligence-Building-Applications/dp/0596529325/ref=sr_1_1?s=books&amp;amp;ie=UTF8&amp;amp;qid=1316982532&amp;amp;sr=1-1&quot;&gt;Programming Collective Intelligence&lt;/a&gt; by Toby Segaran.  It gives the reader an intuition on several machine learning algorithms without delving into the math. (It is in fact nearly void of mathematical equations-- I understand why the author did this but I can't say I agree with the approach.)  It is important to realize that this book is just a stepping stone, because you eventually will need/want to understand the math behind all the algorithms for more advanced work.  Having said that, it is a gentle introduction to get your feet wet and is very hands on (code is in Python) which will help keep you interested.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;&lt;fieldset&gt; &lt;legend style=&quot;font-weight:bold;&quot;&gt;UPDATE&lt;/legend&gt;
More and more of these type books seem to be popping up like &lt;a href=&quot;http://www.amazon.com/Machine-Learning-Hackers-Drew-Conway/dp/1449303714&quot;&gt;Machine Learning for Hackers&lt;/a&gt;. The same caveats apply to them... while a great start, they are just that. Eventually you'll need to look inside the black box.
&lt;/fieldset&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&quot;http://www.manning.com/owen/&quot;&gt;Mahout in Action&lt;/a&gt; by Sean Owen, Robin Anil, Ted Dunning, and Ellen Friedman - This book is well written, provides intuitive explanations of the algorithms involved, and gives a lot of good practical advice. One thing I really appreciate about the book is that instead of simply ignoring more advanced algorithms (e.g. &lt;a href=&quot;http://www.slideshare.net/bmabey/svd-and-the-netflix-dataset-presentation&quot;&gt;SVD for recommendation systems&lt;/a&gt;) it will mention them and acknowledge that they are out of the scope of the book. Another interesting aspect of this book is that it talks about the implementation details of the Mahout library (e.g. data structures used, and integration with Hadoop).  When I got the book I was afraid it would simply be a manual for Mahout, but I was pleasantly surprised by the general nature of the book.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&quot;http://www.amazon.com/Data-Mining-Practical-Techniques-Management/dp/0123748569/ref=sr_1_1?s=books&amp;amp;ie=UTF8&amp;amp;qid=1316990059&amp;amp;sr=1-1&quot;&gt;Data Mining: Practical Machine Learning Tools and Techniques&lt;/a&gt; by Ian H. Witten, Eibe Frank and Mark A. Hall - This has more math than the two previous books but it highlights the &quot;mathy&quot; sections that the reader can safely skip if they so choose.  It offers a detailed survey of many algorithms along with practical advice on model and feature selection (i.e. how to compare performance of different models and how to choose the data for training).  It doesn't include programming examples throughout the book like the two above do and so it can be hard to make it through some sections (i.e. it can be dry at times).  It does have coding examples at the end using &lt;a href=&quot;http://www.cs.waikato.ac.nz/ml/weka/&quot;&gt;Weka&lt;/a&gt;... which makes sense because the authors of the book also created/contribute to Weka.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Leveling up, the math behind the curtains&lt;/h2&gt;

&lt;p&gt;If you feel like you have a decent math background here are some good starting points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&quot;http://www.amazon.com/Machine-Learning-Algorithmic-Perspective-Recognition/dp/1420067184/ref=sr_1_1?s=books&amp;amp;ie=UTF8&amp;amp;qid=1316982239&amp;amp;sr=1-1&quot;&gt;Machine Learning: An Algorithmic Perspective&lt;/a&gt; by Stephen Marsland -  I have not read this one personally but it has been recommended to me, by people I respect, as being a good beginner book.  The code examples are in Python.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&quot;http://www.amazon.com/Machine-Learning-Tom-M-Mitchell/dp/0070428077/ref=sr_1_2?s=books&amp;amp;ie=UTF8&amp;amp;qid=1316982239&amp;amp;sr=1-2&quot;&gt;Machine Learning&lt;/a&gt;
by Tom Mitchell. A classic.  This was the book used in my university course and I thought it provided a good survey of the field.  Tom Mitchell recently released an excellent set of &lt;a href=&quot;http://www.cs.cmu.edu/~tom/10701_sp11/lectures.shtml&quot;&gt;video lectures&lt;/a&gt; as well. On that page you will also find some updated material that would supplement the book well.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&quot;http://www.amazon.com/Artificial-Intelligence-Modern-Approach-3rd/dp/0136042597/ref=sr_1_1?s=books&amp;amp;ie=UTF8&amp;amp;qid=1316986126&amp;amp;sr=1-1&quot;&gt;Artificial Intelligence: A Modern Approach&lt;/a&gt; by Stuart Russell and Peter Norvig - Another classic, but this one covers the broader AI familiy in addition to ML specific parts.  Of course, if you are really interested in this book you should also sign up for &lt;a href=&quot;http://www.ai-class.com/&quot;&gt;ai-class&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&quot;http://cs229.stanford.edu/materials.html&quot;&gt;Andrew Ng's cs229 course lecture notes&lt;/a&gt; - Don't let the &quot;notes&quot; part fool you... this is really a free mini e-book on Machine Learning.  The notes are more mathematically heavy than many of the previous books I've mentioned.  However, since math is really the &quot;beef&quot;&lt;sup&gt;&lt;a href=&quot;#fn:beef&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; of Machine Learning you have to learn it sometime and these notes provide a nice concise introduction.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&quot;http://www.autonlab.org/tutorials/&quot;&gt;Andrew Moore's tutorial slides&lt;/a&gt; - Again, not a book but a great resource.  The slides aren't detailed enough sometimes but they are valuable supplemental material.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;&quot;My middle name is Guass&quot;&lt;/h2&gt;

&lt;p&gt;Now, if you have the mathematical chops&lt;sup&gt;&lt;a href=&quot;#fn:gauss&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; you might as well dive in with these commonly recommended books:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&quot;http://www.amazon.com/Pattern-Recognition-Learning-Information-Statistics/dp/0387310738/ref=sr_1_1?s=books&amp;amp;ie=UTF8&amp;amp;qid=1316988299&amp;amp;sr=1-1&quot;&gt;Pattern Recognition and Machine Learning&lt;/a&gt; by Christopher Bishop - In the preface Bishop states that the book was intended for graduates and advanced undergraduates. I've only read sections of the book (so far!) but I've learned something new each time I've read it. I highly reccomend this book.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&quot;http://www-stat.stanford.edu/~tibs/ElemStatLearn/&quot;&gt;The Elements of Statistical Learning: Data Mining, Inference, and Prediction&lt;/a&gt; by Hastie, Tibshirani, Friedman - I've not yet read this but the material appears to be on the same level as Bishop's book.  I'd be interested to hear comparisons if anyone has read both.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;I think if you were to read either one of these you would be in a good position to understand most of the machine learning literature out there and start researching more specific areas of interests (e.g. information retrieval, computer vision, NLP, etc...).&lt;/p&gt;

&lt;h2&gt;Everyone needs to start somewhere&lt;/h2&gt;

&lt;p&gt;I hope this helps people out there who are just staring to look into the field of Machine Learning.  While learning the needed math can look daunting at first it is actually quite fun once you get into it.  Everyone has to start somewhere and with all the amazing resources available online today any place is a good place to start. :)&lt;/p&gt;

&lt;p&gt;Happy learning!&lt;/p&gt;

&lt;hr/&gt;


&lt;p&gt;Footnotes:&lt;/p&gt;

&lt;p id=&quot;fn:beef&quot;&gt;
1. Math is the beef behind Machine Learning:

&lt;blockquote&gt;
'To the reviewer who said &quot;I was looking forward to a detailed insight into neural networks in this book. Instead, almost every page is plastered up with sigma notation&quot;, that's like saying about a book on music theory &quot;Instead, almost every page is plastered with black-and-white ovals (some with sticks on the edge).&quot; Or to the reviewer who complains this book is limited to the mathematical side of neural nets, that's like complaining about a cookbook on beef being limited to the carnivore side. ...if you want understanding of the techniques, you have to understand the math. Otherwise, there's no &lt;em style=&quot;font-weight:bold&quot;&gt;beef&lt;/em&gt;.'
&lt;footer&gt;— Peter Norvig on an &lt;a href=&quot;http://www.amazon.com/review/RZ7FBFHHLJHYE/&quot;&gt;Amazon Book Review&lt;/a&gt;&lt;/footer&gt;

&lt;/blockquote&gt;
&lt;/p&gt;




&lt;p id=&quot;fn:gauss&quot;&gt;
2. I'm totally kidding about needing to be a Gauss (good thing too since I am the farthest thing from).  All you need is a basic background in (multivariate) Calculus, Linear Algebra, and Probability.  Also, I can't pass up an opportunity to recommend &lt;a href=&quot;http://ocw.mit.edu/courses/mathematics/18-06-linear-algebra-spring-2010/&quot;&gt;Gilbert Strang's award winning Linear Algebra lectures&lt;/a&gt; for those uninitiated to the joys of Linear Algebra.
&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Word Break in Clojure and Ruby, and Laziness in Ruby</title>
   <link href="http://benmabey.com/2011/08/14/word-break-in-clojure-and-ruby-and-laziness-in-ruby.html"/>
   <updated>2011-08-14T00:00:00-06:00</updated>
   <id>http://benmabey.com/2011/08/14/word-break-in-clojure-and-ruby-and-laziness-in-ruby</id>
   <content type="html">&lt;h2&gt;TL;DR&lt;/h2&gt;

&lt;p&gt;I implemented &quot;word break&quot; in Clojure and Ruby and yammered on about how laziness can lead to better code with higher reuse.  Lazy lists in Ruby are easy to do but the laziness performance tax may be too high to bear (with the implementation used in this post). That said, it is still a valid tool when used appropriately.&lt;/p&gt;

&lt;p&gt;A gist of all the code found in this post can be found &lt;a href=&quot;https://gist.github.com/1145373&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;Word Break&lt;/h2&gt;

&lt;p&gt;Daniel Tunkelang recently &lt;a href=&quot;http://thenoisychannel.com/2011/08/08/retiring-a-great-interview-problem/&quot; title=&quot;Daniel Tunkelang's original post&quot;&gt;posted one of his favorite interview questions&lt;/a&gt; called &quot;word break&quot;.  From his blog, this is the description of the problem:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Given an input string and a dictionary of words,
segment the input string into a space-separated
sequence of dictionary words if possible. For
example, if the input string is &quot;applepie&quot; and
dictionary contains a standard set of English words,
then we would return the string &quot;apple pie&quot; as output.
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Clojure version&lt;/h2&gt;

&lt;p&gt;I was curious how a Clojure version would compare to the &lt;a href=&quot;http://thenoisychannel.com/2011/08/08/retiring-a-great-interview-problem/&quot; title=&quot;Daniel Tunkelang's original post&quot;&gt;posted Java version&lt;/a&gt; so I implemented the following recursive backtracking solution:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;clojure&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;ns &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;word-break.core&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:use&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;midje.sweet&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;clojure.contrib.core&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:only&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;-?&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;-?&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;defn &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;word-break&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;input&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;dict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;contains? &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;dict&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;input&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;count &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;nv&quot;&gt;candidates&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;map &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;subs &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;input&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;range &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
          &lt;span class=&quot;nv&quot;&gt;prefixes&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;filter &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;contains? &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;dict&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;candidates&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;first &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;for &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;prefix&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;prefixes&lt;/span&gt;
                   &lt;span class=&quot;ss&quot;&gt;:let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;suffix&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-?&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;subs &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;input&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;word-break&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;dict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))]&lt;/span&gt;
                   &lt;span class=&quot;ss&quot;&gt;:when&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;suffix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
               &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;prefix&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot; &amp;quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;suffix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))))&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;;; tests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;facts&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;word-break&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;applepie&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;apple&amp;quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;pie&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;apple pie&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;word-break&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;anappleaday&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;an&amp;quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;apple&amp;quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;a&amp;quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;day&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;an apple a day&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;word-break&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;aaaab&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;a&amp;quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;aa&amp;quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;aaa&amp;quot;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;aaaa&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;I could have gone golfing with this but I decided to leave the intermediate bindings for clarity.  The noteworthy difference with this implementation and the original Java version is the use of lazy lists (sequences in Clojure).  Rather than looping and explicitly terminating with a &lt;code&gt;return&lt;/code&gt; statement this version is able to specify all valid breaks (lazily) and simply return the first one. Since the entire list is never fully realized it is equivalent in terms of runtime complexity, O(2&lt;sup&gt;n),&lt;/sup&gt; with the provided Java one.&lt;/p&gt;

&lt;p&gt;For completeness here is a memoized version:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;clojure&quot;&gt;&lt;span class=&quot;c1&quot;&gt;;; inspired by http://stackoverflow.com/questions/3906831/how-do-i-generate-memoized-recursive-functions-in-clojure&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;defmacro &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;memoize-fn&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;&amp;quot;Produces a memoized anonymous function that can recursively call itself.&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;fn-name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;fn-args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;with-local-vars&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;fn-name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;memoize&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;~@&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;fn-args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))]&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.bindRoot&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;fn-name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@~&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;fn-name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
     &lt;span class=&quot;o&quot;&gt;@~&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;fn-name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;defn &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;word-break-with-memoization&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;input&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;dict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;word-break&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;memoize-fn&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;word-break&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;contains? &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;dict&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                      &lt;span class=&quot;nv&quot;&gt;input&lt;/span&gt;
                      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;count &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                            &lt;span class=&quot;nv&quot;&gt;candidates&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;map &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;list &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;subs &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;input&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;range &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                            &lt;span class=&quot;nv&quot;&gt;prefixes&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;filter &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;contains? &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;dict&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;candidates&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;first &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;for &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;prefix&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;prefixes&lt;/span&gt;
                                     &lt;span class=&quot;ss&quot;&gt;:let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;suffix&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-?&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;subs &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;input&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;word-break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
                                     &lt;span class=&quot;ss&quot;&gt;:when&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;suffix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
                                 &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;prefix&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot; &amp;quot;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;suffix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))))]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;word-break&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;;; tests are the same as above&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;As an interesting side note, the memoization could also have been implemented using the &lt;a href=&quot;http://en.wikipedia.org/wiki/Fixed_point_combinator&quot;&gt;Y Combinator&lt;/a&gt;. Matt Might has a &lt;a href=&quot;http://matt.might.net/articles/implementation-of-recursive-fixed-point-y-combinator-in-javascript-for-memoization/&quot;&gt;great article on this exact use case&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;Ruby version&lt;/h2&gt;

&lt;p&gt;I next implemented the algorithm in Ruby (1.9.2-p290) to see how it would compare:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;WordBreak&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;segmentize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dictionary&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;include?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;times&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;.i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;next&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dictionary&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;include?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;remaining_segment&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;segmentize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;remaining_segment&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot; &amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;remaining_segment&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
      &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;I would consider the above to be idiomatic Ruby and I don't find it hard to follow.  That said, I think it does seem more complex that the Clojure version.  This imperative version has more conditionals, early termination with an explicit &lt;code&gt;return&lt;/code&gt;, and is less modular.  By &quot;less modular&quot; I mean it doesn't take advantage of the core &lt;code&gt;Enumerable&lt;/code&gt; methods like &lt;code&gt;#map&lt;/code&gt; and &lt;code&gt;#select&lt;/code&gt; (&lt;code&gt;filter&lt;/code&gt; in other languages).  For example, it uses the &lt;code&gt;next&lt;/code&gt; keyword with a conditional to reimplment what &lt;code&gt;#select&lt;/code&gt; already does.&lt;/p&gt;

&lt;p&gt;In short, I am in complete agreement with John Hughes that laziness lends itself to more modular and clear code.  If you haven't already, I highly suggest reading his paper on the subject, &lt;a href=&quot;http://www.cs.utexas.edu/~shmat/courses/cs345/whyfp.pdf&quot;&gt;&quot;Why Functional Programming Matters&quot;&lt;/a&gt;&amp;#91;PDF&amp;#93;&lt;/p&gt;

&lt;p&gt;Again, for completeness here is the memoized version:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;WordBreak&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;memoize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
      &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;include?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;segmentize_with_memoization&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;segmentize&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;memoize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dictionary&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;include?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;times&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;.i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;next&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dictionary&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;include?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;remaining_segment&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;segmentize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;remaining_segment&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot; &amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;remaining_segment&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
        &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;segmentize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;h2&gt;Being Lazy in Ruby&lt;/h2&gt;

&lt;p&gt;There are a number of ways one could implement lazy lists in Ruby. (There are also quite a few libraries... &lt;a href=&quot;https://github.com/harukizaemon/hamster&quot;&gt;Hamster&lt;/a&gt; is one of the more interesting ones as it also implements a number of immutable persistent data structures.)  One of the easier ways to implement them however is to use the built in &lt;a href=&quot;http://www.ruby-doc.org/core/classes/Enumerator.html&quot;&gt;&lt;code&gt;Enumerator&lt;/code&gt; class&lt;/a&gt; (&lt;code&gt;Enumerable::Enumerator&lt;/code&gt; in ruby 1.8x).  For example, one could implement an &lt;code&gt;iterate&lt;/code&gt; method, as is found in many functional languages, to generate infinite lists:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Enumerator&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;iterate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;initial_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;iterate_fn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Enumerator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;yielder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;yielder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;initial_value&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;current_value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;initial_value&lt;/span&gt;
      &lt;span class=&quot;kp&quot;&gt;loop&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;current_value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;iterate_fn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current_value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;yielder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;current_value&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;doubles&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Enumerator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;iterate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;doubles&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;take&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# =&amp;gt; [1, 2, 4, 8]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;&lt;code&gt;Enumerator&lt;/code&gt; is a great starting place but you still need a way to call all your regular &lt;code&gt;Enumerable&lt;/code&gt; methods that you would want to (e.g. &lt;code&gt;#map&lt;/code&gt;, &lt;code&gt;#take&lt;/code&gt;).  Adding these methods is easy as well, but we don't even need to do that since the fantastic &lt;a href=&quot;http://rubyworks.github.com/facets/&quot;&gt;facets&lt;/a&gt; library has already done this for us with the &lt;a href=&quot;https://github.com/rubyworks/facets/blob/master/lib/core/facets/enumerable/defer.rb#L5-45&quot;&gt;&lt;code&gt;Enumerable#defer&lt;/code&gt; method&lt;/a&gt; and &lt;a href=&quot;https://github.com/rubyworks/facets/blob/master/lib/core/facets/denumerable.rb&quot;&gt;&lt;code&gt;Denumerable&lt;/code&gt; class&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Using facet's &lt;code&gt;#defer&lt;/code&gt;, here is a lazy version of &quot;word break&quot; that follows the same strategy as the Clojure version:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;set&amp;#39;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;rubygems&amp;#39;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;facets/enumerator&amp;#39;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;facets/enumerable/defer&amp;#39;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# This monkey patch allows for destructuring of the generated/yielded items...&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# TODO: open a pull request with Facets with this change...&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Denumerator&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;to_ary&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_ary&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;WordBreak&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;lazy_segmentize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dictionary&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;include?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;defer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# defer starts the laziness&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;.i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dictionary&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;include?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;remaining_segment&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lazy_segmentize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;remaining_segment&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot; &amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;remaining_segment&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;reject&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nil?&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;first&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;As with the Clojure version, this lazy Ruby version builds on top of all the common functions/methods that you normally use on collections. The &lt;code&gt;#times&lt;/code&gt; loop has been replaced with &lt;code&gt;#map&lt;/code&gt;, the &lt;code&gt;next&lt;/code&gt; keyword with &lt;code&gt;#select&lt;/code&gt;, and the early &lt;code&gt;return&lt;/code&gt; statement with &lt;code&gt;#first&lt;/code&gt;.  While most Rubyists would say the above is non-idiomatic they would all understand each individual chained component since they use them everyday.&lt;/p&gt;

&lt;p&gt;The memoized version, no surprises:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;lazy_segmentize_with_memoization&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;segmentize&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;memoize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dictionary&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;include?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;defer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;.i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dictionary&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;include?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;remaining_segment&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;segmentize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;remaining_segment&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot; &amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;remaining_segment&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;reject&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nil?&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;segmentize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;h2&gt;The cost of being lazy in Ruby&lt;/h2&gt;

&lt;p&gt;&lt;a name=&quot;jruby-update&quot;&gt; &lt;/a&gt;
&lt;fieldset&gt;
&lt;legend style=&quot;font-weight:bold;&quot;&gt; JRUBY UPDATE&lt;/legend&gt;
With the help of &lt;a href=&quot;https://twitter.com/jc00ke&quot;&gt;Jesse Cooke&lt;/a&gt; and serendiptiy we discovered an issue with JRuby in 1.9 mode.  In 1.9 mode the lazy version performs much slower than when in 1.8 mode.  I plan on rerunning the benchmarks in 1.8.  For now, please disregard all but the last (&lt;code&gt;lazy_benchmark.rb&lt;/code&gt;) JRuby benchmarks. We let the JRuby people know but I haven't heard back whether or not this is a known issue.
&lt;/fieldset&gt;&lt;/p&gt;

&lt;p&gt;&lt;a name=&quot;lib-update&quot;&gt; &lt;/a&gt;
&lt;fieldset&gt;
&lt;legend style=&quot;font-weight:bold;&quot;&gt; ALTERNATE LIBRARY UPDATE&lt;/legend&gt;
Since publishing this post I've been directed to a new library called &lt;a href=&quot;https://github.com/mdub/enumerating&quot;&gt;Enumerating&lt;/a&gt;.  The author claims it is more efficient than &lt;code&gt;Denumerable&lt;/code&gt; and appears to have some &lt;a href=&quot;https://github.com/mdub/enumerating/blob/master/benchmarks/pipeline_bench.rb&quot;&gt;benchmarks&lt;/a&gt; to prove it.  I may do a follow-up post with this library to compare times.
&lt;/fieldset&gt;&lt;/p&gt;

&lt;p&gt;Without running any benchmarks it is obvious that the imperative version of this algorithm will be faster, as it is doing less work. For each lazy operation in the chain a new &lt;code&gt;Denumerable&lt;/code&gt; is created along with it's associated block that needs to be called when a new item is demanded.  Additionally, while the &lt;code&gt;Enumerable&lt;/code&gt; core methods are written in C (or Java for JRuby) the &lt;code&gt;Denumerable&lt;/code&gt; counter-part methods are written in Ruby.  The call stack for the lazy version also grows larger since each intermediate lazy operation is a call to a block. In short, &lt;em&gt;being lazy in Ruby is not a performance optimization but rather a code modularity optimization&lt;/em&gt;.  Depending on the use case a lazy version will have significant advantages over a more imperative version, while in other cases the requirements of the problem will demand the most efficient solution.  The advantages of the lazy approach are generally compounded as you build up a set of highly reusable functions/methods.  (Again, see &lt;a href=&quot;http://www.cs.utexas.edu/~shmat/courses/cs345/whyfp.pdf&quot;&gt;John Hughes' paper&lt;/a&gt;&amp;#91;PDF&amp;#93;).&lt;/p&gt;

&lt;p&gt;I was still morbidly curious what the performance differences were so I ran some benchmarks.  Here on some benchmarks on some random input:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;benchmark&amp;#39;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;rubygems&amp;#39;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;facets/random&amp;#39;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;dictionary&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;/usr/share/dict/words&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enum_for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:each_line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;defer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;strip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;take&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;300&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_a&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# see the laziness?&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;dictionary&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;random_words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num_words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;Random&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;srand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# make it deterministic&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rand_subset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num_words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;


&lt;span class=&quot;n&quot;&gt;warmup&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# The additional warmups are for HotSpot&amp;#39;s benefit&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;warmup&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;times&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;Benchmark&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bmbm&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num_words&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;input&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dictionary&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;random_words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num_words&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;report&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;non-lazy w/memo; num_words = &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num_words&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;, n = &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;WordBreak&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;segmentize_with_memoization&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;report&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;lazy w/memo; num_words = &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num_words&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;, n = &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;WordBreak&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lazy_segmentize_with_memoization&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;pre&gt;&lt;code&gt;$ rvm use ruby-1.9.2-p290 &amp;amp;&amp;amp; ruby word_break.rb
...
                                              user     system total        real
non-lazy w/memo; num_words = 10, n = 87     0.010000   0.000000 0.010000 (0.014502)
lazy w/memo; num_words = 10, n = 87         0.020000   0.000000 0.020000 (0.023510)
non-lazy w/memo; num_words = 50, n = 428    0.350000   0.000000 0.350000 (0.350354)
lazy w/memo; num_words = 50, n = 428        0.510000   0.000000 0.510000 (0.509684)
non-lazy w/memo; num_words = 100, n = 846   1.400000   0.010000 1.410000 (1.409881)
lazy w/memo; num_words = 100, n = 846       1.730000   0.010000 1.740000 (1.738787)
non-lazy w/memo; num_words = 200, n = 1657  5.380000   0.050000 5.430000 (5.427849)
lazy w/memo; num_words = 200, n = 1657      5.770000   0.040000 5.810000 (5.807468)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The same with JRuby (granted, not ideal JVM benchmarking):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ rvm use jruby-1.6.3 &amp;amp;&amp;amp; ruby --server --1.9 word_break.rb
...
                                                 user     system total        real
non-lazy w/memo; num_words = 10, n = 93      0.005000   0.000000 0.005000 (  0.005000)
lazy w/memo; num_words = 10, n = 93          0.041000   0.000000 0.041000 (  0.042000)
non-lazy w/memo; num_words = 50, n = 395     0.120000   0.000000 0.120000 (  0.120000)
lazy w/memo; num_words = 50, n = 395         0.588000   0.000000 0.588000 (  0.588000)
non-lazy w/memo; num_words = 100, n = 785    0.492000   0.000000 0.492000 (  0.492000)
lazy w/memo; num_words = 100, n = 785        2.069000   0.000000 2.069000 (  2.069000)
non-lazy w/memo; num_words = 200, n = 1605   1.976000   0.000000 1.976000 (  1.976000)
lazy w/memo; num_words = 200, n = 1605       8.715000   0.000000 8.715000 (  8.715000)
                                              user     system total        real
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Since this input was random I don't want to read too much in to these numbers. Based on how it was created though my guess is that not a lot of backtracking was needed (if any) so they are close to ideal inputs for the algorithm. It is interesting to note that JRuby is quite a bit faster that MRI in the non-lazy version &lt;strike&gt;but is much slower in the lazy version.  Since the lazy version requires a lot of additional block invocations my &lt;em&gt;guess&lt;/em&gt; is that it may be the slow spot.  I didn't profile to verify, but I'd be curious if someone does or if a JRuby expert could shed some light on the discrepancy&lt;/strike&gt;. (See the &lt;a href=&quot;#jruby-update&quot;&gt;update&lt;/a&gt; above.)&lt;/p&gt;

&lt;p&gt;Next I created some benchmarks with worst-case inputs, meaning the entire tree would need to be searched (w/memoization however):&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;benchmark&amp;#39;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;worst_case&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;a&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;b&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
   &lt;span class=&quot;no&quot;&gt;Enumerator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;iterate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;a&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;a&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;take&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# see what I did here? ;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;warmup&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# The additional warmups are for HotSpot&amp;#39;s benefit&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;warmup&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;times&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;Benchmark&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bmbm&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;300&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;400&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dictionary&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;worst_case&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;report&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;non-lazy w/memo; n = &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;WordBreak&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;segmentize_with_memoization&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;report&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;lazy w/memo; n = &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;WordBreak&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lazy_segmentize_with_memoization&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dictionary&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;The results:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ rvm use ruby-1.9.2-p290 &amp;amp;&amp;amp; ruby word_break.rb
                            user     system      total        real
non-lazy w/memo; n = 10   0.000000   0.000000   0.000000 (0.000345)
lazy w/memo; n = 10       0.000000   0.000000   0.000000 (0.000827)
non-lazy w/memo; n = 50   0.010000   0.000000   0.010000 (0.006957)
lazy w/memo; n = 50       0.020000   0.000000   0.020000 (0.025863)
non-lazy w/memo; n = 100  0.040000   0.000000   0.040000 (0.041895)
lazy w/memo; n = 100      0.100000   0.010000   0.110000 (0.094248)
non-lazy w/memo; n = 200  0.200000   0.000000   0.200000 (0.204303)
lazy w/memo; n = 200      0.260000   0.000000   0.260000 (0.266870)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The difference between the two seem to go down as n increases which I believe is a result of the memoization being used more. Unfortunately, with n &gt; 219 the call stack blew up on the lazy version. JRuby numbers (I was able to take n higher with JRuby):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ rvm use jruby-1.6.3 &amp;amp;&amp;amp; jruby --server --1.9 word_break.rb
...
                               user     system      total real
non-lazy w/memo; n = 10    0.001000   0.000000   0.001000 ( 0.001000)
lazy w/memo; n = 10        0.014000   0.000000   0.014000 ( 0.014000)
non-lazy w/memo; n = 50    0.005000   0.000000   0.005000 ( 0.005000)
lazy w/memo; n = 50        0.109000   0.000000   0.109000 ( 0.109000)
non-lazy w/memo; n = 100   0.018000   0.000000   0.018000 ( 0.019000)
lazy w/memo; n = 100       0.326000   0.000000   0.326000 ( 0.326000)
non-lazy w/memo; n = 200   0.113000   0.000000   0.113000 ( 0.113000)
lazy w/memo; n = 200       1.350000   0.000000   1.350000 ( 1.349000)
non-lazy w/memo; n = 300   0.245000   0.000000   0.245000 ( 0.245000)
lazy w/memo; n = 300       4.459000   0.000000   4.459000 ( 4.459000)
non-lazy w/memo; n = 400   0.524000   0.000000   0.524000 ( 0.524000)
lazy w/memo; n = 400       9.576000   0.000000   9.576000 ( 9.576000)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strike&gt;With the worst-case inputs JRuby does worse in both versions and really bad with the lazy version.  My best explanation is that JRuby doesn't perform as well with recursion and many block invocations. Again, I didn't take the time to dig deeper and would welcome a more informed explanation.&lt;/strike&gt;
(Again, see the &lt;a href=&quot;#jruby-update&quot;&gt;update&lt;/a&gt; above.) (For the curious, the Clojure version was able to perform n=200 in ~0.033secs and n=400 in ~0.14secs.)&lt;/p&gt;

&lt;p&gt;The benchmarks above are interesting but they are trying to test too many things.  I was curious what the cost of the lazy operations were in comparison to their &lt;code&gt;Enumerable&lt;/code&gt; counterparts.  The world doesn't have enough flawed microbenchmarks so I created this one:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;benchmark&amp;#39;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;rubygems&amp;#39;&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;facets/enumerator&amp;#39;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;facets/enumerable/defer&amp;#39;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;warmup&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# The additional warmups are for HotSpot&amp;#39;s benefit&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;warmup&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;times&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;Benchmark&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bmbm&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;report&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;non-lazy one op&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;report&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;lazy one op&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;defer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_a&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;report&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;non-lazy two ops&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;report&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;lazy two ops&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;defer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_a&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;report&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;non-lazy three ops&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;report&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;lazy three ops&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;defer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_a&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Again, keep in mind that &lt;code&gt;Enumerable&lt;/code&gt;'s methods are in C/Java while the &lt;code&gt;Denumerable&lt;/code&gt;'s methods are in Ruby.  I ran the benchmark in 1.8.7, 1.9.2, and JRuby:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ rvm use ruby-1.8.7-p248 &amp;amp;&amp;amp; ruby lazy_benchmark.rb
                      user     system      total        real
non-lazy one op     0.030000   0.010000   0.040000 (  0.030534)
lazy one op         0.790000   0.000000   0.790000 (  0.793148)
non-lazy two ops    0.090000   0.000000   0.090000 (  0.089110)
lazy two ops        1.080000   0.000000   1.080000 (  1.087114)
non-lazy three ops  0.110000   0.010000   0.120000 (  0.109865)
lazy three ops      1.440000   0.000000   1.440000 (  1.441663)


$ rvm use ruby-1.9.2-p290 &amp;amp;&amp;amp; ruby lazy_benchmark.rb
                      user     system      total        real
non-lazy one op     0.010000   0.000000   0.010000 (  0.011690)
lazy one op         0.100000   0.010000   0.110000 (  0.103982)
non-lazy two ops    0.020000   0.000000   0.020000 (  0.021354)
lazy two ops        0.160000   0.000000   0.160000 (  0.159124)
non-lazy three ops  0.030000   0.000000   0.030000 (  0.033827)
lazy three ops      0.240000   0.000000   0.240000 (  0.240330)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Looking at these comparisons of 1.8.7 and 1.9.2 reminded me of this tweet from a coworker:&lt;/p&gt;

&lt;p&gt;&lt;span class=&quot;tweet clearfix&quot;&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;span class=&quot;tweet_avatar&quot;&amp;gt;&amp;lt;a href=&quot;http://twitter.com/bmidgley/status/101702746717175808&quot; target=&quot;_blank&quot;&amp;gt;&amp;lt;img alt=&quot;Mug_normal&quot; height=&quot;48&quot; src=&quot;http://a0.twimg.com/profile_images/202032736/mug_normal.jpg&quot; width=&quot;48&quot;&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/span&amp;gt;
ruby 1.9.2 would be dead last in the performance tests if it weren't for ruby 1.8.7! ruby... making ruby look good
&amp;lt;span class=&quot;tweet_author&quot;&amp;gt;— &amp;lt;a href=&quot;http://twitter.com/bmidgley/status/101702746717175808&quot; class=&quot;tweet_url&quot; target=&quot;_blank&quot;&amp;gt;@bmidgley&amp;lt;/a&amp;gt; via Twitter&amp;lt;/span&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;  &lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Here are the JRuby results:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ rvm use jruby-1.6.3 &amp;amp;&amp;amp; jruby --1.8 --server lazy_benchmark.rb
                         user     system      total        real
non-lazy one op      0.010000   0.000000   0.010000 (  0.010000)
lazy one op          0.093000   0.000000   0.093000 (  0.093000)
non-lazy two ops     0.016000   0.000000   0.016000 (  0.017000)
lazy two ops         0.134000   0.000000   0.134000 (  0.134000)
non-lazy three ops   0.024000   0.000000   0.024000 (  0.024000)
lazy three ops       0.196000   0.000000   0.196000 (  0.196000)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Looks like JRuby barely wins this microbenchmark. I'll leave the Rubinius benchmark as an exercise for the reader since RVM failed to install &lt;code&gt;rbx-1.2.4&lt;/code&gt; for me. The differences between the lazy and non-lazy versions in MRI 1.9.2 and JRuby 1.6.3 at least seem to be uniform in this case.  Given that, I feel that these numbers are a better indication of the overhead incurred when using &lt;code&gt;Denumerable&lt;/code&gt; in Ruby.  Again, the trade off is for modularity and each use case is different.  The performance hit may be worth it at times while other times it may not even be an option.&lt;/p&gt;

&lt;p&gt;I'd love to hear about lazy-list Ruby libraries with better performance so if you have some benchmarks please send them &lt;a href=&quot;http://twitter.com/bmabey&quot;&gt;my way&lt;/a&gt;. UPDATE: &lt;a href=&quot;#lib-update&quot;&gt;Someone did :)&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Oh yeah, I have a blog...</title>
   <link href="http://benmabey.com/2011/08/13/oh-yeah-i-have-a-blog.html"/>
   <updated>2011-08-13T00:00:00-06:00</updated>
   <id>http://benmabey.com/2011/08/13/oh-yeah-i-have-a-blog</id>
   <content type="html">&lt;p&gt;How embarassing, I haven't posted for two years!  The silliest thing about this all is that I have several draft posts that were almost done that I somehow put off polishing and publishing them.  I'll have to dust them off and see if any are still relevant... (Topics varying from Ruby, testing, Clojure, machine learning, and conference reports.)&lt;/p&gt;

&lt;p&gt;I have been quite busy these past two years so I can somewhat blame that for the lack of blog posts (but not really).  So whats new?  I've been adjusting to being a new dad (this past July 4th, yay!!), training and competing in my first triathlons, programming clojure, and (re)learing math and machine learning.  I'll expand on the last two since this is a tech blog. :)&lt;/p&gt;

&lt;p&gt;Shortly after my last blog post, in 2009, I started investigating Clojure.  I found it to be a very well thought-out language with some powerful ideas and concepts ingrained in it (e.g. laziness, immutability, STM, and it's overall approach to state).  I eventually got some people on board at &lt;a href=&quot;http://leadtune.com&quot;&gt;work&lt;/a&gt; with it (#protip: instigating a &lt;a href=&quot;http://mitpress.mit.edu/sicp/&quot;&gt;SICP&lt;/a&gt; study group is a good first step in doing this).  After some prototypes in early 2010 we decided that it was a good fit for some parts of our new infrastructure. I won't go into the details now, but the short story is that we are very pleased with it.&lt;/p&gt;

&lt;p&gt;One of my favorite projects with Clojure has been to automate the building of all of our predictive models in the cloud (via the fantastic &lt;a href=&quot;http://palletops.com/&quot;&gt;pallet&lt;/a&gt; and &lt;a href=&quot;http://www.jclouds.org&quot;&gt;jclouds&lt;/a&gt; projects).  Being able to fire up X spot instances and issue commands to them all from the comfort of your REPL is really useful and.. well, fun to do. :)&lt;/p&gt;

&lt;p&gt;My current job also involves a fair amount of machine learning so for the past two years I've been furiously studying all the math and ML algorithms that I learned (ostensibly at least) in school.  I ended up retaking a Probability course at the &lt;a href=&quot;http://utah.edu&quot;&gt;University of Utah&lt;/a&gt; and did some online courses, such as &lt;a href=&quot;http://ocw.mit.edu/courses/mathematics/18-06-linear-algebra-spring-2010/&quot;&gt;Gilbert Strang's fantastic Linear Algebra course&lt;/a&gt;, and &lt;a href=&quot;http://see.stanford.edu/see/courseinfo.aspx?coll=348ca38a-3a6d-4052-937d-cb017338d7b1&quot;&gt;Andrew Ng's Machine Learning course&lt;/a&gt; (still in progress actually).  With my stronger foundation it has been fulfilling to be able to read papers and apply the ideas to problems at work and &lt;a href=&quot;http://www.kaggle.com/users/5346/ben-mabey&quot;&gt;competitions&lt;/a&gt;. While I'm still primarily an engineer, I've really enjoyed the area of machine learning and hope to continue working in it in some way.&lt;/p&gt;

&lt;p&gt;So, that is what I've been up to for the past couple years.  I'll try hard not to allow such a long writing hiatus to happen in the future.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Writing Software not Code with Cucumber - slides from Ruby Hoedown</title>
   <link href="http://benmabey.com/2009/08/28/writing-software-not-code-with-cucumber-slides-from-ruby-hoedown.html"/>
   <updated>2009-08-28T00:00:00-06:00</updated>
   <id>http://benmabey.com/2009/08/28/writing-software-not-code-with-cucumber-slides-from-ruby-hoedown</id>
   <content type="html">&lt;p&gt;Hah!  My last blog post was for slides when I presented last... I need to write more I think.&lt;/p&gt;


&lt;p&gt;Anyways, I just finished presenting at the &lt;a href=&quot;http://rubyhoedown.com/&quot;&gt;Ruby Hoedown&lt;/a&gt;.  It was roughly the same &lt;a href=&quot;http://mwrc2009.confreaks.com/14-mar-2009-15-00-bdd-with-cucumber-ben-mabey.html&quot;&gt;talk I gave at Mountain West Ruby conf&lt;/a&gt; but with a lot more advanced material and content in general. &lt;!-- -**-END-**- --&gt; It went somewhat okay.  I think I went too fast leaving some people behind when I was racing through the &quot;basics&quot;.  I had wanted to cover some of the more advanced topics that don't get covered usually, but in the end I feel like the presentation came off as rushed and unfocused. It didn't help that I was going off of 3 hours of sleep either. :/  Another issue was the color of my code slides, which looked awesome at MWRC but just didn't project well on this projector- so, planning fail on my part.  I apologize for failing to recognizing my audience and the sub-par delivery. Regardless, I'm glad I did it and I'd like to thank Jeremy for inviting me to speak.  &amp;lt;/presentation_retrospective&amp;gt;
&lt;/p&gt;


&lt;p&gt;
The presentation was a little meta... I was using a sinatra app I wrote called &lt;a href=&quot;http://github.com/bmabey/codenote&quot;&gt;CodeNote&lt;/a&gt; that allows audience members to follow along but not go ahead.  A presenter is able to define the slides in a variant of Pat Nakajima's awesome &lt;a href=&quot;http://github.com/patnakajima/slidedown&quot;&gt;slidedown&lt;/a&gt; format.  I also integrated it with twitter so that I could ask questions and people could respond to them via twitter to win a book.  This explains the spamming of the #cucumber hash tag during my presentation. :)  The meta part was that I showed how to test the twitter integration during the presentation.  Writing a test for that involved using a webservice (twitter), AJAX, and running background jobs.  I didn't get into it nearly as much as I had hoped (which may be a good thing in retrospect) so if you are curious I would suggest checking out the code on github.
&lt;/p&gt;


&lt;p&gt;Here are the slides:&lt;/p&gt;


&lt;p&gt;
Note: Since SlideShare is lame now and won't let you download the slides without an account &lt;a href=&quot;http://benmabey.com/downloads/writing_software_not_code_with_cucumber.pdf&quot;&gt;here is a PDF copy of the slides&lt;/a&gt; if interested.
&lt;/p&gt;




&lt;div style=&quot;width:600px;text-align:left&quot; id=&quot;__ss_1146069&quot;&gt;&lt;a style=&quot;font:14px Helvetica,Arial,Sans-serif;display:block;margin:12px 0 3px 0;text-decoration:underline;&quot; href=&quot;http://www.slideshare.net/bmabey/writing-software-not-code-with-cucumber?type=powerpoint&quot; title=&quot;Writing Software not Code with Cucumber&quot;&gt;Writing Software not Code with Cucumber&lt;/a&gt;&lt;object style=&quot;margin:0px&quot; width=&quot;600&quot; height=&quot;490&quot;&gt;&lt;param name=&quot;movie&quot; value=&quot;http://static.slideshare.net/swf/ssplayer2.swf?doc=writingsoftwarenotcodewithcucumber-090828141124-phpapp02&amp;rel=0&amp;stripped_title=writing-software-not-code-with-cucumber&quot; /&gt;&lt;param name=&quot;allowFullScreen&quot; value=&quot;true&quot;/&gt;&lt;param name=&quot;allowScriptAccess&quot; value=&quot;always&quot;/&gt;&lt;embed src=&quot;http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=writingsoftwarenotcodewithcucumber-090828141124-phpapp02&amp;rel=0&amp;stripped_title=writing-software-not-code-with-cucumber&quot; type=&quot;application/x-shockwave-flash&quot; allowscriptaccess=&quot;always&quot; allowfullscreen=&quot;true&quot; width=&quot;600&quot; height=&quot;490&quot;&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div style=&quot;font-size:11px;font-family:tahoma,arial;height:26px;padding-top:2px;&quot;&gt;View more &lt;a style=&quot;text-decoration:underline;&quot; href=&quot;http://www.slideshare.net/&quot;&gt;presentations&lt;/a&gt; from &lt;a style=&quot;text-decoration:underline;&quot; href=&quot;http://www.slideshare.net/bmabey&quot;&gt;Ben Mabey&lt;/a&gt;.&lt;/div&gt;&lt;/div&gt;

</content>
 </entry>
 
 <entry>
   <title>Slides from Outside-In Development with Cucumber</title>
   <link href="http://benmabey.com/2009/03/14/slides-from-outside-in-development-with-cucumber.html"/>
   <updated>2009-03-14T00:00:00-06:00</updated>
   <id>http://benmabey.com/2009/03/14/slides-from-outside-in-development-with-cucumber</id>
   <content type="html">&lt;p&gt;I had great time presenting at &lt;a href=&quot;http://mtnwestrubyconf.org/2009/&quot;&gt;Mountain West RubyConf&lt;/a&gt;.  It was my first time presenting to a larger group and I was pretty happy how it all turned out.  For those present I would love to get some feedback on how I could improve.  I spoke on &lt;a href=&quot;http://cukes.info&quot;&gt;Cucumber&lt;/a&gt; and Outside-In development with it.  Here is the actual blurb about the presentation:&lt;/p&gt;


&lt;p&gt;
&lt;em&gt;&lt;br /&gt;
Cucumber is a &lt;span class=&quot;caps&quot;&gt;BDD&lt;/span&gt; tool that aids in outside-in development by executing plain-text features/stories as automated acceptance tests. Written in conjunction with the stakeholder, these Cucumber features clearly articulate business value and also serve as a practical guide throughout the development process: by explicitly outlining the expected outcomes of various scenarios developers know both where to begin and when they are finished. I will present the basic usage of Cucumber, primarily in the context of web applications, which will include a survey of the common tools used for simulated and automated browser-testing. Common questions and pitfalls that arise will also be discussed.&lt;br /&gt;
&lt;/em&gt;
&lt;/p&gt;


&lt;p&gt;MountainWest decided to cut the presentation times down to 30 minutes this year.  I think the new format was awesome and I hope they stick to it. However, I did have to cut some topics from my presentation.  Namely, I wasn&amp;#8217;t able to explain Selenium and Celerity so those interested in testing JS with Cucumber should take a look at webrat&amp;#8217;s &lt;a href=&quot;http://github.com/brynary/webrat/blob/master/lib/webrat/selenium.rb&quot;&gt;selenium adapter&lt;/a&gt;, &lt;a href=&quot;http://github.com/jarib/celerity/tree/master&quot;&gt;Celerity&lt;/a&gt;, &lt;a href=&quot;http://github.com/langalex/culerity/tree/master&quot;&gt;Culerity&lt;/a&gt;, and the &lt;a href=&quot;http://github.com/kamal/webrat/blob/master/lib/webrat/celerity.rb&quot;&gt;Celeriry webrat adapter&lt;/a&gt; (work-in-progress). on github.  I should also point out that you can use Watir or any other tool within Cucumber as well.&lt;/p&gt;


&lt;p&gt;&lt;fieldset&gt;
&lt;legend style=&quot;font-weight:bold;&quot;&gt;UPDATE&lt;/legend&gt;&lt;/p&gt;

&lt;p&gt;You can download &lt;a href=&quot;http://mwrc2009.confreaks.com/14-mar-2009-15-00-bdd-with-cucumber-ben-mabey.html&quot;&gt;the presentation&lt;/a&gt; over at Confreaks!&lt;/p&gt;


&lt;p&gt;&lt;/fieldset&gt;&lt;/p&gt;

&lt;p&gt;&lt;embed src='http://mwrc2009.confreaks.com/player.swf' height='380' width='640' allowscriptaccess='always' allowfullscreen='true' flashvars='image=images%2F14-mar-2009-15-00-bdd-with-cucumber-ben-mabey-preview.png&amp;#038;file=http%3A%2F%2Fmwrc2009.confreaks.com%2Fvideos%2F14-mar-2009-15-00-bdd-with-cucumber-ben-mabey-small.mp4&amp;#038;plugins=viral-1'/&gt;&lt;/p&gt;


&lt;p&gt;Note: Some of the code samples are not properly highlighted when viewing the presentation from slideshare.net so I would suggest &lt;a href=&quot;http://www.slideshare.net/bmabey/outsidein-development-with-cucumber/download&quot;&gt;downloading the PDF version&lt;/a&gt;.&lt;/p&gt;


&lt;div style=&quot;width:600px;text-align:left&quot; id=&quot;__ss_1146069&quot;&gt;&lt;a style=&quot;font:14px Helvetica,Arial,Sans-serif;display:block;margin:12px 0 3px 0;text-decoration:underline;&quot; href=&quot;http://www.slideshare.net/bmabey/outsidein-development-with-cucumber?type=powerpoint&quot; title=&quot;Outside-In Development With Cucumber&quot;&gt;Outside-In Development With Cucumber&lt;/a&gt;&lt;object style=&quot;margin:0px&quot; width=&quot;600&quot; height=&quot;490&quot;&gt;&lt;param name=&quot;movie&quot; value=&quot;http://static.slideshare.net/swf/ssplayer2.swf?doc=outside-indevelopmentwithcucumber-090314154342-phpapp02&amp;#038;rel=0&amp;#038;stripped_title=outsidein-development-with-cucumber&quot; /&gt;&lt;param name=&quot;allowFullScreen&quot; value=&quot;true&quot;/&gt;&lt;param name=&quot;allowScriptAccess&quot; value=&quot;always&quot;/&gt;&lt;embed src=&quot;http://static.slideshare.net/swf/ssplayer2.swf?doc=outside-indevelopmentwithcucumber-090314154342-phpapp02&amp;#038;rel=0&amp;#038;stripped_title=outsidein-development-with-cucumber&quot; type=&quot;application/x-shockwave-flash&quot; allowscriptaccess=&quot;always&quot; allowfullscreen=&quot;true&quot; width=&quot;600&quot; height=&quot;490&quot;&gt;&lt;/embed&gt;&lt;/object&gt;
&lt;div style=&quot;font-size:11px;font-family:tahoma,arial;height:26px;padding-top:2px;&quot;&gt;View more &lt;a style=&quot;text-decoration:underline;&quot; href=&quot;http://www.slideshare.net/&quot;&gt;presentations&lt;/a&gt; from &lt;a style=&quot;text-decoration:underline;&quot; href=&quot;http://www.slideshare.net/bmabey&quot;&gt;Ben Mabey&lt;/a&gt;.&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;During the presentation I was serving up my slides with a little sinatra app called slide_server. (The app didn&amp;#8217;t allow people to skip ahead in the presentation.) I&amp;#8217;ll be putting that code on github for any of those interested. Many thanks to &lt;a href=&quot;http://twitter.com/binary42&quot;&gt;Brian Mitchel&lt;/a&gt; for helping me out with running the server during the presentation.&lt;/p&gt;



</content>
 </entry>
 
 <entry>
   <title>Using Cucumber to Integrate Distributed Systems and Test Messaging</title>
   <link href="http://benmabey.com/2009/02/17/using-cucumber-to-integrate-distributed-systems-and-test-messaging.html"/>
   <updated>2009-02-17T00:00:00-07:00</updated>
   <id>http://benmabey.com/2009/02/17/using-cucumber-to-integrate-distributed-systems-and-test-messaging</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;http://cukes.info&quot;&gt;Cucumber&lt;/a&gt; is a fantastic tool that can be used in many ways to accomplish different goals.  One interesting use of Cucumber is to facilitate integration and communication between different systems.  At my last job we had several distributed systems that communicated via a messaging broker.  It was very important that the messages sent between the different systems be kept in sync and handled appropriately.  For example, System A needed to know the exact message format and queue that System B was going to be using, and vice versa.  This type of integration between systems is very error prone and when something goes awry the problems can be very hard to track down.  In order to ensure both systems were on the same page we used the exact same Cucumber feature in both systems but had the step definitions verify different things on the respective systems.  In this post I&amp;#8217;ll walk through a quick example illustrating the tools and techniques we used to do this.&lt;/p&gt;




&lt;h2&gt;The Feature&lt;/h2&gt;


&lt;p&gt;Developers often struggle when writing features that entail systems or are purely technical in nature.  I highly recommend &lt;a href=&quot;http://lizkeogh.com/2008/09/10/feature-injection-and-handling-technical-stories/&quot;&gt;Liz Keogh&amp;#8217;s post about this subject&lt;/a&gt; as it greatly helped me in learning how to phrase such technical stories.  With that said, lets look at the example feature:&lt;/p&gt;




&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;gherkin&quot;&gt;&lt;span class=&quot;k&quot;&gt;Feature:&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt; foo&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;  In order to enable accurate reporting&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;  As an analyst I want System A&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;  to keep it&amp;#39;s widget data in sync with System B&lt;/span&gt;

&lt;span class=&quot;nf&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Scenario:&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt; widget creation&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;    Given &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;I am logged in to System A&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;When &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;I create a widget&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Then &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;the same widget should exist in System B&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;As you can see there is not much to the actual feature itself.  In real situations more context (more Givens) is usually needed to set up needed data, but outside of that the above is a good representation of what we found to be a good scenario format to stick to.  The key is to be simple and state declaratively the behaviour of the two systems.  This allows you keep the details of the implementations in the step definitions and not clutter the overall intent of the scenario with technical noise.&lt;/p&gt;

&lt;h2&gt;Step Definitions&lt;/h2&gt;


&lt;p&gt;I said above that both projects use the exact same plain-text feature, but implement the steps differently.  While this is true, we actually ended up having the exact same step definition files as well.  Blocks were then used to distinguish the code for either system within each step.  Continuing the example from above the pertinent step definitions would be:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;When&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;I create a widget&amp;quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@expected_message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;name&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Foo&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;color&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Red&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; 

  &lt;span class=&quot;n&quot;&gt;system_a&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;create_widget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@expected_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# this call should then make system_a publish a message&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;system_b&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# when system_b runs the feature it needs to simulate a message from system_a&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;publish_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@expected_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:to&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:widget_queue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;Then&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;the same widget should exist in System B&amp;quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;system_a&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
   &lt;span class=&quot;c1&quot;&gt;# to ensure system_a is working correctly we need to make sure that the message was published&lt;/span&gt;
   &lt;span class=&quot;vi&quot;&gt;@expected_message&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;should&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;be_published_to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:widget_queue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  
  &lt;span class=&quot;n&quot;&gt;system_b&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;consume_once_with&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;WidgetConsumer&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# have the consumer process the expected message from above&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Widget&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:conditions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@expected_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;should&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;We gravitated to this solution because by keeping the files the same it was easy to keep the two systems in sync.  If one system changed the expected message or queue the step definitions would be updated and then both systems would make the feature pass again.&lt;/p&gt;

&lt;p&gt;To be clear, these step definitions do not run both systems simultaneously.  Each system runs the feature independent of the other.  That is why System B in our example simulates what System A is publishing.  At this point we are trusting that the messaging system will do it's job and deliver the message. If the systems are publishing and subscribing to the correct queues with an agreed upon message format then the messaging system should take care of the rest.  We did investigate running both systems from the same feature but in our situation the hassle and cost was not justified by the little value we saw that adding.&lt;/p&gt;

&lt;p&gt;In order to accommodate this the following helpers were needed:&lt;/p&gt;

&lt;p&gt;System A:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;MessagingHelpers&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;system_a&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;system_b&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;#no-op&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;World&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;MessagingHelpers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;For System B the opposite was needed:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;MessagingHelpers&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;system_a&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# no-op&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;system_b&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;World&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;MessagingHelpers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;




&lt;p&gt;Of course you will need the actual messaging helpers too&amp;#8230;&lt;/p&gt;




&lt;h2&gt;Introducing Rosetta Queue&lt;/h2&gt;


&lt;p&gt;The above step definitions have some nice messaging helpers (i.e. publish_message, should be_published_to, etc&amp;#8230;).  These are not just imaginary helpers for use in the example, but are helpers in a library Chris Wyckoff and I recently released called &lt;a href=&quot;http://github.com/bmabey/rosetta_queue/tree&quot;&gt;Rosetta Queue&lt;/a&gt;.  Rosetta Queue is named such because it has an adapter layer built-in that allows you to easily swap out which messaging system you are using.  For example, you could start out by using the stomp adapter with ActiveMQ but then later switch to use the &lt;span class=&quot;caps&quot;&gt;AMQP&lt;/span&gt; adapter for use with RabbitMQ.  We used a real messaging system in our Cucumber features but for our RSpec code examples (unit tests) we disconnected the messaging system and used null and fake adapters provided by Rosetta Queue.  You can read more about how to use Rosetta Queue on &lt;a href=&quot;http://github.com/bmabey/rosetta_queue/tree/master&quot;&gt;github&lt;/a&gt;.&lt;/p&gt;


&lt;p&gt;As a disclaimer, I should say that Rosetta Queue is still relatively immature.  It is being used in production systems, but we aren&amp;#8217;t happy with the &lt;span class=&quot;caps&quot;&gt;API&lt;/span&gt; for publishing and receiving messages.  We will most likely be moving to an &lt;span class=&quot;caps&quot;&gt;API&lt;/span&gt; that looks like:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;queue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:widgets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;some message&amp;quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;queue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:widgets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pop&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;The fake adapter is also really a dummy adapter that I want to replace with an actual in-memory fake adapter.  But overall, it is a nice library that aids in testing asynchronous messaging on the acceptance and unit level.&lt;/p&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;


&lt;p&gt;The pattern I&amp;#8217;ve outlined above could be used with any messaging library, not just Rosetta Queue.  We found that using Cucumber in this fashion greatly eased the work required to integrate our systems and reduced our error rate.  By hitting the real messaging systems in our features we gained confidence in our system and helped prevent against regressions.  This also allowed us to disconnect our messaging systems from our unit tests so we could test our objects in isolation and keep them lightning fast.  (Yep, our unit tests were actual unit tests!)&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Leveraging Test Data Builders in Cucumber Steps</title>
   <link href="http://benmabey.com/2009/02/05/leveraging-test-data-builders-in-cucumber-steps.html"/>
   <updated>2009-02-05T00:00:00-07:00</updated>
   <id>http://benmabey.com/2009/02/05/leveraging-test-data-builders-in-cucumber-steps</id>
   <content type="html">&lt;p&gt;Finding the balance between writing &lt;a href=&quot;http://www.benmabey.com/2008/05/19/imperative-vs-declarative-scenarios-in-user-stories/&quot;&gt;declarative&lt;/a&gt; scenarios and step reuse in Cucumber can be tricky at times.  I have found leveraging a Test Data Builder library (&lt;a href=&quot;http://rubyforge.org/pipermail/rspec-users/2009-February/012122.html&quot;&gt;often selling itself as an Object Mother library&lt;/a&gt;) helps in step reuse and prevents your scenarios from being overloaded with a lot of noise.  There are many test data builder libraries out there to do this.  Some of the popular ones are: &lt;a href=&quot;http://github.com/nakajima/fixjour/tree/master&quot;&gt;Fixjour&lt;/a&gt;, &lt;a href=&quot;http://github.com/smtlaissezfaire/fixturereplacement/tree/master&quot;&gt;FixtureReplacement&lt;/a&gt;, and &lt;a href=&quot;http://github.com/thoughtbot/factory_girl/tree&quot;&gt;FactoryGirl&lt;/a&gt;.  (There are tons more on github.)&lt;/p&gt;


&lt;p&gt;In the context of Cucumber these test builders work well when you need to create some records with default values in Given steps.  Combined with the power of Cucumber step tables you can further leverage the builder pattern by providing the pertinent values to just the attributes that matter in each scenario.  The code below works out of the box for builders that adhere to the &amp;#8220;create_model_name&amp;#8221; &lt;span class=&quot;caps&quot;&gt;API&lt;/span&gt;, but can be easily adapted to any &lt;span class=&quot;caps&quot;&gt;API&lt;/span&gt;.  I have some thoughts on how to further leverage these builders in Cucumber along with Webrat but that is for another post. In the mean time, enjoy and &lt;a href=&quot;http://gist.github.com/59007&quot;&gt;fork away!&lt;/a&gt;&lt;/p&gt;




&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# step_definitions/builder_steps.rb&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;BuilderStepHelpers&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;model_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attributes&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{})&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;create_&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;model_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gsub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;_&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;World&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;world&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;world&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;extend&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;BuilderStepHelpers&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Examples:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Given the following widget exists:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# | Name  | Price |&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# | Foo   | 20.00 |&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Given the following pets exist:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# |  Pet Type | Months Old     |&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# | Dog       | 23             |&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# | Cat       | 34             |&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Given&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/^the following (.+?)(?:s|) exist(?:s|):$/&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;model_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;table&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hashes&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;hash&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;attributes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;hash&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attributes&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gsub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;underscore&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;create_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;model_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Example:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Given widgets named &amp;#39;Foo&amp;#39;, &amp;#39;Bar&amp;#39;, and &amp;#39;Car&amp;#39; exist&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Given&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/^(.+?)(?:s|) named (.+) exist$/&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;model_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;names&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;names&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;extract_list&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;create_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;model_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Example:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Given an expensive widget exists  (assumes you have a create_expensive_widget method)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Given a widget exists&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Given 3 widgets exist&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Given 33 widgets exist&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Warning: this one can be a little too greedy at times so YMMV from project to project.&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;Given&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/^(a|an|\d+) (.+?)(?:s|) exist(?:s|)$/&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ammount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;model_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;how_many&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ammount&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=~&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/a|an/&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ammount&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_i&lt;/span&gt;
  &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;upto&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;how_many&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;create_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;model_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;




&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# support/string.rb&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Examples:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# &amp;quot;&amp;#39;Foo&amp;#39;, &amp;#39;Bar&amp;#39;, and &amp;#39;Jar&amp;#39;&amp;quot;.extract_list # =&amp;gt; [&amp;quot;Foo&amp;quot;, &amp;quot;Bar&amp;quot;, &amp;quot;Jar&amp;quot;]&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# &amp;#39;&amp;quot;Dog&amp;quot;, &amp;quot;Cat&amp;quot;&amp;#39;.extract_list # =&amp;gt; [&amp;quot;Dog&amp;quot;, &amp;quot;Cat&amp;quot;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;String&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;extract_list&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;scan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/[&amp;#39;&amp;quot;](.*?)[&amp;quot;&amp;#39;]/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;flatten&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;



</content>
 </entry>
 
 <entry>
   <title>GitHub rocks!</title>
   <link href="http://benmabey.com/2008/12/18/github-rocks.html"/>
   <updated>2008-12-18T00:00:00-07:00</updated>
   <id>http://benmabey.com/2008/12/18/github-rocks</id>
   <content type="html">&lt;p&gt;I know you have heard this before (and probably already believe it), but I just had to say it.  &lt;a href=&quot;http://GitHub.com&quot;&gt;GitHub&lt;/a&gt; has truly, &lt;span class=&quot;caps&quot;&gt;IMO&lt;/span&gt;, changed the way open source projects can be started and developed. Let me tell you a little story in form of a timeline:&lt;/p&gt;


&lt;ul&gt;
  &lt;li&gt;&lt;b&gt;December 9 2008&lt;/b&gt; &amp;#8211; While hanging out in #&lt;a href=&quot;http://GitHub.com/aslakhellesoy/cucumber/wikis&quot;&gt;cucumber&lt;/a&gt;, on freenode, agib (&lt;a href=&quot;http://GitHub.com/agibralter&quot;&gt;Aaron Gibralter&lt;/a&gt;) asked about testing emails in a rails app.  I had some old RSpec Story Runner steps for such the thing so I quickly extracted them into a plugin and modified them to work in Cucumber. I pushed it to GitHub and &lt;a href=&quot;http://GitHub.com/bmabey/email-spec/tree/master&quot;&gt;email-spec&lt;/a&gt; was born.&lt;/li&gt;
&lt;/ul&gt;


&lt;ul&gt;
  &lt;li&gt;&lt;b&gt;December 11&lt;/b&gt; &amp;#8211; Aaron sent me a pull request with some &lt;a href=&quot;http://GitHub.com/agibralter/email-spec/commit/56b32c4660777fb1dc8008d84dc241b2c762bd7b&quot;&gt;additional functionality he had added&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;ul&gt;
  &lt;li&gt;&lt;b&gt;December 17 7:46pm&lt;/b&gt; &amp;#8211; &lt;a href=&quot;http://GitHub.com/mischa/&quot;&gt;Mischa Fierer&lt;/a&gt; sent an email to the &lt;a href=&quot;http://rubyforge.org/mailman/listinfo/rspec-users&quot;&gt;rspec-users mailing list&lt;/a&gt; telling about the new plugin and some cool additions he made in his own fork (which he had forked off of Aaron&amp;#8217;s.) He also added a cucumber feature example in the &lt;span class=&quot;caps&quot;&gt;README&lt;/span&gt; on how to use the steps.&lt;/li&gt;
&lt;/ul&gt;


&lt;ul&gt;
  &lt;li&gt;&lt;b&gt;December 17 9:37pm&lt;/b&gt; &amp;#8211; I see Mischa&amp;#8217;s email and pull in his changes.  I then created a sample rails app to illustrate how to use the plugin and ended up implementing the feature Mischa listed in the &lt;span class=&quot;caps&quot;&gt;README&lt;/span&gt;.  I also did this so we could verify that the plugin still worked after we made further changes. In one of my comments I alluded to making it more accessible to other web frameworks.&lt;/li&gt;
&lt;/ul&gt;


&lt;ul&gt;
  &lt;li&gt;&lt;b&gt;December 18 8:00am&lt;/b&gt; &amp;#8211; I woke up and found two GitHub pull requests.  Shortly after I had pushed the sample app to GitHub Mischa had taken it, extended it and then used it to improve the error messaging in the plugin.  Then he converted the plugin into a gem (making it more accessible to other web frameworks), rolled the sample app into it with a handy rake task to test it, and improved the documentation! I merged in his changes and then made some minor tweaks.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;So in less than 10 days code that was sitting around in one of my projects had been extracted, forked twice, and turned into a gem.  Pretty cool, huh?  This is why I believe GitHub has changed the way open source projects are started and developed.  Yes, I could have released my code on RubyForge, or SourceForge, etc.. but the fact is that I never would have.  It takes so much longer, and is more intimidating to start a project on *Forge that I simply never would have done it. The code I was sharing was small, had some rough edges and just wasn&amp;#8217;t &amp;#8220;official&amp;#8221; looking enough to warrant spending the time to do that.&lt;/p&gt;


&lt;p&gt;When someone asks for code on a mailing list or in an &lt;span class=&quot;caps&quot;&gt;IRC&lt;/span&gt; room you used to have two options: copy and paste the code into the email/room or use a site like &lt;a href=&quot;http://pastie.org/&quot;&gt;pastie&lt;/a&gt; and send the link. With how easy it is to create repos on GitHub you can now simply &amp;#8220;throw it on GitHub&amp;#8221; (yes. you could also do a &lt;a href=&quot;http://gist.GitHub.com&quot;&gt;gist&lt;/a&gt;.)  Since forking and sending pull requests is just as easy you will likely see more patches and contributors with it on GitHub than you would otherwise.  GitHub also spreads the word of your code/project a lot faster too since it starts showing up in people&amp;#8217;s activity feeds.  I had originally put the code on GitHub for Aaron to look at and other people seemed to just stumble upon it.  In short, the combination of git and GitHub&amp;#8217;s collaboration tools makes one awesome platform to develop open source software on.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Global Setup In RSpec or How To Add Logging For Specs</title>
   <link href="http://benmabey.com/2008/07/04/global-setup-in-rspec-or-how-to-add-logging-for-specs.html"/>
   <updated>2008-07-04T00:00:00-06:00</updated>
   <id>http://benmabey.com/2008/07/04/global-setup-in-rspec-or-how-to-add-logging-for-specs</id>
   <content type="html">&lt;p&gt;Brian Morearty had an interesting post on &lt;a href=&quot;http://bmorearty.wordpress.com/2008/06/18/find-tests-more-easily-in-your-testlog/&quot;&gt;how to add additional information to a rails testing log&lt;/a&gt; when using Test::Unit.  The benefit of this is that you can quickly scan your log file when a test fails and see clues on why it failed.  If you're using RSpec you can add the same functionality all within the confines of the RSpec API for both specs (examples) and the story runner.&lt;/p&gt;




&lt;h2&gt;Global Setup for Specs&lt;/h2&gt;


&lt;p&gt;In order to announce the running of a spec in rail's test log for all of the specs you need to take advantage of RSpec's global setup capabilities.  To recreate the same type of logging message that &lt;a href=&quot;http://bmorearty.wordpress.com/2008/06/18/find-tests-more-easily-in-your-testlog/&quot;&gt;Brian did for Test::Unit&lt;/a&gt; in RSpec you would simply need to add this in your spec_helper.rb:&lt;/p&gt;




&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;    &lt;span class=&quot;ss&quot;&gt;Spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:Runner&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;configure&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;before&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:each&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;full_example_description&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Starting &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@method_name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
          &lt;span class=&quot;no&quot;&gt;RAILS_DEFAULT_LOGGER&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n\n&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;full_example_description&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;-&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;full_example_description&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;      
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
      
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;




&lt;p&gt;Note, that I am using the RAILS_DEFAULT_LOGGER constant instead of Rails::logger which was just introduced in Rails 2.1.  If you are using Rails 2.1 then I would suggest using the prettier and newer way.  I personally don't feel the need to have &quot;Starting&quot; preface each description in the log file and prefer just to use the example description:&lt;/p&gt;




&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;    &lt;span class=&quot;ss&quot;&gt;Spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:Runner&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;configure&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      
      &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;before&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:each&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;full_example_description&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@method_name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
          &lt;span class=&quot;no&quot;&gt;RAILS_DEFAULT_LOGGER&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n\n&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;full_example_description&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;-&amp;#39;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;full_example_description&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;




&lt;h2&gt;Global Setup for Stories&lt;/h2&gt;


&lt;p&gt;When using the RSpec Story Runner you often want to do some standard setup and teardown for each story and/or scenario.  (Such as cleaning up the database, or resetting ActionMailer::Base.deliveries.)  While the Story language does not provide a way to express this sort of setup and teardown (which is a good thing IMO) you can achieve the same effect by registering Listeners with the correct hooks.  The useful available hooks you can use are as follows:&lt;/p&gt;




&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyStoryListener&lt;/span&gt;
       &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;run_started&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;number_of_scenarios&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
       &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;story_started&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;story&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;narrative&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
       &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;story_ended&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;story&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;narrative&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
       &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;scenario_started&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;story&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scenario&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
       &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;scenario_succeeded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;story&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scenario&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
       &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;scenario_pending&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;story&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scenario&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
       &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;scenario_failed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;story&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scenario&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
       &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;run_ended&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;




&lt;p&gt;For example, if you wanted to add similar logging capabilities to your stories as we did with our specs above you would need to create a custom Listener and register it.  The code to do that is as follows:  (this should live in your stories/helper.rb file)&lt;/p&gt;




&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;    &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;StoryLoggerListener&lt;/span&gt;

       &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;scenario_started&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;story&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scenario&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;story_description&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Story: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;story&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;scenario_description&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;  Scenario: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;scenario&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;line_width&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;story_description&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scenario_description&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;story_description&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;scenario_description&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;length&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# yes, I know I could do [story_description.length, scenario_description.length].max&lt;/span&gt;
        &lt;span class=&quot;no&quot;&gt;RAILS_DEFAULT_LOGGER&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n\n&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;story_description&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;scenario_description&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;-&amp;quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line_width&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
       &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

       &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;method_missing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;c1&quot;&gt;# no-op&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    
    &lt;span class=&quot;c1&quot;&gt;# This is how you register your listener&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;Spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:Story&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Runner&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;register_listener&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;StoryLoggerListener&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;




&lt;p&gt;The trick is to have method_missing handle all of your unimplemented hooks and just implement the ones you care about.  The last line is what lets the Story Runner know about your listener so that the hooks are actually called.&lt;/p&gt;




&lt;p&gt;Since scenarios tend to take a lot more space in a log file than an individual spec I don't find this logging as useful.  The use of Listeners within the Story framework, in general, can be extremely useful though and is a nice feature to know about.&lt;/p&gt;




&lt;p&gt;I hope this post helps in shedding some light on some lesser known features of RSpec and results in more useful log files. :) &lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Writing Macros in RSpec</title>
   <link href="http://benmabey.com/2008/06/08/writing-macros-in-rspec.html"/>
   <updated>2008-06-08T00:00:00-06:00</updated>
   <id>http://benmabey.com/2008/06/08/writing-macros-in-rspec</id>
   <content type="html">&lt;p&gt;Thoughtbot put up a good post about &lt;a href=&quot;http://giantrobots.thoughtbot.com/2008/6/3/testing-paperclip-with-shoulda&quot;&gt;creating &quot;macro&quot;s with Shoulda&lt;/a&gt; .  Shoulda has some very nice built-in macros which help a lot in keeping tests DRY without sacrificing the documentation aspect of tests.  (In fact I think these improve the readability of most tests because it removes some of the noise.)  As Tammer Saleh said in the post these &quot;macros&quot; are nothing more than normal class methods, and so you can have the same sort of macros in any testing framework.  I have been creating and using my own custom macros in RSpec for some time now.  RSpec's awesome DSL adds a little more complexity when creating such macros, but it is still easy to do once you understand some of RSpec's DSL internals.  In this article I explain RSpec's DSL enough to provide insight on how to write macros.  I will then show an example of writing a macro and illustrate how to extract it for reuse.&lt;/p&gt;

&lt;p&gt;I should preface this explanation by saying that RSpec's built in &lt;a href=&quot;http://rspec.info/documentation/&quot;&gt;Shared Behaviour&lt;/a&gt; capabilities are very powerful and are usually the appropriate solution when abstracting common behaviour.  &lt;a href=&quot;http://evang.eli.st/blog/2008/5/14/refactoring-with-shared-example-groups&quot;&gt;Pat Maddox recently outlined some best practices of using shared behaviours in a refactoring&lt;/a&gt; .  I generally use macros when I want to pass in an argument so that I can either a) use them multiple times in a given describe context, or b) customize the example and/or the description for specdoc output.&lt;/p&gt;

&lt;h2&gt;Macro Example: it_should_assign&lt;/h2&gt;


&lt;p&gt;A useful macro I use in my Rails controller specs is &lt;code&gt;it_should_assign&lt;/code&gt;.  For example, say we have this simple show action on a nested resource controller:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;UserPhotosController&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;show&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;user_id&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@photo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;photos&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;id&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@page_title&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Best photo evar.&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;And the specs dealing with assignment:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;UserPhotosController&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;GET &amp;#39;users/1/photos/2&amp;#39;&amp;quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;before&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:each&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stub!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;and_return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mock_user&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stub_association!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:photos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:find&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@users_photo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mock_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Photo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;do_get&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:user_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@users_photo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;should assign the user to the view&amp;quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;do_get&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;assigns&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;should&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@user&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;should assign the photo to the view&amp;quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;do_get&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;assigns&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:photo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;should&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@users_photo&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;should assign the page title to the view&amp;quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;do_get&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;assigns&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:page_title&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;should&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Best photo evar.&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;As you can see, the three specs are very similar and have some duplication between them.  I should point out that duplication in tests is expected and is usually not a bad thing since documentation and readability are primary goals of having a test/spec suite.  When extracting duplication from your specs you need to be very careful that these two aspects of the specs are not diminished.  A lot of this will depend on the situation and the skill level of your team.  In this example I would argue that this is such a common pattern that an abstraction wouldn't hurt readability and in fact would help reduce some of the implementation noise (such as &lt;code&gt;do_get&lt;/code&gt;.)&lt;/p&gt;

&lt;p&gt;How do we want the &lt;code&gt;it_should_assign&lt;/code&gt; macro to work?  This example illustrates the different ways I use it in my specs:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;UserPhotosController&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;GET &amp;#39;users/1/photos/2&amp;#39;&amp;quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;before&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:each&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stub!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;and_return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mock_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stub_association!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:photos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:find&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@users_photo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mock_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Photo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:caption&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Best photo evar.&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;do_get&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:user_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@users_photo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;it_should_assign&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:user&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Here, the macro assumes assigns[:user].should == @user&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;it_should_assign&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:photo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;@users_photo&amp;quot;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Have flexibility when the name differs in the spec&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;it_should_assign&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:page_title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Best photo evar.&amp;quot;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Ability to check literals&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;




&lt;h3&gt;Dissecting part of RSpec's DSL&lt;/h3&gt;


&lt;p&gt;Before I show the implementation, it is important to realize what the &lt;code&gt;describe&lt;/code&gt; keyword really does.  The &lt;code&gt;describe&lt;/code&gt; keyword in rspec is actually a factory method (that in turn delegates to a factory object) that creates ExampleGroup sub-classes.  In rspec source terminology your &lt;code&gt;describe&lt;/code&gt; blocks are example groups and your &lt;code&gt;it&lt;/code&gt; blocks are the examples. So, you can think of &lt;code&gt;describe&lt;/code&gt; as being a sort of wrapper for &lt;code&gt;class&lt;/code&gt;. To make this point more clear, we can forgo the use of the describe keyword and subclass the ExampleGroup class ourselves:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;UserPhotosControllerSpec&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;Spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:Rails&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;Example&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:ControllerExampleGroup&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;UserPhotosController&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;GET &amp;#39;users/1/photos/2&amp;#39;&amp;quot;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;before&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:each&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stub!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;and_return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mock_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stub_association!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:photos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:find&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@users_photo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mock_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Photo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:caption&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Best photo evar.&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;do_get&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:user_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@users_photo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;it_should_assign&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:user&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;it_should_assign&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:photo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;@users_photo&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;it_should_assign&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:page_title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Best photo evar.&amp;quot;&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;




&lt;h3&gt;Inline the macro implementation into the current example group&lt;/h3&gt;


&lt;p&gt;Alright, so &lt;code&gt;describe&lt;/code&gt; just subclasses the correct example group class for us and the block that gets passed in becomes our class definition.  With that knowledge we can create our macro as a regular class method inside our example group:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;UserPhotosController&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;GET &amp;#39;users/1/photos/2&amp;#39;&amp;quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;before&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:each&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stub!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;and_return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mock_user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stub_association!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:photos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:find&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@users_photo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mock_model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Photo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;do_get&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:user_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@users_photo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;it_should_assign&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;variable_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;should assign &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;variable_name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; to the view&amp;quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Variable &amp;#39;@&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;variable_name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;#39; was not defined in the spec&amp;quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nil?&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;instance_variables&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;include?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;@&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;variable_name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;instance_variable_get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;@&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;variable_name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kind_of?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;amp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;amp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;starts_with?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;@&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;instance_variable_get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;do_get&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;assigns&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;variable_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;should&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;it_should_assign&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:user&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;it_should_assign&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:photo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;@users_photo&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;it_should_assign&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:page_title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Best photo evar.&amp;quot;&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Pretty simple, huh?  At this point the macro is only available to that example group.  There are several ways you can extract it so that it can be used elsewhere.  Before we extract the macro we need to address the problem with the &lt;code&gt;do_get&lt;/code&gt; call.&lt;/p&gt;

&lt;p&gt;In my controller specs I follow the convention of defining a &lt;code&gt;do_get&lt;/code&gt;, &lt;code&gt;do_put&lt;/code&gt;, etc. based on the HTTP verb the action responds to.  In order for the assigns macro to work across specs for different actions it needs to be able to call the correct do_verb method.  We can accomplish this by defining a &lt;code&gt;do_action&lt;/code&gt; method which calls the correct method:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;UserPhotosController&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;GET &amp;#39;users/1/photos/2&amp;#39;&amp;quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;

    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;do_action&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;verb&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:put&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:delete&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;verb&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;respond_to?&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:&amp;quot;do_&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;verb&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;No do_get, do_post_ do_put, or do_delete has been defined!&amp;quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;verb&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;do_&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;verb&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;do_get&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:show&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:user_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@users_photo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;it_should_assign&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;variable_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;should assign &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;variable_name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; to the view&amp;quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;do_action&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;assigns&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;variable_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;should&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;




&lt;h3&gt;Extracting the macro&lt;/h3&gt;


&lt;p&gt;With the do_verb calls abstracted into the do_action method we can now extract the macro.  The most flexible option is to turn it into a module. The following is a standard ruby module taking advantage of the Module#included hook so both instance and class methods are mixed in:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;AssignMacro&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;ExampleMethods&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;do_action&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;verb&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:put&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:delete&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;verb&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;respond_to?&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:&amp;quot;do_&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;verb&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;No do_get, do_post_ do_put, or do_delete has been defined!&amp;quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;verb&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;do_&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;verb&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;ExampleGroupMethods&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;it_should_assign&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;variable_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;should assign &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;variable_name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; to the view&amp;quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Variable &amp;#39;@&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;variable_name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;#39; was not defined in the spec&amp;quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nil?&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;instance_variables&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;include?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;@&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;variable_name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;instance_variable_get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;@&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;variable_name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kind_of?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;amp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;amp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;starts_with?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;@&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;instance_variable_get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;do_action&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;assigns&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;variable_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;should&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;included&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;receiver&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;receiver&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;extend&lt;/span&gt;         &lt;span class=&quot;no&quot;&gt;ExampleGroupMethods&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;receiver&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;send&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:include&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ExampleMethods&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Now, we can just include it wherever we need it...&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;UserPhotosController&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;GET &amp;#39;users/1/photos/2&amp;#39;&amp;quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;AssignMacro&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Extracting the macros out into modules allows you to mix them only into example groups that you want them to be in.  In the case of this macro it would be nice to have it available for all controller specs.  This is possible by monkey patching the ControllerExampleGroup in your spec_helper.rb:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Spec::Rails::Example&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ControllerExampleGroup&lt;/span&gt;
    &lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;AssignMacro&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;&lt;fieldset&gt;
&lt;legend style=&quot;font-weight:bold;&quot;&gt;UPDATE&lt;/legend&gt;
Please see David's comment at the bottom of the post.  It turns out that RSpec's API provides a safer way to mix in modules into certain example groups.  Monkey patching can be brittle and subject to breaks if RSpec's underlying structure/implementation changes.
&lt;/fieldset&gt;&lt;/p&gt;

&lt;p&gt;For controller macros I usually don't take the extra step of extracting it into a standalone module.  Instead, I just monkey patch my class and instance methods directly into ControllerExampleGroup.  If you have a macro that you want available for all of your example groups rspec already has a place where you can put them:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;    &lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Spec::Example&lt;/span&gt;

      &lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;ExampleGroupMethods&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# place example group methods (class methods) here&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

      &lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;ExampleMethods&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# place your example helper methods (instance methods) like do_action here&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;




&lt;h2&gt;Summary&lt;/h2&gt;


&lt;p&gt;Macros are useful when extracting granular facets of behaviour which you want to customize by passing in arguments.  They are similar to shared behaviours but live at a different level (the class level) which allows for the additional flexibility.  Shared behaviours are usually a better fit when a refactoring causes you to consolidate behaviour in a module or a class higher up in the inheritance chain.  Macros tend to evolve out of common usage patterns within your specs and should be created when you want to dynamically create these slightly different specs.  Documentation and readability are paramount when using both methods.  If a macro saves you a couple of lines of duplicated code, but ends up hurting those two aspects of the spec, don't use it!&lt;/p&gt;

&lt;p&gt;I hope this article is helpful to people new to RSpec and to those who haven't yet discovered this pattern.  If anything is unclear just ask. :)  Several people have asked me to release some of my macros and other rspec extensions that I use in all of my rails and merb projects.  When I get the time I will clean them up and put them onto my &lt;a href=&quot;http://github.com/bmabey&quot;&gt;github account&lt;/a&gt; .  Until then, happy specing!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Imperative vs Declarative Scenarios in User Stories</title>
   <link href="http://benmabey.com/2008/05/19/imperative-vs-declarative-scenarios-in-user-stories.html"/>
   <updated>2008-05-19T00:00:00-06:00</updated>
   <id>http://benmabey.com/2008/05/19/imperative-vs-declarative-scenarios-in-user-stories</id>
   <content type="html">&lt;p&gt;Last week I was looking at some of the &lt;a href=&quot;http://goruco2008.confreaks.com/&quot;&gt;presentations from GoRuCo that Confreaks recently posted&lt;/a&gt;.  One of them was a presentation on rspec's story runner given by &lt;a href=&quot;http://www.brynary.com/2008/4/26/story-driven-development-slides-posted&quot;&gt;Bryan&lt;/a&gt; &lt;a href=&quot;http://goruco2008.confreaks.com/01_helmkamp.html&quot;&gt;Helmkamp&lt;/a&gt;'s (of webrat fame.)  It is a great presentation which I highly recommend for anyone looking into incorporating the story runner into their development process.  In the presentation Bryan talked about the differences between imperative and declarative scenarios.  In my opinion, both styles have benefits and should be used appropriately based on the situation.  The majority of examples on rspec's story runner currently on the web, including &lt;a href=&quot;http://www.benmabey.com/2008/02/04/rspec-plain-text-stories-webrat-chunky-bacon/&quot;&gt;mine&lt;/a&gt;, are of the imperative type.  Since the declarative type has many advantages I thought it would be worth while to present some examples and contrast the differences between the two styles.&lt;/p&gt;




&lt;h2&gt;Imperative Style&lt;/h2&gt;


&lt;p&gt;Lets look at the example I gave in my previous post (slightly modified):&lt;/p&gt;




&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;gherkin&quot;&gt;&lt;span class=&quot;nf&quot;&gt;Story: Animal Submission&lt;/span&gt;

&lt;span class=&quot;nf&quot;&gt;  As a Zoologist&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;I &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;want to add a new animal to the site&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;  So that I can share my animal knowledge with the community&lt;/span&gt;

&lt;span class=&quot;nf&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Scenario:&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt; successful submission&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;  Given &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;I&amp;#39;m on the animal creation page&lt;/span&gt;

&lt;span class=&quot;nf&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;When &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;I fill in Name with &amp;#39;Alligator&amp;#39;&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;And &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;select Phylum as &amp;#39;Chordata&amp;#39;&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;And &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fill in Animal Class with &amp;#39;Sauropsida&amp;#39;&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;And &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fill in Order with &amp;#39;Crocodilia&amp;#39;&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;And &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fill in Family with &amp;#39;Alligatoridae&amp;#39;&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;And &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fill in Genus with &amp;#39;Alligator&amp;#39;&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;And &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;check Lay Eggs&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;And &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;click the Create button&lt;/span&gt;

&lt;span class=&quot;nf&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Then &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;I should see the notice &amp;#39;Thank you for your animal submission!&amp;#39;&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;And &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;the page should include the animal&amp;#39;s name, phylum, animal class, order, family, and genus&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;




&lt;p&gt;The imperative style uses highly reusable granular steps which outlines much of the user interface.  This binds the scenario to that interface and requires more design decisions made up front.  The step matchers for these granular steps are very easy to write as I demonstrated in my &lt;a href=&quot;http://www.benmabey.com/2008/02/04/rspec-plain-text-stories-webrat-chunky-bacon/&quot;&gt;webrat post&lt;/a&gt;.  Once these steps are in place you can write the majority of your scenarios in this fashion without having to write custom step matchers.  Due to the granularity of the scenarios however they become very brittle as they are subject to requirement changes from the customer.  If a new field is added, for example, you must update the scenario to reflect this even though the underlying goal of the scenario has not changed.&lt;/p&gt;




&lt;h2&gt;Declarative Style&lt;/h2&gt;


&lt;p&gt;Lets rewrite the above example in a more declarative fashion.  The story narrative and scenario title will remain the same.&lt;/p&gt;


&lt;p&gt;&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;gherkin&quot;&gt;&lt;span class=&quot;nf&quot;&gt;Story: Animal Submission&lt;/span&gt;

&lt;span class=&quot;nf&quot;&gt;  As a Zoologist&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;I &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;want to add a new animal to the site&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;  So that I can share my animal knowledge with the community&lt;/span&gt;

&lt;span class=&quot;nf&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Scenario:&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt; successful submission&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;  Given &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;I&amp;#39;m on the animal creation page&lt;/span&gt;

&lt;span class=&quot;nf&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;When &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;I add a new animal&lt;/span&gt;

&lt;span class=&quot;nf&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Then &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;I should see the page for my newly created animal&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;And &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;the notice &amp;#39;Thank you for your animal submission!&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;




&lt;p&gt;This style is more aligned with User Stories in the agile sense having more of the &quot;token for conversation&quot; feel to it.  The first thing that you should observe about this style is how much smaller it is than the imperative one.  This is a good thing.  The imperative style tends to produce noisy scenarios that drown out the signal.  With the declarative style the goal of the scenario remains clear.  When a new field is added to the form the scenario does not have to be modified.  Yes, you will have to modify the underlying step matcher but the scenario does not have to suffer dilution due to the change.  The trade off is, of course, that you will now be writing step matchers for all of your scenarios.  Looking at our example, the implementation for the When step is very similar to the imperative scenario steps:&lt;/p&gt;




&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# animal_steps.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;When&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;I add a new animal&amp;quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;fills_in&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Name&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:with&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Alligator&amp;#39;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;selects&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Chordata&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:from&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Phylum&amp;#39;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;fills_in&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Animal Class&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:with&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Sauropsida&amp;#39;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;fills_in&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Order&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:with&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Crocodilia&amp;#39;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;fills_in&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Family&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:with&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Alligatoridae&amp;#39;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;fills_in&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Genus&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:with&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Alligator&amp;#39;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;checks&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Lay Eggs&amp;#39;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;clicks_button&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;Create&amp;#39;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;




&lt;p&gt;The majority of the imperative step matchers were merely wrappers for webrat so creating custom steps for each scenario is not much of a deterrent.  Additionally, the reuse of steps will only take you so far before your stories start to feel unnatural as you try to force each scenario to use the same phrasing.  Creating custom steps for each scenario actually turns out to be better for reducing duplication in most cases.  That is because you can now extract duplicate code from the step matchers into helper methods.  The place to put these helpers is in the Spec::Story::World module (monkey patch it.)  &lt;/p&gt;


&lt;p&gt;Another way to reduce duplication in step matchers that I learned recently is to call other steps directly within another one.  For example, if you have this step:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;Given&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;a user named $user_name&amp;quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;create!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;and you now want to have a step that matches 'Given a user named Jim with a photo album' you can reuse the above step directly in the new one like so:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;Given&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;a user named $user_name with a photo album&amp;quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;Given&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;a user named &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;I haven't experimented with this approach too much so I'm not sure how much I like it, but I can see occasions where it would prove useful.&lt;/p&gt;

&lt;h2&gt;Choosing which style to use&lt;/h2&gt;




&lt;p&gt;I have made a strong case for the declarative style of writing scenarios.  It would seem that one should never write a scenario imperatively based solely on the merits of maintenance and communicating story intent.  While I think the declarative style has many strengths it is not the best choice for all situations.  The imperative style should not be discounted entirely because when used judiciously in the right scenario it can highlight certain aspects of the functionality and improve communication.  It is also important to realize that the two types are not mutually exclusive.  The styles can be mixed throughout an app, a story, and even an individual scenario to provide the appropriate level of granularity as the situation demands.&lt;/p&gt;


&lt;p&gt;One of the most important factors in deciding which type of style to adopt however has nothing to do with maintenance or code duplication; that factor is the customer.  While these stories may be acting as integration tests for the developer that is not the original purpose.  The stories are meant to facilitate communication between developer and stakeholder about business value and functionality.  If your stakeholder needs each form field outlined in the scenario in order to have confidence in the system then the imperative style is a better route to go.  Specs (unit tests) are just for the developer but stories need to appease the wider audience of developer and stakeholder so an appropriate balance needs to be reached.  As in most areas of software development there is no right answer and in the end it just depends on the situation.&lt;/p&gt;




&lt;h2&gt;References&lt;/h2&gt;


&lt;p&gt;Aside from the materials already linked to in this post another great resource (actually, it is the best that I have found) on the various ways to approach story runner is &lt;a href=&quot;http://www.chariotsolutions.com/slides/pdfs/ete2008-IntegrationTestingWithRSpec.pdf&quot;&gt;David Chelimsky's ETEC slides&lt;/a&gt;.  In the slides he refers to the imperative style as detailed scenarios.  He will be giving &lt;a href=&quot;http://en.oreilly.com/rails2008/public/schedule/detail/2055&quot;&gt;a similar talk at at RailsConf&lt;/a&gt;, so be sure to catch it if your able to make it to RailsConf this year.&lt;/p&gt;


&lt;p&gt;&lt;i&gt;I'd like to thank David Chelimsky, Kyle Hargraves, and Zach Dennis for their discussion on this topic on the rspec-users mailing list.&lt;/i&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Slides and code from my BDD/rspec presentation</title>
   <link href="http://benmabey.com/2008/05/10/slides-and-code-from-my-bddrspec-presentation.html"/>
   <updated>2008-05-10T00:00:00-06:00</updated>
   <id>http://benmabey.com/2008/05/10/slides-and-code-from-my-bddrspec-presentation</id>
   <content type="html">&lt;p&gt;Here are the slides from my &lt;a href=&quot;http://urug.org&quot;&gt;URUG (Utah Ruby User Group)&lt;/a&gt; presentation on 5/7/2008:&lt;/p&gt;

&lt;div style=&quot;width:425px;text-align:left&quot; id=&quot;__ss_398309&quot;&gt;&lt;object style=&quot;margin:0px&quot; width=&quot;425&quot; height=&quot;355&quot;&gt;&lt;param name=&quot;movie&quot; value=&quot;http://static.slideshare.net/swf/ssplayer2.swf?doc=bdd-and-the-how-with-rspec-1210474916409535-9&quot;/&gt;&lt;param name=&quot;allowFullScreen&quot; value=&quot;true&quot;/&gt;&lt;param name=&quot;allowScriptAccess&quot; value=&quot;always&quot;/&gt;&lt;embed src=&quot;http://static.slideshare.net/swf/ssplayer2.swf?doc=bdd-and-the-how-with-rspec-1210474916409535-9&quot; type=&quot;application/x-shockwave-flash&quot; allowscriptaccess=&quot;always&quot; allowfullscreen=&quot;true&quot; width=&quot;425&quot; height=&quot;355&quot;&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div style=&quot;font-size:11px;font-family:tahoma,arial;height:26px;padding-top:2px;&quot;&gt;&lt;a href=&quot;http://www.slideshare.net/?src=embed&quot;&gt;&lt;img src=&quot;http://static.slideshare.net/swf/logo_embd.png&quot; style=&quot;border:0px none;margin-bottom:-5px&quot; alt=&quot;SlideShare&quot;/&gt;&lt;/a&gt; | &lt;a href=&quot;http://www.slideshare.net/bmabey/the-why-behind-tddbdd-and-the-how-with-rspec?src=embed&quot; title=&quot;View 'The WHY behind TDD/BDD and the HOW with RSpec' on SlideShare&quot;&gt;View&lt;/a&gt; | &lt;a href=&quot;http://www.slideshare.net/upload?src=embed&quot;&gt;Upload your own&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;


&lt;p&gt;During the presentation I attempted to show some actual code.  Using git I had previously walked through the different ways of using the rspec story runner (ruby with inlined steps, with steps libraries, and plain text with steps) and tagged the progress as I went.  You can download the git repo and look at the different ways of using the story runner by checking out the different tags.&lt;/p&gt;

&lt;p&gt;To list the tags simply type 'git tag' and to checkout a tag into a new branch type 'git checkout -b tag_name tag_name'.&lt;/p&gt;

&lt;div class=&quot;download-link&quot;&gt;
  &lt;a href=&quot;http://www.benmabey.com/wp-content/uploads/2008/05/story_runner_demo.tar.gz&quot;&gt;&lt;img src=&quot;http://www.benmabey.com/wp-content/icons/downloads.png&quot; alt=&quot;Download&quot; class=&quot;leftalign&quot; /&gt;&lt;/a&gt;
  &lt;h4&gt;
    &lt;a href=&quot;http://www.benmabey.com/wp-content/uploads/2008/05/story_runner_demo.tar.gz&quot;&gt;story_runner_demo.tar.gz &lt;/a&gt;
  &lt;/h4&gt;
&lt;/div&gt;

</content>
 </entry>
 

</feed>
