Permalink
Switch branches/tags
Nothing to show
Find file
Fetching contributors…
Cannot retrieve contributors at this time
1376 lines (977 sloc) 144 KB
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title><![CDATA[Craig Ambrose]]></title>
<link href="http://www.craigambrose.com/atom.xml" rel="self"/>
<link href="http://www.craigambrose.com/"/>
<updated>2012-08-06T08:19:00+12:00</updated>
<id>http://www.craigambrose.com/</id>
<author>
<name><![CDATA[Craig Ambrose]]></name>
</author>
<generator uri="http://octopress.org/">Octopress</generator>
<entry>
<title type="html"><![CDATA[Upgrading to Refinery CMS 2.0]]></title>
<link href="http://www.craigambrose.com/blog/2012/03/04/upgrading-to-refinery-cms-2-dot-0/"/>
<updated>2012-03-04T18:19:00+13:00</updated>
<id>http://www.craigambrose.com/blog/2012/03/04/upgrading-to-refinery-cms-2-dot-0</id>
<content type="html"><![CDATA[<p>Refinery 2.0 is out, and I certainly think it&#8217;s the best option for doing content management in Rails at the moment. It&#8217;s rails 3.2 compliant, uses the asset pipeline, and it&#8217;s all built using engines in the rails way.</p>
<p>Since the last stable release (1.0.8) however, Refinery has changed so much that there is no longer an official or automated upgrade path. An upgrade is possible however, and I&#8217;ll run you through the steps below. Your Refinery 1.0 app will of course be running on rails 3.0, and so upgrading you application also involves an upgrade of Rails at the same time. This article will assume that you already have some knowledge about upgrading from rails 3.0 to 3.2, but if not you can read plenty of great articles on that. Be sure to read up on the asset pipeline if you haven&#8217;t used it before.</p>
<p>Before you start, I also suggest that you create a sample Refinery 2.0 application to use as a reference. Perhaps even give it the same name as your real application, to help minimize the differences between your sample app and your real one if you wish to compare them in a file comparison tool.</p>
<p>This guide covers the main Refinery CMS engines. It doesn&#8217;t cover other commonly used engines, not even Blog or Search. What I&#8217;ve done is disabled those engines (comment the gems out in your Gemfile) for the upgrade, and then they can be re-enabled after the migration is complete. Leave me lots of love in the comments if you want me to post upgrade guides for those other engines.</p>
<p>Also, I&#8217;m sure I don&#8217;t even need to mention, do this upgrade in a branch.</p>
<h2>STEP 1: Migrate Data</h2>
<p>Refinery 2.0 does not know how to migrate your Refinery 1.0 database. You&#8217;ll need to manually insert a migration into your application and run it before you upgrade anything. I&#8217;ve written a migration, so go grab it here (<a href="https://gist.github.com/1964785">https://gist.github.com/1964785</a>) and paste it into a migration file in your app, then run it.</p>
<h2>STEP 2: Upgrade Gems</h2>
<p>Upgrade you Gemfile to rails 3.2.1 and Refinery 2.0. Comment out any other refinery engines temporarily.</p>
<h2>STEP 3: Rails update</h2>
<p>Perform the normal Rails 3.2 upgrade steps</p>
<ul>
<li>Upgrade to a recent version of mysql2 if using an old mysql gem.</li>
<li>rake rails:update</li>
<li>Create assets dir, and move application images, stylesheets, javascripts</li>
</ul>
<h2>STEP 4: Upgrade refinery generated files</h2>
<p>Run the refinery generator. This is normally done on a new app, but like rails:update will ask if you want to overwrite files. You&#8217;ll need to go through them one-by-one.</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>rails generate refinery:cms
</span></code></pre></td></tr></table></div></figure>
<p>In particular I found I had to ensure that I replaced:
<code>Refinery.rescue_not_found = false</code>
with
<code>Refinery::Core.rescue_not_found = false</code></p>
<h2>STEP 5: Refresh cookies</h2>
<p>Refresh your browser cookies. This is necessary if you have run the old version of the site in that browser. I imagine that this might also cause problems in a production deploy for users who still have their browser open.</p>
<h2>STEP 6: Change partial paths</h2>
<p>If you have overridden your application layout, you may have partial tags like like:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='erb'><span class='line'><span class="cp">&lt;%=</span> <span class="n">render</span> <span class="ss">:partial</span> <span class="o">=&gt;</span> <span class="s2">&quot;/shared/html_tag&quot;</span> <span class="cp">%&gt;</span><span class="x"></span>
</span></code></pre></td></tr></table></div></figure>
<p>Change to something like the following:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='erb'><span class='line'><span class="cp">&lt;%=</span> <span class="n">render</span> <span class="s1">&#39;/refinery/html_tag&#39;</span> <span class="cp">%&gt;</span><span class="x"></span>
</span></code></pre></td></tr></table></div></figure>
<p>You many need to consult the application layout template inside refinery.</p>
<h2>STEP 7: Move settings into config files</h2>
<p>Move settings out of database. Settings now go in initializer files. Copy the entire config/initializers/refinery directory out of a sample refinery 2.0 app into your application, and go through and fill out the settings. Use the settings table in your database as a guide, but some settings have changed, so read the new initializer files right through.</p>
<h2>STEP 8: Move overridden Refinery templates</h2>
<p>For any templates you have overridden, they are probably now in the wrong paths. In particular, most are namespaced inside a refinery folder now. Consult the refiney codebase to find the new location of the overridden template. For example, /layouts/_header is now /refinery/header. Simply move the file in your application into a folder called &#8220;refinery&#8221; in your views folder.</p>
<h2>STEP 9: Change custom template code</h2>
<p>In your custom templates, you will need to update any code calling now invalid refinery APIs. In particular, calls to settings will now be invalid. For example, replace:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="no">RefinerySetting</span><span class="o">.</span><span class="n">find_or_set</span><span class="p">(</span><span class="ss">:site_name</span><span class="p">,</span> <span class="s2">&quot;Company Name&quot;</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>
<p>with</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="no">Refinery</span><span class="o">::</span><span class="no">Core</span><span class="o">.</span><span class="n">site_name</span>
</span></code></pre></td></tr></table></div></figure>
<p>Likewise, all refinery routes are now namespaced. Replace <code>root_path</code> with <code>refinery.root_path</code>.</p>
<h2>STEP 10: Create page slugs.</h2>
<p>Refinery now uses friendly ID. These &#8220;slugs&#8221; are created for each page object on save. Create slugs for all pages by re-saving them all. <code>Refinery::Page.all.map(&amp;:save)</code></p>
<h2>STEP 11: Profit!</h2>
<p>You should be good to go now. Let me know how it went. :)</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Building Rails Apps from Bigger Blocks]]></title>
<link href="http://www.craigambrose.com/blog/2011/05/04/building-rails-apps-from-bigger-blocks/"/>
<updated>2011-05-04T18:17:00+12:00</updated>
<id>http://www.craigambrose.com/blog/2011/05/04/building-rails-apps-from-bigger-blocks</id>
<content type="html"><![CDATA[<p>With rails, we build up our application using the chunks of functionality that the framework and it&#8217;s libraries provide. This is different to web platforms like Drupal, where we might reasonable expect to get a basically working application (for the default use cases) out of the box, and be able to configure it to change it slightly.</p>
<p>The problem with configuring a monolithic platform is of course that we&#8217;ll never get it to work exactly the way we want. That approach works well if our requirements are close to it&#8217;s area of core functionality, but for custom software, we want to build <strong>up</strong>, rather than configure <strong>downward</strong>.</p>
<p>Building our apps up has been a key philosophy in rails from the beginning. In fact, it&#8217;s been so important that the core team has strongly resisted any changes to rails which looked too much like they encouraged monolithic, configurable software.</p>
<p><a href="http://www.flickr.com/photos/hansandcarolyn/2990870358/" title="Large Tower by Hans and Carolyn, on Flickr" style="float: right"><img src="http://farm4.static.flickr.com/3286/2990870358_607c0e9768_m.jpg" width="160" height="240" alt="Large Tower"></a></p>
<p>So, if we think of a rails app as a structure built up from lots of little blocks, which all do different things and are often interchangeable, then we have a good working metaphor for the ideal rails development process. Rails 3 (due to the Merb influence) has done a lot of work to ensure that some of the fundamental building blocks of rails are easily swappable. This blocks, like which ORM to use, form the foundations of the awesome block fortress that we want to build.</p>
<p>With each layer of blocks that we build up, the blocks tend to be somewhat dependent on the interface of the blocks below. For example, we might build a login system that requires a user model that basically behaves like an ActiveModel object, although we don&#8217;t care if it&#8217;s implemented using ActiveRecord, DataMapper, or something else.</p>
<p>Once the tower of blocks rises above the foundation of the Rails framework itself, then adding more blocks becomes dependent on some sort of agreement on those interfaces. This tends to happen when gems or plugins become so commonly used that supporting them, or something that looks a bit like them, is a natural approach for further libraries.</p>
<p>For example, there are a number of libraries supporting user authentication, and most of them expose a current_user method which returns some sort of user object. Although this is far from being a clearly defined interface, it&#8217;s almost all you need for other libraries to do things conditional on being logged in.</p>
<p>As we reach a consensus on certain libraries being good ones, even the ones that follow them tend to implement these core bits of interface. For example, you&#8217;ll find that changing an app from using Authlogic to using Devise is fairly simple.</p>
<p>This process made it feel a bit like the blocks were slowly getting bigger, and we could build bigger and bigger towers out of them without needing to write our own code from scratch. Certain parts of our app are good candidates for giving a lot of thought and custom code, but other parts are simply the bolt-ons that we need to get there, and it&#8217;s great to be able to use a generic implementation without having to re-invent the wheel. We don&#8217;t rewrite paperclip when we want to store images, and most people tend to use an authentication system these days.</p>
<p>So, why did the progression of these bigger and bigger blocks stop? Where are the rest?</p>
<p>If we want to be able to build up, rather than configure down, from any level of detail, then we need blocks at each level of detail. We need something to store files (paperclip), something to upload and store files (which uses the previous), something to provide an image gallery (which uses the previous), etc. It&#8217;s up to us which areas of our app use large blocks, and which ones use small ones.</p>
<p>When we start to talk about blocks that are large chunks of our application, they start needing to include user interface. This tends to be where we get scared and run away. The tricky bit here is the lack of defined interface, and that&#8217;s why you tend to see entire open source rails apps that do useful things like run a blog, forum, image gallery, social profiles, but not so many rails plugins that do these, and even less rails plugins that do it by building the functionality up from even smaller blocks which you can swap out.</p>
<p>In my next post, I&#8217;ll lay out some examples of what these &#8220;slightly larger that presently available&#8221; blocks of code might be, and take a stab at figuring out how we might start to define interfaces for them, while keeping a decentralized community process.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Quick and Easy ruby quality thresholds]]></title>
<link href="http://www.craigambrose.com/blog/2010/04/14/quick-and-easy-ruby-quality-thresholds/"/>
<updated>2010-04-14T18:14:00+12:00</updated>
<id>http://www.craigambrose.com/blog/2010/04/14/quick-and-easy-ruby-quality-thresholds</id>
<content type="html"><![CDATA[<p>Ruby on Rails&#8217; opinionated attitude towards testing has introduced a lot of people to test driven development. Whether it&#8217;s because rails development has encouraged good testing practices, or because it has attracted pro-testing developers, rails (and ruby in general) now has a large ecosystem of testing and quality tools.</p>
<p>If you&#8217;re a rails developer, you&#8217;re probably already writing tests. You&#8217;re probably using <a href="http://rspec.info/">RSpec</a> or <a href="http://github.com/thoughtbot/shoulda">Shoulda</a> (and also Cucumber, but I&#8217;m just talking about unit tests here). You&#8217;ve probably run your code through <a href="http://github.com/relevance/rcov">rcov</a>, to see how well much code your tests cover, and you&#8217;ve probably also looked at other code quality metrics, like the ones generated by <a href="http://github.com/jscruggs/metric_fu">metric_fu</a> (or a hosted service like <a href="http://www.codeyak.com/">codeyak</a>).</p>
<p>Quality metrics for your code force you to write good code. They are an essential part of test-driven development, because in TDD our design is supposed to be driven by spotting code smells. Unless we think we can get the design (both in the macro and micro) correct first time (and we can&#8217;t), then we always have to be on the lookout for these code smells that will drive us to refactor and evolve the design into something better. Since I&#8217;m using words like &#8220;drive&#8221; and &#8220;force&#8221;, obviously I intend these metrics to be enforced. If you aren&#8217;t already doing it, start now.</p>
<p>The following example presumes that you&#8217;re testing with rspec, but it only requires slight modifications to make the rcov threshold work with other testing libraries.</p>
<p>First up, ensure that your app has the following gems installed (bundled):</p>
<p>metric_fu, flay, flog, reek, roodi, rspec, rcov</p>
<p>Then, grab my continuous integration rake tasks from here:</p>
<p><a href="http://gist.github.com/364034">http://gist.github.com/364034</a></p>
<p>Finally, tell your continuous integration server to run &#8220;rake ci&#8221;. I use cruise_control.rb, but there are plenty to choose from.</p>
<p>You can modify the hash values in the THRESHOLDS constant to set the quality thresholds for your project. Set them so that they just pass at the moment, and then try to improve the code and slowly crank them tighter. For rcov that means raising it (towards 100.0), and for the others, that means lowering them.</p>
<p>Since you&#8217;re using metric_fu, you&#8217;ll also get graphs and pretty reports of the output. If you&#8217;re using cruise_control.rb, look for the &#8220;output&#8221; link in your build artifacts on the web interface. I&#8217;m not executing rcov via metric_fu in my code, because the metric_fu rcov task hides errors in the specs, and we also want the build to fail if a spec fails.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Thumbnailing to a fixed size without stretching using attachment_fu]]></title>
<link href="http://www.craigambrose.com/blog/2009/05/10/thumbnailing-to-a-fixed-size-without-stretching-using-attachment-fu/"/>
<updated>2009-05-10T17:35:00+12:00</updated>
<id>http://www.craigambrose.com/blog/2009/05/10/thumbnailing-to-a-fixed-size-without-stretching-using-attachment-fu</id>
<content type="html"><![CDATA[<p>I was going to write a blog post congratulating myself on being clever for writing a little extension to the attachment_fu plugin that allowed Procs to be used as thumbnail geometry strings, thus allowing you to write custom resizing code. This was all so that I could get it to perform a resize that worked exactly as I wanted, to give me a thumbnail of fixed dimensions which scaled and cropped but never stretched the image.</p>
<p>Before I did so, I thought I&#8217;d better just check that attachment_fu hadn&#8217;t changed recently to make my extension not work. I take a peek at github and lo and behold, Rick and other committers have been busy beavers on attachment_fu this year, and in fact another kiwi has already added the functionality that I need.</p>
<p>You can find David Jones&#8217; post on how his cropping functionality works <a href="http://www.d-jones.com/2007/10/11/cropping-support-for-acts_as_attachment">here</a>, although it is describing his old patch for acts_as_attachment, and with attachment_fu it&#8217;s used slightly differently, as described below.</p>
<p>Lets say that your users are uploading photos to your site, in a range of aspect ratios (including common landscape and portrait photos). Cropping to a square thumbnail is a problem already discussed on this blog, but what if we want to crop to fixed size that isn&#8217;t square?</p>
<p>Image magic geometry strings, when passed to the resize function, do not force the image to become that size if the aspect ratio doesn&#8217;t match. For example, if I specify a size of <code>"100x75"</code>, then an image already in a 4:3 aspect ratio will scale just fine, but a portrait image in the opposite ratio will end up 75 pixels tall as desired, but only 19 pixels wide, in order to preserve it&#8217;s aspect ratio. If instead I force the new image size, using the geometry string <code>"100x75!"</code>, I will get an image of the correct size, but it will be stretched and distorted.</p>
<p>It goes without saying that we never want to stretch. Some people like to stick with the behaviour of the first example, and simply fill the missing sections of the image with a background colour. The other option, which I prefer, is to crop the image in the dimension that doesn&#8217;t fit into the new aspect ratio. The goal is to get an image of exactly my desired dimensions, which is scaled to maintain aspect ratio, and then cropped as little as possible.</p>
<p>Doing this with the <a href="http://wiki.github.com/technoweenie/attachment_fu">latest version of attachment_fu from github</a> is as easy as specifying the geometry string <code>"100x75c"</code>. The &#8220;c&#8221; at the end is used to indicate that we want to use the cropping algorithm. It&#8217;s not a normal part of an image magick geometry string and it does get removed by attachment_fu when it decided what algorithm to use.</p>
<p>If this is not quite what you need, you might also want to check out what the <em>&#8220;e&#8221;</em> option does.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[PluginInstances - A different way to use rails plugins]]></title>
<link href="http://www.craigambrose.com/blog/2009/03/18/plugininstances-a-different-way-to-use-rails-plugins/"/>
<updated>2009-03-18T17:32:00+13:00</updated>
<id>http://www.craigambrose.com/blog/2009/03/18/plugininstances-a-different-way-to-use-rails-plugins</id>
<content type="html"><![CDATA[<p>I&#8217;ve just released the PluginInstances pluging at:
<a href="http://github.com/craigambrose/plugin_instances/">http://github.com/craigambrose/plugin_instances/</a></p>
<p>This plugin allows you to have individual route sets for other plugins, including a unique instance id.</p>
<p>Without using this plugin you can specify a routes.rb in your plugins (as of rails 2.3), and these routes are merged into the global route set used when determining how to process the current request. This is great for allowing a plugin to introduce a set of functionality which exists only once on the site (like a login system).</p>
<p>The PluginInstances plugin is designed to enable individual instances of plugins to be placed in different places on the site. For example, lets say that your site is content managed, and has tabs across the top which link to various types of functionality:</p>
<p>In your application&#8217;s routes.rb file:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'> <span class="n">map</span><span class="o">.</span><span class="n">plugin_instances</span> <span class="s2">&quot;/tabs/:id&quot;</span>
</span></code></pre></td></tr></table></div></figure>
<p>When a request is detected with a path like &#8220;/tabs/23/admin&#8221;, it realises that this matches the plugin_instances route, finds the relevant instance (eg: PluginInstance.find(23)), asks that instance what plugin it represents (eg: a forum, user profile, etc), and then passes the route &#8220;/admin&#8221; to the route set for that plugin, along with the plugin instance object. Thus, a forum plugin could be written which can be instantiated at different places in the site, and just has to scope itself using the plugin instance id.</p>
<p>For detailed usage instructions, see the README file displayed at the <a href="http://github.com/craigambrose/plugin_instances/">github project page</a>.</p>
<p>I&#8217;m using this plugin at present to build a pluggable wiki, where each new page that you create could be just a simple page of versioned text (like a regular wiki), or it could be a calendar, a forum, etc. People using drupal will also find this sort of routing system familiar, so I&#8217;m sure there are plenty of other useful applications for it.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[I sold my cow and all I got were these url helpers]]></title>
<link href="http://www.craigambrose.com/blog/2008/04/20/i-sold-my-cow-and-all-i-got-were-these-url-helpers/"/>
<updated>2008-04-20T14:51:00+12:00</updated>
<id>http://www.craigambrose.com/blog/2008/04/20/i-sold-my-cow-and-all-i-got-were-these-url-helpers</id>
<content type="html"><![CDATA[<p>In rails applications, we link to other pages in our application by generating a url which maps to a particular controller (class) and action (method) using a rule which we call a route. Back in rails 1.0, we would do something like this:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='erb'><span class='line'><span class="cp">&lt;%=</span> <span class="n">link_to</span> <span class="s1">&#39;Edit User&#39;</span><span class="p">,</span> <span class="p">{</span><span class="ss">:controller</span> <span class="o">=&gt;</span> <span class="s1">&#39;users&#39;</span><span class="p">,</span> <span class="ss">:action</span> <span class="o">=&gt;</span> <span class="s1">&#39;edit&#39;</span><span class="p">,</span> <span class="ss">:id</span> <span class="o">=&gt;</span> <span class="vi">@user</span><span class="p">}</span> <span class="cp">%&gt;</span><span class="x"></span>
</span></code></pre></td></tr></table></div></figure>
<p>This is not an article on routing for dummies, I presume you already know this stuff. However, I want to recap why we do this, in case anyone has forgotten the reason for all this.</p>
<p>To give a point of comparison, lets assume that there were no routes in rails, and that code was directed to a particular place based on the default rules of <code>:controller/:action/:id?:other_params</code>. A link might look like this:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='erb'><span class='line'><span class="cp">&lt;%=</span> <span class="n">link_to</span> <span class="s1">&#39;Edit User&#39;</span><span class="p">,</span> <span class="s2">&quot;/users/edit/</span><span class="si">#{</span><span class="vi">@user</span><span class="o">.</span><span class="n">to_param</span><span class="si">}</span><span class="s2">&quot;</span> <span class="cp">%&gt;</span><span class="x"></span>
</span></code></pre></td></tr></table></div></figure>
<p>That&#8217;s actually shorter than the above. Clearly brevity isn&#8217;t the main goal here. So what are the goals of customisable routed?</p>
<h2>Human (and SEO) Friendly URLS</h2>
<p>If we need further parameters, we don&#8217;t want to introduce a question mark into the url. We want it to keep looking like a directory structure. If we are creating a new user inside group 5, we want a url like <code>/groups/5/users/new</code>, instead of <code>/users/new?group_id=5</code>. This goal is probably not one you have forgotten, so lets jump on to the next one.</p>
<h2>A Single Point of Change For Url Mappings</h2>
<p>It&#8217;s a well known bad smell in any piece of software if changing your mind about one simple concept requires you to make changes all through the code (Martin Fowler calls this &#8220;Shotgun Surgery&#8221;). If our client says, &#8220;can you change all references to &#8216;users&#8217; in the urls to saying &#8216;people&#8217; instead&#8221;, or &#8220;can you prefix all admin urls with /admin&#8221; then we would expect to be able to do so without too much trouble.</p>
<p>The beautiful thing about routing in rails is that the routes control both the <strong>generation</strong> and the <strong>parsing</strong> of urls. Back when I wrote PHP apps, I had code to parse the urls (big ugly case statements) and in some cases I had code to generate the urls, but I never bothered to create one simple system for doing both.</p>
<p>So, with those two goals in mind, lets travel back to the present and look at resource routes in rails 2. When we create a route with map.resource, a bunch of special helper methods are also created. This allows us to replace our initial example with</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='erb'><span class='line'><span class="cp">&lt;%=</span> <span class="n">link_to</span> <span class="s1">&#39;Edit User&#39;</span><span class="p">,</span> <span class="n">edit_user_path</span><span class="p">(</span><span class="vi">@user</span><span class="p">)</span> <span class="cp">%&gt;</span><span class="x"></span>
</span></code></pre></td></tr></table></div></figure>
<p>Lets look at the pros and cons of that.</p>
<p>Firstly, it&#8217;s much shorter. The hard coded string was a fair bit shorter too, so we know that brevity isn&#8217;t always the main goal, but short is generally not a bad thing.</p>
<p>It&#8217;s a little bit more english-like, in that it contains less symbols. However, this also means that it is less semantic. It&#8217;s easy to learn how to read urls that are specified as a hash of controller, action and params. I know how to read the resource helpers too, but there are a few different rules to learn in order to parse them mentally, and I find it takes new rails programmers a little while to figure them out.</p>
<p>It&#8217;s overloadable. Since it&#8217;s a method, we can declare a helper of the same name and do something completely different. This can be handy, although in practice it&#8217;s a bit dangerous since there are other ways to declare the route, so you&#8217;re not guaranteed to intercept all calls. Also, it would then give behaviour that our programmer who has now worked so hard to figure out how the restful routing helpers work something of a surprise. The principle of least surprise is worth considering.</p>
<p>By in large, I&#8217;m still kind of in favour of the new notation at this stage. When I first learnt it, I thought, &#8220;wow, that looks much nicer&#8221;, and that feeling is a very important argument in it&#8217;s favour. We&#8217;ve gotten a lot with this new functionality. I just want to mention the one feature we sold off without even noticing.</p>
<p>When generating a url we are new <strong>directly linking the view code to a single routing rule</strong>.</p>
<p><strong>&#8220;WTF?&#8221;</strong>, I hear you say.</p>
<p>Haven&#8217;t we already established that the rails routing system decouples url generation from the controller code that it maps to, allowing us to configure the interface between them in one place (routes.rb). Why am I now saying that I&#8217;ve linked the url generation in my view to a <strong>single</strong> route and lost my ability to vary it at will?</p>
<p>Lets say that we wanted to map all uses of the user edit action to a totally new url. It could look like anything we wanted, previously, we had the power to do so because our request to generate a url just gave the keys and values, and our action just accepted key/value parameters, and the string we used for the url in between was totally up to the routes.</p>
<p>Now we&#8217;re using a method unique to this action to generate the url. By calling <code>edit_user_path(@user)</code>, we&#8217;re not actually giving up the flexibility to decide what that method does, but if we wanted to make it map to anything other than the edit action on the users controller, nested inside no other resources, then we&#8217;d be violating all the conventions that we&#8217;d built up in order to understand the user of these helpers.</p>
<p>So, if we want to do something like move this action to a different resource, we find that we need to go through and use a new set of helper methods for all the links. Since we need to change each link to do this, we&#8217;re really not much better of that if we&#8217;d used hard coded strings in the links.</p>
<p>If you wanted to rename the users resource to &#8216;people&#8217;, it&#8217;s quite a tricky operation. I&#8217;ve done it many times and without foolproof refactoring tools, you need to search and replace for strings like <code>user_</code> and look at each method call to see if it&#8217;s a url helper which should be renamed to <code>edit_people_path</code> or similar.</p>
<p>Recently, I&#8217;ve been experimenting with going back to expressing things as a hash. Also, in doing so I follow a set of conventions.</p>
<ul>
<li>Always put spaces either side of the @=>@ operator.</li>
<li>Always use symbols for the keys</li>
<li>Always use single quotes as string delimiters for the values, if they are string literals.</li>
</ul>
<p>This gives much more precise things to search and replace for. Lets say that I want to rename the users resource to people. I can search for all strings matching <code>:controller =&gt; 'users'</code>.</p>
<p>I&#8217;m not necessarily saying that this approach is the best, but I think that we should all consider that the goal here is simplicity. The simplest code isn&#8217;t necessary the shortest. The simplest code is the easiest to read, the easiest to learn it&#8217;s full meaning, and the easiest to change. When we got all excited about named url helper methods, I&#8217;m not sure it was at all clear how much we were giving up in return.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Using Model factories with RSpec]]></title>
<link href="http://www.craigambrose.com/blog/2008/02/23/using-model-factories-with-rspec/"/>
<updated>2008-02-23T14:48:00+13:00</updated>
<id>http://www.craigambrose.com/blog/2008/02/23/using-model-factories-with-rspec</id>
<content type="html"><![CDATA[<p>I had a lot of questions and concerns about RSpec when I first made the switch a few months ago. I&#8217;d seen a few talks on BDD in general, and watched the peepcode screencasts on rspec, and I could see that BDD tended to encourage a fairly good style of testing. I managed to find answers for most of my questions, so here&#8217;s a little list of the challenges and resolutions that I faced when I made the switch.</p>
<p>When I first started on rails, like a good little agile developer, I mocked out everything for my unit tests and ensured that I never actually hit the database and thus never required fixtures. I used the mocha library predominantly, but after a month or so of this I had gotten totally tangled up.</p>
<p>The problem was that active record objects are a bit like an iceberg where only a part of them is visible in the code, and the rest is lurking below the murky waters of your database. My tests were missing heaps of bugs caused by queries generating invalid SQL, but reliance upon properties which may or may not exist depending on the database schema, and were also far too complicated as they required lines and lines of setup code to handle all the wacky ways in which a model could interact with a database.</p>
<p>My solution eventually was to give in, and test things the way DHH does. Not particularly elegant, certainly not &#8220;correct&#8221; in terms of what I&#8217;d been taught regarding how to test, but it did work. The upside of these tests which often integrated a number of layers together was that I got great coverage. The downside was that I&#8217;d often get a raft of test failures if I broke a single line of code, and some things were just a bit too much effort to test and tended to motivate me not to bother. Also, the dependence on fixtures meant that as the test situations got more complex, I&#8217;d have to add more fixtures, and in doing so break other tests.</p>
<p>When moving to RSpec, I threw all this away, and started again on mocking everything out. RSpec helped out with a few nice extra methods, like mock_model, which can sensibly provide a mock ActiveRecord object that has the bare essentials already (like the id and to_param methods). This time I did better, but I still got a little tangled up, and I found it really hard to test methods where I was querying for record. With a mock based approach, all I could test was that I&#8217;d sent the query that I expected, not that it was actually valid SQL or that it would fetch any sensible results.</p>
<p>The resolution, as it turns out, lies somewhere in between.</p>
<p>I use mocking pretty heavily still, particularly when I&#8217;m creating or updating records. But, I also often want to deal with real data, particularly when it&#8217;s being fetched rather than created. Fixtures were a bad idea, because they created a great big data set that had to be valid for all tests. There are some plugins to provide different sets of fixtures for different scenarios, but I think that having the data used for a test in a different file is also downright confusing. Setting up for each test needs to be easy.</p>
<p>The solution for me was the use of a model factory, which is a pattern in some use amongst my colleagues at <a href="http://www.cogentconsulting.com.au">Cogent</a>. I&#8217;d left the worlds of C# and C++ with a bit of a dislike of factories, being a pattern that, like dependency injection, seemed to add a great deal of complexity to a problem that is much more easily solved in a dynamic language. However, a simple model factory for testing is a different kettle of fish.</p>
<p>Lets say I&#8217;m testing that I can&#8217;t create a user if their email address already exists. If I was using fixtures, I&#8217;d create a user with the email address I was going to try out, but that would affect all other tests. If I created a user directly in the test then I&#8217;d need to update my test each time I changed the information needed to create a valid user. Instead, a model factory makes it look like this:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">describe</span> <span class="no">User</span><span class="p">,</span> <span class="s2">&quot;when being created&quot;</span> <span class="k">do</span>
</span><span class='line'> <span class="n">it</span> <span class="s2">&quot;should require email address to be unique&quot;</span> <span class="k">do</span>
</span><span class='line'> <span class="n">model_factory</span><span class="o">.</span><span class="n">user</span><span class="p">(</span><span class="ss">:email</span> <span class="o">=&gt;</span> <span class="s2">&quot;craig@craigambrose.com&quot;</span><span class="p">)</span>
</span><span class='line'> <span class="n">user</span> <span class="o">=</span> <span class="no">User</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="ss">:email</span> <span class="o">=&gt;</span> <span class="s2">&quot;craig@craigambrose.com&quot;</span><span class="p">)</span>
</span><span class='line'> <span class="n">user</span><span class="o">.</span><span class="n">should_not</span> <span class="n">be_valid</span>
</span><span class='line'> <span class="n">user</span><span class="o">.</span><span class="n">errors</span><span class="o">.</span><span class="n">on</span><span class="p">(</span><span class="ss">:email</span><span class="p">)</span><span class="o">.</span><span class="n">should</span> <span class="o">==</span> <span class="s2">&quot;must be unique&quot;</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>
<p>The user method on model_factory creates and returns a valid user. It can be called as many times as I want, and it will always return a valid user. If User contains fields that must be unique, then the data I use to populate it contains integers which increment each time. The factory methods also take arrays of options which can be used instead of these default values, which I use whenever I want to set an attribute that I care about in the test. This way, if I change what is required to create a user, all I need to change is the model factory, not every single test.</p>
<p>I don&#8217;t use the model factory all the time. I still make heavy use of mock_model. For each case, I try and determine which method will yield the simplest test that actually forces me to write the code that is needed, rather than just appearing to give coverage over the lines of code. Sometimes I use some mocking and some concrete objects in the same test.</p>
<p>I haven&#8217;t provided the code for the model_factory itself. It&#8217;s very straightforward and I&#8217;ll leave it as an exercise for the reader. Drop me a line if you have a particularly clever implementation that you&#8217;d like to share or are interested in seeing some of my code.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[respond_to.email, or how to handle incoming emails in rails RESTfully]]></title>
<link href="http://www.craigambrose.com/blog/2008/02/09/respond-to-dot-email/"/>
<updated>2008-02-09T14:41:00+13:00</updated>
<id>http://www.craigambrose.com/blog/2008/02/09/respond-to-dot-email</id>
<content type="html"><![CDATA[<p>There&#8217;s a bunch of information around on how to handle incoming emails with your rails application, in particular the <a href="http://wiki.rubyonrails.org/rails/pages/HowToReceiveEmailsWithActionMailer">wiki page</a>, but I have some concerns with the methods that are being suggested, and in this article I present an alternative which I&#8217;ve been trying out and I really like.</p>
<p>Handling incoming email is, in essence, very simple. All you need to do is get the email, which is a big chunk of text, parse it with a ruby email class, such as TMail (which is used by ActionMailer), and perform some action. If you&#8217;re only handling a few specific addresses, it might be best to fetch the email via POP3, and I&#8217;ve done that before using a daemon to regularly poll the pop account.</p>
<p>POP3 is not a viable solution if you want to handle all email for a certain domain. At this point, we probably want to talk about SMTP.</p>
<h2>A Very Short Guide to SMTP</h2>
<p><strong>Simple Mail Transport Protocol</strong> is pretty damn cool if you ask me. It&#8217;s dead simple, basically the client can only say &#8220;hello, here&#8217;s an email from X to Y&#8221;. Just like HTTP, it&#8217;s fully push based. There&#8217;s no polling, emails get pushed across the internet. Just like HTTP, it has a whole stack of response codes which are of course appropriate to trying to send an email, rather than talk to a web resource.</p>
<h2>Using Postfix Mail Filters to Call Ruby</h2>
<p>Postfix is a common open source SMTP server. Before I looked at it, it was big and scary. After a few hours of expert help, I wonder what seemed so complicated. One of the basic ways that we can use postfix to push mail to our rails app is by specifying a command like script which gets executed whenever postfix gets an email. This is the first option presented on the rails wiki, and they suggest using a script which calls the receive method of one of your ActionMailer classes.</p>
<h2>My Concern</h2>
<p>If we&#8217;re going to use ActionMailer to parse an email, and then presumably fire off a bunch of ActiveRecord code to make changes to your database as a result, clearly we&#8217;re loading the entire rails stack. Every time we get an email we&#8217;re loading the entire rails stack. This seems like how we handled web requests back in the day when there was only mod_cgi. No shared resources between requests, a big performance hit for loading all of rails and then getting rid of it each time, and the concern that we can only handle as many incoming emails as we have RAM on our server as the rails code takes up a bunch of memory.</p>
<h2>What I Want</h2>
<p>I don&#8217;t want to have to worry about the resources I need to scale my email server, I already do that with my application servers. I want to handle emails in a way that re-uses an in-memory copy of the rails classes and called be scaled in a predictable way.</p>
<p>That sounds a lot like a mongrel cluster.</p>
<p>We all have one of those already right. So why not handle incoming mail over HTTP? It&#8217;s dead easy, it scales well, and the result is really Rails-ish.</p>
<h2>respond_to.email</h2>
<p>I was hoping to get a plugin out of this. It&#8217;d be so handy that people would queue for miles to download it. The trouble is, it&#8217;s actually not even enough code to bother, it&#8217;s only about three lines of ruby and the same of postfix config. So, lets call this a pattern. I&#8217;ll describe how to do it, and you can all run off and do it yourself.</p>
<h3>Step One: Install Postfix</h3>
<p>Install postfix on one of your servers. For any sizable rails site, I like to have a little VPS just for daemons, cron jobs, scripts, and the mail server, to keep it separate from all the web stuff. On ubuntu, this was as easy as &#8220;sudo apt-get install postfix&#8221;. For the default configuration type, I chose &#8220;internet site&#8221;.</p>
<h3>Step Two: Setup Your MX Record</h3>
<p>For mail to start arriving at your mail server, you need to add a MX record to your DNS which points at the url of your server. Depending on your host, you probably have a web interface to do this, and it&#8217;s probably dead easy.</p>
<h3>The Magic Script!</h3>
<p>Create a file called mail_handler.rb, and pop it somewhere in your rails project. I created a /bin directory for it. Don&#8217;t use a rake task, the goal here is not to load in any unecessary stuff. Here&#8217;s the contents.</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="c1">#!/usr/bin/ruby</span>
</span><span class='line'><span class="nb">require</span> <span class="s1">&#39;net/http&#39;</span>
</span><span class='line'><span class="nb">require</span> <span class="s1">&#39;uri&#39;</span>
</span><span class='line'>
</span><span class='line'><span class="no">Net</span><span class="o">::</span><span class="no">HTTP</span><span class="o">.</span><span class="n">post_form</span> <span class="no">URI</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="s1">&#39;http://www.craigambrose.com/emails&#39;</span><span class="p">),</span> <span class="p">{</span> <span class="s2">&quot;email&quot;</span> <span class="o">=&gt;</span> <span class="no">STDIN</span><span class="o">.</span><span class="n">read</span> <span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
<p>If ruby is somewhere else on your machine, change the line at the top to be correct (try &#8220;which ruby&#8221; on that machine to see where it is). I&#8217;ve chosen to hardcode in the url that I want to post the email to so that I don&#8217;t have to load any other files. If you have more deployment environments to worry about, you might want to put the target url in a yml file and parse it here. Just don&#8217;t load your rails environment file, that&#8217;s the whole point of this.</p>
<h3>Configuring Postfix to Call the Script</h3>
<p>In this example, the domain that I want to handle email for is &#8220;craigambrose.com&#8221;. Everywhere you see this, replace it with your own domain name. Most of the commands below need root access.</p>
<p>In /etc/postfix/main.cf</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">mydestination</span> <span class="o">=</span> <span class="n">localhost</span><span class="o">.</span><span class="n">localdomain</span><span class="p">,</span> <span class="n">localhost</span><span class="p">,</span> <span class="n">craigambrose</span><span class="o">.</span><span class="n">com</span>
</span><span class='line'><span class="n">virtual_maps</span> <span class="o">=</span> <span class="nb">hash</span><span class="ss">:/</span><span class="n">etc</span><span class="o">/</span><span class="n">postfix</span><span class="o">/</span><span class="n">virtual</span>
</span><span class='line'><span class="n">alias_maps</span> <span class="o">=</span> <span class="nb">hash</span><span class="ss">:/</span><span class="n">etc</span><span class="o">/</span><span class="n">aliases</span>
</span></code></pre></td></tr></table></div></figure>
<p>In /etc/postfix/virtual (this is a file, you may need to create this)</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="vi">@craigambrose</span><span class="o">.</span><span class="n">com</span> <span class="n">rails_mailer</span>
</span></code></pre></td></tr></table></div></figure>
<p>The above says to redirect any address at craigambrose.com to the alias &#8220;rails_mailer&#8221;, which I&#8217;ll create next. You could run multiple rails apps of the same server by giving them all unique aliases. On the left, you can use a regular expression to match addresses if you only want to match some of them.</p>
<p>To apply this change to virtuals, run:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">postmap</span> <span class="sr">/etc/</span><span class="n">postfix</span><span class="o">/</span><span class="n">virtual</span>
</span></code></pre></td></tr></table></div></figure>
<p>In /etc/aliases</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">rails_mailer</span><span class="p">:</span> <span class="s2">&quot;|/var/www/apps/craigambrose/current/bin/mail_handler.rb&quot;</span>
</span></code></pre></td></tr></table></div></figure>
<p>That&#8217;s the alias we created on the left. On the right is the path to my script, change as necessary. The pipe character before the script path means &#8220;the following is a shell command, not an email address&#8221;.</p>
<p>To apply this change to aliases, run:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">postalias</span> <span class="sr">/etc/</span><span class="n">aliases</span>
</span></code></pre></td></tr></table></div></figure>
<p>To apply the main configuration changes to postfix, run:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="sr">/etc/ini</span><span class="n">t</span><span class="o">.</span><span class="n">d</span><span class="o">/</span><span class="n">postfix</span> <span class="n">reload</span>
</span></code></pre></td></tr></table></div></figure>
<h3>Testing the Setup</h3>
<p>I should be able to send an email now to &#8220;someaddress@craigambrose.com&#8221;. To see it get process by postfix, we might want to watch the postfix info log:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">tail</span> <span class="o">-</span><span class="n">f</span> <span class="sr">/var/</span><span class="n">log</span><span class="o">/</span><span class="n">mail</span><span class="o">.</span><span class="n">info</span>
</span></code></pre></td></tr></table></div></figure>
<p>When the mail is process, you should see a line like:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">to</span><span class="o">=&lt;</span><span class="n">someaddress</span><span class="vi">@craigambrose</span><span class="o">.</span><span class="n">com</span><span class="o">&gt;</span><span class="p">,</span> <span class="n">orig_to</span><span class="o">=&lt;</span><span class="n">root</span><span class="o">&gt;</span><span class="p">,</span> <span class="n">relay</span><span class="o">=</span><span class="n">local</span><span class="p">,</span> <span class="n">delay</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">status</span><span class="o">=</span><span class="n">sent</span> <span class="p">(</span><span class="n">delivered</span> <span class="n">to</span> <span class="n">command</span><span class="p">:</span> <span class="sr">/var/</span><span class="n">www</span><span class="o">/</span><span class="n">apps</span><span class="o">/</span><span class="n">craigambrose</span><span class="o">/</span><span class="n">current</span><span class="o">/</span><span class="n">mail_handler</span><span class="o">.</span><span class="n">rb</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>
<p>Then, go peek at your rails app logs. You should see that the mail has been passed through by the script. Even if you haven&#8217;t written an action to handle it yet, the log entry should be there.</p>
<h3>Troubleshooting</h3>
<p>If you didn&#8217;t see the correct line in your postfix logs, then perhaps there&#8217;s a problem with your DNS Set. You could try talking to postfix directly. Mail servers listen on port 25, and you can telnet into them and speak directly. Try <code>telnet YOUR_SERVER_IP 25</code> And the try typing in what the client says in the <a href="http://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol#Sample_communications">sample SMTP communication on wikipedia</a> with the example address changed to the domain that you want to test. If that works, but sending email didn&#8217;t, you&#8217;ll need to investigate your DNS setup.</p>
<h3>Handling the Rails Action</h3>
<p>The target url I put in my mail script was http://www.craigambrose.com/emails, so the mail is going to get POSTed to that resource. With normal rails resource routes, that means that we&#8217;re expecting to handle the email in the create action of the EmailsController. That seems very sensible to me. My script puts the unparsed email into <code>params[:email]</code>.</p>
<p>To parse it with TMail, all you need to do is:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'> <span class="nb">require</span> <span class="s1">&#39;tmail&#39;</span>
</span><span class='line'> <span class="n">email</span> <span class="o">=</span> <span class="no">TMail</span><span class="o">::</span><span class="no">Mail</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:email</span><span class="o">]</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>
<p>Alternatively you could pass it to the &#8220;receive&#8221; method of any ActionMailer derived class, which does the above automatically.</p>
<p>I&#8217;ve had some reports that TMail is both a little slow, and also not quite up to parsing all the possible ways that an email might be encoded in the big bad world. That&#8217;s a subject for another blog post.</p>
<h3>Final Performance Note</h3>
<p>When postfix is calling your script, it makes so that only a certain number of calls are occurring concurrently, the default is 20, which seems pretty good to me. If you&#8217;d like to tweak this, use the following setting in main.cf (and don&#8217;t forget to reload postfix afterwards).</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">default_destination_concurrency_limit</span> <span class="o">=</span> <span class="mi">30</span>
</span></code></pre></td></tr></table></div></figure>
<h2>Acknowledgments</h2>
<p>Setting up servers is not my area of expertise. Many thanks to Andrew Snow of <a href="http://www.octopus.com.au">Octopus</a> for the postfix help and <a href="http://notahat.com">Pete Yandell</a> for sharing some of the lessons learned on his great mailing list site <a href="http://9cays.com/">9cays</a></p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Leaving the beaten track with RSpec]]></title>
<link href="http://www.craigambrose.com/blog/2007/12/05/leaving-the-beaten-track-with-rspec/"/>
<updated>2007-12-05T14:39:00+13:00</updated>
<id>http://www.craigambrose.com/blog/2007/12/05/leaving-the-beaten-track-with-rspec</id>
<content type="html"><![CDATA[<p>I&#8217;ve spent today learning RSpec. To get my head around it, I decided to spec a controller action which I&#8217;d already written and tested with Test::Unit. It&#8217;s a pretty basic CRUD action, requiring login and ownership of the record.</p>
<p>Here&#8217;s the spec and test code side by side:</p>
<p><a href="http://pastie.caboo.se/124898">http://pastie.caboo.se/124898</a></p>
<p>I&#8217;m not sure which one was easier. The RSpec version is certainly a lot longer. Having said that, the Test::Unit one uses fixtures, which aren&#8217;t included in the dump.</p>
<p>In RSpec&#8217;s favour, is that the specifications run much faster (as they don&#8217;t use the database), and they also better decouple the controller from the model, as it&#8217;s a more true &#8220;unit&#8221; test. Having said that, I believe that the RSpec version might actually be a little bit more fragile, as it would fail if the code under test changed to perform the same operation using different methods (such as calling ActiveRecord create, instead of new and save).</p>
<p>Also, some of the things I specified in terms of expectations are actually order dependent, which is not captured in my specs, so if I deliberately messed up the order of some of the code, I might get false positives with my specifications.</p>
<p>For the record, here&#8217;s the code under test.</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">before_filter</span> <span class="ss">:login_required</span><span class="p">,</span> <span class="ss">:only</span> <span class="o">=&gt;</span> <span class="o">[</span><span class="ss">:new</span><span class="p">,</span> <span class="ss">:create</span><span class="p">,</span> <span class="ss">:edit</span><span class="p">,</span> <span class="ss">:update</span><span class="o">]</span>
</span><span class='line'><span class="n">before_filter</span> <span class="ss">:load_user</span>
</span><span class='line'><span class="n">before_filter</span> <span class="ss">:current_user_can_edit_user</span><span class="p">,</span> <span class="ss">:only</span> <span class="o">=&gt;</span> <span class="o">[</span><span class="ss">:new</span><span class="p">,</span> <span class="ss">:create</span><span class="p">,</span> <span class="ss">:edit</span><span class="p">,</span> <span class="ss">:update</span><span class="o">]</span>
</span><span class='line'><span class="n">before_filter</span> <span class="ss">:load_user_base_tabs</span>
</span><span class='line'>
</span><span class='line'><span class="k">def</span> <span class="nf">create</span>
</span><span class='line'> <span class="vi">@photo_set</span> <span class="o">=</span> <span class="no">PhotoSet</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:photo_set</span><span class="o">]</span><span class="p">)</span>
</span><span class='line'> <span class="vi">@photo_set</span><span class="o">.</span><span class="n">profile</span> <span class="o">=</span> <span class="vi">@profile</span>
</span><span class='line'> <span class="n">success</span> <span class="o">=</span> <span class="vi">@photo_set</span><span class="o">.</span><span class="n">save</span>
</span><span class='line'>
</span><span class='line'> <span class="n">respond_to</span> <span class="k">do</span> <span class="o">|</span><span class="nb">format</span><span class="o">|</span>
</span><span class='line'> <span class="nb">format</span><span class="o">.</span><span class="n">html</span> <span class="k">do</span>
</span><span class='line'> <span class="k">if</span> <span class="n">success</span>
</span><span class='line'> <span class="n">redirect_to</span> <span class="n">user_photo_set_path</span><span class="p">(</span><span class="vi">@user</span><span class="p">,</span> <span class="vi">@photo_set</span><span class="p">)</span>
</span><span class='line'> <span class="k">else</span>
</span><span class='line'> <span class="n">render</span> <span class="ss">:action</span> <span class="o">=&gt;</span> <span class="s1">&#39;new&#39;</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Image cropping with Mini Magick and attachment_fu]]></title>
<link href="http://www.craigambrose.com/blog/2007/12/03/image-cropping-with-mini-magick-and-attachment-fu/"/>
<updated>2007-12-03T14:36:00+13:00</updated>
<id>http://www.craigambrose.com/blog/2007/12/03/image-cropping-with-mini-magick-and-attachment-fu</id>
<content type="html"><![CDATA[<p>As I mentioned in my last post, I&#8217;ve recently switched a lot of my code from RMagic to Mini Magick. The API provided by mini magick isn&#8217;t quite as nice, and you have to jump through a few hoops to get it to do what you want, so I thought I&#8217;d post an example.</p>
<p>Whenever clients talk about image thumbnailing, what they actually want is for the image to be cropped square, and then resized down to thumbnail size. They never, ever, want you to provide a thumbnail that isn&#8217;t square, or to scale the image out of proportion in order to stretch it into a square shape. It&#8217;s always crop and scale.</p>
<p>Strangely, this isn&#8217;t what attachment_fu does out of the box, with either rmagick or mini magick. Others have discussed this with regard to rmagick, but I couldn&#8217;t find a good mini-magick solution that worked, so here&#8217;s why I&#8217;ve come up with.</p>
<p>Replace the resize_image method in attachment_fu&#8217;s mini_magick_processor.rb file with the following:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'> <span class="k">def</span> <span class="nf">resize_image</span><span class="p">(</span><span class="n">img</span><span class="p">,</span> <span class="n">size</span><span class="p">)</span>
</span><span class='line'> <span class="n">size</span> <span class="o">=</span> <span class="n">size</span><span class="o">.</span><span class="n">first</span> <span class="k">if</span> <span class="n">size</span><span class="o">.</span><span class="n">is_a?</span><span class="p">(</span><span class="nb">Array</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="n">size</span><span class="o">.</span><span class="n">length</span> <span class="o">==</span> <span class="mi">1</span>
</span><span class='line'> <span class="k">if</span> <span class="n">size</span><span class="o">.</span><span class="n">is_a?</span><span class="p">(</span><span class="no">Fixnum</span><span class="p">)</span> <span class="o">||</span> <span class="p">(</span><span class="n">size</span><span class="o">.</span><span class="n">is_a?</span><span class="p">(</span><span class="nb">Array</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="n">size</span><span class="o">.</span><span class="n">first</span><span class="o">.</span><span class="n">is_a?</span><span class="p">(</span><span class="no">Fixnum</span><span class="p">))</span>
</span><span class='line'> <span class="k">if</span> <span class="n">size</span><span class="o">.</span><span class="n">is_a?</span><span class="p">(</span><span class="no">Fixnum</span><span class="p">)</span>
</span><span class='line'> <span class="n">resize_and_crop</span><span class="p">(</span><span class="n">img</span><span class="p">,</span> <span class="n">size</span><span class="p">)</span>
</span><span class='line'> <span class="k">else</span>
</span><span class='line'> <span class="n">size</span><span class="o">[</span><span class="mi">0</span><span class="o">]</span> <span class="o">==</span> <span class="n">size</span><span class="o">[</span><span class="mi">1</span><span class="o">]</span> <span class="p">?</span> <span class="n">resize_and_crop</span><span class="p">(</span><span class="n">img</span><span class="p">,</span> <span class="n">size</span><span class="o">[</span><span class="mi">0</span><span class="o">]</span><span class="p">)</span> <span class="p">:</span> <span class="n">img</span><span class="o">.</span><span class="n">resize</span><span class="p">(</span><span class="n">size</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s1">&#39;x&#39;</span><span class="p">))</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'> <span class="k">else</span>
</span><span class='line'> <span class="n">img</span><span class="o">.</span><span class="n">resize</span><span class="p">(</span><span class="n">size</span><span class="o">.</span><span class="n">to_s</span><span class="p">)</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'> <span class="nb">self</span><span class="o">.</span><span class="n">temp_path</span> <span class="o">=</span> <span class="n">img</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'>
</span><span class='line'> <span class="k">def</span> <span class="nf">resize_and_crop</span><span class="p">(</span><span class="n">image</span><span class="p">,</span> <span class="n">square_size</span><span class="p">)</span>
</span><span class='line'> <span class="k">if</span> <span class="n">image</span><span class="o">[</span><span class="ss">:width</span><span class="o">]</span> <span class="o">&lt;</span> <span class="n">image</span><span class="o">[</span><span class="ss">:height</span><span class="o">]</span>
</span><span class='line'> <span class="n">shave_off</span> <span class="o">=</span> <span class="p">((</span><span class="n">image</span><span class="o">[</span><span class="ss">:height</span><span class="o">]</span> <span class="o">-</span> <span class="n">image</span><span class="o">[</span><span class="ss">:width</span><span class="o">]</span><span class="p">)</span><span class="o">/</span><span class="mi">2</span><span class="p">)</span><span class="o">.</span><span class="n">round</span>
</span><span class='line'> <span class="n">image</span><span class="o">.</span><span class="n">shave</span><span class="p">(</span><span class="s2">&quot;0x</span><span class="si">#{</span><span class="n">shave_off</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
</span><span class='line'> <span class="k">elsif</span> <span class="n">image</span><span class="o">[</span><span class="ss">:width</span><span class="o">]</span> <span class="o">&gt;</span> <span class="n">image</span><span class="o">[</span><span class="ss">:height</span><span class="o">]</span>
</span><span class='line'> <span class="n">shave_off</span> <span class="o">=</span> <span class="p">((</span><span class="n">image</span><span class="o">[</span><span class="ss">:width</span><span class="o">]</span> <span class="o">-</span> <span class="n">image</span><span class="o">[</span><span class="ss">:height</span><span class="o">]</span><span class="p">)</span><span class="o">/</span><span class="mi">2</span><span class="p">)</span><span class="o">.</span><span class="n">round</span>
</span><span class='line'> <span class="n">image</span><span class="o">.</span><span class="n">shave</span><span class="p">(</span><span class="s2">&quot;</span><span class="si">#{</span><span class="n">shave_off</span><span class="si">}</span><span class="s2">x0&quot;</span><span class="p">)</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'> <span class="n">image</span><span class="o">.</span><span class="n">resize</span><span class="p">(</span><span class="s2">&quot;</span><span class="si">#{</span><span class="n">square_size</span><span class="si">}</span><span class="s2">x</span><span class="si">#{</span><span class="n">square_size</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
</span><span class='line'> <span class="k">return</span> <span class="n">image</span>
</span><span class='line'> <span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>
<p>The resize_image method is changed a little to ensure that resize_and_crop is called if both requested dimensions are the same. This occurs if you specify it as a single number, or as an array of two numbers that are the same.</p>
<p>eg: <code>:thumb =&gt; [80,80]</code></p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Image management that will scale]]></title>
<link href="http://www.craigambrose.com/blog/2007/11/27/image-management-that-will-scale/"/>
<updated>2007-11-27T14:33:00+13:00</updated>
<id>http://www.craigambrose.com/blog/2007/11/27/image-management-that-will-scale</id>
<content type="html"><![CDATA[<p>There is a lot of conflicting information around about handling user uploaded images in rails applications. I&#8217;ve done it a number of different ways, and the good news is that it&#8217;s not too hard to move from one system to another. However, dealing with scaling issues is a pain and it&#8217;s nice to get it right first go. So, here are some problems that I&#8217;ve encountered recently, along with some solutions.</p>
<h2>Files Per Directory Limit</h2>
<p>Depending on which OS you use for hosting, you&#8217;ve probably got a limit to the number of files (or directories) you can put inside a given directory. It&#8217;s usually about 32,000. While this seems like a long way off, if your site accepts user content then hopefully this will eventually become a problem for you. There have been various talks and articles written about different hashing systems for file names, but it&#8217;s worth mentioning that this is basically a solved problem, and you shouldn&#8217;t have to tackle it yourself.</p>
<p>If you&#8217;re still using file_column, as I am for a few things, then this one might bite you. The simplest solution, I think, is to migrate to attachment_fu. The file system store for attachment_fu implements file name based hashing, and the s3 and database stores don&#8217;t suffer from the problem at all. Also, the way in which attachment_fu handles pluggable storage classes means that you could also slip in your own custom storage system later without having to change the way that you use attachment_fu in your models.</p>
<p>If you&#8217;re thinking of making the switch, consult the article I wrote on migrating from file column to attachment_fu.</p>
<h2>RMagick Memory Leaks</h2>
<p>RMagick is really handy, and so just about every rails image handling tutorial on the internet recommends it&#8217;s use. I&#8217;m using it all over the place. My advice to you, is don&#8217;t ever do this. It turns out that RMagick leaks memory every time it manipulates an image. I haven&#8217;t measured the amount myself, but I&#8217;m told it&#8217;s quite a bit. Certainly I&#8217;ve been having resource consumption problems with scripts using RMagick heavily. So, say goodbye to it.</p>
<p>DHH recommended just using the image magick binaries manually. That&#8217;s basically a good idea, but a slightly easier way of doing that is to use the mini_magick gem. Mini magick provides a ruby API, but under the hood it just calls the image magick command line tools. Attachment_fu comes with a mini magic processor, so you can just add <code>:processor =&gt; :MiniMagick</code> to your call to has_attachment and you&#8217;re in business. Khamsouk Souvanlasy wrote <a href="http://khamsouk.souvanlasy.com/2007/5/1/ajax-file-uploads-in-rails-using-attachment_fu-and-responds_to_parent/comments/292">a good tutorial</a> on using mini_magick with acts_as_attachment.</p>
<h2>Cropping</h2>
<p>The one thing I noticed in using attachment_fu instead of file column is that file column resizes images and crops them nicelly, the way that you would expect. By default, attachment_fu tends to stretch them. This has been covered better by other people, so I just want to mention it because stretching is almost certainly not what you want, and until Rick fixes it, I&#8217;d suggest making a small change to the plugin yourself. There are a number of articles on the subject, but I think the best one is probably over at <a href="http://toolmantim.com/article/2006/9/12/generating_cropped_thumbnails_with_acts_as_attachment">toolman tim&#8217;s</a> blog.</p>
<p>Don&#8217;t just go with Tim&#8217;s solution though, have a look at the comments, and you will find options for the different image processesors. I used &#8220;labrat&#8217;s&#8221; suggested fix for mini magick (paste <a href="http://pastie.caboo.se/64069">here</a>)</p>
<h2>Amazon S3</h2>
<p>Amazon S3 appears to be a great solution for handling user generated images, and I&#8217;m starting to use it a fair bit. One word of warning however, is that I&#8217;ve already started to encounter an occasional communication error with amazon, as discussed in <a href="http://www.ruby-forum.com/topic/110842">this thread</a> and I don&#8217;t yet know how serious it is or how easily fixed. I&#8217;ll post some more on this subject when I&#8217;m better informed.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Caching Makes Your Brain Explode]]></title>
<link href="http://www.craigambrose.com/blog/2007/11/13/caching-makes-your-brain-explode/"/>
<updated>2007-11-13T14:29:00+13:00</updated>
<id>http://www.craigambrose.com/blog/2007/11/13/caching-makes-your-brain-explode</id>
<content type="html"><![CDATA[<p>I&#8217;ve been spending a lot of time recently trying to make <a href="http://boxedup.com">boxedup.com</a> scale. Before I started, I&#8217;d watched the right screen-casts, read the right books, and I thought I knew what had to be done to speed up rails applications when the need arose.</p>
<p><strong>Boy, was I wrong.</strong></p>
<p>A quick look at the three methods of caching rails pages reveals that page caching is of no use to a site which insists on displaying the current user on all pages (as most of them seem to). Next up is action caching, which does let me execute before and after filters, allowing me to handle to logged in user, but caches the entire rendered action, including the layout, so once again I can&#8217;t display the currently logged in user. There are possibly some ways around this, but since action caching is really just a specialised form of fragment caching, lets talk about that.</p>
<h2>Fragment Caching</h2>
<p>Fragment caching does work. In fact, my first attempts at it benchmarked so well in my simplistic &#8220;load this page 100 times in httperf&#8221; tests that I dived in head first. The books on this subject, particularly the pragprog one, give the impression that this is pretty straight forward. It&#8217;s not. There are some <strong>massive</strong> gotchas that will bring even a fairly low traffic site to it&#8217;s knees if you don&#8217;t watch out for them.</p>
<h2>It&#8217;s All About Expiry</h2>
<p>You can&#8217;t consider caching without thinking about cache expiry. In rails, this is typically done with cache sweepers. For fragment caching, the sweepers call the expire fragment method. This can take a string, which matches the fragment name exactly, or it can take a regular expression.</p>
<h3>Gotcha #1, Don&#8217;t Use expire_fragment With A Regex</h3>
<p>First up, this doesn&#8217;t work with memcache anyway, it only works with the file system cache. There&#8217;s nothing that wrong with the file system cache. Reading from it is faster than rendering a template. Expiring from it, however, is pretty slow. Expiring from it using a regex is absolutely appalling. The reason why is better explained <a href="http://gurge.com/blog/2007/02/04/rails-expire_fragmentregex-considered-harmful/">in this article</a> by Adam Doppelt.</p>
<p>So, if you can&#8217;t expire it with a regex, that leaves you the following options for expiry:</p>
<ul>
<li>Time based expiry. There are some plugins that add this feature to the file system store. Memcache gives it to you for free, and if you&#8217;re relying on this heavily, I&#8217;d use memcache.</li>
<li>Being in one of those good situations where the number of possible fragments is known, and you can expire them each explicitly. This didn&#8217;t work for me in some of the critical areas that I needed to cache.</li>
<li>Storing a list (in the database) of the caches that you built up which need to be expired if a certain thing is changed.</li>
</ul>
<h2>Don&#8217;t Expire, Just Render it Obsolete</h2>
<p>This article so far doesn&#8217;t really capture how much pain this stuff has caused me, and I&#8217;ll try and cover some other points in other articles. For now, lets jump straight to the good bit.</p>
<p>I&#8217;ve read a lot of articles on caching, but this is the best, go read it:</p>
<p><a href="http://blog.leetsoft.com/2007/5/22/the-secret-to-memcached">The Secret To Memcached - by Tobias Lütke</a></p>
<p>Tobias also struggled with expiry, and his solution is to take advantage of the fact that if you&#8217;re using memcache, then you can never cache too many items. The oldest ones get pushed out when you run out of of space.</p>
<p>So, here&#8217;s my first bit of advice. If you&#8217;re building a real site, go straight to memcache. If you&#8217;re not building a site for big traffic, don&#8217;t cache, just optimise any really stupid queries that are giving you trouble. If you&#8217;re using memcache, be sure to run monit too.</p>
<p>So, we&#8217;re running memcache, and we don&#8217;t want to expire our fragments. Instead, try and find fragment keys that don&#8217;t need to be expired, because they will be replaced if the data changes.</p>
<p>The one I&#8217;ve just implemented was a stream of recent activity, much like facebook. Each little type of activity had a different template, and the rendering of this took up a lot of time. Fetching the data was also non-trivial. However, if I wanted to expire a cache of the activity stream, then I&#8217;d need to do so anytime something occured on the site that triggered an activity for this user.</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='erb'><span class='line'><span class="cp">&lt;%</span> <span class="n">cache</span> <span class="o">[</span><span class="s2">&quot;activity_stream&quot;</span><span class="p">,</span> <span class="vi">@latest_activity</span><span class="o">.</span><span class="n">id</span><span class="p">,</span> <span class="vi">@user</span><span class="o">.</span><span class="n">id</span><span class="o">].</span><span class="n">to_s</span> <span class="k">do</span> <span class="cp">%&gt;</span><span class="x"></span>
</span><span class='line'><span class="x"> ... render the activities</span>
</span><span class='line'><span class="cp">&lt;%</span> <span class="k">end</span> <span class="cp">%&gt;</span><span class="x"></span>
</span></code></pre></td></tr></table></div></figure>
<p>There&#8217;s the code. The real example had a few more parameters, but you can see here that the magic is in the fact that I used <code>@latest_activity.id</code> as part of the key. I&#8217;m still having to query that from the database, but it&#8217;s pretty simple to do, and all I really need is one little integer, instead of all the activities and their associated objects. If a new activity is created for this user, then this id will have changed, and so I&#8217;ll me asking for a different cache key.</p>
<p>Benchmarks for this are looking really good. I&#8217;ll let you know how it goes in the wild, but I&#8217;m not expecting too many problems as most of my previous troubles have been to do with expiry, and this code doesn&#8217;t need any expiry. No sweepers, no regular expressions. It&#8217;s simple, and it scales.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Migrating from Typo to Simplelog]]></title>
<link href="http://www.craigambrose.com/blog/2007/05/17/migrating-from-typo-to-simplelog/"/>
<updated>2007-05-17T14:27:00+12:00</updated>
<id>http://www.craigambrose.com/blog/2007/05/17/migrating-from-typo-to-simplelog</id>
<content type="html"><![CDATA[<p>I&#8217;ve moved this blog from &#8220;Typo&#8221;:http://www.typosphere.org/, to &#8220;Simplelog&#8221;:http://simplelog.net/, both rails based blogging systems. Normally I don&#8217;t do this sort of meta-blogging, but I though I&#8217;d share a few tips about the migration, and some comments on Simplelog.</p>
<p>h2. Installation</p>
<p>Like the other rails blogs, simplelog has some installation instructions for how to get the simplelog files on your sever, and like all rails developers, I promptly ignored them, and setup a copy of simplelog in a subversion repository of my own and deployed to my server using capistrano. This makes deployment and upgrading much easier, as I can test new simplelog versions on my local machine first, and then roll out updates using @cap deploy@ as normal.</p>
<p>h2. Migrating your Data</p>
<p>There don&#8217;t seem to be any nice scripts for migrating data from Typo to Simplelog. There are some scripts for exporting typo in data into Moveable Type or word press format, and you could probably hook together two or three different scripts to eventually get from Typo to Simplelog, but I couldn&#8217;t figure out a simple way to do that without also installing a PHP app, and that just seemed way too complex, so I thought I&#8217;d roll my own.</p>
<p>The bad news I&#8217;m afraid, is that I haven&#8217;t bothered to sufficiently generalise it to make it work for everyone, but if you want to do this too, then you can use my code as a starting point and refine it for your needs.</p>
<p>When you want to script some little task in rails, the tool for the job is of course rake. I started working on a little rake task inside my typo installation, to export the data in a format I could use for simplelog. I started by comparing both database schemas, and seeing how I could generate SQL to use with simplelog.</p>
<p>It turns out that both these databases are non-trivial. They use single-table inheritance, and the also both have cache columns full of keywords and search text and so forth. The active record classes in both apps have the code to do this, so the best way to convert your data is to leverage that code. You could write a ruby app that loads both model layers and connects to both database, but that didn&#8217;t seem too trivial, so what I ended up doing was creating a typo rake export task that generated a simplelog rake import task.</p>
<p>h2. The Code</p>
<p>Here&#8217;s the code, it only copies the data I was interested in (articles and comments), and it does so with a few specific values (such as a fixed author id). It also, on large databases, will hit some id conflicts as it does so. To fix this, run it multiple times until the auto-numbered ids become bigger than the ids in your old database (or solve that problem properly). Also, it&#8217;s butt ugly, remember, I just wanted to do this in an hour and never see it again.</p>
<p>Before running this script, make sure that you have setup your simplelog database (with the migrations), and created a user (my had id 2, which is hardcoded in the script). Also, set the default markup format in simplelog to be whatever you used in typo (I was using textile).</p>
<pre><code>
def q(value)
value = '' if value.nil?
if value.is_a? String
escaped = value.gsub(/\\/, '\\\\').gsub(/\r\n/, "\n").gsub(/'/, "\\\\'").gsub(/\n/, '\' + "\\\\n" + \'')
return "'#{escaped}'"
end
value
end
</code></pre>
<pre><code>
task :typo_export => [:environment] do
file = File.new("/path_to_craigs_simplelog/lib/tasks/import_from_typo.rake", 'w')
file << "task :import_from_typo => [:environment] do \n"
file << " require 'find'\n"
file << " require 'post'\n"
file << " require 'preference'\n"
file << " require 'application'\n"
file << " Post.delete_all\n"
file << " update_posts = \"\"\n"
for article in Article.find(:all)
file << " new_record = Post.create(:author_id => 2, :created_at => '#{article.created_at.to_s(:db)}'.to_time, :modified_at => '#{article.updated_at.to_s(:db)}'.to_time, :permalink => #{q article.permalink}, :title => #{q article.title}, :body_raw => #{q article.body}, :comment_status => 1, :text_filter => #{q 'textile'})\n"
file << " update_posts += \"UPDATE posts SET id = #{article.id} WHERE id = \#{new_record.id}; \"\n\n"
end
file << " Post.connection.execute update_posts\n\n"
file "\n\n"
file << " Comment.delete_all\n"
file << " update_comments = \"\"\n"
for comment in Comment.find(:all)
file << " rew_record = Comment.create(:post_id => #{comment.article.id}, :name => #{q comment.author}, :email => #{q (comment.email.blank? ? 'anon@noemail.com' : comment.email)}, :subject => '', :body_raw => #{q comment.body}, :ip => #{q comment.ip}, :is_approved => 1, :url => #{q comment.url})\n"
file << " update_comments += \"UPDATE comments SET id = #{comment.id}, is_approved = 1 WHERE id = \#{new_record.id}\"\n\n"
end
file << " Post.connection.execute update_comments\n\n"
file << "end\n"
end
</code></pre>
<p>Once this is run, you can go to the simplelog installation and run @rake import_from_typo@. If it doesn&#8217;t work, figure out what went wrong, modify the script, and repeat. Please notice that this is a destructive import (note the calls to &#8220;delete_all&#8221;).</p>
<p>This is a long post already, so I&#8217;ll post comments on simplelog next time.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[How Do You Freeze Your Rails Version?]]></title>
<link href="http://www.craigambrose.com/blog/2007/05/15/how-do-you-freeze-your-rails-version/"/>
<updated>2007-05-15T14:24:00+12:00</updated>
<id>http://www.craigambrose.com/blog/2007/05/15/how-do-you-freeze-your-rails-version</id>
<content type="html"><![CDATA[<p>The first time a Ruby on Rails release introduced a few backwards incompatible changes, it caused a bit of an uproar (the one I&#8217;m thinking of was 1.0.1, which broke Typo, amongst other things). Suddenly, everyone realised that we were writing web applications that weren&#8217;t just dependent on Ruby on Rails, they were dependent on <strong>a particular version</strong> of Rails.</p>
<p>So, these days we all know that we need to lock our Rails applications into using a set version. There are two ways of doing this, and each has its pros and cons.</p>
<h2>[1] Freezing Rails in the Vendor Directory</h2>
<p>If your application finds a copy of Rails in the ./vendor/rails directory, then it will use that instead of whatever rails gems are installed on the system. This is really handy, and it&#8217;s the method that I initially adopted for all my sites when the rails 1.0.1 problem occurred. At first, I was copying the Rails files into my project subversion repositories, but I quickly learned that the easier way to do it was with subversion externals.</p>
<p>From the root of your rails application, execute:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>svn propedit svn:externals vendor/
</span></code></pre></td></tr></table></div></figure>
<p>The subversion externals properties from that directory will now be editable in your default editor. Each line in this folder represents a link to an external repository, with the name of the local folder to export to first, then the repository URL. So, for example, to use rails version 1.2.3, we would use the following.</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>rails http://svn.rubyonrails.org/rails/tags/rel_1-2-3/
</span></code></pre></td></tr></table></div></figure>
<p>You can find the URL of that tag, or any other rails tag or branch (or the trunk itself) by browsing the <a href="http://svn.rubyonrails.org/rails/">rails repository</a>.</p>
<p>The upside of this method, is that your application is safe to deploy on almost any machine with the correct ruby stack installed, even if the rails gems are not present.</p>
<p>The downside, is that rails is pretty large, and every time you do a svn checkout, it has to grab it all. In particular this slows down your @svn deploy@ command. If your using deprec, it can really slow down your <code>svn setup</code> command too, because the file permissions have to be set on all those folders.</p>
<h2>[2] Specifying the Rails Version in environment.rb</h2>
<p>This is the accepted method now, and is done by default in all new rails applications. To lock in that same version number, all you need to do is add the following to your environment.rb, if it isn&#8217;t already present:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="no">RAILS_GEM_VERSION</span> <span class="o">=</span> <span class="s1">&#39;1.2.3&#39;</span> <span class="k">unless</span> <span class="n">defined?</span>
</span></code></pre></td></tr></table></div></figure>
<p>This means that rails will load the gem for rails 1.2.3, if it exists. If it doesn&#8217;t exist, it will throw an error, rather than run your application. Remember that <code>gem</code> doesn&#8217;t remove old versions when it installs new ones, so even if a newer version of rails is installed on the server, if the correct version was there once, it should stay better.</p>
<p>These days, I&#8217;m moving my sites over to using this method, for the reasons of hard disk space and speed that I mentioned above.</p>
<p>The only downside is, I need to make sure that the right version of the gem is installed. However, most applications have other gem dependencies too, apart from just rails.</p>
<p>How do <strong>you</strong> ensure that the required gems are installed on a new server? If you&#8217;re logging in to the server manually and installing the gems, have a think about automation. Surely this is the province of the <code>cap setup</code> task. It might seem easy to do it now, but don&#8217;t forget that you might have to do it again when you move servers in six months time. Also, when that happens, you might be in a hurry. You might also be migrating to a multi-server cluster.</p>
<p>Don&#8217;t wait, automate now. Wack all your setup needs into cap tasks. Consider using before or after filters on the <code>cap setup</code> action. Please note that if you&#8217;re using deprec, it already adds these filters, so you&#8217;ll need to use an action called <code>after_after_setup</code>. This problem is fixed in capistrano 2, which should be out as a stable release very soon, but until then, you can do it the hard way.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Securing your Mongrel Processes with Deprec]]></title>
<link href="http://www.craigambrose.com/blog/2007/05/08/securing-your-mongrel-processes-with-deprec/"/>
<updated>2007-05-08T14:21:00+12:00</updated>
<id>http://www.craigambrose.com/blog/2007/05/08/securing-your-mongrel-processes-with-deprec</id>
<content type="html"><![CDATA[<p>For a long time, on many of my Rails apps, I&#8217;ve been letting my Mongrel processes run as the root user. Any sysadmin worth her salt will tell you that this is a bad idea, because any exploits in you web application will give the malicious user root access to your machine, and also any bugs in your application have the potential to destroy all sorts of important data. For example, try adding <code>&lt;% rm_rf '/' %&gt;</code> to one of your rails templates and you&#8217;ll see what I mean (that&#8217;s sarcasm by the way, don&#8217;t do it).</p>
<p>So, we need to run those mongrel processes with a user with less privileges. Our mongrel user should be able to read our entire rails application, and only be able to write to those relevant directories, such as logs, cache, sessions, pids and so on.</p>
<p>This security problem was a significant issue for my current client, and so we hired Mike Bailey (the guy who writes the deprec gem), to ensure that deprec has a system for handling this properly. It&#8217;s in the latest deprec release, and here&#8217;s how it works.</p>
<h2>Deprec 1.7</h2>
<p>Deprec has had a whole swathe of small releases in the last couple of weeks. Mike is back from his adventures in india, and busy fixing bugs and adding new features. By the time you read this, the release number might be even bigger, but these instructions on how to apply to old sites will probably still be valid. For new sites, you don&#8217;t really have to worry about this stuff.</p>
<h2>Before Upgrading</h2>
<p>Make sure that your gems are up to date on the server.</p>
<p>Why do you need to do this? Well it turns out that mongrel 1.0.1 came out recently, and at the moment, deprec can&#8217;t correctly answer the questions that gem will ask about which version to use when upgrading. If you hit this problem, or want to avoid it, log into your server(s) and run <code>sudo gem update</code> now. You can decide which version of mongrel you want (probably 2 - ruby) and then deprec wont have any troubles.</p>
<p>On an app that&#8217;s been around for a while you might also want to run <code>cap cleanup</code>, if you don&#8217;t already do this every now and then to remove old releases on the server. This will make the following tasks run much faster.</p>
<h2>Applying the New Groups and Permissions</h2>
<p>Remember the <code>cap setup</code> task that sets up your initial directory structure for an application? Deprec also adds to that task to ensure that file permissions are correct, and the mongrel cluster set up. This task is re-runnable. It doesn&#8217;t hurt to run it as often as you want, although it is pretty slow. By running it with the new deprec release, you can setup the right file permissions to use mongrel as a non-root user.</p>
<p>Please note that the last step performed by the setup task is to create the database. This currently fails, as the database already exists. This bug will get fixed, but it doesn&#8217;t matter, just ignore the error.</p>
<h2>Roll Out a New Version</h2>
<p>To get your app rolled out with the new permissions, run <code>cap deploy</code>, even if it hasn&#8217;t changed since the last release.</p>
<h2>Consider Updating Your Rails Stack</h2>
<p>If you setup your machine with deprec some time ago, as I did, then there will be a few bugs. In particular there was an old bug where correct initialisation scripts for mongrel and apache where not added to the default runlevel, and they didn&#8217;t start on launch. To make sure that your system is configured as per the latest deprec, run: <code>cap install_rails_stack</code>. This is also always re-runnable to bring your system up to date.</p>
<h2>Summary</h2>
<p>So, for upgrading an old server to the latest deprec, my recommendations are:</p>
<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class=''><span class='line'># first, login to the server and run "sudo gem update"
</span><span class='line'>cap cleanup
</span><span class='line'>cap setup
</span><span class='line'>cap deploy
</span><span class='line'>cap install_rails_stack
</span><span class='line'>cap restart_apache</span></code></pre></td></tr></table></div></figure>
<p>For setting up a new server, instructions vary slightly according to your host, but for an Ubuntu 6.0.6 server at slicehost.com, I use the following process:</p>
<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>export HOSTS=your.hostname.com
</span><span class='line'>cap setup_admin_account_as_root
</span><span class='line'>cap setup_ssh_keys
</span><span class='line'>cap install_rails_stack
</span><span class='line'>cap setup
</span><span class='line'>cap deploy_with_migrations
</span><span class='line'>cap restart_apache</span></code></pre></td></tr></table></div></figure>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[A Rake Task to Grab Data From Your Production Site]]></title>
<link href="http://www.craigambrose.com/blog/2007/04/17/a-rake-task-to-grab-data-from-your-production-site/"/>
<updated>2007-04-17T14:19:00+12:00</updated>
<id>http://www.craigambrose.com/blog/2007/04/17/a-rake-task-to-grab-data-from-your-production-site</id>
<content type="html"><![CDATA[<p>When you&#8217;re fixing bugs that occur in a production rails application, the first step is often to reproduce them locally in development mode. Every now and then, I find that I want to make my local development site run with the same data as my live production site, so here&#8217;s a little rake task to make it easier.</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">desc</span> <span class="s2">&quot;Make local develoment site look like the live site&quot;</span>
</span><span class='line'><span class="n">task</span> <span class="ss">:sync</span> <span class="k">do</span>
</span><span class='line'> <span class="n">host</span> <span class="o">=</span> <span class="s1">&#39;craigambrose.com&#39;</span>
</span><span class='line'> <span class="n">path</span> <span class="o">=</span> <span class="s1">&#39;/var/www/apps/craigambrose/current&#39;</span>
</span><span class='line'>
</span><span class='line'> <span class="n">db_config</span> <span class="o">=</span> <span class="no">YAML</span><span class="o">.</span><span class="n">load_file</span><span class="p">(</span><span class="s1">&#39;config/database.yml&#39;</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'> <span class="nb">system</span> <span class="s2">&quot;ssh </span><span class="si">#{</span><span class="n">host</span><span class="si">}</span><span class="s2"> </span><span class="se">\&quot;</span><span class="s2">mysqldump -u </span><span class="si">#{</span><span class="n">db_config</span><span class="o">[</span><span class="s1">&#39;production&#39;</span><span class="o">][</span><span class="s2">&quot;username&quot;</span><span class="o">]</span><span class="si">}</span><span class="s2"> -p</span><span class="si">#{</span><span class="n">db_config</span><span class="o">[</span><span class="s1">&#39;production&#39;</span><span class="o">][</span><span class="s2">&quot;password&quot;</span><span class="o">]</span> <span class="si">}</span><span class="s2"> -Q --add-drop-table -O add-locks=FALSE -O lock-tables=FALSE --ignore-table=</span><span class="si">#{</span><span class="n">db_config</span><span class="o">[</span><span class="s1">&#39;production&#39;</span><span class="o">][</span><span class="s2">&quot;database&quot;</span><span class="o">]</span><span class="si">}</span><span class="s2">.sessions </span><span class="si">#{</span><span class="n">db_config</span><span class="o">[</span><span class="s1">&#39;production&#39;</span><span class="o">][</span><span class="s2">&quot;database&quot;</span><span class="o">]</span><span class="si">}</span><span class="s2"> &gt; ~/dump.sql</span><span class="se">\&quot;</span><span class="s2">&quot;</span>
</span><span class='line'> <span class="nb">system</span> <span class="s2">&quot;rsync -az --progress </span><span class="si">#{</span><span class="n">host</span><span class="si">}</span><span class="s2">:~/dump.sql ./db/production_data.sql&quot;</span>
</span><span class='line'> <span class="nb">system</span> <span class="s2">&quot;mysql -u </span><span class="si">#{</span><span class="n">db_config</span><span class="o">[</span><span class="s1">&#39;development&#39;</span><span class="o">][</span><span class="s2">&quot;username&quot;</span><span class="o">]</span><span class="si">}</span><span class="s2"> -p</span><span class="si">#{</span><span class="n">db_config</span><span class="o">[</span><span class="s1">&#39;development&#39;</span><span class="o">][</span><span class="s2">&quot;password&quot;</span><span class="o">]</span><span class="si">}</span><span class="s2"> </span><span class="si">#{</span><span class="n">db_config</span><span class="o">[</span><span class="s1">&#39;development&#39;</span><span class="o">][</span><span class="s2">&quot;database&quot;</span><span class="o">]</span><span class="si">}</span><span class="s2"> &lt; ./db/production_data.sql&quot;</span>
</span><span class='line'> <span class="nb">system</span> <span class="s2">&quot;rsync -az --progress </span><span class="si">#{</span><span class="n">host</span><span class="si">}</span><span class="s2">:</span><span class="si">#{</span><span class="n">path</span><span class="si">}</span><span class="s2">/public/system/ ./public/system&quot;</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>
<h2>Installation</h2>
<p>Rake tasks can go in any file ending in <em>.rake</em> inside your <em>lib/tasks</em> directory (within a rails application). You might put this in a file specific to your application, like <em>craigambrose.rake</em>. You can also namespace rake tasks by putting them inside a blog like:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="n">namespace</span> <span class="ss">:craigambrose</span> <span class="k">do</span>
</span><span class='line'> <span class="c1"># your tasks go here</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>
<p>With my task, you&#8217;ll need to set the value of two local variables, host and path. Or, perhaps you can think of a way to read those from your deploy.rb?</p>
<h2>Usage</h2>
<p>If you&#8217;ve namespaced it like I have, then it&#8217;ll be something like:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='bash'><span class='line'>rake craigambrose:sync
</span></code></pre></td></tr></table></div></figure>
<p>Note that this uses a lot of shell commands, and basically assumes that you are developing on a unix box of some sort. You can delete the rsync for /public/system if you don&#8217;t store any files there.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Feed Fetcher: A plugin for finding RSS feeds from URLs]]></title>
<link href="http://www.craigambrose.com/blog/2007/03/27/feed-fetcher-a-plugin-for-finding-rss-feeds-from-urls/"/>
<updated>2007-03-27T14:16:00+12:00</updated>
<id>http://www.craigambrose.com/blog/2007/03/27/feed-fetcher-a-plugin-for-finding-rss-feeds-from-urls</id>
<content type="html"><![CDATA[<p>I&#8217;ve got a site with a few blog aggregation features, and when users type in their blog URL to add to the site, they don&#8217;t seem to know the difference between the blog html page URL and the blog RSS feed URL.</p>
<p>So, I wanted the site to work regardless of which one they used, by finding RSS resource links from the HTML page (if that&#8217;s what they enter). This is what Google Reader and all the other nice looking blog readers do.</p>
<p>It&#8217;s not earth shatteringly exciting code, but it&#8217;s good enough to re-use, and it&#8217;s fully tested, so I&#8217;ve made into a plugin. Now there&#8217;s no excuse for offering a sloppy user experience for subscribing to feeds.</p>
<h2>Installation</h2>
<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'> script/plugin install svn://rubyforge.org/var/svn/ambroseplugins/feed_fetcher</span></code></pre></td></tr></table></div></figure>
<h2>Usage</h2>
<p>The main function that you&#8217;ll be using is the class method get_feed_source on the FeedFetcher class. If successful, it&#8217;ll return a FeedSource object, that you can interrogate for the feed details or the items in the current feed. If it fails, it&#8217;ll throw an exception that you can use to determine the problem and return a message to the user.</p>
<p>Here&#8217;s the code that I use to call the feed fetcher from within my Blog model. If it works, I&#8217;m setting a couple of instance members on self.</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">begin</span>
</span><span class='line'> <span class="n">result</span> <span class="o">=</span> <span class="no">FeedFetcher</span><span class="o">::</span><span class="no">FeedFetcher</span><span class="o">.</span><span class="n">get_feed_source</span><span class="p">(</span><span class="n">site_url</span><span class="p">)</span>
</span><span class='line'> <span class="k">if</span> <span class="n">result</span>
</span><span class='line'> <span class="nb">self</span><span class="o">.</span><span class="n">feed_url</span> <span class="o">=</span> <span class="n">result</span><span class="o">.</span><span class="n">url</span>
</span><span class='line'> <span class="nb">self</span><span class="o">.</span><span class="n">title</span> <span class="o">=</span> <span class="n">result</span><span class="o">.</span><span class="n">title</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'><span class="k">rescue</span> <span class="no">FeedFetcher</span><span class="o">::</span><span class="no">NoFeedForPageError</span>
</span><span class='line'> <span class="vi">@feed_error</span> <span class="o">=</span> <span class="s2">&quot;Sorry, we couldn&#39;t find a feed for this URL. Your blog needs to have a RSS feed facility for us to use it on Playful Bent.&quot;</span>
</span><span class='line'><span class="k">rescue</span> <span class="no">FeedFetcher</span><span class="o">::</span><span class="no">PageFeedError</span>
</span><span class='line'> <span class="vi">@feed_error</span> <span class="o">=</span> <span class="s2">&quot;You blog has a RSS feed, which is great. However, it doesn&#39;t work for us right now, which is less great. Sorry, this wont work.&quot;</span>
</span><span class='line'><span class="k">rescue</span> <span class="no">FeedFetcher</span><span class="o">::</span><span class="no">PageDoesntExistError</span>
</span><span class='line'> <span class="vi">@feed_error</span> <span class="o">=</span> <span class="s2">&quot;Are you sure you typed that right? We just tried to fetch that URL and we couldn&#39;t find anything there at all.&quot;</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[The Dangers of Removing Duplication]]></title>
<link href="http://www.craigambrose.com/blog/2007/03/16/the-dangers-of-removing-duplication/"/>
<updated>2007-03-16T14:14:00+13:00</updated>
<id>http://www.craigambrose.com/blog/2007/03/16/the-dangers-of-removing-duplication</id>
<content type="html"><![CDATA[<p>Now there is a controversial title.</p>
<p>I&#8217;m not trying to say that it&#8217;s dangerous to remove duplication in your code, merely that there are dangers that you might face when doing so. Duplication in code is a <strong>bad smell</strong>. It&#8217;s a reminder to us that we need to refactor it in some way in order to improve the design and make the duplication go away.</p>
<p>Sometimes, we get so caught up in the idea the removing the duplication is the only goal that we forget about the fact that this process is actually supposed to drive good object oriented design. Looking back at rails code that I&#8217;ve written over the last year, the place where I&#8217;m seeing this more than any other is in helpers.</p>
<p>Rails helper methods are places to extract logic from the view. They are tremendously handy, and a nice easy place to stick logic that appears in several different view templates, or contains so much conditional logic that it would make our rhtml templates unreadable. However, they are also just procedures. I find myself slipping into the trap of using procedural programming techniques to remove duplication here, rather than good design practices. Sometimes you can spot this because you find yourself starting to add a lot of arguments to you helper methods.</p>
<p>I&#8217;m going to share with you an example of some of my <strong>bad</strong> code. No programmer likes to do this, but I don&#8217;t feel too bad about it because I&#8217;ve just removed this method from a project, so I can at least say <strong>I did write this bad code, but now I&#8217;ve improved it</strong>.</p>
<h2>Some Code in Need of Refactoring</h2>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">def</span> <span class="nf">editable_section_if</span><span class="p">(</span><span class="n">condition</span><span class="p">,</span> <span class="nb">name</span><span class="p">,</span> <span class="n">resource_name</span> <span class="o">=</span> <span class="kp">nil</span><span class="p">,</span> <span class="n">form_url</span> <span class="o">=</span> <span class="kp">nil</span><span class="p">)</span>
</span><span class='line'> <span class="n">rollover_link_name</span> <span class="o">=</span> <span class="s2">&quot;rollover_link_</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">&quot;</span>
</span><span class='line'> <span class="n">display_div</span> <span class="o">=</span> <span class="s2">&quot;display_</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">&quot;</span>
</span><span class='line'> <span class="n">edit_div</span> <span class="o">=</span> <span class="s2">&quot;edit_</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">&quot;</span>
</span><span class='line'> <span class="n">partial_prefix</span> <span class="o">=</span> <span class="n">resource_name</span><span class="o">.</span><span class="n">nil?</span> <span class="p">?</span> <span class="s1">&#39;&#39;</span> <span class="p">:</span> <span class="s2">&quot;</span><span class="si">#{</span><span class="n">resource_name</span><span class="si">}</span><span class="s2">/&quot;</span>
</span><span class='line'>
</span><span class='line'> <span class="n">result</span> <span class="o">=</span> <span class="s2">&quot;&quot;</span>
</span><span class='line'>
</span><span class='line'> <span class="k">if</span> <span class="n">condition</span>
</span><span class='line'> <span class="n">result</span> <span class="o">&lt;&lt;</span> <span class="s2">&quot;&lt;div class=</span><span class="se">\&quot;</span><span class="s2">editable_text</span><span class="se">\&quot;</span><span class="s2"> onmouseover=</span><span class="se">\&quot;</span><span class="s2">Element.show(&#39;</span><span class="si">#{</span><span class="n">rollover_link_name</span><span class="si">}</span><span class="s2">&#39;)</span><span class="se">\&quot;</span><span class="s2"> onmouseout=</span><span class="se">\&quot;</span><span class="s2">Element.hide(&#39;</span><span class="si">#{</span><span class="n">rollover_link_name</span><span class="si">}</span><span class="s2">&#39;)</span><span class="se">\&quot;</span><span class="s2"> id=</span><span class="se">\&quot;</span><span class="si">#{</span><span class="n">display_div</span><span class="si">}</span><span class="se">\&quot;</span><span class="s2">&gt;&quot;</span>
</span><span class='line'> <span class="n">result</span> <span class="o">&lt;&lt;</span> <span class="s1">&#39;&lt;div class=&quot;rollover_link_bar&quot;&gt;&#39;</span>
</span><span class='line'> <span class="n">result</span> <span class="o">&lt;&lt;</span> <span class="n">link_to_function</span><span class="p">(</span><span class="s2">&quot;Click to Edit&quot;</span><span class="p">,</span> <span class="s2">&quot;Element.hide(&#39;</span><span class="si">#{</span><span class="n">display_div</span><span class="si">}</span><span class="s2">&#39;); Element.show(&#39;</span><span class="si">#{</span><span class="n">edit_div</span><span class="si">}</span><span class="s2">&#39;)&quot;</span><span class="p">,</span> <span class="p">{</span><span class="ss">:class</span> <span class="o">=&gt;</span> <span class="s2">&quot;rollover_link&quot;</span><span class="p">,</span> <span class="ss">:id</span> <span class="o">=&gt;</span> <span class="n">rollover_link_name</span><span class="p">,</span> <span class="ss">:style</span> <span class="o">=&gt;</span> <span class="s2">&quot;display: none;&quot;</span><span class="p">})</span>
</span><span class='line'> <span class="n">result</span> <span class="o">&lt;&lt;</span> <span class="s1">&#39;&amp;nbsp;&lt;/div&gt;&#39;</span>
</span><span class='line'> <span class="n">result</span> <span class="o">&lt;&lt;</span> <span class="n">render</span><span class="p">(</span><span class="ss">:partial</span> <span class="o">=&gt;</span> <span class="s2">&quot;</span><span class="si">#{</span><span class="n">partial_prefix</span><span class="si">}</span><span class="s2">display_</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
</span><span class='line'> <span class="n">result</span> <span class="o">&lt;&lt;</span> <span class="s1">&#39;&lt;/div&gt;&#39;</span>
</span><span class='line'>
</span><span class='line'> <span class="n">result</span> <span class="o">&lt;&lt;</span> <span class="s2">&quot;&lt;div id=</span><span class="se">\&quot;</span><span class="si">#{</span><span class="n">edit_div</span><span class="si">}</span><span class="se">\&quot;</span><span class="s2"> style=</span><span class="se">\&quot;</span><span class="s2">display: none</span><span class="se">\&quot;</span><span class="s2">&gt;&quot;</span>
</span><span class='line'>
</span><span class='line'> <span class="n">form_url</span> <span class="o">=</span> <span class="p">{</span><span class="ss">:action</span> <span class="o">=&gt;</span> <span class="s2">&quot;update_</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">,</span> <span class="ss">:id</span> <span class="o">=&gt;</span> <span class="n">params</span><span class="o">[</span><span class="ss">:id</span><span class="o">]</span><span class="p">}</span> <span class="k">if</span> <span class="n">form_url</span><span class="o">.</span><span class="n">nil?</span>
</span><span class='line'> <span class="n">form_url</span><span class="o">.</span><span class="n">merge!</span><span class="p">(</span><span class="ss">:controller</span> <span class="o">=&gt;</span> <span class="n">resource_name</span><span class="p">)</span> <span class="k">unless</span> <span class="n">resource_name</span><span class="o">.</span><span class="n">nil?</span>
</span><span class='line'> <span class="n">result</span> <span class="o">&lt;&lt;</span> <span class="n">form_remote_tag</span><span class="p">(</span><span class="ss">:url</span> <span class="o">=&gt;</span> <span class="n">form_url</span><span class="p">,</span> <span class="ss">:method</span> <span class="o">=&gt;</span> <span class="ss">:put</span><span class="p">,</span> <span class="ss">:update</span> <span class="o">=&gt;</span> <span class="nb">name</span><span class="p">,</span> <span class="ss">:loading</span> <span class="o">=&gt;</span> <span class="s2">&quot;Element.show(&#39;spinner_</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">&#39;)&quot;</span><span class="p">,</span> <span class="ss">:loaded</span> <span class="o">=&gt;</span> <span class="s2">&quot;Element.hide(&#39;spinner_</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">&#39;)&quot;</span><span class="p">,</span> <span class="ss">:html</span> <span class="o">=&gt;</span> <span class="p">{</span><span class="ss">:class</span> <span class="o">=&gt;</span> <span class="s1">&#39;inline&#39;</span><span class="p">})</span>
</span><span class='line'>
</span><span class='line'> <span class="n">result</span> <span class="o">&lt;&lt;</span> <span class="n">render</span><span class="p">(</span><span class="ss">:partial</span> <span class="o">=&gt;</span> <span class="s2">&quot;</span><span class="si">#{</span><span class="n">partial_prefix</span><span class="si">}</span><span class="s2">edit_</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
</span><span class='line'> <span class="n">result</span> <span class="o">&lt;&lt;</span> <span class="s2">&quot;&lt;div class=</span><span class="se">\&quot;</span><span class="s2">editable_form_buttons</span><span class="se">\&quot;</span><span class="s2">&gt;&quot;</span>
</span><span class='line'> <span class="n">result</span> <span class="o">&lt;&lt;</span> <span class="n">submit_tag</span><span class="p">(</span><span class="s2">&quot;Save&quot;</span><span class="p">)</span>
</span><span class='line'> <span class="n">result</span> <span class="o">&lt;&lt;</span> <span class="s2">&quot; or &quot;</span>
</span><span class='line'> <span class="n">result</span> <span class="o">&lt;&lt;</span> <span class="n">link_to_function</span><span class="p">(</span><span class="s2">&quot;Cancel&quot;</span><span class="p">,</span> <span class="s2">&quot;Element.hide(&#39;</span><span class="si">#{</span><span class="n">edit_div</span><span class="si">}</span><span class="s2">&#39;); Element.show(&#39;</span><span class="si">#{</span><span class="n">display_div</span><span class="si">}</span><span class="s2">&#39;)&quot;</span><span class="p">)</span>
</span><span class='line'> <span class="n">result</span> <span class="o">&lt;&lt;</span> <span class="s2">&quot;&lt;/div&gt;&quot;</span>
</span><span class='line'>
</span><span class='line'> <span class="n">result</span> <span class="o">&lt;&lt;</span> <span class="s1">&#39;&lt;/form&gt;&#39;</span>
</span><span class='line'> <span class="n">result</span> <span class="o">&lt;&lt;</span> <span class="s1">&#39;&lt;/div&gt;&#39;</span>
</span><span class='line'> <span class="k">else</span>
</span><span class='line'> <span class="n">result</span> <span class="o">&lt;&lt;</span> <span class="n">render</span><span class="p">(</span><span class="ss">:partial</span> <span class="o">=&gt;</span> <span class="s2">&quot;</span><span class="si">#{</span><span class="n">partial_prefix</span><span class="si">}</span><span class="s2">display_</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'>
</span><span class='line'> <span class="n">result</span>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>
<h2>About the Code</h2>
<p>This is a helper method to generate an inline form, which remains hidden in the page. The div displaying the data is loaded from a partial, as is the contents of the form, and edit and cancel buttons are used to toggle between them with javascript. The submit button uses ajax to submit the form. Also, it uses a condition to determine if the data is even editable, and if not, just displays the data, not the form.</p>
<p>This approach is a fairly standard thing when you realise that you want to do some inline editing that&#8217;s more than just one field, and so is a bit more complex that the built in rails inline editor helpers.</p>
<p>So what are the bad smells here?</p>
<p>First up is the really heavy string manipulation. I find myself doing this in helpers a fair bit. I probably started writing this in a rhtml template and then moved it into a helper when I needed to re-use it. The ERb of a rhtml template is more suitable for something which is so html heavy. All this string append operations, and bits of html everywhere make this method really hard to read. If I was missing a closing html tag, would I be able to easily tell? Do you find yourself writing helpers like this, or is it just me? Perhaps it should be a partial.</p>
<p>Secondly, the last two parameters were actually added latter. They sound important, but they have default values of nil. They are actually an attempt to try and make this system compatible with REST. They don&#8217;t entirely succeed, and this is an ugly function to try and make work in this situation.</p>
<p>Thirdly, this method is too long. Some programmers believe that methods of this length are just fine. If you are one such, please don&#8217;t ever bother to send me your resume.</p>
<p>I have already refactored this code, and I like the way it&#8217;s coming along, so keep an eye out for another blog post soon where I&#8217;ll suggest some better ways of doing this.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Pagination Using AJAX]]></title>
<link href="http://www.craigambrose.com/blog/2007/03/12/pagination-using-ajax/"/>
<updated>2007-03-12T14:12:00+13:00</updated>
<id>http://www.craigambrose.com/blog/2007/03/12/pagination-using-ajax</id>
<content type="html"><![CDATA[<p>The rails pagination helpers automatically give us a set of links to load different pages of a result set. Occasionally, you might not want to load these other pages of results as full page loads. There&#8217;s no reason why you can&#8217;t call an index action using AJAX, and have it respond with some javascript to update the list.</p>
<p>If you&#8217;re going to do this, you&#8217;ll need a different pagination helper. Since I&#8217;m such a nice guy, I&#8217;ve already written it for you.</p>
<h2>The Code</h2>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'> <span class="k">def</span> <span class="nf">ajax_pagination_links</span><span class="p">(</span><span class="n">paginator</span><span class="p">,</span> <span class="n">options</span><span class="o">=</span><span class="p">{},</span> <span class="n">html_options</span><span class="o">=</span><span class="p">{})</span>
</span><span class='line'> <span class="nb">name</span> <span class="o">=</span> <span class="n">options</span><span class="o">[</span><span class="ss">:name</span><span class="o">]</span> <span class="o">||</span> <span class="no">ActionView</span><span class="o">::</span><span class="no">Helpers</span><span class="o">::</span><span class="no">PaginationHelper</span><span class="o">::</span><span class="no">DEFAULT_OPTIONS</span><span class="o">[</span><span class="ss">:name</span><span class="o">]</span>
</span><span class='line'> <span class="n">url_params</span> <span class="o">=</span> <span class="p">(</span><span class="n">options</span><span class="o">[</span><span class="ss">:params</span><span class="o">]</span> <span class="o">||</span> <span class="no">ActionView</span><span class="o">::</span><span class="no">Helpers</span><span class="o">::</span><span class="no">PaginationHelper</span><span class="o">::</span><span class="no">DEFAULT_OPTIONS</span><span class="o">[</span><span class="ss">:params</span><span class="o">]</span><span class="p">)</span><span class="o">.</span><span class="n">clone</span>
</span><span class='line'>
</span><span class='line'> <span class="n">links</span> <span class="o">=</span> <span class="n">pagination_links_each</span><span class="p">(</span><span class="n">paginator</span><span class="p">,</span> <span class="n">options</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">n</span><span class="o">|</span>
</span><span class='line'> <span class="n">url_params</span><span class="o">[</span><span class="nb">name</span><span class="o">]</span> <span class="o">=</span> <span class="n">n</span>
</span><span class='line'> <span class="n">link_to_remote</span><span class="p">(</span><span class="n">n</span><span class="o">.</span><span class="n">to_s</span><span class="p">,</span> <span class="p">{</span><span class="ss">:url</span> <span class="o">=&gt;</span> <span class="n">url_params</span><span class="p">,</span> <span class="ss">:method</span> <span class="o">=&gt;</span> <span class="ss">:get</span><span class="p">,</span> <span class="ss">:loading</span> <span class="o">=&gt;</span> <span class="s2">&quot;Element.update(&#39;</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">_loading_number&#39;, </span><span class="si">#{</span><span class="n">n</span><span class="si">}</span><span class="s2">); Element.hide(&#39;</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">_links&#39;); Element.show(&#39;</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">_loading&#39;)&quot;</span><span class="p">,</span> <span class="ss">:complete</span> <span class="o">=&gt;</span> <span class="s2">&quot;Element.show(&#39;</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">_links&#39;); Element.hide(&#39;</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">_loading&#39;)&quot;</span><span class="p">},</span> <span class="n">html_options</span><span class="p">)</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'> <span class="n">loading_number</span> <span class="o">=</span> <span class="n">content_tag</span><span class="p">(</span><span class="s1">&#39;span&#39;</span><span class="p">,</span> <span class="s1">&#39;&#39;</span><span class="p">,</span> <span class="ss">:id</span> <span class="o">=&gt;</span> <span class="s2">&quot;</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">_loading_number&quot;</span><span class="p">)</span>
</span><span class='line'> <span class="n">loading_spinner</span> <span class="o">=</span> <span class="n">image_tag</span> <span class="s1">&#39;spinner.gif&#39;</span>
</span><span class='line'> <span class="n">loading</span> <span class="o">=</span> <span class="n">content_tag</span><span class="p">(</span><span class="s1">&#39;div&#39;</span><span class="p">,</span> <span class="s2">&quot;...loading page &quot;</span> <span class="o">+</span> <span class="n">loading_number</span> <span class="o">+</span> <span class="s1">&#39; &#39;</span> <span class="o">+</span> <span class="n">loading_spinner</span><span class="p">,</span> <span class="ss">:id</span> <span class="o">=&gt;</span> <span class="s2">&quot;</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">_loading&quot;</span><span class="p">,</span> <span class="ss">:style</span> <span class="o">=&gt;</span> <span class="s2">&quot;display:none&quot;</span><span class="p">,</span> <span class="ss">:class</span> <span class="o">=&gt;</span> <span class="s1">&#39;loading_text&#39;</span><span class="p">)</span>
</span><span class='line'> <span class="n">links_tag</span> <span class="o">=</span> <span class="n">content_tag</span><span class="p">(</span><span class="s1">&#39;div&#39;</span><span class="p">,</span> <span class="n">links</span><span class="p">,</span> <span class="ss">:id</span> <span class="o">=&gt;</span> <span class="s2">&quot;</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">_links&quot;</span><span class="p">)</span>
</span><span class='line'> <span class="n">links_tag</span> <span class="o">+</span> <span class="n">loading</span>
</span><span class='line'> <span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>
<h2>Usage</h2>
<p>Simply wack that method in a relevant helper module, and use as in the following example:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='erb'><span class='line'><span class="cp">&lt;%=</span> <span class="n">ajax_pagination_links</span> <span class="vi">@message_pages</span><span class="p">,</span> <span class="ss">:name</span> <span class="o">=&gt;</span> <span class="s1">&#39;message_pages&#39;</span> <span class="cp">%&gt;</span><span class="x"></span>
</span></code></pre></td></tr></table></div></figure>
<p>You only really need to specify the name option if there is going to be more that one of these in the current DOM and thus you&#8217;re going to hit problems with multiples of the same ID.</p>
<p>Also, I&#8217;ve referenced a spinner image. You may want to provide that, or pull the image tag bit out of the code.</p>
<p>Finally, I&#8217;ve added classes to things if you want to style it. I think it looks good with:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class='css'><span class='line'><span class="nt">div</span><span class="nc">.loading_text</span>
</span><span class='line'><span class="p">{</span>
</span><span class='line'> <span class="k">color</span><span class="o">:</span> <span class="m">#666</span><span class="p">;</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Loading Parts of a Page in the Background]]></title>
<link href="http://www.craigambrose.com/blog/2007/03/05/loading-parts-of-a-page-in-the-background/"/>
<updated>2007-03-05T14:09:00+13:00</updated>
<id>http://www.craigambrose.com/blog/2007/03/05/loading-parts-of-a-page-in-the-background</id>
<content type="html"><![CDATA[<p>Occasionally I find that one of my apps ends up with <strong>&#8220;the page from hell&#8221;</strong>. The design seems to require that a certain page pull in all sorts of information from different places, and perhaps some of it isn&#8217;t even viewed straight away, it&#8217;s just being loaded into the background so that the user can access it instantly.</p>
<p>I had such a page, which had a growing number of tab pages that I wanted to load without any delay at all. I was starting to get worried about the page load time, but perhaps even worse was the fact that the controller loading this page was becoming quite messy, since some of the tabs clearly involved other resources.</p>
<p>The following helper module might be of some use to you. It defines a helper method called call_remote_queue, which executes a sequence of queued ajax requests.</p>
<h2>The Code</h2>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
</pre></td><td class='code'><pre><code class='ruby'><span class='line'><span class="k">module</span> <span class="nn">RemoteQueueHelper</span>
</span><span class='line'>
</span><span class='line'> <span class="k">def</span> <span class="nf">call_remote_queue</span><span class="p">(</span><span class="n">urls</span> <span class="o">=</span> <span class="o">[]</span><span class="p">,</span> <span class="n">spinner_id</span> <span class="o">=</span> <span class="kp">nil</span><span class="p">)</span>
</span><span class='line'> <span class="n">javascript_tag</span> <span class="n">remote_queue_js</span><span class="p">(</span><span class="n">urls</span><span class="o">.</span><span class="n">reverse</span><span class="p">,</span> <span class="n">spinner_id</span><span class="p">)</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'>
</span><span class='line'><span class="kp">protected</span>
</span><span class='line'>
</span><span class='line'> <span class="k">def</span> <span class="nf">remote_queue_js</span><span class="p">(</span><span class="n">urls</span><span class="p">,</span> <span class="n">spinner_id</span><span class="p">)</span>
</span><span class='line'> <span class="n">this_url</span> <span class="o">=</span> <span class="n">urls</span><span class="o">.</span><span class="n">pop</span>
</span><span class='line'> <span class="n">this_options</span> <span class="o">=</span> <span class="n">remote_url_options</span><span class="p">(</span><span class="n">this_url</span><span class="p">)</span>
</span><span class='line'>
</span><span class='line'> <span class="k">return</span> <span class="kp">nil</span> <span class="k">unless</span> <span class="n">this_url</span>
</span><span class='line'>
</span><span class='line'> <span class="k">if</span> <span class="n">urls</span><span class="o">.</span><span class="n">empty?</span>
</span><span class='line'> <span class="n">this_options</span><span class="o">[</span><span class="ss">:complete</span><span class="o">]</span> <span class="o">=</span> <span class="s2">&quot;Element.hide(&#39;</span><span class="si">#{</span><span class="n">spinner_id</span><span class="si">}</span><span class="s2">&#39;)&quot;</span>
</span><span class='line'> <span class="k">else</span>
</span><span class='line'> <span class="n">this_options</span><span class="o">[</span><span class="ss">:complete</span><span class="o">]</span> <span class="o">=</span> <span class="n">remote_queue_js</span><span class="p">(</span><span class="n">urls</span><span class="p">,</span> <span class="n">spinner_id</span><span class="p">)</span> <span class="k">unless</span> <span class="n">urls</span><span class="o">.</span><span class="n">empty?</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'> <span class="n">remote_function</span><span class="p">(</span><span class="n">this_options</span><span class="p">)</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'>
</span><span class='line'> <span class="k">def</span> <span class="nf">remote_url_options</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>
</span><span class='line'> <span class="k">if</span> <span class="n">url</span><span class="o">.</span><span class="n">is_a?</span> <span class="no">Hash</span>
</span><span class='line'> <span class="n">result</span> <span class="o">=</span> <span class="n">url</span>
</span><span class='line'> <span class="k">else</span>
</span><span class='line'> <span class="n">result</span> <span class="o">=</span> <span class="p">{}</span>
</span><span class='line'> <span class="n">result</span><span class="o">[</span><span class="ss">:url</span><span class="o">]</span> <span class="o">=</span> <span class="n">url</span>
</span><span class='line'> <span class="n">result</span><span class="o">[</span><span class="ss">:method</span><span class="o">]</span> <span class="o">=</span> <span class="ss">:get</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'> <span class="n">result</span>
</span><span class='line'> <span class="k">end</span>
</span><span class='line'>
</span><span class='line'><span class="k">end</span>
</span></code></pre></td></tr></table></div></figure>
<h2>Usage</h2>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='erb'><span class='line'><span class="cp">&lt;%=</span> <span class="n">call_remote_queue</span> <span class="o">[</span><span class="n">user_stories_path</span><span class="p">(</span><span class="vi">@user</span><span class="p">),</span> <span class="n">user_dares_path</span><span class="p">(</span><span class="vi">@user</span><span class="p">)</span><span class="o">]</span><span class="p">,</span> <span class="s1">&#39;area_tabbar_spinner&#39;</span> <span class="cp">%&gt;</span><span class="x"></span>
</span></code></pre></td></tr></table></div></figure>
<p>In my case, the urls in question used rjs templates to add the tab page to the list of tab pages. The second, optional parameter is the id of a spinner element to disable when the chain of loading is complete.</p>
]]></content>
</entry>
</feed>