<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom">
 <title>Tate Johnson</title>
 
 <link href="http://tatey.com/" />
 <updated>2013-05-20T22:22:25+10:00</updated>
 <id>http://tatey.com/</id>
 <author>
   <name>Tate Johnson</name>
   <email>tate@tatey.com</email>
 </author>
 
   <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/tatejohnson" /><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="tatejohnson" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry>
     <title>Experimenting With Continuous Deployment for Feature Branches</title>
     <link href="http://tatey.com/2013/05/06/experimenting-with-continuous-deployment-for-feature-branches/" />
     <updated>2013-05-06T00:00:00+10:00</updated>
     <id>http://tatey.com/2013/05/06/experimenting-with-continuous-deployment-for-feature-branches</id>
     <content type="html">&lt;p&gt;At work we&amp;#8217;ve been &lt;a href='https://twitter.com/tatejohnson/status/311268504995254273'&gt;experimenting with a tool&lt;/a&gt; that deploys our project to a staging environment every time a developer pushes to GitHub. If the branch has already been deployed, it will drop, create and seed the database. If the branch has never been deployed, it will provision a new instance. The end result is the same. A usable, working instance of the project accessible to anyone in the company, especially non-developers. You could think of it as continuous deployment for feature branches.&lt;/p&gt;

&lt;p&gt;Internally the tool is known as Tug and it deploys straight from GitHub to Heroku. Tug announces when the deploy has finished in Campfire along with the URL. There&amp;#8217;s also a dashboard for quickly seeing which branches have been deployed and their URLs. You can also get a log of the output captured from the API and command line to troubleshoot any problems. To date, we&amp;#8217;ve had 10 failed deploys out of 460.&lt;/p&gt;

&lt;p&gt;&lt;a href='/images/posts/2013-05-06-experimenting-with-continuous-deployment-for-feature-branches/tug.png'&gt;&lt;img alt='' src='/images/posts/2013-05-06-experimenting-with-continuous-deployment-for-feature-branches/tug_thumb.png' /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tug works well for us because we use feature branches heavily for development. We&amp;#8217;re a small team, but it&amp;#8217;s not uncommon to have 2 or 3 concurrent branches. The last thing we want to be thinking about is how are we going to get some early feedback from other people in the company. Even as a developer, it&amp;#8217;s nice to be able to look at a pull request and then go and try the code out without having to mess around with my local repository.&lt;/p&gt;

&lt;p&gt;What I&amp;#8217;m trying to gauge is whether other developers would find this useful in their organisation, or if we have our processes wrong. This idea is not unique to me. We practised this process without the automation at &lt;a href='http://everydayhero.com'&gt;Everyday Hero&lt;/a&gt; and the guys at &lt;a href='http://ennova.com.au'&gt;Ennova&lt;/a&gt; shipped &lt;a href='https://koideploy.com/'&gt;Koi Deploy&lt;/a&gt; for Rails Rumble.&lt;/p&gt;

&lt;h4 id='updated_20130520'&gt;Updated 2013-05-20&lt;/h4&gt;

&lt;p&gt;&lt;a href='https://speakerdeck.com/tatey/experimenting-with-continuous-deployment-for-feature-branches'&gt;View the slides&lt;/a&gt; from my lightning talk which I gave at &lt;a href='http://www.meetup.com/devops-sydney/'&gt;Sydney DevOps&lt;/a&gt; on 16th May, 2013.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/tatejohnson/~4/VQrGtyYUk34" height="1" width="1"/&gt;</content>
   </entry>
 
   <entry>
     <title>Antenna Mate Ships in the United Kingdom</title>
     <link href="http://tatey.com/2013/05/05/antenna-mate-ships-in-the-united-kingdom/" />
     <updated>2013-05-05T00:00:00+10:00</updated>
     <id>http://tatey.com/2013/05/05/antenna-mate-ships-in-the-united-kingdom</id>
     <content type="html">&lt;p&gt;&lt;a href='http://antennamate.com'&gt;Antenna Mate&lt;/a&gt; has been for sale on the &lt;a href='http://appstore.com/antennamate'&gt;Australian App Store&lt;/a&gt; since January 2011. It&amp;#8217;s sold over 6,000 copies and has an average rating of 4.5 stars from 48 ratings. It&amp;#8217;s appeared in a &lt;a href='http://www.candm.com.au/magazine'&gt;magazine&lt;/a&gt; and gets recommended by customers on &lt;a href='http://car.caravanersforum.com/~caravane/viewtopic.php?f=2&amp;amp;t=32604'&gt;various&lt;/a&gt; &lt;a href='http://www.candm.com.au/forum/viewtopic.php?f=6&amp;amp;t=8586&amp;amp;p=90562'&gt;caravanning&lt;/a&gt; &lt;a href='http://www.exploroz.com/Forum/Topic/101815/TV_Reception_iPad_app.aspx'&gt;forums&lt;/a&gt;. Not bad for a little project I started in my final year of university.&lt;/p&gt;

&lt;p&gt;I&amp;#8217;ve always been interested to know what would happen if these numbers could be replicated in other countries. If Antenna Mate went on sale in the United Kingdom would the total number of sales double?&lt;/p&gt;

&lt;p&gt;Picking the second country for Antenna Mate required some research. I started by looking at existing traffic to the website. The United States seemed like an obvious choice. After Australia, the United States made up 15% of the total traffic. More interestingly Americans were landing on the website through organic searches with keywords like &amp;#8220;tv antenna direction app&amp;#8221;. Very promising.&lt;/p&gt;

&lt;p&gt;The problem is data. The &lt;a href='http://www.fcc.gov/'&gt;FCC&lt;/a&gt; publishes terrestrial television data in a pipe delimited text file, but doesn&amp;#8217;t provide enough context to turn raw data into meaningful information for an antenna installer or caravaner. Admittedly I don&amp;#8217;t know much about terrestrial television in the United States.&lt;/p&gt;

&lt;p&gt;Going back to the website, the next biggest group was from the United Kingdom. They made up 5% of the total traffic and were arriving through similar organic searches. Terrestrial television is culturally popular in the United Kingdom and there are a couple of big &lt;a href='http://www.caravantalk.co.uk/'&gt;caravanning websites&lt;/a&gt;. More importantly, &lt;a href='http://www.ofcom.org.uk/'&gt;Ofcom&lt;/a&gt; publishes similar raw data to the &lt;a href='http://www.acma.gov.au/'&gt;ACMA&lt;/a&gt; in Australia. That meant there would be less changes required to the code and the data could be reliably imported.&lt;/p&gt;

&lt;p&gt;I estimated that it would take about 10-16 hours to write a script to import the data, verify that it worked from inside the United Kingdom, localise the application and update the website. It probably took closer to 20 hours, but I&amp;#8217;m happy to say that it&amp;#8217;s now on the &lt;a href='http://appstore.com/antennamate'&gt;App Store&lt;/a&gt;. If I were to fund that 20 hours from revenue at market contracting rates then it costs me about 3 months of revenue. But since it&amp;#8217;s more of a fun side project, I don&amp;#8217;t.&lt;/p&gt;

&lt;p&gt;If you&amp;#8217;re in the United Kingdom give it a try and let me know what you think. If you&amp;#8217;re in the United States and could help with data, I&amp;#8217;d also be interested to &lt;a href='mailto:tate@tatey.com'&gt;hear from you&lt;/a&gt;.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/tatejohnson/~4/Um-taO0J0z4" height="1" width="1"/&gt;</content>
   </entry>
 
   <entry>
     <title>Design is an Iterative Process</title>
     <link href="http://tatey.com/2013/04/29/design-is-an-iterative-process/" />
     <updated>2013-04-29T00:00:00+10:00</updated>
     <id>http://tatey.com/2013/04/29/design-is-an-iterative-process</id>
     <content type="html">&lt;p&gt;I&amp;#8217;ve been watching my girlfriend get frustrated at designing her first website. She gets put off after working so hard on something that doesn&amp;#8217;t work out the way she thought it would. I keep reminding her that design is an iterative process. The Antenna Mate that thousands of people &lt;a href='http://www.flickr.com/photos/tatejohnson/8691229793/in/set-72157633377668866/'&gt;use today&lt;/a&gt; is not the same Antenna Mate that &lt;a href='http://www.flickr.com/photos/tatejohnson/8692347604/in/set-72157633377668866/'&gt;first shipped&lt;/a&gt; on the App Store. In fact, I&amp;#8217;m really embarrassed by the earlier versions. But I shipped, and I couldn&amp;#8217;t have gotten where I did without iterating. You only need to look at the earlier versions of &lt;a href='http://blog.directededge.com/wp-content/uploads/2009/03/github.png'&gt;GitHub&lt;/a&gt; and &lt;a href='http://upload.wikimedia.org/wikipedia/en/d/d8/MacOSX10-0screenshot.png'&gt;Mac OS X&lt;/a&gt; to see the same pattern repeat itself. If your core purpose is solid, then hard work and polish will pay off.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/tatejohnson/~4/8urRHSMhYTM" height="1" width="1"/&gt;</content>
   </entry>
 
   <entry>
     <title>Next Stop is Hopping Off</title>
     <link href="http://tatey.com/2013/04/07/next-stop-is-hopping-off/" />
     <updated>2013-04-07T00:00:00+11:00</updated>
     <id>http://tatey.com/2013/04/07/next-stop-is-hopping-off</id>
     <content type="html">&lt;p&gt;Today I made the decision to remove Next Stop for sale on the App Store. I posted this message on the &lt;a href='http://nextstop.me'&gt;Next Stop website&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Next Stop is no longer available on the App Store. It was fun to build and made sure I got off at the right stop. The truth is, it hasn&amp;#8217;t sold that well and apps like TripGo do the same thing and everything else a commuter could want. At the end of the day, I support any well built piece of software that helps to increase public transport consumption. Everyone wins when someone chooses to catch a bus instead of driving a car. Thank you to everyone who supported Next Stop, especially Jason Weathered, Odin Dutton and Rachael Battle.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;How well is not well? About 1-3 sales per day. &lt;a href='http://antennamate.com'&gt;Antenna Mate&lt;/a&gt; sells &lt;em&gt;much&lt;/em&gt; better than that and at for 2.5 times the price. Next Stop was an enormous effort to build. In retrospect, there were a lot of things I could have cut from scope. It took a weekend to build logging with the expectation that people would submit logs so I could help debug Core Location. Number of logs I received? Zero.&lt;/p&gt;

&lt;p&gt;Since building Next Stop I&amp;#8217;ve strived to keep focus on the core problem and shipping as early as possible. At the end of the day, you can only commit a finite number of hours to a project and you&amp;#8217;ve got to make that project count for something.&lt;/p&gt;

&lt;p&gt;What I&amp;#8217;ve always wanted is an app I could tell where I&amp;#8217;m going and it figures out the route and tells me where to get off. That&amp;#8217;s what &lt;a href='http://skedgo.com'&gt;TripGo&lt;/a&gt; does. There&amp;#8217;s some good people behind it and I wish them all the best. Now I feel free to focus.&lt;/p&gt;

&lt;p&gt;I&amp;#8217;m finishing this post off with an experimental design I was contemplating for Next Stop. It was intentionally high contrast to increase visibility for people with vision impairments.&lt;/p&gt;

&lt;p&gt;&lt;a href='http://www.flickr.com/photos/tatejohnson/8627276780/'&gt;&lt;img alt='' src='http://farm9.staticflickr.com/8532/8627276780_157262c3af_o.png' /&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/tatejohnson/~4/ybzHSJhhVq0" height="1" width="1"/&gt;</content>
   </entry>
 
   <entry>
     <title>Adding and Removing Children with Rails Nested Forms and AngularJS</title>
     <link href="http://tatey.com/2013/01/13/adding-and-removing-children-with-rails-nested-forms-and-angularjs/" />
     <updated>2013-01-13T00:00:00+11:00</updated>
     <id>http://tatey.com/2013/01/13/adding-and-removing-children-with-rails-nested-forms-and-angularjs</id>
     <content type="html">&lt;p&gt;&lt;a href='http://angularjs.org'&gt;AngularJS&lt;/a&gt; had appeared on my radar before, but I wasn&amp;#8217;t inspired to try it until I read &lt;a href='http://icelab.com.au/articles/click-to-edit-with-angularjs/'&gt;Click to Edit with AngularJS&lt;/a&gt; by Tim Riley. AngularJS takes a different approach to enhancing pages. Instead of thinking about DOM manipulation, you think about data and bindings.&lt;/p&gt;

&lt;p&gt;We have a project at work that has nested forms. You can add and remove children before saving the entire object graph. We also have a rule where you cannot remove a child if there is only one child. When you&amp;#8217;re thinking about DOM manipulation, you think about adding your event listeners, cloning the element, changing its attributes and inserting it back into the DOM. In contrast, when you think about data and bindings, all you need to think is &amp;#8220;I want another child object&amp;#8221;.&lt;/p&gt;

&lt;p&gt;I&amp;#8217;ll start by giving context, and then break everything down line by line. Starting with the Rails model, we have a plan that accepts nested attributes for polls.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='c1'&gt;# app/models/plan.rb&lt;/span&gt;
&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;Plan&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='ss'&gt;ActiveRecord&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='ss'&gt;:Base&lt;/span&gt;
  &lt;span class='n'&gt;has_many&lt;/span&gt; &lt;span class='ss'&gt;:polls&lt;/span&gt;

  &lt;span class='n'&gt;accepts_nested_attributes_for&lt;/span&gt; &lt;span class='ss'&gt;:polls&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In the Rails controller we setup a default poll.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='c1'&gt;# app/controllers/plans_controller.rb&lt;/span&gt;
&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;PlansController&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='no'&gt;ApplicationController&lt;/span&gt;
  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;new&lt;/span&gt;
    &lt;span class='vi'&gt;@plan&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;Plan&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
    &lt;span class='vi'&gt;@plan&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;polls&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;build&lt;/span&gt; &lt;span class='ss'&gt;title&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;Time&amp;#39;&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then we write the view.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='erb'&gt;&lt;span class='x'&gt;&amp;lt;!-- app/views/new.html.erb --&amp;gt;&lt;/span&gt;
&lt;span class='x'&gt;&amp;lt;div ng-app&amp;gt;&lt;/span&gt;
&lt;span class='x'&gt;  &lt;/span&gt;&lt;span class='cp'&gt;&amp;lt;%=&lt;/span&gt; &lt;span class='n'&gt;form_for&lt;/span&gt; &lt;span class='vi'&gt;@plan&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;form&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt; &lt;span class='cp'&gt;%&amp;gt;&lt;/span&gt;&lt;span class='x' /&gt;
&lt;span class='x'&gt;    &amp;lt;div ng-controller=&amp;quot;PollCtrl&amp;quot; ng-init=&amp;quot;polls = &lt;/span&gt;&lt;span class='cp'&gt;&amp;lt;%=&lt;/span&gt; &lt;span class='vi'&gt;@plan&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;polls&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;to_json&lt;/span&gt; &lt;span class='cp'&gt;%&amp;gt;&lt;/span&gt;&lt;span class='x'&gt;&amp;quot;&amp;gt;&lt;/span&gt;
&lt;span class='x'&gt;      &amp;lt;div ng-repeat=&amp;quot;poll in polls&amp;quot;&amp;gt;&lt;/span&gt;
&lt;span class='x'&gt;        &lt;/span&gt;&lt;span class='cp'&gt;&amp;lt;%=&lt;/span&gt; &lt;span class='n'&gt;form&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;fields_for&lt;/span&gt; &lt;span class='ss'&gt;:polls&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='no'&gt;Poll&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;child_index&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;{{$index}}&amp;#39;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;poll_form&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt; &lt;span class='cp'&gt;%&amp;gt;&lt;/span&gt;&lt;span class='x' /&gt;
&lt;span class='x'&gt;          &lt;/span&gt;&lt;span class='cp'&gt;&amp;lt;%=&lt;/span&gt; &lt;span class='n'&gt;poll_form&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;text_field&lt;/span&gt; &lt;span class='ss'&gt;:title&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nb'&gt;id&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;plan_poll_{{$index}}&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;value&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;{{poll.title}}&amp;#39;&lt;/span&gt; &lt;span class='cp'&gt;%&amp;gt;&lt;/span&gt;&lt;span class='x' /&gt;
&lt;span class='x'&gt;        &lt;/span&gt;&lt;span class='cp'&gt;&amp;lt;%&lt;/span&gt; &lt;span class='k'&gt;end&lt;/span&gt; &lt;span class='cp'&gt;%&amp;gt;&lt;/span&gt;&lt;span class='x' /&gt;
&lt;span class='x'&gt;        &amp;lt;a href=&amp;quot;#&amp;quot; ng-click=&amp;quot;remove($index)&amp;quot; ng-show=&amp;quot;isRemovable()&amp;quot;&amp;gt;Remove&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;span class='x'&gt;      &amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class='x'&gt;      &amp;lt;a href=&amp;quot;#&amp;quot; ng-click=&amp;quot;add()&amp;quot;&amp;gt;Add&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;span class='x'&gt;    &amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class='x'&gt;  &lt;/span&gt;&lt;span class='cp'&gt;&amp;lt;%&lt;/span&gt; &lt;span class='k'&gt;end&lt;/span&gt; &lt;span class='cp'&gt;%&amp;gt;&lt;/span&gt;&lt;span class='x' /&gt;
&lt;span class='x'&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And the AngularJS controller.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='javascript'&gt;&lt;span class='c1'&gt;// app/assets/javascripts/plans.js&lt;/span&gt;
&lt;span class='kd'&gt;function&lt;/span&gt; &lt;span class='nx'&gt;PollCtrl&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;$scope&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
  &lt;span class='nx'&gt;$scope&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;isRemovable&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='nx'&gt;$scope&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;polls&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;length&lt;/span&gt; &lt;span class='o'&gt;&amp;gt;&lt;/span&gt; &lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
  &lt;span class='p'&gt;};&lt;/span&gt;

  &lt;span class='nx'&gt;$scope&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;add&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='nx'&gt;$scope&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;polls&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;push&lt;/span&gt;&lt;span class='p'&gt;({});&lt;/span&gt;
  &lt;span class='p'&gt;};&lt;/span&gt;

  &lt;span class='nx'&gt;$scope&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;remove&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;index&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
    &lt;span class='nx'&gt;$scope&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;polls&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;splice&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;index&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
  &lt;span class='p'&gt;};&lt;/span&gt;
&lt;span class='p'&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You really need to &lt;a href='http://jsfiddle.net/tatejohnson/vLhNb/'&gt;try this jsfiddle to see it in action&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id='view_and_angularjs_controller_line_by_line'&gt;View and AngularJS Controller Line by Line&lt;/h3&gt;

&lt;p&gt;The AngularJS controller is associated with &lt;code&gt;PollCtrl&lt;/code&gt;. &lt;code&gt;PollCtrl&lt;/code&gt; is our wrapper around &lt;code&gt;$scope&lt;/code&gt; for handling the bindings we declare in the view. The AngularJS controller is intialized with the Rails model objects that were setup in the Rails controller earlier. As a side-effect, these will be the same Rails model objects we&amp;#8217;d get back from a failed POST to the create action.&lt;/p&gt;

&lt;p&gt;&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='erb'&gt;&lt;span class='x'&gt;&amp;lt;div ng-controller=&amp;quot;PollCtrl&amp;quot; ng-init=&amp;quot;polls = &lt;/span&gt;&lt;span class='cp'&gt;&amp;lt;%=&lt;/span&gt; &lt;span class='vi'&gt;@plan&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;polls&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;to_json&lt;/span&gt; &lt;span class='cp'&gt;%&amp;gt;&lt;/span&gt;&lt;span class='x'&gt;&amp;quot;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;

&lt;p&gt;Next, we want the input and remove anchor elements repeated for each poll JavaScript object.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='erb'&gt;&lt;span class='x'&gt;&amp;lt;div ng-repeat=&amp;quot;poll in polls&amp;quot;&amp;gt;&lt;/span&gt;
&lt;span class='x'&gt;  &amp;lt;!-- ... --&amp;gt;&lt;/span&gt;
&lt;span class='x'&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Using &lt;code&gt;fields_for&lt;/code&gt; we instantiate a poll Ruby object to give us access to the nested form builder. We want AngularJS to manage the indexes for us, so instead of specifying an explicit index, we&amp;#8217;re going to set the index of the input element to a special variable available within the scope of repeating elements. As a side-effect of using &lt;code&gt;child_index&lt;/code&gt;, it is necessary to specify the ID of the input element to avoid ID collisions in the DOM. If you don&amp;#8217;t care, then you could safely omit the ID &lt;em&gt;(Thanks to &lt;a href='https://twitter.com/Sutto/status/290318902750216192'&gt;Darcy Laycock&lt;/a&gt; for the suggestion to use &lt;code&gt;child_index&lt;/code&gt;)&lt;/em&gt;.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='erb'&gt;&lt;span class='cp'&gt;&amp;lt;%=&lt;/span&gt; &lt;span class='n'&gt;form&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;fields_for&lt;/span&gt; &lt;span class='ss'&gt;:polls&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='no'&gt;Poll&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;child_index&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;{{$index}}&amp;#39;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt; &lt;span class='o'&gt;|&lt;/span&gt;&lt;span class='n'&gt;poll_form&lt;/span&gt;&lt;span class='o'&gt;|&lt;/span&gt; &lt;span class='cp'&gt;%&amp;gt;&lt;/span&gt;&lt;span class='x' /&gt;
&lt;span class='x'&gt;  &lt;/span&gt;&lt;span class='cp'&gt;&amp;lt;%=&lt;/span&gt; &lt;span class='n'&gt;poll_form&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;text_field&lt;/span&gt; &lt;span class='ss'&gt;:title&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nb'&gt;id&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;plan_poll_{{$index}}&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;value&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;{{poll.title}}&amp;#39;&lt;/span&gt; &lt;span class='cp'&gt;%&amp;gt;&lt;/span&gt;&lt;span class='x' /&gt;
&lt;span class='cp'&gt;&amp;lt;%&lt;/span&gt; &lt;span class='k'&gt;end&lt;/span&gt; &lt;span class='cp'&gt;%&amp;gt;&lt;/span&gt;&lt;span class='x' /&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;These two anchors are bound to the add and remove functions in our AngularJS controller. The &lt;code&gt;ng-show&lt;/code&gt; attribute tells AngularJS to show this anchor when our AngularJS&amp;#8217;s controller &lt;code&gt;isRemovable&lt;/code&gt; function returns true.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='erb'&gt;&lt;span class='x'&gt;&amp;lt;a href=&amp;quot;#&amp;quot; ng-click=&amp;quot;remove($index)&amp;quot; ng-show=&amp;quot;isRemovable()&amp;quot;&amp;gt;Remove&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;span class='x'&gt;&amp;lt;a href=&amp;quot;#&amp;quot; ng-click=&amp;quot;add()&amp;quot;&amp;gt;Add&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Expanding on the &lt;code&gt;isRemovable&lt;/code&gt; function, a poll is only removable if there is more than one of them.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='javascript'&gt;&lt;span class='nx'&gt;$scope&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;isRemovable&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
  &lt;span class='k'&gt;return&lt;/span&gt; &lt;span class='nx'&gt;$scope&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;polls&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;length&lt;/span&gt; &lt;span class='o'&gt;&amp;gt;&lt;/span&gt; &lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;;&lt;/span&gt;
&lt;span class='p'&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Add inserts an empty object, and remove deletes an object at the given index.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='javascript'&gt;&lt;span class='nx'&gt;$scope&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;add&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
  &lt;span class='nx'&gt;$scope&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;polls&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;push&lt;/span&gt;&lt;span class='p'&gt;({});&lt;/span&gt;
&lt;span class='p'&gt;};&lt;/span&gt;

&lt;span class='nx'&gt;$scope&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;remove&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='kd'&gt;function&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;index&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt; &lt;span class='p'&gt;{&lt;/span&gt;
  &lt;span class='nx'&gt;$scope&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;polls&lt;/span&gt;&lt;span class='p'&gt;.&lt;/span&gt;&lt;span class='nx'&gt;splice&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='nx'&gt;index&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='mi'&gt;1&lt;/span&gt;&lt;span class='p'&gt;);&lt;/span&gt;
&lt;span class='p'&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The index came from the special index variable available inside the scope of repeating elements.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='erb'&gt;&lt;span class='x'&gt;&amp;lt;a href=&amp;quot;#&amp;quot; ng-click=&amp;quot;remove($index)&amp;quot; ng-show=&amp;quot;isRemovable()&amp;quot;&amp;gt;Remove&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id='rails_nested_forms'&gt;Rails Nested Forms&lt;/h3&gt;

&lt;p&gt;Some people might be surprised at my use of nested forms. Nested forms can suck, and when they do its time to consider something else like an &lt;a href='http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/'&gt;intermediary view object&lt;/a&gt;. But until you reach that point, nested forms are the quickest way to ship.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/tatejohnson/~4/LefWvE0BqPA" height="1" width="1"/&gt;</content>
   </entry>
 
   <entry>
     <title>Shipped&amp;#58; Next Stop for iPhone</title>
     <link href="http://tatey.com/2012/12/16/shipped-next-stop-for-iphone/" />
     <updated>2012-12-16T00:00:00+11:00</updated>
     <id>http://tatey.com/2012/12/16/shipped-next-stop-for-iphone</id>
     <content type="html">&lt;p&gt;&lt;a href='http://nextstop.me'&gt;Next Stop&lt;/a&gt; lets you set an alarm for the bus stop you want to hop off at. Before you reach the bus stop, you get a push-style notification telling you to hop off. Next Stop became available on the &lt;a href='https://itunes.apple.com/au/app/next-stop/id583674519?ls=1&amp;amp;mt=8'&gt;App Store&lt;/a&gt; on the 12th of December. It&amp;#8217;s the third app I&amp;#8217;ve published to the App Store.&lt;/p&gt;

&lt;p&gt;The motivation behind the project came from being anxious when catching new bus routes. I remember getting off a bus stop early and having to walk 1KM in the rain when I was staying in &lt;a href='https://itunes.apple.com/au/app/next-stop/id583674519?ls=1&amp;amp;mt=8'&gt;Fig Tree Pocket&lt;/a&gt;. I commuted on public transport everyday, wishing more people would catch &lt;a href='http://translink.com.au/'&gt;public transport&lt;/a&gt;. I&amp;#8217;m not a town planner, but I can write software. Building Next Stop is my small contribution toward making public transport better for everyone.&lt;/p&gt;

&lt;p&gt;If you&amp;#8217;re in Brisbane, &lt;a href='http://nextstop.me/'&gt;give it a try&lt;/a&gt;. I&amp;#8217;d love to &lt;a href='mailto:support@nextstop.me?subject=Feedback'&gt;have your feedback&lt;/a&gt;.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/tatejohnson/~4/fdF1_Xu2-Lk" height="1" width="1"/&gt;</content>
   </entry>
 
   <entry>
     <title>It was a Mistake to Undervalue the &amp;ldquo;Contact Us&amp;rdquo; Button in my iOS application</title>
     <link href="http://tatey.com/2012/11/27/it-was-a-mistake-to-undervalue-the-contact-us-button-in-my-ios-application/" />
     <updated>2012-11-27T00:00:00+11:00</updated>
     <id>http://tatey.com/2012/11/27/it-was-a-mistake-to-undervalue-the-contact-us-button-in-my-ios-application</id>
     <content type="html">&lt;p&gt;I&amp;#8217;m talking about the &amp;#8220;Contact Us&amp;#8221; button you see hidden away in the &amp;#8220;About&amp;#8221; screen of your favourite iOS app. The way it works is you tap &amp;#8220;Contact Us&amp;#8221; and a new email pops up with the &lt;em&gt;to&lt;/em&gt; and &lt;em&gt;subject&lt;/em&gt; pre-filled. Seems like a lot of work, especially when the email is already linked to from iTunes and the app&amp;#8217;s website. I had never used one myself and naively assumed that nobody uses them. After adding a &amp;#8220;Contact Us&amp;#8221; button to &lt;a href='http://antennamate.com/'&gt;Antenna Mate&lt;/a&gt;, I&amp;#8217;ve received more emails this month than I have in the last three months.&lt;/p&gt;

&lt;p&gt;Most of the emails were &amp;#8220;I love your app, but&amp;#8230;&amp;#8221; and validating some &lt;a href='http://tatey.com/2011/01/09/planning-the-future-of-antenna-mate/'&gt;old assumptions&lt;/a&gt; I had forgotten about. I used this feedback to drive a major enhancement which I&amp;#8217;m hoping will continue to strengthen the app&amp;#8217;s position as the best in its category. But more importantly, it gave me a dialogue with my customers. I&amp;#8217;m looking forward to emailing them back and going &amp;#8220;Hey, I listened, and here you go&amp;#8221;.&lt;/p&gt;

&lt;p&gt;Sure you&amp;#8217;re going to receive more emails, but this dialogue can pay off. One of my customers wants the app in New Zealand (It&amp;#8217;s Australia only) so badly that they found and linked me to the New Zealand data. Something I had spent thirty minutes trying to find, but couldn&amp;#8217;t.&lt;/p&gt;

&lt;p&gt;In case you&amp;#8217;re wondering what to put in the &amp;#8220;Contact Us&amp;#8221; button. It opens up a &lt;code&gt;MFMailComposeViewController&lt;/code&gt; that sets the email to the support email address and the subject to &amp;#8220;Antenna Mate vX.X.X Feedback&amp;#8221; leaving the customer to focus on the content. If email isn&amp;#8217;t configured on the device they get an alert &amp;#8220;We tried to open an email - Email isn&amp;#8217;t configured on this device. Try sending an email to support@antennamate.com from your computer. We&amp;#8217;d love to have your feedback&amp;#8221;.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/tatejohnson/~4/4yh8XGTZISE" height="1" width="1"/&gt;</content>
   </entry>
 
   <entry>
     <title>Testing Private and Protected Methods in Rails Controllers Without Being Awkward</title>
     <link href="http://tatey.com/2012/11/23/testing-private-and-protected-methods-in-rails-controllers-without-being-awkward/" />
     <updated>2012-11-23T00:00:00+11:00</updated>
     <id>http://tatey.com/2012/11/23/testing-private-and-protected-methods-in-rails-controllers-without-being-awkward</id>
     <content type="html">&lt;p&gt;In Rails, it&amp;#8217;s considered best practice to mark non-action methods as private or protected. This is because Rails will never route HTTP requests to methods which cannot be called publicly. This practice encourages us to hide important behaviour in methods which are not easily tested. There are many awkward techniques such as &lt;a href='http://stackoverflow.com/questions/4271696/rspec-rails-how-to-test-private-methods-of-controllers'&gt;chaining &lt;code&gt;instance_eval&lt;/code&gt; and &lt;code&gt;send&lt;/code&gt;&lt;/a&gt;, &lt;a href='https://www.relishapp.com/rspec/rspec-rails/docs/controller-specs/anonymous-controller'&gt;creating an anonymous subclass and drawing a route&lt;/a&gt;, a slow integration test or &lt;a href='http://stackoverflow.com/questions/7607419/how-to-write-rspec-for-private-method-in-controller-with-params'&gt;omitting the test altogether&lt;/a&gt;. None of these test the desired behaviour in an intention revealing way. Nor would they be considered acceptable if it were any other class.&lt;/p&gt;

&lt;p&gt;Testing doesn&amp;#8217;t have to be awkward. It can be greatly simplified if we think about it slightly differently. The idea isn&amp;#8217;t radical. It&amp;#8217;s to move hidden behaviour into distinct classes which are then invoked privately from inside the controller. A distinct class with a single responsibility becomes easier to reason with. Easier to test. Invoking it privately means we&amp;#8217;ll never accidentally route HTTP requests to non-action methods.&lt;/p&gt;

&lt;p&gt;Lets start with a typical controller. This controller has been extracted from a code base that I recently refactored. &lt;code&gt;AdminController&lt;/code&gt; is an abstract controller that gets subclassed by any controller in the admin section of the website. Authorization is common behaviour shared between all controllers. On each request, we check that the member is an admin. If the member is not an admin we return a 403 and stop them from viewing the page. Authorization is critical behaviour that must be correct. We could never let a member who isn&amp;#8217;t an admin have privileged access.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='c1'&gt;# app/controllers/admin_controller.rb&lt;/span&gt;
&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;AdminController&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='ss'&gt;ActionController&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='ss'&gt;:Base&lt;/span&gt;
  &lt;span class='n'&gt;before_filter&lt;/span&gt; &lt;span class='ss'&gt;:authorize_member!&lt;/span&gt;

&lt;span class='kp'&gt;protected&lt;/span&gt;

  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;authorize_member!&lt;/span&gt;
    &lt;span class='k'&gt;unless&lt;/span&gt; &lt;span class='n'&gt;current_member&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;admin_role?&lt;/span&gt;
      &lt;span class='n'&gt;render&lt;/span&gt; &lt;span class='ss'&gt;text&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;403: Forbidden&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;status&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt; &lt;span class='ss'&gt;:forbidden&lt;/span&gt;
      &lt;span class='kp'&gt;false&lt;/span&gt;
    &lt;span class='k'&gt;end&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Your fire up the server and everything is working great. But how do you have confidence that you&amp;#8217;re not going to suffer from a regression in the future? If a regression does occur, how do you quickly pin point where it went wrong? Moreover, as our authorization scheme grows in complexity it becomes increasingly difficult to reason with in the context of hidden behaviour.&lt;/p&gt;

&lt;p&gt;Lets move the behaviour into a new class called &lt;code&gt;AuthorizeMember&lt;/code&gt; and nest it underneath &lt;code&gt;AdminController&lt;/code&gt;. We could organise it into its own file, but it&amp;#8217;s such a small class and only makes sense in the context of &lt;code&gt;AdminController&lt;/code&gt;. &lt;code&gt;AuthorizeMember&lt;/code&gt; is comprised of a single, stateless class method &lt;code&gt;authorize!&lt;/code&gt; which takes two arguments. The member who is authenticated and the controller where the request orignated from. It might seem strange to pass around a controller. As magical as controllers are, they&amp;#8217;re still objects which respond to methods making them no different to passing around a hash or an array. Finally, we delegate our controller&amp;#8217;s protected method to &lt;code&gt;AuthorizeMember.authorize_member!&lt;/code&gt;.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='c1'&gt;# app/controllers/admin_controller.rb&lt;/span&gt;
&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;AdminController&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='ss'&gt;ActionController&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='ss'&gt;:Base&lt;/span&gt;
  &lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;AuthorizeMember&lt;/span&gt;
    &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nc'&gt;self&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='nf'&gt;authorize!&lt;/span&gt; &lt;span class='n'&gt;member&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;controller&lt;/span&gt;
      &lt;span class='k'&gt;unless&lt;/span&gt; &lt;span class='n'&gt;member&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;admin_role?&lt;/span&gt;
        &lt;span class='n'&gt;controller&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;render&lt;/span&gt; &lt;span class='ss'&gt;text&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;403: Forbidden&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;status&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt; &lt;span class='ss'&gt;:forbidden&lt;/span&gt;
        &lt;span class='kp'&gt;false&lt;/span&gt;
      &lt;span class='k'&gt;end&lt;/span&gt;
    &lt;span class='k'&gt;end&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;

  &lt;span class='n'&gt;before_filter&lt;/span&gt; &lt;span class='ss'&gt;:authorize_member!&lt;/span&gt;

&lt;span class='kp'&gt;protected&lt;/span&gt;

  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;authorize_member!&lt;/span&gt;
    &lt;span class='no'&gt;AuthorizeMember&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;authorize!&lt;/span&gt; &lt;span class='n'&gt;current_member&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='nb'&gt;self&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The end result is identical, but now it exists in a distinct class where collaborators are injected. Injecting collaborators frees us from the constraints of their implementation. Instead of depending on an instance of &lt;code&gt;ActionController::Base&lt;/code&gt;, we now depend on an object that responds to &lt;code&gt;render&lt;/code&gt;. The difference is small, but the impact is massive. With only a handful of lines we can write a focused, intention revealing test that runs fast in isolation. &lt;code&gt;ActionController::Base&lt;/code&gt; is really well tested, so there is no need for us to double up on that.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='c1'&gt;# spec/controllers/admin_controller_spec.rb&lt;/span&gt;
&lt;span class='nb'&gt;require&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;spec_helper&amp;#39;&lt;/span&gt;

&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;MockController&lt;/span&gt;
  &lt;span class='kp'&gt;attr_reader&lt;/span&gt; &lt;span class='ss'&gt;:last_options&lt;/span&gt;

  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;initialize&lt;/span&gt;
    &lt;span class='vi'&gt;@last_options&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='p'&gt;{}&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;

  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;render&lt;/span&gt; &lt;span class='n'&gt;options&lt;/span&gt;
    &lt;span class='vi'&gt;@last_options&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;options&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='n'&gt;describe&lt;/span&gt; &lt;span class='no'&gt;AdminController&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
  &lt;span class='n'&gt;describe&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;AuthorizeMember.authorize&amp;#39;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
    &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;is forbidden&amp;#39;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
      &lt;span class='n'&gt;member&lt;/span&gt;     &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;OpenStruct&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt; &lt;span class='n'&gt;admin_role?&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt; &lt;span class='kp'&gt;false&lt;/span&gt;
      &lt;span class='n'&gt;controller&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;MockController&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
      &lt;span class='ss'&gt;AdminController&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='ss'&gt;:AuthorizeMember&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;authorize!&lt;/span&gt; &lt;span class='n'&gt;member&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;controller&lt;/span&gt;
      &lt;span class='n'&gt;controller&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;last_options&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;should_not&lt;/span&gt; &lt;span class='n'&gt;be_empty&lt;/span&gt;
      &lt;span class='n'&gt;controller&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;last_options&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:text&lt;/span&gt;&lt;span class='o'&gt;].&lt;/span&gt;&lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='kp'&gt;include&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='s1'&gt;&amp;#39;403&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
      &lt;span class='n'&gt;controller&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;last_options&lt;/span&gt;&lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='ss'&gt;:status&lt;/span&gt;&lt;span class='o'&gt;].&lt;/span&gt;&lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='n'&gt;eq&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:forbidden&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
    &lt;span class='k'&gt;end&lt;/span&gt;

    &lt;span class='n'&gt;it&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;does nothing&amp;#39;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
      &lt;span class='n'&gt;member&lt;/span&gt;     &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;OpenStruct&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt; &lt;span class='n'&gt;admin_role?&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt; &lt;span class='kp'&gt;true&lt;/span&gt;
      &lt;span class='n'&gt;controller&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;MockController&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
      &lt;span class='ss'&gt;AdminController&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='ss'&gt;:AuthorizeMember&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;authorize!&lt;/span&gt; &lt;span class='n'&gt;member&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;controller&lt;/span&gt;
      &lt;span class='n'&gt;controller&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;last_options&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;should&lt;/span&gt; &lt;span class='n'&gt;be_empty&lt;/span&gt;
    &lt;span class='k'&gt;end&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The thing I love most about this technique is that you already know it. You practice it every time you refactor a big piece of code into smaller pieces of code. By thinking about controllers as something less magical, and something more like any other ruby class, you enable yourself to reuse your existing knowledge. There&amp;#8217;s no special rules like calling &lt;code&gt;instance_eval&lt;/code&gt; or additional libraries like RSpec&amp;#8217;s anonymous controller. Instead, we write distinct classes focused on a single piece of behaviour that we delegate to from hidden methods in our controllers. Testing private and protected methods in Rails controllers doesn&amp;#8217;t have to be awkward. We can write plain old Ruby and keep things simple.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/tatejohnson/~4/KpRHKtFE5Yc" height="1" width="1"/&gt;</content>
   </entry>
 
   <entry>
     <title>SimpleDelegator + MiniTest::Mock = SimpleMock.</title>
     <link href="http://tatey.com/2012/02/22/simple-delegator-plus-minitest-mock-equal-simple-mock/" />
     <updated>2012-02-22T00:00:00+11:00</updated>
     <id>http://tatey.com/2012/02/22/simple-delegator-plus-minitest-mock-equal-simple-mock</id>
     <content type="html">&lt;p&gt;&lt;a href='http://github.com/tatey/simple_mock'&gt;SimpleMock&lt;/a&gt; is a fast, powerfully tiny mocking library for Ruby 1.9.2 or greater. Under the hood it uses &lt;a href='http://www.ruby-doc.org/stdlib-1.9.3/libdoc/minitest/mock/rdoc/MiniTest/Mock.html'&gt;MiniTest::Mock&lt;/a&gt; and &lt;a href='http://www.ruby-doc.org/stdlib-1.9.3/libdoc/delegate/rdoc/SimpleDelegator.html'&gt;SimpleDelegator&lt;/a&gt; to mix classical mocking with real objects (Partial mocking). An idea born out of an &lt;a href='http://tatey.com/2012/02/07/mocking-with-minitest-mock-and-simple-delegator/'&gt;earlier blog post&lt;/a&gt; and later captured in &lt;a href='https://twitter.com/#!/tatejohnson/status/167215752036888577'&gt;these&lt;/a&gt; &lt;a href='https://twitter.com/#!/tatejohnson/status/167591787937017856'&gt;tweets&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Where &lt;a href='http://mocha.rubyforge.org/'&gt;other libraries&lt;/a&gt; temporarily replaces methods on objects, SimpleMock never mutates its delegate leaving objects completely untainted. SimpleMock is isolated in its own namespace and can be safely required in any Ruby project. SimpleMock depends only on the standard library and plays nicely with MiniTest and &lt;a href='http://rspec.info/'&gt;RSpec&lt;/a&gt;. The library code with whitespace is a tiny 81 lines. &lt;a href='https://github.com/tatey/simple_mock/blob/v0.0.1/test/unit/mock_delegator_test.rb#L56'&gt;Parts&lt;/a&gt; of the library are tested against itself. The API it exposes is 100% compatible with MiniTest::Mock so there is nothing new to learn.&lt;/p&gt;

&lt;h2 id='using_simplemock'&gt;Using SimpleMock&lt;/h2&gt;

&lt;p&gt;For the most part classical mocking is satisfactory.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='n'&gt;mock&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;SimpleMock&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
&lt;span class='n'&gt;mock&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;expect&lt;/span&gt; &lt;span class='ss'&gt;:valid?&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='kp'&gt;false&lt;/span&gt;
&lt;span class='n'&gt;mock&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;valid?&lt;/span&gt;
&lt;span class='n'&gt;mock&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;verify&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Changing the behaviour of real objects should be a last resort. If you find yourself needing to regularly set expectations on real objects it may indicate that code is too tightly coupled. However, there are use cases where this cannot be avoided.&lt;/p&gt;

&lt;p&gt;Rails route helpers are one of those use cases. Rails uses introspection to build routes from model objects. That means less code is written in controllers, but testing controllers in isolation is difficult. You may start off mocking dependancies, but you&amp;#8217;ll quickly end up with a test that has lots of setup. Not to mention your making assumptions about the interfaces of objects out of your control. In such a scenario is makes sense to set expectations on a real model object.&lt;/p&gt;

&lt;p&gt;In the comparison below SimpleMock and Mocha are the same number of lines of code.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='c1'&gt;# Mocha&lt;/span&gt;
&lt;span class='nb'&gt;test&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;POST create&amp;quot;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
  &lt;span class='n'&gt;mock_items&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;mock&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt;
  &lt;span class='n'&gt;mock_items&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;expects&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:create&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;with&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='no'&gt;Hash&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;returns&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='no'&gt;Item&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='n'&gt;mock_order&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;Order&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
  &lt;span class='n'&gt;mock_order&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;expects&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:items&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;returns&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;mock_items&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='n'&gt;controller&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;order&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;mock_order&lt;/span&gt;
  &lt;span class='n'&gt;post&lt;/span&gt; &lt;span class='ss'&gt;:create&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:format&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;json&amp;#39;&lt;/span&gt;
  &lt;span class='c1'&gt;# ...&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='c1'&gt;# MiniTest::Mock and SimpleDelegator&lt;/span&gt;
&lt;span class='nb'&gt;test&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;POST create&amp;quot;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
  &lt;span class='n'&gt;mock_order&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;Class&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt; &lt;span class='no'&gt;SimpleDelegator&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
    &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;items&lt;/span&gt;
      &lt;span class='n'&gt;mock&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='ss'&gt;MiniTest&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='ss'&gt;:Mock&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
      &lt;span class='n'&gt;mock&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;expect&lt;/span&gt; &lt;span class='ss'&gt;:create&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='no'&gt;Item&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='no'&gt;Hash&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;
    &lt;span class='k'&gt;end&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;
  &lt;span class='n'&gt;controller&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;order&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;mock_order&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='no'&gt;Order&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='n'&gt;post&lt;/span&gt; &lt;span class='ss'&gt;:create&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:format&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;json&amp;#39;&lt;/span&gt;
  &lt;span class='c1'&gt;# ...&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='c1'&gt;# SimpleMock&lt;/span&gt;
&lt;span class='nb'&gt;test&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;POST create&amp;quot;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
  &lt;span class='n'&gt;mock_items&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;SimpleMock&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
  &lt;span class='n'&gt;mock_items&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;expect&lt;/span&gt; &lt;span class='ss'&gt;:create&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='no'&gt;Item&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='no'&gt;Hash&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;
  &lt;span class='n'&gt;mock_order&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;SimpleMock&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt; &lt;span class='no'&gt;Order&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
  &lt;span class='n'&gt;mock_order&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;expect&lt;/span&gt; &lt;span class='ss'&gt;:items&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='n'&gt;mock_items&lt;/span&gt;
  &lt;span class='n'&gt;controller&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;order&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;mock_order&lt;/span&gt;
  &lt;span class='n'&gt;post&lt;/span&gt; &lt;span class='ss'&gt;:create&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:format&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;json&amp;#39;&lt;/span&gt;
  &lt;span class='c1'&gt;# ...&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id='fast_tests_are_fast'&gt;Fast Tests are Fast&lt;/h2&gt;

&lt;p&gt;SimpleMock is fast. While it may have a slightly higher overhead than using MiniTest::Mock with SimpleDelegator you get the benefit of a familiar and consistent API. In &lt;a href='https://gist.github.com/1871840'&gt;this benchmark&lt;/a&gt; we create an array, set an expectation and call the method 10,000 times.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='text'&gt;                  user       system     total      real
mocha:            0.000000   0.000000   0.000000   (0.000279)
simple_delegator: 0.000000   0.000000   0.000000   (0.000029)
simple_mock:      0.000000   0.000000   0.000000   (0.000057)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;SimpleMock is available as a &lt;a href='http://rubygems.org/gems/simple_mock'&gt;gem&lt;/a&gt;. Try SimpleMock in your next project and let &lt;a href='mailto:tate@tatey.com'&gt;me&lt;/a&gt; know what you think.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/tatejohnson/~4/uOnJJkRqJAQ" height="1" width="1"/&gt;</content>
   </entry>
 
   <entry>
     <title>Mocking with MiniTest::Mock and SimpleDelegator</title>
     <link href="http://tatey.com/2012/02/07/mocking-with-minitest-mock-and-simple-delegator/" />
     <updated>2012-02-07T00:00:00+11:00</updated>
     <id>http://tatey.com/2012/02/07/mocking-with-minitest-mock-and-simple-delegator</id>
     <content type="html">&lt;p&gt;Libraries like &lt;a href='http://mocha.rubyforge.org/'&gt;Mocha&lt;/a&gt; let you temporarily change the behaviour of objects. This is especially useful when mocking models in rails functional tests. Many of the built-in helper methods introspect models to generate URLs. The same behaviour can be achieved with &lt;a href='http://www.ruby-doc.org/stdlib-1.9.3/libdoc/minitest/mock/rdoc/MiniTest/Mock.html'&gt;MiniTest::Mock&lt;/a&gt; and &lt;a href='http://www.ruby-doc.org/stdlib-1.9.3/libdoc/delegate/rdoc/SimpleDelegator.html'&gt;SimpleDelegator&lt;/a&gt;. Both classes are included in the standard library, it&amp;#8217;s fast and there&amp;#8217;s no monkey patching.&lt;/p&gt;

&lt;p&gt;MiniTest::Mock is a factory for creating light weight objects. Behaviour is controlled by defining expectations. The first argument is the method&amp;#8217;s name and the second argument is the return value. There&amp;#8217;s an optional third argument for specifying the type of arguments that method takes.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='n'&gt;order&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='ss'&gt;MiniTest&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='ss'&gt;:Mock&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
&lt;span class='n'&gt;order&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;expect&lt;/span&gt; &lt;span class='ss'&gt;:update_attributes&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='kp'&gt;false&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='no'&gt;Hash&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;
&lt;span class='n'&gt;order&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;expect&lt;/span&gt; &lt;span class='ss'&gt;:valid?&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='kp'&gt;false&lt;/span&gt;

&lt;span class='n'&gt;order&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;update_attributes&lt;/span&gt; &lt;span class='ss'&gt;:desc&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;Shirt&amp;#39;&lt;/span&gt; &lt;span class='c1'&gt;# =&amp;gt; false&lt;/span&gt;
&lt;span class='n'&gt;order&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;valid?&lt;/span&gt;                             &lt;span class='c1'&gt;# =&amp;gt; false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;SimpleDelegator forwards all missing method calls to a delegate. Inheriting from SimpleDelegator lets you change the behaviour of methods you care about while preserving the behaviour of everything else. In this example we stop &lt;code&gt;#valid?&lt;/code&gt; from being forwarded by defining it in an anonymous class. &lt;code&gt;#attributes&lt;/code&gt; is missing from the anonymous class and is forwarded to the delegate. The initializer takes one argument which is the delegate.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='k'&gt;class&lt;/span&gt; &lt;span class='nc'&gt;Order&lt;/span&gt; &lt;span class='o'&gt;&amp;lt;&lt;/span&gt; &lt;span class='ss'&gt;ActiveRecord&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='ss'&gt;:Base&lt;/span&gt;
  &lt;span class='c1'&gt;# ...&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='n'&gt;invalid_delegator&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;Class&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt; &lt;span class='no'&gt;SimpleDelegator&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
  &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;valid?&lt;/span&gt;
    &lt;span class='kp'&gt;false&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='n'&gt;order&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;invalid_delegator&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt; &lt;span class='no'&gt;Order&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:desc&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;Shirt&amp;#39;&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
&lt;span class='n'&gt;order&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;valid?&lt;/span&gt;      &lt;span class='c1'&gt;# =&amp;gt; false&lt;/span&gt;
&lt;span class='n'&gt;order&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;attributes&lt;/span&gt;  &lt;span class='c1'&gt;# =&amp;gt; #&amp;lt;Order desc: &amp;#39;Shirt&amp;#39;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As a rule of thumb you should use MiniTest::Mock unless neighbouring objects expect specific behaviour. In the case of the latter, you&amp;#8217;ll know when to use SimpleDelegator as methods being called further down the stack are beyond your control and confuse the purpose of your test (Eg: Rails built-in helper methods for generating URLs).&lt;/p&gt;

&lt;p&gt;The real power of MiniTest::Mock and SimpleDelegator is realised when the two are used together. Below is a comparison of Mocha versus MiniTest::Mock and SimpleDelegator.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='c1'&gt;# Mocha&lt;/span&gt;
&lt;span class='nb'&gt;test&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;POST create&amp;quot;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
  &lt;span class='n'&gt;mock_items&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;mock&lt;/span&gt;&lt;span class='p'&gt;()&lt;/span&gt;
  &lt;span class='n'&gt;mock_items&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;expects&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:create&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;with&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='no'&gt;Hash&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;returns&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='no'&gt;Item&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='n'&gt;mock_order&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;Order&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
  &lt;span class='n'&gt;mock_order&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;expects&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='ss'&gt;:items&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;returns&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='n'&gt;mock_items&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='n'&gt;controller&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;order&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;mock_order&lt;/span&gt;
  &lt;span class='n'&gt;post&lt;/span&gt; &lt;span class='ss'&gt;:create&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:format&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;json&amp;#39;&lt;/span&gt;
  &lt;span class='c1'&gt;# ...&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;

&lt;span class='c1'&gt;# MiniTest::Mock and SimpleDelegator&lt;/span&gt;
&lt;span class='nb'&gt;test&lt;/span&gt; &lt;span class='s2'&gt;&amp;quot;POST create&amp;quot;&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
  &lt;span class='n'&gt;mock_order&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='no'&gt;Class&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt; &lt;span class='no'&gt;SimpleDelegator&lt;/span&gt; &lt;span class='k'&gt;do&lt;/span&gt;
    &lt;span class='k'&gt;def&lt;/span&gt; &lt;span class='nf'&gt;items&lt;/span&gt;
      &lt;span class='n'&gt;mock&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='ss'&gt;MiniTest&lt;/span&gt;&lt;span class='p'&gt;:&lt;/span&gt;&lt;span class='ss'&gt;:Mock&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;
      &lt;span class='n'&gt;mock&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;expect&lt;/span&gt; &lt;span class='ss'&gt;:create&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='no'&gt;Item&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='o'&gt;[&lt;/span&gt;&lt;span class='no'&gt;Hash&lt;/span&gt;&lt;span class='o'&gt;]&lt;/span&gt;
    &lt;span class='k'&gt;end&lt;/span&gt;
  &lt;span class='k'&gt;end&lt;/span&gt;
  &lt;span class='n'&gt;controller&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;order&lt;/span&gt; &lt;span class='o'&gt;=&lt;/span&gt; &lt;span class='n'&gt;mock_order&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;(&lt;/span&gt;&lt;span class='no'&gt;Order&lt;/span&gt;&lt;span class='o'&gt;.&lt;/span&gt;&lt;span class='n'&gt;new&lt;/span&gt;&lt;span class='p'&gt;)&lt;/span&gt;
  &lt;span class='n'&gt;post&lt;/span&gt; &lt;span class='ss'&gt;:create&lt;/span&gt;&lt;span class='p'&gt;,&lt;/span&gt; &lt;span class='ss'&gt;:format&lt;/span&gt; &lt;span class='o'&gt;=&amp;gt;&lt;/span&gt; &lt;span class='s1'&gt;&amp;#39;json&amp;#39;&lt;/span&gt;
  &lt;span class='c1'&gt;# ...&lt;/span&gt;
&lt;span class='k'&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;MiniTest::Mock and SimpleDelegator are more verbose than Mocha but I like that it uses the standard library and doesn&amp;#8217;t introduce new methods on Object, Module or Class. In addition MiniTest::Mock and SimpleDelegator are faster than Mocha. The &lt;a href='https://gist.github.com/1755458'&gt;benchmark is available as a gist&lt;/a&gt;.&lt;/p&gt;
&lt;div class='highlight'&gt;&lt;pre&gt;&lt;code class='text'&gt;                  user       system     total      real
mocha:            0.000000   0.000000   0.000000   (0.000277)
simple_delegator: 0.000000   0.000000   0.000000   (0.000024)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4 id='updated_20120221'&gt;Updated 2012-02-21&lt;/h4&gt;

&lt;p&gt;&lt;a href='https://github.com/tatey/simple_mock'&gt;SimpleMock&lt;/a&gt; takes the idea of this blog post and turns it into a fast, tiny library. The API is is 100% compatible with MiniTest::Mock and it plays nicely with MiniTest and RSpec. SimpleMock depends only on the standard library in Ruby 1.9.2 or greater. Instead of writing your own helpers, consider using SimpleMock. You&amp;#8217;ll keep tests fast and write less code.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/tatejohnson/~4/i9utXsk_syY" height="1" width="1"/&gt;</content>
   </entry>
 
</feed>
