<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:blogger="http://schemas.google.com/blogger/2008" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" gd:etag="W/&quot;DEMFQn09cCp7ImA9WhBUGU8.&quot;"><id>tag:blogger.com,1999:blog-5548895711100825622</id><updated>2013-05-07T07:33:33.368-04:00</updated><category term="content management systems" /><category term="radiant" /><category term="ruby" /><category term="spork" /><category term="haml" /><category term="mocks" /><category term="domain-modelling" /><category term="templates" /><category term="cufon" /><category term="bundler" /><category term="rails-3.2" /><category term="tools" /><category term="javascript" /><category term="s3" /><category term="workflow" /><category term="validations" /><category term="uploads" /><category term="error-handling" /><category term="strategy" /><category term="mocha" /><category term="osx" /><category term="customer-centered web design" /><category term="application development" /><category term="html-5" /><category term="fundamentals" /><category term="application-architecture" /><category term="css" /><category term="agile" /><category term="git" /><category term="rails" /><category term="saving" /><category term="maintenance" /><category term="rails-3.0" /><category term="rmagick" /><category term="launch" /><category term="vim" /><category term="user-stories" /><category term="productivity" /><category term="usability" /><category term="rake" /><category term="safari" /><category term="facebook" /><category term="craftmanship" /><category term="searchlogic" /><category term="i18n" /><category term="centos" /><category term="spree" /><category term="business" /><category term="personas" /><category term="sass" /><category term="ask for help" /><category term="sftp" /><category term="checklists" /><category term="refinery" /><category term="rdoc" /><category term="named scopes" /><category term="capybara" /><category term="cucumber" /><category term="google-site-search" /><category term="shopify" /><category term="textmate" /><category term="rvm" /><category term="monitoring" /><category term="compass" /><category term="meta-programming" /><category term="gems" /><category term="life hacks" /><category term="rest" /><category term="jquery" /><category term="rspec" /><category term="interview" /><category term="static-matic" /><category term="memory-consumption" /><category term="sql" /><category term="active-record" /><category term="unix" /><category term="ember.js" /><category term="html" /><category term="errors" /><category term="optimization" /><category term="memcached" /><category term="domain-patterns" /><category term="railsconf" /><category term="associations" /><category term="stand-up" /><category term="heroku" /><category term="redmine" /><category term="testing" /><category term="internet-explorer" /><category term="capistrano" /><category term="blank-static" /><category term="web design" /><category term="bourne" /><title>Firsthand Web Design, Ruby on Rails, Montréal</title><subtitle type="html">All things Ruby on Rails, Radiant CMS and Web Design</subtitle><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://blog.firsthand.ca/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://blog.firsthand.ca/" /><link rel="next" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default?start-index=26&amp;max-results=25&amp;redirect=false&amp;v=2" /><author><name>Nicholas Henry</name><uri>https://plus.google.com/109341983975580064085</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-Ub9vGydp-YQ/AAAAAAAAAAI/AAAAAAAAAAA/ag9kkr9R0AU/s512-c/photo.jpg" /></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>138</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/firsthand-web-design" /><feedburner:info uri="firsthand-web-design" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><link rel="license" type="text/html" href="http://creativecommons.org/licenses/by-nc-sa/2.0/" /><logo>http://creativecommons.org/images/public/somerights20.gif</logo><feedburner:emailServiceId>firsthand-web-design</feedburner:emailServiceId><feedburner:feedburnerHostname>http://feedburner.google.com</feedburner:feedburnerHostname><entry gd:etag="W/&quot;DEMFQn0zfSp7ImA9WhBUGU8.&quot;"><id>tag:blogger.com,1999:blog-5548895711100825622.post-1550558856789239607</id><published>2013-05-07T07:33:00.001-04:00</published><updated>2013-05-07T07:33:33.385-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-05-07T07:33:33.385-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="agile" /><title>Ron Jeffries on Requirements</title><content type="html">&lt;p&gt;This is one of my favourite quotes on requirements:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Most of us were taught to write down all our requirements at the very beginning of the project. There are only three things wrong with this: “requirements,” “the very beginning,” and “all.” At the very beginning, we know less about our project than we’ll ever know again. This is the worst possible moment to be making firm decisions about what we “require.”&lt;/p&gt;&lt;p&gt;From &lt;a href="http://pragprog.com/magazines/2013-02/estimation-is-evil"&gt;Estimation is Evil&lt;/a&gt;, Ron Jeffries: Signatory of the Manifesto for Agile Software Development ￼&lt;/p&gt;&lt;/blockquote&gt;&lt;img src="http://feeds.feedburner.com/~r/firsthand-web-design/~4/VMzUb4G4hPw" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/1550558856789239607?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/1550558856789239607?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/firsthand-web-design/~3/VMzUb4G4hPw/ron-jeffries-on-requirements.html" title="Ron Jeffries on Requirements" /><author><name>Nicholas Henry</name><uri>https://plus.google.com/109341983975580064085</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-Ub9vGydp-YQ/AAAAAAAAAAI/AAAAAAAAAAA/ag9kkr9R0AU/s512-c/photo.jpg" /></author><feedburner:origLink>http://blog.firsthand.ca/2013/05/ron-jeffries-on-requirements.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C08AR3c8eSp7ImA9WhBVGUU.&quot;"><id>tag:blogger.com,1999:blog-5548895711100825622.post-5339424067280853006</id><published>2013-04-26T09:04:00.001-04:00</published><updated>2013-04-26T09:10:46.971-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-04-26T09:10:46.971-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="launch" /><title>Recently Launched: Tarkett Sports CMS and Websites</title><content type="html">&lt;p&gt;An ongoing project I have been working on at &lt;a href="http://www.godynamo.com" title="http://www.godynamo.com"&gt;Dynamo&lt;/a&gt; over the last year with &lt;a href="http://www.cekoya.com/" title="http://www.cekoya.com/"&gt;Hugo Frappier&lt;/a&gt; is a custom CMS for Tarkett Sports, a division of the Tarkett Group, "a worldwide leader of innovative and sustainable flooring and sports surface solutions".&lt;/p&gt;&lt;h2&gt;A multi-Site, multi-lingual CMS&lt;/h2&gt;&lt;p&gt;Tarkett Sports required a multi-site, multi-lingual CMS to serve all the sites of their subdivisions. FieldTurf (North America) was the first to be launched on the new platform, followed by FieldTurf Europe and an API for their iPad sales tool, "Link". At the time of writing, Beynon Sports Surfaces and Tarkett Sports Indoor (North America) are set to launch within the month.&lt;/p&gt;&lt;h2&gt;Built on Ruby on Rails and MongoDB&lt;/h2&gt;&lt;p&gt;The CMS was built with the usual Rails stack, however &lt;a href="http://www.mongodb.org/" title="http://www.mongodb.org/"&gt;MongoDB&lt;/a&gt; was used with &lt;a href="http://mongoid.org/" title="http://mongoid.org/"&gt;Mongoid&lt;/a&gt;, an Object-Document-Mapper, instead of ActiveRecord and a relational database. This was my first venture in the world of NoSQL, and working with a schema less data store was a perfect match for the CMS. However, it did require a new way of thinking, such as being comfortable with de-normalizing data structures and ensuring those data structures can be properly index. For example, MongoDB does not allow indexing of parallel arrays.&lt;/p&gt;&lt;h2&gt;Support a large content-base&lt;/h2&gt;&lt;p&gt;Why a custom CMS? Why didn't we just use one of the prominent offerings such as RefineryCMS? I've implemented five RefineryCMS installations over the last couple of years, and Refinery's sweet spot is small websites. Tarkett Sports currently holds over 10,000 content items. Refinery's UI and tree data structure is just not appropriate for such a large content-base.&lt;/p&gt;&lt;h2&gt;Publish content collections anywhere in the page hierarchy&lt;/h2&gt;&lt;p&gt;We also took a novel approach to the page hierarchy. With platforms such as RefineryCMS and Radiant, structured content (think news and product catalog) containing large data sets are typically supported by creating custom Rails model view, and controller classes. There's usually wrangling to unify the site URLs with the routing system. The Tarkett Sports CMS supports the concept of a collection. A collection of structured content, small or large, can be published anywhere within the page hierarchy. No URL wrangling required, no custom controller class. Collections are a first class citizen.&lt;/p&gt;&lt;h2&gt;Reduce custom classes, increase content type re-use&lt;/h2&gt;&lt;p&gt;The benefit of all this is that it has reduced the number of custom classes required for a site by increasing the reuse of existing collections and content types. In the past, I have found myself creating custom controllers and models just to support a different content type with the same structure just so I can use it in a different part of the page hierarchy. The concept of publishable collections has solved this.&lt;/p&gt;&lt;p&gt;Below are a few screen shots, showcasing the different sites and portions of the administration screens.&lt;/p&gt;&lt;h2&gt;FieldTurf (North America) Home Page&lt;/h2&gt;&lt;p&gt;&lt;img src="http://firsthand-blog.s3.amazonaws.com/2013/04/tarkett-home-field-turf.png" alt="FieldTurf (North America) Home Page" border="0" /&gt;&lt;/p&gt;&lt;h2&gt;FieldTurf (Europe) Home Page&lt;/h2&gt;&lt;p&gt;&lt;img src="http://firsthand-blog.s3.amazonaws.com/2013/04/tarkett-home-europe.png" alt="FieldTurf (Europe) Home Page" border="0" /&gt;&lt;/p&gt;&lt;h2&gt;FieldTurf Products&lt;/h2&gt;&lt;p&gt;&lt;img src="http://firsthand-blog.s3.amazonaws.com/2013/04/tarkett-products.png" alt="FieldTurf Products" width="" height="" border="0" /&gt;&lt;/p&gt;&lt;h2&gt;FieldTurf Product Detail&lt;/h2&gt;&lt;p&gt;&lt;img src="http://firsthand-blog.s3.amazonaws.com/2013/04/tarkett-product.png" alt="FieldTurf Product Detail" width="" height="" border="0" /&gt;&lt;/p&gt;&lt;h2&gt;Tarkett Sports CMS Admin: Pages&lt;/h2&gt;&lt;p&gt;&lt;img src="http://firsthand-blog.s3.amazonaws.com/2013/04/tarkett-admin-pages.png" alt="Admin Pages" border="0" /&gt;&lt;/p&gt;&lt;h2&gt;Tarkett Sports CMS Admin: Page Detail&lt;/h2&gt;&lt;p&gt;&lt;img src="http://firsthand-blog.s3.amazonaws.com/2013/04/tarkett-admin-page-detail.png" alt="Admin Page Detail" border="0" /&gt;&lt;/p&gt;&lt;h2&gt;Tarkett CMS Sports Admin: Page Publishing&lt;/h2&gt;&lt;p&gt;&lt;img src="http://firsthand-blog.s3.amazonaws.com/2013/04/tarkett-admin-publish.png" alt="Admin Page Publishing" border="0" /&gt;&lt;/p&gt;







&lt;img src="http://feeds.feedburner.com/~r/firsthand-web-design/~4/SKBOBtSs5fY" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/5339424067280853006?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/5339424067280853006?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/firsthand-web-design/~3/SKBOBtSs5fY/recently-launched-tarkett-sports-cms.html" title="Recently Launched: Tarkett Sports CMS and Websites" /><author><name>Nicholas Henry</name><uri>https://plus.google.com/109341983975580064085</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-Ub9vGydp-YQ/AAAAAAAAAAI/AAAAAAAAAAA/ag9kkr9R0AU/s512-c/photo.jpg" /></author><feedburner:origLink>http://blog.firsthand.ca/2013/04/recently-launched-tarkett-sports-cms.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0ENQHk8fip7ImA9WhBVFk8.&quot;"><id>tag:blogger.com,1999:blog-5548895711100825622.post-8830688385833699169</id><published>2013-04-22T07:21:00.001-04:00</published><updated>2013-04-22T07:21:31.776-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-04-22T07:21:31.776-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="vim" /><title>Open Gems with vim-bundler</title><content type="html">&lt;p&gt;When working with platforms such as Spree and RefineryCMS it's more than likely that I will be extending their models more often that not. This means opening the Gem to identify a specific behaviour I want to override. Vim is my text editor of choice, and I typically try to restrict my self to one instance of the editor running. It makes working with multiple buffers a lot easier! Whenever I want to open a Ruby Gem, it always irks me that I have to open another instance of the editor, in a separate window/pane (although Tmux reduces the pain of this a wee bit).&lt;/p&gt;&lt;p&gt;Vim does offer a &lt;a href="http://ajayfromiiit.wordpress.com/2009/10/21/server-and-client-mode-in-vim/"&gt;client-server&lt;/a&gt; option, but admittedly I couldn't get this working and in my search I came across &lt;a href="https://github.com/tpope/vim-bundler"&gt;Tim Pope's vim-bundler&lt;/a&gt;. From the README:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;An internalized version of &lt;tt&gt;bundle open&lt;/tt&gt; : &lt;tt&gt;:Bopen&lt;/tt&gt; (and &lt;tt&gt;:Bsplit&lt;/tt&gt; , &lt;tt&gt;:Btabedit&lt;/tt&gt; , etc.).&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Exactly what I wanted, a way to `bundle open` within Vim. Here's a quick demo of using vim-bundler to open Spree's `Order` model and navigate to the `add_variant` method.&lt;/p&gt;&lt;p&gt;&lt;iframe src="http://player.vimeo.com/video/64499319" width="500" height="306" frameborder="0"&gt;&lt;/iframe&gt;&lt;/p&gt;&lt;p&gt;&lt;a href="http://vimeo.com/64499319"&gt;vim-bundler&lt;/a&gt; from &lt;a href="http://vimeo.com/firsthand"&gt;Nicholas Henry&lt;/a&gt; on &lt;a href="http://vimeo.com"&gt;Vimeo&lt;/a&gt;.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/firsthand-web-design/~4/_gy3NdzZZ00" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/8830688385833699169?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/8830688385833699169?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/firsthand-web-design/~3/_gy3NdzZZ00/open-gems-with-vim-bundler.html" title="Open Gems with vim-bundler" /><author><name>Nicholas Henry</name><uri>https://plus.google.com/109341983975580064085</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-Ub9vGydp-YQ/AAAAAAAAAAI/AAAAAAAAAAA/ag9kkr9R0AU/s512-c/photo.jpg" /></author><feedburner:origLink>http://blog.firsthand.ca/2013/04/open-gems-with-vim-bundler.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUENQHw-fCp7ImA9WhBWFUk.&quot;"><id>tag:blogger.com,1999:blog-5548895711100825622.post-5096608649281527367</id><published>2013-04-09T08:07:00.001-04:00</published><updated>2013-04-09T18:48:11.254-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-04-09T18:48:11.254-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="productivity" /><category scheme="http://www.blogger.com/atom/ns#" term="life hacks" /><title>Taming the pile of unread books</title><content type="html">&lt;p&gt;Recently I posted to Facebook:&lt;/p&gt;&lt;blockquote&gt;You know you have a book buying problem when... you discover that you have two copies of the same book on your bookshelf. Unread.&lt;/blockquote&gt;&lt;p&gt;&lt;strong&gt;Yes, I have a book buying problem.&lt;/strong&gt; And after that post I decided it was time to do something about it. I love reading. I find it extremely motivating and inspiring. But having a few dozen unread or partially started books is not only a waste of money, but just another open loop in my life that just doesn't need to be there. Jumping from book to book also inhibits in my ability to really understanding a book.&lt;/p&gt;&lt;p&gt;And before you start thinking I'm talking about a pile of trashy romance novels, I am talking about computer and business related books!&lt;/p&gt;&lt;p&gt;&lt;strong&gt;So how did it get this way?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;I have always had a stack of unread books around, but I typically got through the queue. However, with eBooks it really has got out of control. I start with reading an inspiring blog post, or watching a video of a conference talk, it references a book and wham! Next moment, I'm online purchasing that sucker.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;So what have I done to resolve this?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;It was pretty simple really. I present to you, my three step plan:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;First, &lt;strong&gt;I took inventory&lt;/strong&gt;. I identified the books as unopened, "briefly" started (read a few chapters), and "well" started (at least half way through).&lt;/li&gt;
&lt;li&gt;For those that were briefly started, I removed bookmarks, virtual or physical, and added them to the unopened pile. Basically, calling &lt;strong&gt;bankruptcy on the "briefly" started&lt;/strong&gt;. This cleared a lot of open loops. A lot.&lt;/li&gt;
&lt;li&gt;With the "well" started books I &lt;strong&gt;recorded them in OmniFocus&lt;/strong&gt;. This has helped me to keep conscious of how many books I have on the go. This ended up being four in total. The goal is to have no more than two in progress.&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;Since starting this two weeks ago, I have completed one book, and will finish the second this week. I will be at my goal of two in progress. Oh, and I haven't bought any new books in the last two weeks -- there have been a couple of occasions where I would have.&lt;/p&gt;&lt;img src="http://firsthand-blog.s3.amazonaws.com/2013/04/taming-the-unread-pile-of-books.png" alt="Book Queue in OmniFocus" width="453" height="276" border="0" /&gt;&lt;p&gt;Yes, I know this all sounds pretty ridiculous. However, I bet I'm not the only developer out there, with a book addiction. If you are, please let me know in the comments, how you have curbed your book buying behaviour, or please give my recovery plan a go!&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/firsthand-web-design/~4/-99Ed1NF4_8" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/5096608649281527367?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/5096608649281527367?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/firsthand-web-design/~3/-99Ed1NF4_8/taming-pile-of-unread-books.html" title="Taming the pile of unread books" /><author><name>Nicholas Henry</name><uri>https://plus.google.com/109341983975580064085</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-Ub9vGydp-YQ/AAAAAAAAAAI/AAAAAAAAAAA/ag9kkr9R0AU/s512-c/photo.jpg" /></author><feedburner:origLink>http://blog.firsthand.ca/2013/04/taming-pile-of-unread-books.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0ECQHk-eCp7ImA9WhBWFUg.&quot;"><id>tag:blogger.com,1999:blog-5548895711100825622.post-5097320118083898059</id><published>2012-11-30T16:55:00.001-05:00</published><updated>2013-04-09T22:07:41.750-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-04-09T22:07:41.750-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="vim" /><title>Have you tried ctags with Vim?</title><content type="html">&lt;p&gt;After reading Mislav Marohnić's excellent post on &lt;a href="http://mislav.uniqpath.com/2011/12/vim-revisited/"&gt;Vim&lt;/a&gt;, I decided to add ctags to my development environment. What does it buy you? It allows you to jump to method or class definitions with these commands:&lt;/p&gt;&lt;pre class="brush: bash;"&gt;&amp;lt;C-]&amp;gt; / :tag foo # jump to tag under cursor/named
g&amp;lt;C-]&amp;gt; / :tjump foo # choose from a list of matching tags
&lt;/pre&gt;&lt;p&gt;Setup is pretty easy:&lt;/p&gt;&lt;pre class="brush: bash;"&gt;brew install ctags
&lt;/pre&gt;&lt;p&gt;You can generate tags with:&lt;/p&gt;&lt;pre class="brush: bash"&gt;ctags -R --languages=ruby --exclude=.git
&lt;/pre&gt;&lt;p&gt;But you probably won't want to do all that manually. As Mislav points out in his post, Tim Pope shows us how to &lt;a href="http://tbaggery.com/2011/08/08/effortless-ctags-with-git.html"&gt;automate tag generation with git hooks&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;I've been using ctags this afternoon and already it has turned Vim into a completely different editor for me.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/firsthand-web-design/~4/fNWzZo0-Kzk" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/5097320118083898059?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/5097320118083898059?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/firsthand-web-design/~3/fNWzZo0-Kzk/have-you-tried-ctags-with-vim.html" title="Have you tried ctags with Vim?" /><author><name>Nicholas Henry</name><uri>https://plus.google.com/109341983975580064085</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-Ub9vGydp-YQ/AAAAAAAAAAI/AAAAAAAAAAA/ag9kkr9R0AU/s512-c/photo.jpg" /></author><feedburner:origLink>http://blog.firsthand.ca/2012/11/have-you-tried-ctags-with-vim.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkQDRXszeyp7ImA9WhNRGE8.&quot;"><id>tag:blogger.com,1999:blog-5548895711100825622.post-6729005645038881484</id><published>2012-11-12T14:09:00.001-05:00</published><updated>2012-11-13T11:32:54.583-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-11-13T11:32:54.583-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="google-site-search" /><title>Using Google Site Search for Content Sites: Things to Keep in Mind</title><content type="html">&lt;p&gt;I've used &lt;a href="http://www.google.com/enterprise/search/products_gss.html"&gt;Google Site Search&lt;/a&gt; for a couple of unstructured content sites (opposed to applications or structured content such as catalogs) and pretty happy with it, but there a certainly some things you should be aware of before committing to this service:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;The service's crawler works the same on Google.com. This means, it will only index publicly available pages that it can access. So you cannot use it on password-protected staging sites.&lt;/li&gt;&lt;li&gt;If you consider removing the password from the staging site you won't want to do that either, as your site must be registered with Google Webmaster tools, so your staging site will eventually end up in the Google.com index. But there's also another issue.&lt;/li&gt;&lt;li&gt;Indexes to URL's contain the domain of your site, so you can't index your staging site, then hope to use that index in your live site.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;If you're re-launching an existing site -- your only option is to enable search after you have made that site publicly available via the production domain. If it's a new site, and you have the option, make the site publicly available a couple of days before hand and index the site. Google Site Search provides on-demand indexing, updating the index within 24 hours.&lt;/p&gt;&lt;p&gt;When indexing the site, you have three options for supplying URL's:&lt;p&gt;&lt;ul&gt;&lt;li&gt;Individual URL's only&lt;/li&gt;&lt;li&gt;URL's linked from a specific page or Sitemap&lt;/li&gt;&lt;li&gt;URL's in a Sitemap only&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Again, you must have your site registered with &lt;a href="http://www.google.com/webmasters/tools/"&gt;Google Webmaster Tools&lt;/a&gt; for the indexing to work.&lt;/p&gt;&lt;p&gt;For more information on indexing checkout &lt;a href="http://support.google.com/customsearch/bin/topic.py?hl=en&amp;topic=16792&amp;parent=&amp;ctx=topic"&gt;Custom Search Help&lt;/a&gt; documentation.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/firsthand-web-design/~4/sU7nwk76Jj4" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/6729005645038881484?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/6729005645038881484?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/firsthand-web-design/~3/sU7nwk76Jj4/using-google-site-search-for-content.html" title="Using Google Site Search for Content Sites: Things to Keep in Mind" /><author><name>Nicholas Henry</name><uri>https://plus.google.com/109341983975580064085</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-Ub9vGydp-YQ/AAAAAAAAAAI/AAAAAAAAAAA/ag9kkr9R0AU/s512-c/photo.jpg" /></author><feedburner:origLink>http://blog.firsthand.ca/2012/11/using-google-site-search-for-content.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0ABQXoyfyp7ImA9WhNSEkQ.&quot;"><id>tag:blogger.com,1999:blog-5548895711100825622.post-7718496753677180276</id><published>2012-10-26T18:48:00.001-04:00</published><updated>2012-10-26T18:49:10.497-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-10-26T18:49:10.497-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="domain-patterns" /><title>Domain Patterns are Alexandrian Patterns</title><content type="html">&lt;p&gt;It's been a while since I have posted on this blog. So just to let you know that I'm still alive, this is what has been keeping me occupied. It's best summed up by a quote from the book &lt;a href="http://www.amazon.com/Object-Thinking-DV-Microsoft-Professional-David/dp/0735619654"&gt;Object Thinking&lt;/a&gt; by David West. This is a must read for any Rails developer who is following the object-oriented Rails culture.&lt;/p&gt;&lt;blockquote&gt;Patterns most useful to object thinkers should be derived from the problem domain, just as objects are. They should facilitate thinking about co-ordination and scripting of objects or about useful ways of assembling objects into components or applications. They could be considered Alexandrian patterns. Few of the patterns (about 6 of the 23) presented in the GoF book satisfy this demand. Martin Fowler’s book on Analysis Patterns presents examples derived from a domain and is much closer to Alexander’s intent than the GoF book.&lt;/blockquote&gt;&lt;p&gt;My intent is to post more on Domain Patterns in a Rails context in the near future. We'll see.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/firsthand-web-design/~4/KhM7weCxzMA" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/7718496753677180276?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/7718496753677180276?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/firsthand-web-design/~3/KhM7weCxzMA/domain-patterns-are-alexandrian-patterns.html" title="Domain Patterns are Alexandrian Patterns" /><author><name>Nicholas Henry</name><uri>https://plus.google.com/109341983975580064085</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-Ub9vGydp-YQ/AAAAAAAAAAI/AAAAAAAAAAA/ag9kkr9R0AU/s512-c/photo.jpg" /></author><feedburner:origLink>http://blog.firsthand.ca/2012/10/domain-patterns-are-alexandrian-patterns.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0AERn06eyp7ImA9WhBWFUg.&quot;"><id>tag:blogger.com,1999:blog-5548895711100825622.post-7129980046001809279</id><published>2012-03-09T10:48:00.001-05:00</published><updated>2013-04-09T22:08:27.313-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-04-09T22:08:27.313-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="ruby" /><category scheme="http://www.blogger.com/atom/ns#" term="rspec" /><category scheme="http://www.blogger.com/atom/ns#" term="testing" /><category scheme="http://www.blogger.com/atom/ns#" term="bourne" /><category scheme="http://www.blogger.com/atom/ns#" term="mocha" /><title>Testing Terminology: Spies, Stubs and Mocks</title><content type="html">&lt;p&gt;When referring to the definitions of Spies, Stubs and Mocks I like to return to &lt;a href="http://www.informit.com/store/product.aspx?isbn=9780132800051"&gt;XUnit Test Patterns&lt;/a&gt;:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Test Spy&lt;/strong&gt;: "We use a Test Double to &lt;em&gt;capture the indirect output calls&lt;/em&gt; made to another component by the SUT for later verification by the test."&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Test Stub&lt;/strong&gt;: "We replace a real object with a test-specific object that feeds the desired indirect inputs into the SUT."&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mock Object&lt;/strong&gt;: "We replace an object the SUT depends on with a test-specific object that verifies it is being used correctly by the SUT."&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;You may have noticed that a Spy and Mock seem to have similar definitions, and do serve similar purposes, however Mocks specify expectations up-front and don't require assertions in contrast to Spies, that verify after the fact. Here's an example that compares &lt;a href="http://rubydoc.info/gems/rspec-mocks/frames"&gt;Rspec Mocks&lt;/a&gt; with &lt;a href="https://github.com/thoughtbot/bourne"&gt;Bourne's&lt;/a&gt; Spies:&lt;/p&gt;&lt;p&gt;SUT is an abbreviation for System Under Test.&lt;/p&gt;&lt;pre class="brush: ruby;"&gt;describe Transfer, "using Rspec Mock" do
  it "deposits amount into destination account" do                                            
    source_account      = stub("source account", withdraw: 10)
    destination_account = mock("destination_account")

    destination_account.should_receive(:deposit).with(10)

    transfer = Transfer.new(source_account, destination_account)    
    transfer.call
  end                                             
end

describe Transfer, "using Bourne Spy" do
  it "deposits amount into destination account" do                                            
    source_account      = stub("source account", withdraw: 10)
    destination_account = mock("destination_account")

    transfer = Transfer.new(source_account, destination_account)    
    transfer.call 
    
    destination_account.should have_received(:deposit).with(10) 
  end                                             
end 

&lt;/pre&gt;&lt;p&gt;The above patterns are collectively know as &lt;strong&gt;Test Doubles&lt;/strong&gt;: "We replace a component on which the SUT depends with a &lt;em&gt;test-specific equivalent&lt;/em&gt;." Rspec's built-in mocking library includes a &lt;tt&gt;double&lt;/tt&gt; method/alias, see &lt;a href="http://blog.firsthand.ca/2011/12/example-using-rspec-double-mock-and.html"&gt;An Example using RSpec double, mock, and stub&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;a href="https://github.com/btakita/rr"&gt;Double-Ruby (RR)&lt;/a&gt; is another example of a test double framework implemented in Ruby.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/firsthand-web-design/~4/rU6SmWGRjFs" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/7129980046001809279?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/7129980046001809279?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/firsthand-web-design/~3/rU6SmWGRjFs/testing-terminology-spies-stubs-and.html" title="Testing Terminology: Spies, Stubs and Mocks" /><author><name>Nicholas Henry</name><uri>https://plus.google.com/109341983975580064085</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-Ub9vGydp-YQ/AAAAAAAAAAI/AAAAAAAAAAA/ag9kkr9R0AU/s512-c/photo.jpg" /></author><feedburner:origLink>http://blog.firsthand.ca/2012/03/testing-terminology-spies-stubs-and.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUAASH87fip7ImA9WhRaEEg.&quot;"><id>tag:blogger.com,1999:blog-5548895711100825622.post-8447884404025022785</id><published>2012-02-12T08:35:00.001-05:00</published><updated>2012-02-12T08:35:49.106-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-02-12T08:35:49.106-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="rails-3.2" /><category scheme="http://www.blogger.com/atom/ns#" term="spork" /><category scheme="http://www.blogger.com/atom/ns#" term="rails" /><category scheme="http://www.blogger.com/atom/ns#" term="rails-3.0" /><title>Heads up: Use spork-rails if you want to use spork 1.0rc in your Rails project</title><content type="html">&lt;p&gt;If you are using &lt;a href="https://github.com/sporkrb/spork"&gt;Spork&lt;/a&gt; 1.0rc in your Rails project and wondering why your controllers etc are not reloading, it's because this functionality has been extracted to the &lt;a href="https://github.com/sporkrb/spork-rails"&gt;spork-rails&lt;/a&gt; plugin gem. Remove the spork reference from your Gem file and add 'spork-rails' instead. spork-rails will include spork 1.0rc as a dependency. This follows the convention for other gems such as &lt;a href="https://github.com/rspec/rspec-rails"&gt;rspec-rails&lt;/a&gt; and &lt;a href="https://github.com/thoughtbot/factory_girl_rails"&gt;factory_girl_rails&lt;/a&gt;. Note spork-rails follows the versioning of minor Rails versions. For example, the current version is 3.2.0, compatible with Rails 3.2.x down to 3.0.0.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/firsthand-web-design/~4/dRm4IimILo8" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/8447884404025022785?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/8447884404025022785?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/firsthand-web-design/~3/dRm4IimILo8/heads-up-use-spork-rails-if-you-want-to.html" title="Heads up: Use spork-rails if you want to use spork 1.0rc in your Rails project" /><author><name>Nicholas Henry</name><uri>https://plus.google.com/109341983975580064085</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-Ub9vGydp-YQ/AAAAAAAAAAI/AAAAAAAAAAA/ag9kkr9R0AU/s512-c/photo.jpg" /></author><feedburner:origLink>http://blog.firsthand.ca/2012/02/heads-up-use-spork-rails-if-you-want-to.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUMDSH48cCp7ImA9WhRWFk0.&quot;"><id>tag:blogger.com,1999:blog-5548895711100825622.post-8652770484422143626</id><published>2012-01-03T10:11:00.001-05:00</published><updated>2012-01-03T10:11:19.078-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-01-03T10:11:19.078-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="ember.js" /><title>Some rules to remember when writing Ember.js code</title><content type="html">&lt;ul&gt;&lt;li&gt;Display logic only goes in the view layer&lt;/li&gt;&lt;li&gt;Never modify the DOM in View API&lt;/li&gt;&lt;li&gt;App logic only goes in the controller layer&lt;/li&gt;&lt;li&gt;Limit custom code in the controller&lt;/li&gt;&lt;li&gt;Domain logic only goes in the model layer&lt;/li&gt;&lt;li&gt;Server calls belong to the model layer&lt;/li&gt;&lt;li&gt;Only cross layers going up (V -&amp;gt; C -&amp;gt; M)&lt;/li&gt;&lt;li&gt;Never observe across layers&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;From &lt;a href="http://vimeo.com/24779602"&gt;Sproutcore Training: Part 2 - Core Concepts&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/firsthand-web-design/~4/SK79C5TO7e4" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/8652770484422143626?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/8652770484422143626?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/firsthand-web-design/~3/SK79C5TO7e4/some-rules-to-remember-when-writing.html" title="Some rules to remember when writing Ember.js code" /><author><name>Nicholas Henry</name><uri>https://plus.google.com/109341983975580064085</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-Ub9vGydp-YQ/AAAAAAAAAAI/AAAAAAAAAAA/ag9kkr9R0AU/s512-c/photo.jpg" /></author><feedburner:origLink>http://blog.firsthand.ca/2012/01/some-rules-to-remember-when-writing.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A08AR3w6eSp7ImA9WhBWFUg.&quot;"><id>tag:blogger.com,1999:blog-5548895711100825622.post-8065997206631979834</id><published>2011-12-24T15:14:00.002-05:00</published><updated>2013-04-09T22:10:46.211-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-04-09T22:10:46.211-04:00</app:edited><title>Your Rails Application is Missing a Domain Controller</title><content type="html">&lt;p&gt;The mantra of &lt;a href="http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model"&gt;Skinny Controller, Fat Model&lt;/a&gt; reminds us to keep our business logic in our models, not in Rails Action Controllers. We know Action Controllers, sub-classes of &lt;tt&gt;ActionController::Base&lt;/tt&gt;, are only responsible for delegating service requests to the domain layer. These models, which encapsulate this business logic, forming the domain layer, represent &lt;em&gt;entities&lt;/em&gt; such as a person, place, event or thing. They are often, &lt;strong&gt;but not always&lt;/strong&gt;, mapped directly to database tables through the inheritance of &lt;tt&gt;ActiveRecord::Base&lt;/tt&gt;. When we develop our domain layer for our complex Rails application, purely &lt;strong&gt;with models representing entities&lt;/strong&gt;, we encounter two problems:&lt;/p&gt;&lt;h2&gt;Incoherent domain API&lt;/h2&gt;&lt;p&gt;When Action Controllers interact with model entities directly, the &lt;span class="caps"&gt;API&lt;/span&gt; to the domain layer becomes clouded. It is unclear what services the domain layer offers and how the UI should request those services. Rail&amp;#8217;s ActiveRecord exasperates the issue, for example, there are several ways to create and update model entities and their associations.&lt;/p&gt;&lt;h2&gt;Bloated domain models&lt;/h2&gt;&lt;p&gt;Business logic that is workflow-oriented involving multiple entities is assigned to a single entity, bloating it&amp;#8217;s responsibilities. It becomes difficult to differentiate the entities in the domainrepresenting what the system is, from the use cases or workflow representing what the system does&lt;sup&gt;&lt;a href="#fn1"&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;&lt;p&gt;The solution is a Domain Controller. &lt;strong&gt;A Domain Controller is responsible for co-ordinating service requests&lt;/strong&gt; from your Rails Action Controllers. A Domain Controller can come in two flavours, a &lt;em&gt;facade&lt;/em&gt; or a &lt;em&gt;use case&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;&lt;em&gt;A clarification on terminology: Throughout this blog post I will refer to the typical Rails model as a &lt;strong&gt;Model Entity&lt;/strong&gt;, meaning a model that represents a person, place, event or thing.&lt;/em&gt;&lt;/p&gt;&lt;h2&gt;Facade Controller&lt;/h2&gt;&lt;p&gt;A Facade Controller represents the overall system that encapsulates the domain layer. For example a &lt;em&gt;Catalog&lt;/em&gt; object can act as facade for the domain layer containing taxonomies, taxons, product groups, products and variants.&lt;/p&gt;&lt;p&gt;Without the Facade Controller, our service requests to the domain typically look like this:&lt;/p&gt;&lt;pre class="brush: ruby;"&gt;Product.active
Taxon.active.find(1)
Product.create(:name =&amp;gt; 'Ruby Meta-Programming')
&lt;/pre&gt;&lt;p&gt;With a Facade Controller instance, in this case, a &lt;em&gt;catalog&lt;/em&gt; object, service requests appear as the following, delegating to the appropriate Model Entities:&lt;/p&gt;&lt;pre class="brush: ruby;"&gt;catalog.fetch_products
catalog.fetch_taxon(1)
catalog.create_product(:name =&amp;gt; 'Ruby Meta-Programming')
&lt;/pre&gt;&lt;p&gt;The Facade Controller provides a clear, explicit interface into the catalog domain layer that a programmer can grok within seconds.&lt;/p&gt;&lt;h2&gt;Use Case Controller&lt;/h2&gt;&lt;p&gt;A Use Case Controller is useful for more complex workflows. They are also helpful when a Facade becomes bloated and you wish to split the incoming service requests across multiple Domain Controllers. It&amp;#8217;s important to note, &lt;strong&gt;you can use more than one Domain Controller for your application&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;Let&amp;#8217;s look at an example. The code below represents the processing of a Payroll&lt;sup&gt;&lt;a href="#fn2"&gt;2&lt;/a&gt;&lt;/sup&gt;. Often this type of workflow logic gets placed in a Model Entity that it doesn&amp;#8217;t belong to or worst, left in a Rails Action Controller.&lt;/p&gt;&lt;pre class="brush: ruby;"&gt;date = Date.today
employees = Employee.active
employees.each do |e|
  if e.pay_date?(date)
    pc = PayCheck.new(e.calculate_pay(date))
    e.send_pay(pc)
  end
end
&lt;/pre&gt;&lt;p&gt;So what object is responsible for this workflow logic? We know a Rails Action Controller isn&amp;#8217;t a candidate. Its responsibility is to handle &lt;span class="caps"&gt;HTTP&lt;/span&gt; requests and delegate to the domain layer. Neither the models &lt;tt&gt;Employee&lt;/tt&gt; or &lt;tt&gt;PayCheck&lt;/tt&gt; are valid candidates. They represents the entities in our domain layer. Therefore we, require a new object &lt;em&gt;PayDayService&lt;/em&gt; to encapsulate the logic, a Use Case Controller.&lt;/p&gt;&lt;pre class="brush: ruby;"&gt;class PayDayService
    def initialize(date=Date.now)
      @date = date
    end

    def call
      Employee.active.each do |e|
        if e.pay_date?(@date)
          pc = PayCheck.new(e.calculate_pay(@date))
          e.send_pay(pc)
        end
      end
    end
  end
&lt;/pre&gt;&lt;p&gt;The benefit of this approach is that we have encapsulated this workflow logic without bloating existing Model Entities and allows the reuse of this logic in different contexts.&lt;/p&gt;&lt;h2&gt;Domain Controllers in a Rails Application&lt;/h2&gt;&lt;p&gt;When an application has multiple Domain Controllers I place them in a dedicated directory under app called &lt;em&gt;services&lt;/em&gt; to keep them separate from the Model entities in &lt;tt&gt;app/models&lt;/tt&gt;. This helps communicate that Rails Action Controllers should delegate to the Domain Controllers, not directly to Model Entities. The Domain Controllers and Model Entities together, represent the domain layer of the system. I have also discussed extracting the entire domain layer from the Rails framework, but that&amp;#8217;s &lt;a href="http://blog.firsthand.ca/2011/10/rails-is-not-your-application.html"&gt;another discussion&lt;/a&gt;. Let&amp;#8217;s keep this blog post civil for now!&lt;/p&gt;&lt;p&gt;I hope the Domain Controller pattern provides you with another tool for modelling your domain, reduces the coupling with the UI, and provides a clean &lt;span class="caps"&gt;API&lt;/span&gt; to your application logic.&lt;/p&gt;&lt;h2&gt;Further Reading&lt;/h2&gt;&lt;p&gt;If you are interested in learning more about Domain Controllers and Services, check out the following resources:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.amazon.com/Applying-UML-Patterns-Introduction-Object-Oriented/dp/0131489062"&gt;Applying &lt;span class="caps"&gt;UML&lt;/span&gt; and Patterns&lt;/a&gt; by Craig Larman. Craig includes the &lt;em&gt;Controller&lt;/em&gt; pattern as one of the &lt;em&gt;General Responsibility Assignment Software Patterns or Principles (Grasp)&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://confreaks.net/videos/759-rubymidwest2011-keynote-architecture-the-lost-years"&gt;Architecture the Lost Years&lt;/a&gt; a presentation by Robert Martin discusses modelling your application using Interactors and Entities. &lt;em&gt;Interactors_&lt;/em&gt; (which I have used the name Domain Controller) contain application specific logic, and &lt;em&gt;Entities&lt;/em&gt; (which I have use the term Model Entities) contains application independent logic. Martin choses to use the term &lt;em&gt;Interactors over Controllers&lt;/em&gt; to avoid confusing with Model-View-Controller (MVC). I prefer to use Domain Controller or Service to differentiate from Rails Action Controllers as I just can&amp;#8217;t get my head around the term Interactors.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.amazon.com/Object-Oriented-Software-Engineering-Approach/dp/0201544350"&gt;Object-Oriented Software Engineering&amp;#8212;A Use Case Driven Approach&lt;/a&gt; by Ivar Jacobson introduced the concept of interface, entity (Model Entity) and control (Domain Controller) objects over two decades ago. Robert Martin often refers to Ivar Jacobson when discussing application architecture.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.amazon.com/Patterns-Enterprise-Application-Architecture-Martin/dp/0321127420"&gt;Patterns of Enterprise Application Architecture&lt;/a&gt; by Martin Flower discusses the concept of a &amp;#8220;Service Layer&amp;#8221; to give the domain layer an explicit &lt;span class="caps"&gt;API&lt;/span&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215"&gt;Domain Driven Design&lt;/a&gt; by Eric Evans. Evans discusses the concept of Services for when &amp;#8220;some concepts from the domain aren&amp;#8217;t natural to model as objects&amp;#8221;.&lt;/li&gt;
&lt;/ul&gt;&lt;h2&gt;Footnotes:&lt;/h2&gt;&lt;ol&gt;&lt;li&gt;&amp;#8220;What-the-system-is&amp;#8221; and &amp;#8220;What-the-system-does&amp;#8221; terminology is from the book &lt;a href="http://www.amazon.com/Lean-Architecture-Agile-Software-Development/dp/0470684208/"&gt;Lean Architecture&lt;/a&gt; by James O. Coplien and Gertrud Bjørnvig.&lt;/li&gt;
&lt;li&gt;Logic based on the Payroll case study in &lt;a href="http://www.amazon.com/Software-Development-Principles-Patterns-Practices/dp/0135974445"&gt;Robert Martin&amp;#8217;s Agile Software Development&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;&lt;img src="http://feeds.feedburner.com/~r/firsthand-web-design/~4/BU542tgLUHE" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/8065997206631979834?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/8065997206631979834?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/firsthand-web-design/~3/BU542tgLUHE/your-rails-application-is-missing.html" title="Your Rails Application is Missing a Domain Controller" /><author><name>Nicholas Henry</name><uri>https://plus.google.com/109341983975580064085</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-Ub9vGydp-YQ/AAAAAAAAAAI/AAAAAAAAAAA/ag9kkr9R0AU/s512-c/photo.jpg" /></author><feedburner:origLink>http://blog.firsthand.ca/2011/12/your-rails-application-is-missing.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A08NRnYyeCp7ImA9WhBWFUg.&quot;"><id>tag:blogger.com,1999:blog-5548895711100825622.post-8404720343931231280</id><published>2011-12-21T15:30:00.000-05:00</published><updated>2013-04-09T22:11:37.890-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-04-09T22:11:37.890-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="rspec" /><title>An Example using RSpec double, mock, and stub</title><content type="html">&lt;p&gt;This is a follow-up post for &lt;a href="/2011/01/rspec-double-mock-and-stub.html"&gt;RSpec double, mock, and stub&lt;/a&gt; from earlier this year. A reader asked for an example code to illustrate the various &lt;tt&gt;double&lt;/tt&gt; aliases in RSpec.&lt;/p&gt;&lt;p&gt;For this example we will spec a &lt;tt&gt;Transfer&lt;/tt&gt; class that encapsulates the use case of transferring an amount between two accounts, the source account and destination account. The &lt;tt&gt;Transfer&lt;/tt&gt; is the subject of our examples, while the source and destination accounts are the collaborators.&lt;/p&gt;&lt;p&gt;In the spec below, the first example "should decrease source amount by 10", specifies the interaction for the source account, hence the use of the &lt;tt&gt;mock&lt;/tt&gt; to generate the test double. In this example, the destination account is treated as a secondary collaborator, as we are focusing on the role of the source account. Any object can play the role of the source account, as long as it responds to the &lt;tt&gt;#decrease&lt;/tt&gt; method.&lt;/p&gt;&lt;p&gt;In the second example "should increase destination account by 10", the destination account is the primary collaborator. The use of &lt;tt&gt;mock&lt;/tt&gt; to generate the source account communicates this intent.&lt;/p&gt;&lt;pre class="brush: ruby;"&gt;describe Transfer do
  context "transfer with amount of 10" do
    it "should decrease source account by 10" do
      source_account      = mock('source account')
      destination_account = stub('destination_account').as_null_object
    
      source_account.should_receive('decrease').with(10)
    
      transfer = Transfer.new(source_account, destination_account, 10)
      transfer.call
    end

    it "should increase destination account by 10" do
      source_account      = stub('source account').as_null_object
      destination_account = mock('destination_account')
    
      destination_account.should_receive('increase').with(10)
    
      transfer = Transfer.new(source_account, destination_account, 10)
      transfer.call
    end
  end
end
&lt;/pre&gt;&lt;p&gt;However, there is some duplication in this spec which can be eliminated by moving the test doubles to &lt;tt&gt;let&lt;/tt&gt; blocks. It's appropriate to use &lt;tt&gt;double&lt;/tt&gt; here to generate the source and destination accounts as both play primary and secondary collaborators in the following examples. Below we stub the methods for each collaborator, then set expectations on those methods for the appropriate examples.&lt;/p&gt;&lt;pre class="brush: ruby;"&gt;describe Transfer do
  context "transfer with amount of 10" do
    let(:source_account) { double('source account', :decrease =&gt; nil) }
    let(:destination_account) { double('destination_account', :increase =&gt; nil) }

    it "should decrease source account by 10" do
      source_account.should_receive('decrease').with(10)
    
      transfer = Transfer.new(source_account, destination_account, 10)
      transfer.call
    end

    it "should increase destination account by 10" do
      destination_account.should_receive('increase').with(10)
    
      transfer = Transfer.new(source_account, destination_account, 10)
      transfer.call
    end
  end
end
&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Update: Radoslav Stankov&lt;/strong&gt; provides a &lt;a href="https://gist.github.com/1510673"&gt;cleaner refactoring&lt;/a&gt; of the spec.&lt;/p&gt;&lt;p&gt;The implementation of the transfer class may look something like this:&lt;/p&gt;&lt;pre class="brush: ruby;"&gt;class Transfer
  def initialize(source_account, destination_account, amount)
    @source_account      = source_account
    @destination_account = destination_account
    @amount              = amount
  end
  
  def call
    # In a complete implementation, it would make sense to have the following 
    # lines wrapped in a transaction, but I wanted to keep this example simple
    #
    @source_account.decrease(amount)
    @destination_account.increase(amount)
  end
end
&lt;/pre&gt;&lt;p&gt;It's important to remember that double, mock, and stub all return an instance of &lt;tt&gt;RSpec::Mocks::Mock&lt;/tt&gt;. While the concepts of mocking, (i.e. setting a method expectation), and stubbing are "method-level concepts"[1], the use of &lt;tt&gt;mock&lt;/tt&gt; and &lt;tt&gt;stub&lt;/tt&gt; can communicate which collaborator you are specifying the behaviour for.&lt;/p&gt;&lt;p&gt;[1] For a more complete discussion on this, checkout &lt;a href="http://pragprog.com/book/achbd/the-rspec-book"&gt;The RSpec Book.&lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/firsthand-web-design/~4/m0Raq372lQg" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/8404720343931231280?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/8404720343931231280?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/firsthand-web-design/~3/m0Raq372lQg/example-using-rspec-double-mock-and.html" title="An Example using RSpec double, mock, and stub" /><author><name>Nicholas Henry</name><uri>https://plus.google.com/109341983975580064085</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-Ub9vGydp-YQ/AAAAAAAAAAI/AAAAAAAAAAA/ag9kkr9R0AU/s512-c/photo.jpg" /></author><feedburner:origLink>http://blog.firsthand.ca/2011/12/example-using-rspec-double-mock-and.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUAHQno5eCp7ImA9WhBWFUk.&quot;"><id>tag:blogger.com,1999:blog-5548895711100825622.post-1645778298032679902</id><published>2011-12-21T14:17:00.004-05:00</published><updated>2013-04-09T18:48:53.420-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-04-09T18:48:53.420-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="launch" /><title>Recent Launch: Yellow Bird Project Redesign</title><content type="html">Earlier this year, &lt;a href="http://blog.firsthand.ca/2011/10/yellow-bird-project-spree-upgrade.html"&gt;I upgraded the Yellow Bird Project&lt;/a&gt;, a Spree store, from a forked version pre v0.90.0 to v0.50.x. For for my final project of the year, I upgraded Yellow Bird Project to v0.70.x, along with the implementation of a UI redesign. Jenna Holcombe (&lt;a href="http://www.godynamo.com/en"&gt;Dynamo&lt;/a&gt;) did a great job moving the YBP look to a grid-based design, which Daniel Wright (&lt;a href="http://www.godynamo.com/en"&gt;Dynamo&lt;/a&gt;) implemented this in Sass/Compass.&lt;br /&gt;
Since the redesign initiative included an upgrade to the latest version of Spree, I decided to upgrade the existing site and deploy that to production, before launching the redesign. This allowed me to upgrade our custom extensions to work with 0.70.x and iron out any kinks in the new version. While this was a smoother process than the upgrade earlier this year, it was the first time I had deployed to &lt;strong&gt;Heroku's Cedar stack&lt;/strong&gt; using &lt;strong&gt;Rails 3.1&lt;/strong&gt; and the &lt;strong&gt;asset pipeline&lt;/strong&gt;. I'm a big advocate of breaking down a project, separating major changes, and getting changes pushed to production early, even it there is a little duplication in work. The separate pushes to production for the upgrade and the redesign reduced the risk of something really going haywire with pushing both of these at once.&lt;br /&gt;
The redesign benefited the application from a platform, merchandising, and coding perspectives:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Platform&lt;/strong&gt;: While this initiative did not include a mobile version, the new grid-based design will allow us to cleanly develop a responsive design for mobile platforms in the future using media queries. Replacing hacks like Cufon with more modern technologies like web fonts just makes the UI code cleaner and performance snappier.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Merchandising&lt;/strong&gt;: The store now fully utilizes Spree's powerful Taxonomy and Product Group features. This allows YBP to respond to different marketing initiates such as seasonal events, for example &lt;a href="http://www.yellowbirdproject.com/group/warm-winter-wear"&gt;Warm Winter Wear&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Code&lt;/strong&gt;: One of the cool things with this upgrade was the chance to refactor the code base. Shifting from ERB to Haml and plain CSS to Sass/Compass was a definitely a plus, however implementing a data model that capitalized Spree's catalog management was the biggest win. I was able to remove unnecessary extensions which simplified managing the products. YBP's Matthew Stotland is a code-savvy lad and appreciated the clean implementation. It's great to work on a project where the client appreciates the front-end as much as the code that drives it.&lt;/li&gt;
&lt;/ul&gt;You can checkout the new design below, but better yet, &lt;a href="http://www.yellowbirdproject.com/"&gt;visit the Yellow Bird Project and support a great cause&lt;/a&gt;.&lt;br /&gt;
&lt;img alt="01 home" border="0" height="1524" src="http://firsthand-blog.s3.amazonaws.com/2011/11/recent-launch-yellow-bird-project-01-home.png" width="565" /&gt;&lt;br /&gt;
&lt;img alt="Yellowbird Project Product" border="0" height="1250" src="http://firsthand-blog.s3.amazonaws.com/2011/11/recent-launch-yellow-bird-project-02-product.png" title="02 product.png" width="565" /&gt;&lt;img src="http://feeds.feedburner.com/~r/firsthand-web-design/~4/IPBBqHh8wtE" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/1645778298032679902?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/1645778298032679902?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/firsthand-web-design/~3/IPBBqHh8wtE/recent-launch-yellow-bird-project.html" title="Recent Launch: Yellow Bird Project Redesign" /><author><name>Nicholas Henry</name><uri>https://plus.google.com/109341983975580064085</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-Ub9vGydp-YQ/AAAAAAAAAAI/AAAAAAAAAAA/ag9kkr9R0AU/s512-c/photo.jpg" /></author><feedburner:origLink>http://blog.firsthand.ca/2011/12/recent-launch-yellow-bird-project.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D08NQH08eip7ImA9WhRSFUU.&quot;"><id>tag:blogger.com,1999:blog-5548895711100825622.post-2275851752981302817</id><published>2011-11-17T22:08:00.001-05:00</published><updated>2011-11-17T22:11:31.372-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-11-17T22:11:31.372-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="craftmanship" /><title>Software Craftsmanship Goes Beyond Code</title><content type="html">&lt;p&gt;Often the discussion of Software Craftsmanship is focused on code and tests. But don't forget about the other components that support your project:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Make sure your &lt;strong&gt;source control repository is organized&lt;/strong&gt;. Don't leave dead branches lying around, left for another developer to work out if they have been merged or not; &lt;strong&gt;if&lt;/strong&gt; they should be merged or not. Add a reference to your branch with the issue or story id from your tracker so a developer can read more about the intention of the branch.&lt;/li&gt;&lt;li&gt;And &lt;a href="http://confreaks.net/videos/744-rockymtnruby2011-lightning-talk-do-your-commit-messages-suck"&gt;don't write commit messages that suck&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Keep your &lt;strong&gt;tracker up-to-date and lean&lt;/strong&gt;. Don't mark stories as started when they haven't been. In reality, there should only be one story marked as started per developer on the project. Don't drag stories into the backlog just because you think they are a good idea. Keep the backlog lean, you only need a couple of weeks worth of stories to keep you going. Keep the rest in the icebox. Priorities are going to change.&lt;/li&gt;&lt;li&gt;Make sure it's easy for a developer to &lt;strong&gt;get started with your project&lt;/strong&gt;. Have a &lt;a href="http://blog.firsthand.ca/2010/09/24hrs-firsthand-leave-project-how-you.html"&gt;README that guides a developer&lt;/a&gt; to get the dependencies and database setup, and remind them a test suite exists and how to run it. Obviously the more automated things are the better.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Caring needs to go beyond the code, it needs to include all the supporting components of your project. Of course there are other facets of professionalism we can list, but these are some practical details I have experienced recently. What components of a project, beyond code, that you wish developers would give more care to? Let me know in the comments.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/firsthand-web-design/~4/H79lbTSkU0U" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/2275851752981302817?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/2275851752981302817?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/firsthand-web-design/~3/H79lbTSkU0U/software-craftsmanship-goes-beyond-code.html" title="Software Craftsmanship Goes Beyond Code" /><author><name>Nicholas Henry</name><uri>https://plus.google.com/109341983975580064085</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-Ub9vGydp-YQ/AAAAAAAAAAI/AAAAAAAAAAA/ag9kkr9R0AU/s512-c/photo.jpg" /></author><feedburner:origLink>http://blog.firsthand.ca/2011/11/software-craftsmanship-goes-beyond-code.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUACRHY5eyp7ImA9WhBWFUk.&quot;"><id>tag:blogger.com,1999:blog-5548895711100825622.post-9050690263438665825</id><published>2011-11-12T12:47:00.002-05:00</published><updated>2013-04-09T18:49:25.823-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-04-09T18:49:25.823-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="launch" /><title>Recent Launch: Mr. B's Gentlemen's Boutique for ALDO</title><content type="html">&lt;p&gt;Back in September, I had the pleasure working with &lt;a href="http://twitter.com/#!/nemy"&gt;Alex Nemroff&lt;/a&gt; (Interactive Designer) and Daniel Wright (Front-end Developer) at &lt;a href="http://www.godynamo.com/en"&gt;Dynamo&lt;/a&gt; on &lt;a href="http://www.gentlemensboutique.com/"&gt;Mr. B's Gentlemen's Boutique&lt;/a&gt;, a new product line for ALDO Shoes. A tribute to &lt;a href="http://www.gentlemensboutique.com/about-mr-bs/where-we-began"&gt;Aldo Bensadoun&lt;/a&gt;, this project is dear to everybody's hearts at ALDO and I think we have made them proud. I was responsible for the setup and customizations to &lt;a href="http://www.refinerycms.com/"&gt;Refinery CMS&lt;/a&gt;, including the development of an extension to manage the catalog.&lt;/p&gt;&lt;p&gt;I always have a great timing working on projects Alex leads. He always gets us involved in all aspects of the project and the end result really does feel like a team effort. My favourite times throughout the project is sitting down together at the end of an iteration, reviewing what we have done, and what we can do to improve it in the next iteration. And it doesn't hurt to have a power house like Daniel on the front-end. He really knows his stuff.&lt;/p&gt;&lt;p&gt;Below is a video of the post-mortem at Dynamo, where Alex and myself talk about lessons learned throughout project. Unfortunately, we didn't capture the screen when recording this session (whoops, working on that), but I think you will find the discussion insightful. Please checkout the screenshots below, better yet, &lt;a href="http://www.gentlemensboutique.com/"&gt;visit the site and checkout some beautiful shoes&lt;/a&gt;.&lt;/p&gt;&lt;h2&gt;Post-Mortem at Dynamo&lt;/h2&gt;&lt;iframe src="http://player.vimeo.com/video/30757894?title=0&amp;amp;byline=0&amp;amp;portrait=0"  frameborder="0" width="565" height="343" webkitAllowFullScreen allowFullScreen&gt;&lt;/iframe&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;h2&gt;Screenshots&lt;/h2&gt;&lt;p&gt;&lt;img title="01 home page.png" src="http://firsthand-blog.s3.amazonaws.com/2011/11/recent-launch-mr-b-gentlemen-boutique-01-home-page.png" border="0" alt="Home Page" width="565" height="803" /&gt;&lt;/p&gt;&lt;p&gt;&lt;img title="04 product detail.png" src="http://firsthand-blog.s3.amazonaws.com/2011/11/recent-launch-mr-b-gentlemen-boutique-04-product-detail.png" border="0" alt="Product Detail" width="565" height="1073" /&gt;&lt;/p&gt;&lt;p&gt; &lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/firsthand-web-design/~4/k7xBFQyx4V4" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/9050690263438665825?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/9050690263438665825?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/firsthand-web-design/~3/k7xBFQyx4V4/recent-launch-mr-b-gentlemen-boutique.html" title="Recent Launch: Mr. B&amp;#39;s Gentlemen&amp;#39;s Boutique for ALDO" /><author><name>Nicholas Henry</name><uri>https://plus.google.com/109341983975580064085</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-Ub9vGydp-YQ/AAAAAAAAAAI/AAAAAAAAAAA/ag9kkr9R0AU/s512-c/photo.jpg" /></author><feedburner:origLink>http://blog.firsthand.ca/2011/11/recent-launch-mr-b-gentlemen-boutique.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUQASH84eip7ImA9WhdaE0U.&quot;"><id>tag:blogger.com,1999:blog-5548895711100825622.post-1182285786018927313</id><published>2011-10-22T16:59:00.001-04:00</published><updated>2011-10-23T12:29:09.132-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-10-23T12:29:09.132-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="domain-modelling" /><category scheme="http://www.blogger.com/atom/ns#" term="application-architecture" /><category scheme="http://www.blogger.com/atom/ns#" term="rails" /><title>Rails is Not Your Application</title><content type="html">&lt;h2&gt;Background: The Beginnings of My Obsession with Application Architecture and Domain Modelling&lt;/h2&gt;&lt;p&gt;Last year I was involved in my most complex Ruby on Rails application to date that is now 23K lines of model code, 14K lines of controller code, and 4.5K lines of lib code. You get the point, let's now worry about the view code. It was during this project that I observed the pain of working within the bounds of how we typically build Rails applications. Did I mention it was slow to develop and test? During that contract and ever since, I have been &lt;strong&gt;obsessing about &lt;strong&gt;application architecture and &lt;/strong&gt;domain modelling&lt;/strong&gt;.&lt;/p&gt;&lt;h2&gt;Establish an Application Interface with a Service Layer&lt;/h2&gt;&lt;p&gt;During the time I was working on said application, I introduced the concept of &lt;strong&gt;Services&lt;/strong&gt; (in the early stages I called them managers). Although a lot of domain logic was pushed into the models, there was too much application logic in the controllers. Services, co-ordinating domain models, allowed me to extract that application logic to really keep the controllers thin. I'm not sure exactly sure what prompted me to start using Service objects, but after the fact I found &lt;a href="http://martinfowler.com/eaaCatalog/serviceLayer.html"&gt;references&lt;/a&gt; to them in Martin Fowler's &lt;a href="http://www.amazon.com/Patterns-Enterprise-Application-Architecture-Martin/dp/0321127420"&gt;Patterns of Enterprise Application Architecture&lt;/a&gt; (P of EAA). Other frameworks such as &lt;a href="http://www.grails.org/"&gt;Grails&lt;/a&gt; also use &lt;a href="http://www.grails.org/doc/1.3.x/guide/8.%20The%20Service%20Layer.html"&gt;Services&lt;/a&gt;, it even has a generator for them. Services resolves the &lt;a href="http://david.heinemeierhansson.com/posts/43-think-of-emails-as-views-delivered-through-smtp"&gt;discussion&lt;/a&gt; (&lt;a href="http://www.robbyonrails.com/articles/2009/11/16/sending-email-controllers-versus-models"&gt;started here&lt;/a&gt;) on where to trigger email notifications. Add them to your Services.&lt;/p&gt;&lt;p&gt;In P of EAA, Randy Stafford defines the Service Layer as:&lt;/p&gt;&lt;p&gt;&lt;em&gt;"Defines an application's boundary with a layer of services that establishes a set of available operations and coordinates the application's response in each operation."&lt;/em&gt;&lt;/p&gt;&lt;p&gt;And in a way, this definition helped me recognize another frustration I was having with how I (and most of the Rails community) was developing with Rails. &lt;strong&gt;There was no explicit application&lt;/strong&gt;. Or rather, it was hard to define what the application was and hard to know how to interact with it. The application had no interface. My application was a ball of ActiveRecord objects.&lt;/p&gt;&lt;h2&gt;Lots of Activity in the Rails Community Regarding Domain Modelling&lt;/h2&gt;&lt;p&gt;I haven't been the only one thinking about application architecture and domain modelling. I'm not a unique snowflake. Others in our community have too: Mike Dietz from ThoughtWorks introduce me to&lt;a href="http://en.oreilly.com/rails2011/public/schedule/detail/19424"&gt; DCI at RailsConf 2011&lt;/a&gt;, &lt;a href="http://andrzejonsoftware.blogspot.com/2011/02/dci-and-rails.html"&gt;Andrzej Krzywda has been blogging&lt;/a&gt; about it before that, and &lt;a href="http://www.saturnflyer.com/blog/jim/2011/10/04/oop-dci-and-ruby-what-your-system-is-vs-what-your-system-does/"&gt;Jim Gay has recently been blogging&lt;/a&gt; about it on this side of the pond. On another tact, &lt;a href="http://confreaks.net/videos/641-gogaruco2011-fast-rails-tests"&gt;Corey Haines has been been selling fast tests&lt;/a&gt; over the last few months. Hidden in that sales pitch of course, is a way to extract domain logic. Talking of extracting domain logic, &lt;a href="http://blog.steveklabnik.com/2011/09/22/extracting-domain-models-a-practical-example.html"&gt;Steve Klabnick has illustrated some practical examples&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;These guys have kept me busy! I've been quietly researching this topic for over a year now. You can check out some of my tags on Delicious:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://delicious.com/nicholas.henry/dci"&gt;DCI&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://delicious.com/nicholas.henry/domain-driven-design"&gt;domain-driven-design&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://delicious.com/nicholas.henry/design-patterns"&gt;design-patterns&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://delicious.com/nicholas.henry/software-construction"&gt;software-construction&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;Extracting the Application from the Framework&lt;/h2&gt;&lt;p&gt;Ok, time to finish up this post. All of the efforts describe above have been with the purpose of managing the application and domain logic in the software we build. It benefits us with faster tests and code that clearly communicates it's intent. And although the Service Layer was helping me define an interface to my application, I was only using them when my controllers methods/actions were getting heavy with application logic. Then last night, I watched the &lt;a href="http://www.cleancoders.com/codecast/clean-code-episode-7/show"&gt;latest release of Clean Code&lt;/a&gt; from Uncle Bob that highlights the need to extract your application from the framework you use (he's been &lt;a href="http://cleancoder.posterous.com/architecture-deference"&gt;teasing me&lt;/a&gt; for a while on this topic). With my previous experience with Services, and my interest in DCI, Uncle Bob's guidance was the piece I was missing.&lt;/p&gt;&lt;p&gt;After taking his example pseudo code, and turning it into some Ruby/Rails example (more like snippet) I gleefully &lt;a href="http://twitter.com/#!/nicholasjhenry/status/127779236487507968"&gt;tweeted&lt;/a&gt;:&lt;/p&gt;&lt;p&gt;&lt;em&gt;"Rails is not your application. It might be your views and data source, but it's not your application. Put your app in a Gem or under lib/."&lt;/em&gt;&lt;/p&gt;&lt;p&gt;I really like this approach a lot, the ability to test your application outside of the Rails stack is huge, and viewing Rails as a method to deliver my application will change the way I write code. Anyway here is &lt;a href="https://gist.github.com/1305650"&gt;the code that triggered the Tweet&lt;/a&gt; (see below). &lt;strong&gt;It's not polished, it doesn't execute, it's not 100% right, but boy it's got me thinking&lt;/strong&gt;. It uses Services to represent Use Cases of the application, and roles containing domain logic that is mixed in contextually, extracted into a Gem, delivered via Rails. If you not a fan of the Gem idea, keep it name spaced under your lib directory.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; A clarification on putting your application in lib. I certainly don't mean to put it directly in lib. Name space it, lib/your_application; e.g. &lt;em&gt;lib/pay_roll&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;Now that I have let the code out of the bag, expect some more posts on this topic in the future where I work through and describe this code (and related ideas) in more detail.&lt;/p&gt;&lt;script src="https://gist.github.com/1305650.js?file=your_application.rb"&gt;&lt;/script&gt;&lt;img src="http://feeds.feedburner.com/~r/firsthand-web-design/~4/BosYM9Mpops" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/1182285786018927313?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/1182285786018927313?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/firsthand-web-design/~3/BosYM9Mpops/rails-is-not-your-application.html" title="Rails is Not Your Application" /><author><name>Nicholas Henry</name><uri>https://plus.google.com/109341983975580064085</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-Ub9vGydp-YQ/AAAAAAAAAAI/AAAAAAAAAAA/ag9kkr9R0AU/s512-c/photo.jpg" /></author><feedburner:origLink>http://blog.firsthand.ca/2011/10/rails-is-not-your-application.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0INQXo6eip7ImA9WhdaEk8.&quot;"><id>tag:blogger.com,1999:blog-5548895711100825622.post-5151299628827067073</id><published>2011-10-17T15:26:00.001-04:00</published><updated>2011-10-21T16:39:50.412-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-10-21T16:39:50.412-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="checklists" /><category scheme="http://www.blogger.com/atom/ns#" term="optimization" /><category scheme="http://www.blogger.com/atom/ns#" term="memory-consumption" /><title>References and tools for Memory Consumption in Rails</title><content type="html">&lt;p&gt;It's not often that I need these, but when there's a Memory Consumption issue in a Rails application, these references and tools definitely come in handy:&lt;/p&gt;&lt;h2&gt;Rails 3 References and Tools&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://guides.rubyonrails.org/performance_testing.html"&gt;Ruby on Rails Guides: Performance Testing Rails Applications&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.scribd.com/doc/23548865/Debugging-Ruby"&gt;Debugging Ruby&lt;/a&gt; (Aman Gupta presentation at Rails Conf 2011)&lt;/li&gt;&lt;li&gt;&lt;a href="https://github.com/bhb/rack-perftools_profiler"&gt;Rack-Perftools Profiler&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://github.com/brynary/rack-bug/tree/rails3"&gt;Rack-Bug&lt;/a&gt; (Rails 3 branch)&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;Some Older References and Tools&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.engineyard.com/blog/2009/thats-not-a-memory-leak-its-bloat/"&gt;That's not a Memory Leak, it's Bloat&lt;/a&gt; (Engine Yard Blog Post)&lt;/li&gt;&lt;li&gt;&lt;a href="http://blog.evanweaver.com/articles/2007/04/28/bleak_house/"&gt;Bleak House&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://railslab.newrelic.com/2009/10/23/episode-20-on-the-edge-part-2"&gt;Episode #20 - On The Edge - Part 2&lt;/a&gt;\&lt;/li&gt;&lt;li&gt;&lt;a href="http://github.com/binarylogic/memorylogic"&gt;MemoryLogic&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://github.com/noahd1/oink/"&gt;Oink&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://github.com/brynary/rack-bug/"&gt;Rack-Bug&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;img src="http://feeds.feedburner.com/~r/firsthand-web-design/~4/_lfXkLE1Pd8" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/5151299628827067073?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/5151299628827067073?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/firsthand-web-design/~3/_lfXkLE1Pd8/references-and-tools-for-memory.html" title="References and tools for Memory Consumption in Rails" /><author><name>Nicholas Henry</name><uri>https://plus.google.com/109341983975580064085</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-Ub9vGydp-YQ/AAAAAAAAAAI/AAAAAAAAAAA/ag9kkr9R0AU/s512-c/photo.jpg" /></author><feedburner:origLink>http://blog.firsthand.ca/2011/10/references-and-tools-for-memory.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUEBQHY8cSp7ImA9WhBWFUk.&quot;"><id>tag:blogger.com,1999:blog-5548895711100825622.post-4642088835726132169</id><published>2011-10-15T12:06:00.001-04:00</published><updated>2013-04-09T18:47:31.879-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-04-09T18:47:31.879-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="launch" /><category scheme="http://www.blogger.com/atom/ns#" term="content management systems" /><title>Recent Launch: Media Experts on Refinery CMS</title><content type="html">&lt;p&gt;&lt;a href="http://www.mediaexperts.com"&gt;Media Experts&lt;/a&gt; is a top Canadian media planning and buying agency. During the summer, I was tasked with the first iteration of their website, integrating it with &lt;a href="http://www.refinerycms.com/"&gt;Refinery CMS&lt;/a&gt; (thanks &lt;a href="http://www.godynamo.com/en"&gt;Dynamo&lt;/a&gt;!). One of the joys of using Refinery, is the ease of extending it with engines to support custom content types. For example the experts page seen below contained structured, repetitive data that was not suited for a page tree hierarchy:&lt;/p&gt;&lt;p&gt;&lt;img title="media-experts-people.jpg" src="http://firsthand-blog.s3.amazonaws.com/2011/10/recent-launch-media-experts-people.jpg" border="0" alt="Media Experts - People" width="565" height="580" /&gt;&lt;/p&gt;&lt;p&gt;By generating a &lt;a href="http://www.refinerycms.com/guides/creating-multiple-data-tables-in-a-single-engine"&gt;custom engine&lt;/a&gt;, I was able to easily integrate this structured data into the website, without any impact on the original design or the website's navigation. You can view how the individual experts are edited in the screen shot below:&lt;/p&gt;&lt;p&gt;&lt;img title="refinery-cms.jpg" src="http://firsthand-blog.s3.amazonaws.com/2011/10/recent-launch-media-experts-refinery-cms.jpg" border="0" alt="RefineryCMS" width="565" height="576" /&gt;&lt;/p&gt;&lt;p&gt;Please &lt;a href="http://www.mediaexperts.com"&gt;checkout the Media Experts' website&lt;/a&gt; and view additional screenshots below:&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Home Page&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;img title="media-experts-home.jpg" src="http://firsthand-blog.s3.amazonaws.com/2011/10/recent-launch-media-experts-home.jpg" border="0" alt="Media Experts Home" width="565" height="457" /&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Portfolio Index&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;img title="media-experts-portfolio.jpg" src="http://firsthand-blog.s3.amazonaws.com/2011/10/recent-launch-media-experts-portfolio.jpg" border="0" alt="Media Experts Portfolio" width="565" height="523" /&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/firsthand-web-design/~4/cQXmF2uNAXI" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/4642088835726132169?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/4642088835726132169?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/firsthand-web-design/~3/cQXmF2uNAXI/recent-launch-media-experts.html" title="Recent Launch: Media Experts on Refinery CMS" /><author><name>Nicholas Henry</name><uri>https://plus.google.com/109341983975580064085</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-Ub9vGydp-YQ/AAAAAAAAAAI/AAAAAAAAAAA/ag9kkr9R0AU/s512-c/photo.jpg" /></author><feedburner:origLink>http://blog.firsthand.ca/2011/10/recent-launch-media-experts.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DU8NQnkyeyp7ImA9WhBWFUk.&quot;"><id>tag:blogger.com,1999:blog-5548895711100825622.post-8668477637077644206</id><published>2011-10-09T16:28:00.002-04:00</published><updated>2013-04-09T18:51:33.793-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-04-09T18:51:33.793-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="launch" /><category scheme="http://www.blogger.com/atom/ns#" term="spree" /><title>Yellow Bird Project Spree Upgrade</title><content type="html">&lt;p&gt;During the spring, &lt;a href="http://www.godynamo.com/en"&gt;Dynamo&lt;/a&gt; asked me to upgrade the &lt;a href="http://www.yellowbirdproject.com/"&gt;Yellow Bird Project&lt;/a&gt; to the latest version of &lt;a href="http://spreecommerce.com/"&gt;Spree&lt;/a&gt; (at the time this was v0.50.2). The challenge for this project was managing the upgrade from a forked version of Spree, pre v0.9.0 back in 2009. One tool that was really useful was using Max OS X's FileMerge to compare database schemas, allowing me to determine what the new schema would and determine any naming conflicts with custom tables added previously.&lt;/p&gt;&lt;p&gt;&lt;img title="filemerge schema.png" src="http://firsthand-blog.s3.amazonaws.com/2011/10/yellow-bird-project-spree-upgrade-filemerge-schema.png" border="0" alt="Filemerge schema" width="565" height="379" /&gt;&lt;/p&gt;&lt;p&gt;The upgrade resulted in an application that can now be &lt;strong&gt;easily upgraded to future version of Spree&lt;/strong&gt; and runs on a &lt;strong&gt;more stable, scalable platform&lt;/strong&gt; thanks to &lt;a href="http://www.heroku.com/"&gt;Heroku&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;The Yellow Bird Project is pretty cool - they work with a range of indie rock musicians to create some neat t-shirts that then benefit a charity chosen by the musicians themselves. So put away that ugly university hoodie and &lt;a href="http://www.yellowbirdproject.com/"&gt;go get yourself some cool threads&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;img title="yellow_bird_project_home.png" src="http://firsthand-blog.s3.amazonaws.com/2011/10/yellow-bird-project-spree-upgrade-home.png" border="0" alt="Yellow bird project home" width="565" height="1272" /&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/firsthand-web-design/~4/um39OW46Ads" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/8668477637077644206?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/8668477637077644206?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/firsthand-web-design/~3/um39OW46Ads/yellow-bird-project-spree-upgrade.html" title="Yellow Bird Project Spree Upgrade" /><author><name>Nicholas Henry</name><uri>https://plus.google.com/109341983975580064085</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-Ub9vGydp-YQ/AAAAAAAAAAI/AAAAAAAAAAA/ag9kkr9R0AU/s512-c/photo.jpg" /></author><feedburner:origLink>http://blog.firsthand.ca/2011/10/yellow-bird-project-spree-upgrade.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkcBQn8-fCp7ImA9WhBWFUk.&quot;"><id>tag:blogger.com,1999:blog-5548895711100825622.post-3854264285756839106</id><published>2011-10-01T12:01:00.002-04:00</published><updated>2013-04-09T18:54:13.154-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-04-09T18:54:13.154-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="refinery" /><category scheme="http://www.blogger.com/atom/ns#" term="launch" /><title>Recent Launch: de Gaspé Beaubien Foundation</title><content type="html">&lt;p&gt;OK, hardly a recent launch, de Gaspé Beaubien Foundation was actually launched in April, but I'm a wee bit behind in posting some of the neat projects I have worked on this year. I'm determine to get this resolved over the next couple of weeks.&lt;/p&gt;&lt;p&gt;de Gaspé Beaubien Foundation's website provides a platform to communicate the families contribution to their communities and the broader society. I worked with the great team at &lt;a href="http://www.godynamo.com/en"&gt;Dynamo&lt;/a&gt; on this one: Bob Beck, creative director was responsible for brand identity and video direction, Jenna Holcombe site design, and I was responsible for &lt;strong&gt;front-end development and CMS integration&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;This is the first project I have used &lt;a href="http://www.refinerycms.com/"&gt;RefineryCMS&lt;/a&gt;, a Ruby on Rails-based Content Management System. While I have been a big fan of &lt;a href="http://radiantcms.org/"&gt;Radiant CMS&lt;/a&gt;, I like RefineryCMS for it's polish integrated interface. It worked great for this project, but I would use Radiant when a project has more complex requirements. The front-end was developed using HTML5 and CSS3. CSS was generated using &lt;a href="http://compass-style.org/"&gt;Compass&lt;/a&gt; and &lt;a href="http://sass-lang.com/"&gt;Sass&lt;/a&gt;. &lt;a href="http://compass-style.org/reference/compass/css3/"&gt;Compass' CSS3 helpers&lt;/a&gt; are a real help abstracting away the mess of vendor extensions, and the &lt;a href="http://css3pie.com/"&gt;CSS3 PIE&lt;/a&gt; integration giving older version of IE CSS3 capabilities is invaluable. All video is presented using HTML5, with a Flash fallback, all handled seamlessly by JW Player.&lt;/p&gt;&lt;p&gt;Check out the screenshots below and &lt;a href="http://www.degaspebeaubienfoundation.org/"&gt;please visit the site to find out the great work de Gaspé Beaubien Foundation&lt;/a&gt; is doing.&lt;/p&gt;&lt;p&gt;&lt;img title="01 gasbeau home.png" src="http://firsthand-blog.s3.amazonaws.com/2011/10/recent-launch-de-gaspe-beaubien-01-home.png" border="0" alt="de Gaspé Beaubien Foundation" width="565" height="664" /&gt;&lt;/p&gt;&lt;p&gt;&lt;img title="03 gasbeau legacy.png" src="http://firsthand-blog.s3.amazonaws.com/2011/10/recent-launch-de-gaspe-beaubien-03-legacy.png" border="0" alt="de Gaspé Beaubien Foundation" width="565" height="701" /&gt;&lt;/p&gt;&lt;p&gt;&lt;img title="07 gasbeau secondary.png" src="http://firsthand-blog.s3.amazonaws.com/2011/10/recent-launch-de-gaspe-beaubien-07-secondary.png" border="0" alt="de Gaspé Beaubien Foundation" width="565" height="1170" /&gt;&lt;/p&gt;&lt;p&gt;&lt;img title="09 gasbeau news.png" src="http://firsthand-blog.s3.amazonaws.com/2011/10/recent-launch-de-gaspe-beaubien-09-news.png" border="0" alt="de Gaspé Beaubien Foundation" width="565" height="1060" /&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/firsthand-web-design/~4/gX88Na6XfTk" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/3854264285756839106?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/3854264285756839106?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/firsthand-web-design/~3/gX88Na6XfTk/recent-launch-de-gaspe-beaubien.html" title="Recent Launch: de Gaspé Beaubien Foundation" /><author><name>Nicholas Henry</name><uri>https://plus.google.com/109341983975580064085</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-Ub9vGydp-YQ/AAAAAAAAAAI/AAAAAAAAAAA/ag9kkr9R0AU/s512-c/photo.jpg" /></author><feedburner:origLink>http://blog.firsthand.ca/2011/10/recent-launch-de-gaspe-beaubien.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0YNRXY5eSp7ImA9WhdVEkU.&quot;"><id>tag:blogger.com,1999:blog-5548895711100825622.post-3796076382135683812</id><published>2011-08-06T16:32:00.007-04:00</published><updated>2011-09-17T13:39:54.821-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-17T13:39:54.821-04:00</app:edited><title>My Ruby on Rails Development Environment</title><content type="html">&lt;p&gt;&lt;a href="http://img.skitch.com/20110806-q4um7dtn72d1ui4ky684tsc41h.jpg"&gt;&lt;img style="max-width:565px" src="http://img.skitch.com/20110806-q4um7dtn72d1ui4ky684tsc41h.medium.jpg" alt="My Development Environment" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click image to enlarge&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/firsthand-web-design/~4/4A4g_yusT1U" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/3796076382135683812?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/3796076382135683812?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/firsthand-web-design/~3/4A4g_yusT1U/my-ruby-on-rails-development.html" title="My Ruby on Rails Development Environment" /><author><name>Nicholas Henry</name><uri>https://plus.google.com/109341983975580064085</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-Ub9vGydp-YQ/AAAAAAAAAAI/AAAAAAAAAAA/ag9kkr9R0AU/s512-c/photo.jpg" /></author><feedburner:origLink>http://blog.firsthand.ca/2011/08/my-ruby-on-rails-development.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkcGRno8eip7ImA9WhBWFUs.&quot;"><id>tag:blogger.com,1999:blog-5548895711100825622.post-1893656354873911212</id><published>2011-07-30T10:19:00.001-04:00</published><updated>2013-04-09T22:13:47.472-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-04-09T22:13:47.472-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="rspec" /><category scheme="http://www.blogger.com/atom/ns#" term="rails" /><category scheme="http://www.blogger.com/atom/ns#" term="bourne" /><category scheme="http://www.blogger.com/atom/ns#" term="mocha" /><title>Gotcha with testing expected params with Bourne/Mocha in Rails Controller</title><content type="html">&lt;p&gt;This is something that always trips me up using &lt;a href="https://github.com/thoughtbot/bourne"&gt;Bourne&lt;/a&gt; (a fork of &lt;a href="http://mocha.rubyforge.org/"&gt;Mocha&lt;/a&gt;, a Ruby mocking and stubbing library) to test expected params in a Rails Controller. Take this spec:&lt;/p&gt;&lt;pre class="brush: ruby;"&gt;context "with filter" do
  before do
    get :index, :search =&gt; {:brand_id =&gt; '1'}
  end

  it 'should find latest search results' do
    SearchResult.should have_received(:latest).with('brand_id' =&gt; '1')
  end
end
&lt;/pre&gt;&lt;p&gt;This test fails, but what if I tried this:&lt;/p&gt;&lt;pre name="code" class="ruby"&gt;context "with filter" do
  before do
    get :index, :search =&gt; {:brand_id =&gt; '1'}
  end

  it 'should find latest search results' do
    SearchResult.should have_received(:latest).with(:brand_id =&gt; '1')
  end
end
&lt;/pre&gt;&lt;p&gt;This will fail. Note I am now checking the parameters passed using symbols for the keys. I receive the following error:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;
Failure/Error: SearchResult.should have_received(:latest).with(has_entry(:brand_id =&gt; '1'))
     expected exactly once, not yet invoked: SearchResult(id: integer, search_id: integer, body: text, source: text, url: string, created_at: datetime, updated_at: datetime).latest(has_entry(:brand_id =&gt; '1'))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is because the params that are passed to the controller have keys as strings rather than symbols:&lt;/p&gt;&lt;pre class="brush: ruby;"&gt;{'brand_id' =&gt; '1'}
&lt;/pre&gt;&lt;p&gt;So what's the solution? I could actually use &lt;tt&gt;HashWithIndifferentAccess&lt;/tt&gt; which gives access to a Hash using strings or symbols for keys. This is in fact the class that is used for the params hash in the controller:&lt;/p&gt;&lt;pre name="code" class="ruby"&gt;it 'should find latest search results' do
    SearchResult.should have_received(:latest).with(HashWithIndifferentAccess.new(:brand_id =&gt; '1'))
  end
&lt;/pre&gt;&lt;p&gt;Or, as a standard practice, when dealing with parameters passed to a controller within a spec, always use strings:&lt;/p&gt;&lt;pre class="brush: ruby;"&gt;context "with filter" do
  before do
    get :index, 'search' =&gt; {'brand_id' =&gt; '1'}
  end

  it 'should find latest search results' do
    SearchResult.should have_received(:latest).with('brand_id' =&gt; '1')
  end
end
&lt;/pre&gt;&lt;p&gt;I think this is a lot simpler, cleaner. Interestingly, I didn't pass the &lt;tt&gt;brand_id&lt;/tt&gt; as a &lt;tt&gt;Fixnum&lt;/tt&gt;:&lt;/p&gt;&lt;pre name="code" class="ruby"&gt;before do
    get :index, 'search' =&gt; {'brand_id' =&gt; 1}
  end
&lt;/pre&gt;&lt;p&gt;I would have got the same error:&lt;/p&gt;&lt;pre class="brush: plain;"&gt;Failure/Error: SearchResult.should have_received(:latest).with('brand_id' =&gt; 1)
     expected exactly once, not yet invoked: SearchResult(id: integer, search_id: integer, body: text, source: text, url: string, created_at: datetime, updated_at: datetime).latest('brand_id' =&gt; 1)
&lt;/pre&gt;&lt;p&gt;As the &lt;tt&gt;brand_id&lt;/tt&gt; is present in the hash as a &lt;tt&gt;String&lt;/tt&gt;, not a &lt;tt&gt;Fixnum&lt;/tt&gt;.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/firsthand-web-design/~4/IzfA9G4U7RM" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/1893656354873911212?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/1893656354873911212?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/firsthand-web-design/~3/IzfA9G4U7RM/gotcha-with-testing-expected-params.html" title="Gotcha with testing expected params with Bourne/Mocha in Rails Controller" /><author><name>Nicholas Henry</name><uri>https://plus.google.com/109341983975580064085</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-Ub9vGydp-YQ/AAAAAAAAAAI/AAAAAAAAAAA/ag9kkr9R0AU/s512-c/photo.jpg" /></author><feedburner:origLink>http://blog.firsthand.ca/2011/07/gotcha-with-testing-expected-params.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkQASXo9cCp7ImA9WhBWFUs.&quot;"><id>tag:blogger.com,1999:blog-5548895711100825622.post-6040305396482305984</id><published>2011-07-30T09:22:00.002-04:00</published><updated>2013-04-09T22:19:08.468-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-04-09T22:19:08.468-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="ruby" /><category scheme="http://www.blogger.com/atom/ns#" term="rvm" /><title>Upgrade RVM and Ruby 1.9.2p290</title><content type="html">&lt;p&gt;I've been a little lazy and haven't updated my RVM installation (version 1.1.3, now 1.6.31) for a while and have been stuck on Ruby 1.9.2p0. This morning I upgraded, here's what I did:&lt;/p&gt;&lt;pre class="brush: shell"&gt;$ rvm get head
$ rvm reload
$ rvm install 1.9.2-p290
$ rvm upgrade 1.9.2-p0 1.9.2-p290
&lt;/pre&gt;&lt;p&gt;Hmm… Got some errors, here's the output:&lt;/p&gt;&lt;pre class="brush: plain"&gt;Are you sure you wish to upgrade from ruby-1.9.2-p0 to     ruby-1.9.2-p290? (Y/n): y
Migrating gems from ruby-1.9.2-p0 to ruby-1.9.2-p290
Are you sure you wish to MOVE gems from ruby-1.9.2-p0 to ruby-1.9.2-p290?
This will overwrite existing gems in ruby-1.9.2-p290 and remove them from ruby-1.9.2-p0 (Y/n): y
Moving gemsets...
Moving ruby-1.9.2-p0 to ruby-1.9.2-p290
Making gemset ruby-1.9.2-p290 pristine.
ERROR: Error running 'rvm gemset pristine' under ,
please read /Volumes/Data/Users/nicholas/.rvm/log//gemset.pristine.log
Moving ruby-1.9.2-p0@brandizzle to ruby-1.9.2-p290@brandizzle
Making gemset ruby-1.9.2-p290@brandizzle pristine.
ERROR: Error running 'rvm gemset pristine' under ,
please read /Volumes/Data/Users/nicholas/.rvm/log//gemset.pristine.log
Moving ruby-1.9.2-p0@default to ruby-1.9.2-p290@default
Making gemset ruby-1.9.2-p290@default pristine.
ERROR: Error running 'rvm gemset pristine' under ,
please read /Volumes/Data/Users/nicholas/.rvm/log//gemset.pristine.log
Moving ruby-1.9.2-p0@global to ruby-1.9.2-p290@global
Making gemset ruby-1.9.2-p290@global pristine.
ERROR: Error running 'rvm gemset pristine' under ,
please read /Volumes/Data/Users/nicholas/.rvm/log//gemset.pristine.log
Moving ruby-1.9.2-p0@peters-guide to ruby-1.9.2-p290@peters-guide
Making gemset ruby-1.9.2-p290@peters-guide pristine.
ERROR: Error running 'rvm gemset pristine' under ,
please read /Volumes/Data/Users/nicholas/.rvm/log//gemset.pristine.log
Moving ruby-1.9.2-p0@refinery to ruby-1.9.2-p290@refinery
Making gemset ruby-1.9.2-p290@refinery pristine.
ERROR: Error running 'rvm gemset pristine' under ,
please read /Volumes/Data/Users/nicholas/.rvm/log//gemset.pristine.log
Moving ruby-1.9.2-p0@shoedazzle to ruby-1.9.2-p290@shoedazzle
Making gemset ruby-1.9.2-p290@shoedazzle pristine.
ERROR: Error running 'rvm gemset pristine' under ,
please read /Volumes/Data/Users/nicholas/.rvm/log//gemset.pristine.log
Moving ruby-1.9.2-p0@spree to ruby-1.9.2-p290@spree
Making gemset ruby-1.9.2-p290@spree pristine.
ERROR: Error running 'rvm gemset pristine' under ,
please read /Volumes/Data/Users/nicholas/.rvm/log//gemset.pristine.log
Moving ruby-1.9.2-p0@sproutcore to ruby-1.9.2-p290@sproutcore
Making gemset ruby-1.9.2-p290@sproutcore pristine.
ERROR: Error running 'rvm gemset pristine' under ,
please read /Volumes/Data/Users/nicholas/.rvm/log//gemset.pristine.log
Moving ruby-1.9.2-p0@storeworks to ruby-1.9.2-p290@storeworks
Making gemset ruby-1.9.2-p290@storeworks pristine.
ERROR: Error running 'rvm gemset pristine' under ,
please read /Volumes/Data/Users/nicholas/.rvm/log//gemset.pristine.log
Do you wish to move over aliases? (Y/n): y
Updating alias default to point to ruby-1.9.2-p290@default
Do you wish to move over wrappers? (Y/n): Y
Do you also wish to completely remove ruby-1.9.2-p0 (inc. archive)? (Y/n): Y
ERROR: Error running 'rvm remove ruby-1.9.2-p0 --archive --gems' under ,
please read /Volumes/Data/Users/nicholas/.rvm/log//rvm.remove.log
Successfully migrated ruby-1.9.2-p0 to ruby-1.9.2-p290
Upgrade complete!
&lt;/pre&gt;&lt;p&gt;Note that I opted to move over aliases, wrappers and completely remove my old ruby-1.9.2-p0.&lt;/p&gt;&lt;p&gt;Looking at the gemset.pristine.log I had the following errors for each gemset:&lt;/p&gt;&lt;pre name="code" class="shell"&gt;[2011-07-30 09:08:04] rvm gemset pristine # under ruby-1.9.2-p290@brandizzle
/Volumes/Data/Users/nicholas/.rvm/scripts/functions/environment: line 235: rvm: command not found
&lt;/pre&gt;&lt;p&gt;So for each gemset I executed the following:&lt;/p&gt;&lt;pre name="code" class="shell"&gt;rvm gemset pristine 
&lt;/pre&gt;&lt;p&gt;This seemed to work fine. Next, I checked out the errors in rvm.remove.log:&lt;/p&gt;&lt;pre name="code" class="shell"&gt;[2011-07-30 09:08:32] rvm remove ruby-1.9.2-p0 --archive --gems # under ruby-1.9.2-p0
/Volumes/Data/Users/nicholas/.rvm/scripts/functions/environment: line 235: rvm: command not found
&lt;/pre&gt;&lt;p&gt;Looks like the remove of my old 1.9.2 install failed, so I ran it manually:&lt;/p&gt;&lt;pre name="code" class="shell"&gt;rvm remove ruby-1.9.2-p0 --archive --gems 
&lt;/pre&gt;&lt;p&gt;Boom! Everything is updated and working.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/firsthand-web-design/~4/XmQ-uFy9ylM" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/6040305396482305984?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/6040305396482305984?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/firsthand-web-design/~3/XmQ-uFy9ylM/upgrade-rvm-and-ruby-192p290.html" title="Upgrade RVM and Ruby 1.9.2p290" /><author><name>Nicholas Henry</name><uri>https://plus.google.com/109341983975580064085</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-Ub9vGydp-YQ/AAAAAAAAAAI/AAAAAAAAAAA/ag9kkr9R0AU/s512-c/photo.jpg" /></author><feedburner:origLink>http://blog.firsthand.ca/2011/07/upgrade-rvm-and-ruby-192p290.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUEHQH47eSp7ImA9WhdbFkw.&quot;"><id>tag:blogger.com,1999:blog-5548895711100825622.post-6511919574940549035</id><published>2011-07-28T17:00:00.002-04:00</published><updated>2011-10-14T14:40:31.001-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-10-14T14:40:31.001-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="checklists" /><category scheme="http://www.blogger.com/atom/ns#" term="heroku" /><title>Basic Heroku addons I always use for production websites</title><content type="html">&lt;pre&gt;&lt;code&gt;
heroku addons

cron:daily
custom_domains:basic
logging:expanded
memcache:5mb
newrelic:standard
pgbackups:auto-month
sendgrid:basic
shared-database:5mb
&lt;/code&gt;&lt;/pre&gt;&lt;img src="http://feeds.feedburner.com/~r/firsthand-web-design/~4/90M9lNIYHz4" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/6511919574940549035?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/6511919574940549035?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/firsthand-web-design/~3/90M9lNIYHz4/basic-heroku-addons-i-always-use-for.html" title="Basic Heroku addons I always use for production websites" /><author><name>Nicholas Henry</name><uri>https://plus.google.com/109341983975580064085</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-Ub9vGydp-YQ/AAAAAAAAAAI/AAAAAAAAAAA/ag9kkr9R0AU/s512-c/photo.jpg" /></author><feedburner:origLink>http://blog.firsthand.ca/2011/07/basic-heroku-addons-i-always-use-for.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkMFQH0_eip7ImA9WhBWFUs.&quot;"><id>tag:blogger.com,1999:blog-5548895711100825622.post-3409924723617327598</id><published>2011-07-26T15:05:00.001-04:00</published><updated>2013-04-09T22:20:11.342-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-04-09T22:20:11.342-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="git" /><category scheme="http://www.blogger.com/atom/ns#" term="heroku" /><title>Git: Push a local branch to a remote master</title><content type="html">&lt;pre class="brush: shell"&gt;git push heroku yourbranch:master &lt;/pre&gt;Credit &lt;a href="http://devcenter.heroku.com/articles/git#deploying_code"&gt;Heroku&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/firsthand-web-design/~4/7p554CS9J84" height="1" width="1"/&gt;</content><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/3409924723617327598?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/5548895711100825622/posts/default/3409924723617327598?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/firsthand-web-design/~3/7p554CS9J84/git-push-local-branch-to-remote-master.html" title="Git: Push a local branch to a remote master" /><author><name>Nicholas Henry</name><uri>https://plus.google.com/109341983975580064085</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-Ub9vGydp-YQ/AAAAAAAAAAI/AAAAAAAAAAA/ag9kkr9R0AU/s512-c/photo.jpg" /></author><feedburner:origLink>http://blog.firsthand.ca/2011/07/git-push-local-branch-to-remote-master.html</feedburner:origLink></entry></feed>
