<?xml version="1.0" encoding="UTF-8"?>
<feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom">
  <title>UK Specialists in Ruby on Rails, Exalead, AWS, Consultancy - Blog</title>
  <id>tag:thewebfellas.com,2012:mephisto/blog</id>
  <generator uri="http://mephistoblog.com" version="0.7.3">Mephisto Noh-Varr</generator>
  <link href="http://thewebfellas.com/feed/blog/atom.xml" rel="self" type="application/atom+xml"/>
  <link href="http://thewebfellas.com/blog" rel="alternate" type="text/html"/>
  <updated>2012-01-18T12:20:14Z</updated>
  <entry xml:base="http://thewebfellas.com/">
    <author>
      <name>Chris Anderton</name>
    </author>
    <id>tag:thewebfellas.com,2012-01-18:33488</id>
    <published>2012-01-18T12:17:00Z</published>
    <updated>2012-01-18T12:20:14Z</updated>
    <category term="Blog"/>
    <category term="amazon"/>
    <category term="cloud"/>
    <category term="dynamodb"/>
    <link href="http://thewebfellas.com/blog/2012/1/18/amazon-dynamodb-ssd-backed-non-relational-db-is-here" rel="alternate" type="text/html"/>
    <title>Amazon DynamoDB - SSD backed non-relational DB is here!</title>
<content type="html">
            &lt;p&gt;So - pretty exciting stuff, for us at least - Amazon Dynamo DB (previously &lt;a href=&quot;http://fusible.com/tag/aws-dynamodb/&quot;&gt;mentioned here&lt;/a&gt;) - has now seen the light of day, and is appearing within the console. Instructions are linked from within the console.&lt;/p&gt;

&lt;p&gt;We're already getting cracking on giving it a good play - we'll report back findings of interest!&lt;/p&gt;

&amp;lt;iframe src=&quot;http://www.youtube.com/embed/oz-7wJJ9HZ0&quot; height=&quot;315&quot; width=&quot;560&quot;&gt;&amp;lt;/iframe&gt;
          </content>  </entry>
  <entry xml:base="http://thewebfellas.com/">
    <author>
      <name>Chris Anderton</name>
    </author>
    <id>tag:thewebfellas.com,2011-05-03:26816</id>
    <published>2011-05-03T22:13:00Z</published>
    <updated>2011-05-03T22:13:45Z</updated>
    <category term="Blog"/>
    <category term="jobs"/>
    <category term="rails"/>
    <category term="ruby"/>
    <category term="ui"/>
    <category term="uk"/>
    <category term="ux"/>
    <link href="http://thewebfellas.com/blog/2011/5/3/we-re-hiring" rel="alternate" type="text/html"/>
    <title>We're hiring!</title>
<content type="html">
            &lt;p&gt;Bucking the trend of interplanetary meltdown, we're looking for some 'awesome' people to join our 
team. We're fleshing out the full details right now and will be posting ads in all the predictable 
places in the next few weeks, but in the meantime if you fall into any of the following job
descriptions and are interested in applying send your CV and examples of your work through to &lt;a href=&quot;mailto:jobs@thewebfellas.com&quot; class=&quot;email&quot; title=&quot;Email us your CV&quot;&gt;jobs@thewebfellas.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;These are all permanent positions, salaries are to be decided based on experience and skills
but will be based on market rates, as will the overall package on offer. We're currently based 
in Fareham, Hampshire but may also be relocating soon - wherever we go will be within 15 miles and 
will be near a mainline station. So if you're currently London based then you'll need to suffer
a nice commute (we've all done it the opposite way round!) or perhaps a nice reloaction to the 
Hampshire countryside.&lt;/p&gt;

&lt;h3&gt;Openings:&lt;/h3&gt;

&amp;lt;dl&gt;
&lt;dt&gt;User Interface/Web Designer&lt;/dt&gt;
&lt;dd&gt;
  We need someone with strong skills in designing web based interfaces and graphics. Our clients 
  require designs that are clean, professional and modern with a sprinkling of innovation and spark. 
  We're also interested in people who dabble in pixel art as it's a current favourite of ours and 
  will give us a head start on some of our current internal projects!
&lt;/dd&gt;
&lt;dt&gt;Ruby Developer (x2)&lt;/dt&gt;
&lt;dd&gt;
  We predominantly work with Ruby based applications. Whether it be Rails, 
  Sinatra or anything else, we need someone proficient in Ruby as a language with a strong foundation 
  in clean, sensible programming. The more skills you have, the more chance you have of getting a job
  with us - make sure your CV's are full of the things you can do rather than the names of as many 
  technologies you can think of!
&lt;/dd&gt;
&amp;lt;/dl&gt;
  
&lt;p&gt;Experience is beneficial, but if you can demonstrate a keen background in the scene or can bring along
a strong portfolio of homegrown projects for review then we're happy to consider you. Qualifications are
secondary to skill, attitude and enthusiasm.&lt;/p&gt;

&lt;p&gt;We are also happy to accept interns/gap year students falling into any of the above skillsets - send us 
your details and we'll take a look!&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://thewebfellas.com/">
    <author>
      <name>Rob Anderton</name>
    </author>
    <id>tag:thewebfellas.com,2011-01-25:23189</id>
    <published>2011-01-25T18:00:00Z</published>
    <updated>2011-01-25T17:42:21Z</updated>
    <category term="Blog"/>
    <category term="cookies"/>
    <category term="hmac"/>
    <category term="messageverifier"/>
    <category term="rails"/>
    <category term="rails3"/>
    <category term="security"/>
    <category term="signed"/>
    <category term="tamper"/>
    <link href="http://thewebfellas.com/blog/2011/1/25/revisited-tamper-proof-cookies-in-rails-3" rel="alternate" type="text/html"/>
    <title>Revisited: Tamper-proof cookies in Rails 3</title>
<summary type="html">&lt;p&gt;Here's a revisited post that's fairly short and sweet: way back in 2008 I &lt;a href=&quot;/blog/2008/7/14/a-tamper-proof-cookie-jar-for-rails-development&quot; title=&quot;Read the blog&quot;&gt;blogged about my implementation of tamper-proof cookies&lt;/a&gt; which used a similar technique to that used by Rails for its cookie-based session store. Back then the solution involved a custom cookie jar, the OpenSSL library to generate a HMAC, overriding the &lt;code&gt;ApplicationController#cookies&lt;/code&gt; method and a slightly unorthodox method signature for reading cookie values.&lt;/p&gt;</summary><content type="html">
            &lt;p&gt;Here's a revisited post that's fairly short and sweet: way back in 2008 I &lt;a href=&quot;/blog/2008/7/14/a-tamper-proof-cookie-jar-for-rails-development&quot; title=&quot;Read the blog&quot;&gt;blogged about my implementation of tamper-proof cookies&lt;/a&gt; which used a similar technique to that used by Rails for its cookie-based session store. Back then the solution involved a custom cookie jar, the OpenSSL library to generate a HMAC, overriding the &lt;code&gt;ApplicationController#cookies&lt;/code&gt; method and a slightly unorthodox method signature for reading cookie values.&lt;/p&gt;
&lt;p&gt;Here’s a recap of the code in action:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;# Write a tamper-proof cookie
cookies[:test_protected] = { :value =&gt; &quot;protected&quot;, :protect_from_forgery =&gt; true }

# Read a tamper-proof cookie
cookies[:test_protected, true]
=&gt; &quot;protected&quot;

# Read a raw cookie
# This returns the actual contents of the cookie, including the HMAC, and does not perform tamper checking
cookies[:test_protected]
=&gt; &quot;protected--ecec30eed6e122f3a3d5bb914bdd3cc1da4bd28e&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And here’s the nicer Rails 3 version:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;# Write a tamper-proof cookie
cookies.signed[:test_protected] = &quot;protected&quot;

# Read a tamper-proof cookie
cookies.signed[:test_protected]
=&gt; &quot;protected&quot;

# Read the raw cookie (this gives you the encoded value and the HMAC)
cookies[:test_protected]
=&gt; &quot;BAhJIg5wcm90ZWN0ZWQGOgZFRg==--18368dd442679a298bc98267d1d45c3046f636a7&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see the magic happens when you use the &lt;code&gt;signed&lt;/code&gt; method to read and write cookie values. There are two differences between my code and the Rails 3 version:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Rails 3 automatically Base64 encodes the cookie value and the HMAC is generated using the encoded rather than the raw value.&lt;/li&gt;
  &lt;li&gt;When a tampered with cookie is detected Rails 3 will return &lt;code&gt;nil&lt;/code&gt; for the value whereas my code raised a &lt;code&gt;TamperedWithCookie&lt;/code&gt; exception.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Configuration&lt;/h3&gt;
&lt;p&gt;The only configuration option you might need to adjust is the secret token used when generating the HMAC. Rails will automatically generate a long, random string when you generate a new app that looks something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;# lives in config/initializers/secret_token.rb
MyApp::Application.config.secret_token = 'bdf998dad6dcc939bb3285131f2c902ec6d586ae973c02b05ad5ad2be47ade55054be99717a7c6e4191c3283fe6cedf7555126d9d360b996134f5978bc9520c8'&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Normally you’ll not need to change this, but if you do make sure it is sufficiently long (minimum of 30 characters) and random enough so that it can’t be guessed easily, otherwise your tamper-proof cookies become somewhat less useful.&lt;/p&gt;
&lt;h3&gt;Rails 2.3.6 and newer can do it to&lt;/h3&gt;
&lt;p&gt;Despite the misleading title of this blog it’s actually not just Rails 3 that has this neat functionality: it first appeared in Rails 2.3.6 last May. Usage is exactly the same, but you will need to set the &lt;code&gt;cookie_verifier_secret&lt;/code&gt; setting in an initializer like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;ActionController::Base.cookie_verifier_secret = 'bdf998dad6dcc939bb3285131f2c902ec6d586ae973c02b05ad5ad2be47ade55054be99717a7c6e4191c3283fe6cedf7555126d9d360b996134f5978bc9520c8'&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Remember to set your own secret (don’t just copy this one!), Rails has a handy rake task to help you:&lt;/p&gt;
&lt;pre&gt;&lt;kbd&gt;rake secret&lt;/kbd&gt;
&lt;samp&gt;=&gt; 54540ac4c22d48e24a88398d360b52465e2457a5d6bee99dc897cf5362098555b0940734a1a58b576c2138eea444be392412e81fb712d961a622b099e0cdae74&lt;/samp&gt;&lt;/pre&gt;
&lt;h3&gt;Rails 2.3.0 to 2.3.5 with ActiveSupport MessageVerifier&lt;/h3&gt;
&lt;p&gt;If you’re a fan of the classics you may still be using a version of Rails from 2.3.0 to 2.3.5. Under the hood newer Rails versions are using the &lt;code&gt;ActiveSupport::MessageVerifier&lt;/code&gt; class to sign and verify cookies. The good news is that this class first made an appearance in Rails 2.3.0 so you can enjoy tamper-proof cookie goodness too. For example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;class ApplicationController &amp;lt; ActionController::Base
  protected
  
    def read_verified_cookie(name)
      cookie_verifier.verify(cookies[name])
    rescue ActiveSupport::MessageVerifier::InvalidSignature
      nil
    end

    def write_verified_cookie(name, value, options = {})
      cookies[name] = options.merge(:value =&gt; cookie_verifier.generate(value))
    end
    
    def cookie_verifier
      # Note: the 'secret' string should really live in a configuration file
      @cookie_verifier ||= ActiveSupport::MessageVerifier.new(&quot;54540ac4c22d48e24a88398d360b52465e2457a5d6bee99dc897cf5362098555b0940734a1a58b576c2138eea444be392412e81fb712d961a622b099e0cdae74&quot;)
    end
end&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you don’t mind the ActiveSupport dependency then you can also do a similar thing in a Sinatra application too.&lt;/p&gt;
&lt;p&gt;And finally if your app is using a really old pre-2.3 version of Rails you might as well stick with my trusty &lt;a href=&quot;/blog/2008/7/14/a-tamper-proof-cookie-jar-for-rails-development&quot; title=&quot;Read the blog&quot;&gt;old cookie jar&lt;/a&gt; from the original blog, although I’d recommend tweaking the code to remove the &lt;code&gt;TamperedWithCookie&lt;/code&gt; exception and to Base64 encode the cookie values to make it easier if you migrate to Rails 3 in the future.&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://thewebfellas.com/">
    <author>
      <name>Rob Anderton</name>
    </author>
    <id>tag:thewebfellas.com,2010-08-22:18489</id>
    <published>2010-08-22T09:04:00Z</published>
    <updated>2011-10-28T10:20:38Z</updated>
    <category term="Blog"/>
    <category term="css"/>
    <category term="helper"/>
    <category term="html"/>
    <category term="linkrenderer"/>
    <category term="pagination"/>
    <category term="rails"/>
    <category term="rails3"/>
    <category term="renderer"/>
    <category term="view"/>
    <category term="will_paginate"/>
    <link href="http://thewebfellas.com/blog/2010/8/22/revisited-roll-your-own-pagination-links-with-will_paginate-and-rails-3" rel="alternate" type="text/html"/>
    <title>Revisited: roll your own pagination links with will_paginate and Rails 3</title>
<summary type="html">&lt;p&gt;With a &lt;a href=&quot;http://weblog.rubyonrails.org/2010/7/26/rails-3-0-release-candidate&quot; title=&quot;Read the release candidate announcement&quot;&gt;final release of Rails 3 edging closer&lt;/a&gt; every day it seems like a good time to revisit some of my old articles from the last few years and bring them &lt;a href=&quot;/tags/rails3&quot; title=&quot;See other articles for Rails 3&quot;&gt;up to date&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Back in the summer of 2008 I wrote about &lt;a href=&quot;/blog/2008/8/3/roll-your-own-pagination-links-with-will_paginate&quot; title=&quot;Read the original article&quot;&gt;custom link renderers using will_paginate&lt;/a&gt; and, as it is still one of the most popular posts on the blog, it’s the one I’ve decided to refresh first. Don’t worry if you haven’t read the original article as I’ll be covering the same things here. So without further ado, let’s get stuck in!&lt;/p&gt;</summary><content type="html">
            &lt;p&gt;With a &lt;a href=&quot;http://weblog.rubyonrails.org/2010/7/26/rails-3-0-release-candidate&quot; title=&quot;Read the release candidate announcement&quot;&gt;final release of Rails 3 edging closer&lt;/a&gt; every day it seems like a good time to revisit some of my old articles from the last few years and bring them &lt;a href=&quot;/tags/rails3&quot; title=&quot;See other articles for Rails 3&quot;&gt;up to date&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Back in the summer of 2008 I wrote about &lt;a href=&quot;/blog/2008/8/3/roll-your-own-pagination-links-with-will_paginate&quot; title=&quot;Read the original article&quot;&gt;custom link renderers using will_paginate&lt;/a&gt; and, as it is still one of the most popular posts on the blog, it’s the one I’ve decided to refresh first. Don’t worry if you haven’t read the original article as I’ll be covering the same things here. So without further ado, let’s get stuck in!&lt;/p&gt;
&lt;h3&gt;Getting started&lt;/h3&gt;
&lt;ins&gt;&lt;p&gt;October 28th, 2011 - note that will_paginate has moved on again since this blog was written so make sure you read the comments (particularly &lt;a href=&quot;#comment-30798&quot; title=&quot;Read comment&quot;&gt;this one&lt;/a&gt;) to save yourself getting angry when things don't work as they should.&lt;/p&gt;&lt;/ins&gt;
&lt;p&gt;Of course before going any further you’ll need to have the Rails 3 release candidate installed. If you haven’t already done this I recommend using &lt;a href=&quot;http://rvm.beginrescueend.com/&quot; title=&quot;Read about the Ruby Version Manager&quot;&gt;rvm&lt;/a&gt; to save polluting your existing Rails development environment and as you’d expect Ryan Bates has another of his &lt;a href=&quot;http://railscasts.com/episodes/200-rails-3-beta-and-rvm&quot; title=&quot;Watch the RailsCast&quot;&gt;wonderful RailsCasts&lt;/a&gt; to help you set it all up.&lt;/p&gt;
&lt;p&gt;You’ll also need the Rails 3 version of &lt;a href=&quot;http://github.com/mislav/will_paginate/tree/rails3&quot; title=&quot;Browse the source&quot;&gt;will_paginate&lt;/a&gt; and as Rails 3 uses &lt;a href=&quot;http://gembundler.com/&quot; title=&quot;Read about bundler&quot;&gt;bundler&lt;/a&gt; to manage gem dependencies you won’t have to install it manually like you used to. Simply add the following to the &lt;kbd&gt;Gemfile&lt;/kbd&gt; in your application’s root directory and then run the &lt;kbd&gt;bundle install&lt;/kbd&gt; command to install the gem (along with any others required by your application):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;gem &quot;will_paginate&quot;, &quot;~&gt; 3.0.pre2&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;The standard view helper&lt;/h3&gt;
&lt;p&gt;As with previous versions the standard view helper is creatively named &lt;code&gt;will_paginate&lt;/code&gt; which makes it easy to add pagination links to your views. Here’s an example of its use:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;# In the controller
@users = User.paginate(:page =&gt; params[:page])

# In the view
&amp;lt;%= will_paginate(@users) %&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will produce the following HTML markup (I’ve broken it into multiple lines to make it more readable):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;html&quot;&gt;&amp;lt;div class=&quot;pagination&quot;&amp;gt;
  &amp;lt;span class=&quot;previous_page disabled&quot;&amp;gt;&#8592; Previous&amp;lt;/span&amp;gt; 
  &amp;lt;em&amp;gt;1&amp;lt;/em&amp;gt; 
  &amp;lt;a rel=&quot;next&quot; href=&quot;/users?page=2&quot;&amp;gt;2&amp;lt;/a&amp;gt; 
  &amp;lt;a href=&quot;/users?page=3&quot;&amp;gt;3&amp;lt;/a&amp;gt; 
  &amp;lt;a href=&quot;/users?page=4&quot;&amp;gt;4&amp;lt;/a&amp;gt; 
  &amp;lt;a class=&quot;next_page&quot; rel=&quot;next&quot; href=&quot;/users?page=2&quot;&amp;gt;Next &#8594;&amp;lt;/a&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see the helper uses the fairly common markup of a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; containing links to page numbers along with previous and next page links. The main difference to previous versions is the use of &lt;code&gt;&amp;lt;em&amp;gt;&lt;/code&gt; elements for the current page instead of a &lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt;, so you’ll need to update your CSS if you’re intending re-using an existing stylesheet.&lt;/p&gt;
&lt;p&gt;The helper accepts a hash of options for basic customisation:&lt;/p&gt;
&amp;lt;dl&gt;
  &lt;dt&gt;&lt;code&gt;:container&lt;/code&gt;&lt;/dt&gt;
  &lt;dd&gt;Determines whether or not the container &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; is included in the generated markup (defaults to true).&lt;/dd&gt;
  &lt;dt&gt;&lt;code&gt;:class&lt;/code&gt;&lt;/dt&gt;
  &lt;dd&gt;Allows the CSS class of the container &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; to be specified (defaults to &lt;samp&gt;pagination&lt;/samp&gt;).&lt;/dd&gt;
  &lt;dt&gt;&lt;code&gt;:id&lt;/code&gt;&lt;/dt&gt;
  &lt;dd&gt;This new option allows an ID attribute to be specified for the container &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;. If set to true then an ID will be auto-generated (similar to the way &lt;code&gt;form_for&lt;/code&gt; does) or set to a string to use your own ID.&lt;/dd&gt;
  &lt;dt&gt;&lt;code&gt;:previous_label&lt;/code&gt;&lt;/dt&gt;
  &lt;dt&gt;&lt;code&gt;:next_label&lt;/code&gt;&lt;/dt&gt;
  &lt;dd&gt;Customises the previous and next page link labels.&lt;/dd&gt;
  &lt;dt&gt;&lt;code&gt;:page_links&lt;/code&gt;&lt;/dt&gt;
  &lt;dd&gt;Determines whether or not the individual page links are included in the generated markup (defaults to true). If false then only previous and next page links are generated.&lt;/dd&gt;
  &lt;dt&gt;&lt;code&gt;:inner_window&lt;/code&gt;&lt;/dt&gt;
  &lt;dt&gt;&lt;code&gt;:outer_window&lt;/code&gt;&lt;/dt&gt;
  &lt;dd&gt;Customises the number of links displayed at either side of the current page (&lt;code&gt;:inner_window&lt;/code&gt;) and at the start and end of the page links (&lt;code&gt;:outer_window&lt;/code&gt;). Defaults to 4 and 1 respectively.&lt;/dd&gt;
  &lt;dt&gt;&lt;code&gt;:separator&lt;/code&gt;&lt;/dt&gt;
  &lt;dd&gt;Specifies the text inserted between the individual HTML elements generated by the helper (defaults to a single space).&lt;/dd&gt;
&amp;lt;/dl&gt;
&lt;p&gt;You can also get away without passing a collection to the helper if it follows the naming convention of the controller. So in the above example the &lt;code&gt;@users&lt;/code&gt; collection is named after the &lt;code&gt;UsersController&lt;/code&gt;, so the helper can be used like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&amp;lt;%= will_paginate %&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Doing it your way&lt;/h3&gt;
&lt;p&gt;The standard view helper is fine, but often you’ll need to do your own thing. That’s when you need to use a custom link renderer, and this is where will_paginate has changed the most for Rails 3.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;WillPaginate::LinkRenderer&lt;/code&gt; class has been moved into the &lt;code&gt;ViewHelpers&lt;/code&gt; module and split into two separate classes:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code&gt;&lt;a href=&quot;http://github.com/mislav/will_paginate/blob/rails3/lib/will_paginate/view_helpers/link_renderer_base.rb&quot; title=&quot;View the source&quot;&gt;WillPaginate::ViewHelpers::LinkRendererBase&lt;/a&gt;&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;&lt;a href=&quot;http://github.com/mislav/will_paginate/blob/rails3/lib/will_paginate/view_helpers/link_renderer.rb&quot; title=&quot;View the source&quot;&gt;WillPaginate::ViewHelpers::LinkRenderer&lt;/a&gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;LinkRendererBase&lt;/h4&gt;
&lt;p&gt;The main method of interest in the &lt;code&gt;LinkRendererBase&lt;/code&gt; class is &lt;code&gt;windowed_page_numbers&lt;/code&gt;: it does the work of figuring out which page numbers should be shown using the &lt;code&gt;:inner_window&lt;/code&gt; and &lt;code&gt;:outer_window&lt;/code&gt; settings and is a replacement for the &lt;code&gt;windowed_links&lt;/code&gt; and &lt;code&gt;visible_page_numbers&lt;/code&gt; methods in previous versions of will_paginate. Unlike the methods it replaces, &lt;code&gt;windowed_page_numbers&lt;/code&gt; doesn’t actually return any HTML, it returns a simple array of page numbers.&lt;/p&gt;
&lt;p&gt;The other method to look at is &lt;code&gt;pagination&lt;/code&gt; which calls &lt;code&gt;windowed_page_numbers&lt;/code&gt; (if the &lt;code&gt;:page_links&lt;/code&gt; option has been set) and then adds &lt;code&gt;:previous_page&lt;/code&gt; and &lt;code&gt;:next_page&lt;/code&gt; symbols to the start and end of the array. It is then up to the renderer to convert this array into HTML for display.&lt;/p&gt;
&lt;p&gt;In most cases it is unlikely that you’ll be directly subclassing the &lt;code&gt;LinkRendererBase&lt;/code&gt; class: although I’m sure there’s a good reason for a separate base class, it looks to me like an unnecessary abstraction.&lt;/p&gt;
&lt;h4&gt;LinkRenderer&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;LinkRenderer&lt;/code&gt; class is a subclass of &lt;code&gt;LinkRendererBase&lt;/code&gt; and as this is where the HTML generation takes place it is what you’ll subclass when creating your custom renderer. It provides the &lt;code&gt;to_html&lt;/code&gt; method that is called by the &lt;code&gt;will_paginate&lt;/code&gt; view helper and is responsible for converting the array of page links returned by the &lt;code&gt;pagination&lt;/code&gt; method (from the base class) into HTML markup.&lt;/p&gt;
&lt;p&gt;To work with will_paginate, all your renderer has to do is implement two methods: &lt;code&gt;prepare&lt;/code&gt; and &lt;code&gt;to_html&lt;/code&gt;, so we’ll take a look at them first.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;prepare&lt;/code&gt; method is passed the collection being paginated, a hash of options (from the &lt;code&gt;will_paginate&lt;/code&gt; helper), and a reference to the template being rendered. By subclassing the &lt;code&gt;LinkRenderer&lt;/code&gt; class the preparation of the renderer is taken care of for you, however if you need to do any additional setup in your custom renderer this is the place to put it, keeping in mind this method is called every time the &lt;code&gt;will_paginate&lt;/code&gt; view helper is called.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;to_html&lt;/code&gt; method maps the page number array returned by the &lt;code&gt;pagination&lt;/code&gt; method (inherited from the &lt;code&gt;LinkRendererBase&lt;/code&gt; class) to HTML using the &lt;code&gt;page_number&lt;/code&gt; method for the page number links and the &lt;code&gt;previous_page&lt;/code&gt; and &lt;code&gt;next_page&lt;/code&gt; methods (which in turn call the &lt;code&gt;previous_or_next_page&lt;/code&gt; method) for the previous and next page links.&lt;/p&gt;
&lt;p&gt;As you will see in the examples below the easiest way to create a custom link renderer is by overriding the &lt;code&gt;pagination&lt;/code&gt; method to change the the contents of the array it returns. By default the &lt;code&gt;LinkRenderer&lt;/code&gt; class handles values in the array like this:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Integer values are converted into links to that page number, for example &lt;code&gt;[1, 2, 3]&lt;/code&gt; would result in links to pages 1, 2 and 3 being generated.&lt;/li&gt;
  &lt;li&gt;Symbols represent the names of methods in the renderer class that should return HTML. For each symbol in the array the renderer will call the corresponding method (using &lt;code&gt;send&lt;/code&gt;) and the HTML that the method returns will be added to the pagination output. For example &lt;code&gt;[ :method_one, :method_two ]&lt;/code&gt; will result in the methods &lt;code&gt;method_one&lt;/code&gt; and &lt;code&gt;method_two&lt;/code&gt; being called. If a method name that matches the symbol doesn’t exist then an exception will be raised.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Also note that there is no restriction on the order of values in the array, so you are free to mix integers and symbols however you see fit.&lt;/p&gt;
&lt;p&gt;The actual HTML generation takes place in the &lt;code&gt;link&lt;/code&gt; and &lt;code&gt;tag&lt;/code&gt; methods which are responsible for generating &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; elements for active and &lt;code&gt;&amp;lt;em&amp;gt;&lt;/code&gt; elements for inactive links.&lt;/p&gt;
&lt;p&gt;These methods replace the &lt;code&gt;page_link_or_span&lt;/code&gt;, &lt;code&gt;page_link&lt;/code&gt; and &lt;code&gt;page_span&lt;/code&gt; methods from previous versions of will_paginate. Another change from previous versions is that the &lt;code&gt;initialize&lt;/code&gt; method is no longer used to set the gap marker (the HTML that is used to fill in the gaps in page numbers), instead you can override the protected &lt;code&gt;gap&lt;/code&gt; method in your custom renderer to change the markup:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;def gap
  tag(:li, &quot;&amp;hellip;&quot;, :class =&gt; &quot;gap&quot;)
end&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;And now for an example&lt;/h3&gt;
&lt;p&gt;In addition to the methods described above there are a number of additional methods in the &lt;code&gt;LinkRenderer&lt;/code&gt; class that can be overridden to generate the markup you require. To see them in action let’s copy from the original article on will_paginate and say we want to generate markup like this for our pagination:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;html&quot;&gt;&amp;lt;ul class=&quot;pagination&quot;&amp;gt;
  &amp;lt;li class=&quot;previous_page&quot;&amp;gt;&amp;lt;a href=&quot;/users?page=1&quot;&amp;gt; &#8592; Previous&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&quot;/users?page=1&quot;&amp;gt;1&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
  &amp;lt;li class=&quot;current&quot;&amp;gt;2&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;a href=&quot;/users?page=3&quot;&amp;gt;3&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
  &amp;lt;li class=&quot;next_page&quot;&amp;gt;&amp;lt;a href=&quot;/users?page=3&quot;&amp;gt;Next &#8594;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our custom link renderer will look like this (put the code in the &lt;kbd&gt;lib&lt;/kbd&gt; folder of your application):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;class PaginationListLinkRenderer &amp;lt; WillPaginate::ViewHelpers::LinkRenderer

  protected

    def page_number(page)
      unless page == current_page
        tag(:li, link(page, page, :rel =&amp;gt; rel_value(page)))
      else
        tag(:li, page, :class =&amp;gt; &quot;current&quot;)
      end
    end

    def previous_or_next_page(page, text, classname)
      if page
        tag(:li, link(text, page), :class =&amp;gt; classname)
      else
        tag(:li, text, :class =&amp;gt; classname + ' disabled')
      end
    end

    def html_container(html)
      tag(:ul, html, container_attributes)
    end

end&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first thing you’ll probably notice is that there was no need to override the &lt;code&gt;to_html&lt;/code&gt; method. In previous versions if you wanted to change the element used to contain the pagination links from a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; to something else you had no choice, but the new &lt;code&gt;html_container&lt;/code&gt; method means this is no longer the case. The &lt;code&gt;page_number&lt;/code&gt; and &lt;code&gt;previous_or_next_page&lt;/code&gt; methods also need to be overridden to wrap the page links in &lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt; tags. Compare this with the code for previous versions of will_paginate and you can see we have a lot less work to do now:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;class PaginationListLinkRenderer &amp;lt; WillPaginate::LinkRenderer

  def to_html
    links = @options[:page_links] ? windowed_links : []

    links.unshift(page_link_or_span(@collection.previous_page, 'previous', @options[:previous_label]))
    links.push(page_link_or_span(@collection.next_page, 'next', @options[:next_label]))

    html = links.join(@options[:separator])
    @options[:container] ? @template.content_tag(:ul, html, html_attributes) : html
  end

protected

  def windowed_links
    visible_page_numbers.map { |n| page_link_or_span(n, (n == current_page ? 'current' : nil)) }
  end

  def page_link_or_span(page, span_class, text = nil)
    text ||= page.to_s
    if page &amp;amp;&amp;amp; page != current_page
      page_link(page, text, :class =&amp;gt; span_class)
    else
      page_span(page, text, :class =&amp;gt; span_class)
    end
  end

  def page_link(page, text, attributes = {})
    @template.content_tag(:li, @template.link_to(text, url_for(page)), attributes)
  end

  def page_span(page, text, attributes = {})
    @template.content_tag(:li, text, attributes)
  end

end&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Using the custom renderer&lt;/h3&gt;
&lt;p&gt;As with previous versions of will_paginate, having created a custom renderer you need to tell the view helper to use it. There are two ways to do this, the first is to pass the &lt;code&gt;:renderer&lt;/code&gt; option like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&amp;lt;%= will_paginate(@users, :renderer =&amp;gt; PaginationListLinkRenderer) %&amp;gt;&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;:renderer&lt;/code&gt; option accepts a class name string, a class or an instance of a link renderer. If you want your custom link renderer to be the default for all pagination you don’t have to add a &lt;code&gt;:renderer&lt;/code&gt; option to all of your &lt;code&gt;will_paginate&lt;/code&gt; calls, instead you can specify the default in a Rails initializer like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;WillPaginate::ViewHelpers.pagination_options[:renderer] = 'PaginationListLinkRenderer'&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your view can then contain &lt;code&gt;&amp;lt;%= will_paginate(@users) %&amp;gt;&lt;/code&gt; and your link renderer will be used automatically.&lt;/p&gt;
&lt;h3&gt;A Rails 3 autoloading gotcha&lt;/h3&gt;
&lt;p&gt;If you try and use your custom link renderer at this point you’re likely to get an &lt;samp&gt;uninitialized constant ActionView::CompiledTemplates::PaginationListLinkRenderer&lt;/samp&gt; exception. This is because, unlike Rails 2.x, the current release candidate of Rails 3 doesn’t autoload code in the &lt;kbd&gt;lib&lt;/kbd&gt; directory. There’s a lengthy and at times heated discussion of this over on the &lt;a href=&quot;https://rails.lighthouseapp.com/projects/8994/tickets/5218-rails-3-rc-does-not-autoload-from-lib&quot; title=&quot;Read the ticket&quot;&gt;Rails bug tracker&lt;/a&gt; which has not yet been formally resolved. In the meantime you can restore the old Rails 2.x behaviour by changing the autoload paths in &lt;kbd&gt;config/application.rb&lt;/kbd&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;config.autoload_paths += %W(#{config.root}/lib)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Restart your server and you should be good to go.&lt;/p&gt;
&lt;h3&gt;How do I...?&lt;/h3&gt;
&lt;p&gt;So that covers the basics of creating your own link renderer. If you take a look at the &lt;a href=&quot;/blog/2008/8/3/roll-your-own-pagination-links-with-will_paginate#comments&quot; title=&quot;Read the comments on the original article&quot;&gt;comments&lt;/a&gt; on my original article you’ll see some people have asked how to achieve different kinds of pagination markup. To finish I’ll update my replies for use with Rails 3.&lt;/p&gt;
&lt;h4&gt;Remove page numbers and just have first, previous, next and last page links&lt;/h4&gt;
&lt;p&gt;Turning off page numbers is a simple case of passing &lt;code&gt;:page_links =&gt; false&lt;/code&gt; to the &lt;code&gt;will_paginate&lt;/code&gt; view helper which means all you get is previous and next page links. But in order to add first and last links as well five things need to be done:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;The &lt;code&gt;container_attributes&lt;/code&gt; method must be overridden to prevent custom options being output as HTML attributes for the pagination container.&lt;/li&gt;
  &lt;li&gt;An overridden &lt;code&gt;pagination&lt;/code&gt; method that returns an array of symbols and does not call the &lt;code&gt;windowed_page_numbers&lt;/code&gt; method.&lt;/li&gt;
  &lt;li&gt;A &lt;code&gt;first_page&lt;/code&gt; method that handles the generation of HTML for the first page link.&lt;/li&gt;
  &lt;li&gt;A &lt;code&gt;last_page&lt;/code&gt; method that handles the generation of HTML for the last page link.&lt;/li&gt;
  &lt;li&gt;Setting new label options for the first and last page links.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here’s the code:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;class MinimalPaginationLinkRenderer &amp;lt; WillPaginate::ViewHelpers::LinkRenderer

  def container_attributes
    super.except(:first_label, :last_label)
  end

  protected

    def pagination
      [ :first_page, :previous_page, :next_page, :last_page ]
    end

    def first_page
      previous_or_next_page(current_page == 1 ? nil : 1, @options[:first_label], &quot;first_page&quot;)
    end

    def last_page
      previous_or_next_page(current_page == total_pages ? nil : total_pages, @options[:last_label], &quot;last_page&quot;)
    end

end&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see the renderer re-uses the &lt;code&gt;previous_or_next_page&lt;/code&gt; method to render the first and last page links as it handles the generation of the correct markup for when the current page is the first or last page. To use this in your view use:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&amp;lt;%= will_paginate(:renderer =&amp;gt; MinimalPaginationLinkRenderer, :first_label =&amp;gt; &quot;&amp;amp;#8676; First&quot;, :last_label =&amp;gt; &quot;Last &amp;amp;#8677;&quot;) %&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Alternatively you can put these options in an initializer using the &lt;code&gt;WillPaginate::ViewHelpers.pagination_options&lt;/code&gt; hash.&lt;/p&gt;
&lt;h4&gt;Change the order of links&lt;/h4&gt;
&lt;p&gt;To change the order of links, for example to show the page numbers followed by the previous and next page links, is even more straightforward with the Rails 3 version of will_paginate. All you need to do is override the &lt;code&gt;pagination&lt;/code&gt; method to change the order of elements in the array it returns, for example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;class ReorderedPaginationLinkRenderer &amp;lt; WillPaginate::ViewHelpers::LinkRenderer

  protected

    def pagination
      items = @options[:page_links] ? windowed_page_numbers : []
      items.push :previous_page, :next_page
    end

end&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The only change here from the method in &lt;code&gt;LinkRendererBase&lt;/code&gt; is that the &lt;code&gt;:previous_page&lt;/code&gt; symbol is being pushed onto the end of the array instead of being added to the beginning. Similarly if you wanted the previous and next links to appear before the page numbers, just change the second line of the method to &lt;code&gt;items.unshift :previous_page, :next_page&lt;/code&gt;. As always, either pass the &lt;code&gt;:renderer&lt;/code&gt; option to the view helper or set the global option in an initializer to use this renderer.&lt;/p&gt;
&lt;h4&gt;Only show previous, current and next page links&lt;/h4&gt;
&lt;p&gt;This is also a trivial task with will_paginate, here’s the code:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;class SinglePagePaginationLinkRenderer &amp;lt; WillPaginate::ViewHelpers::LinkRenderer

  protected

    def pagination
      [ :previous_page, current_page, :next_page ]
    end

end&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It’s important to note that the reference to &lt;code&gt;current_page&lt;/code&gt; in the array is not a symbol like &lt;code&gt;:previous_page&lt;/code&gt; and &lt;code&gt;:next_page&lt;/code&gt; but is actually a call to the &lt;code&gt;current_page&lt;/code&gt; method provided by the &lt;code&gt;LinkRendererBase&lt;/code&gt; class (which delegates to the underlying collection to get the current page number).&lt;/p&gt;
&lt;h3&gt;This isn’t over yet!&lt;/h3&gt;
&lt;p&gt;And finally a disclaimer: at the time of writing Rails 3 is only at first release candidate stage and in Rails-land this means things can, and likely will, still change substantially before final release. The will_paginate gem will also continue to be fine tuned as time goes by, so be warned if you’re reading this in 2012 and find that something’s not working, its probably because the APIs have moved on again - and it’ll be time for me to write another new version of this article!&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://thewebfellas.com/">
    <author>
      <name>Rob Anderton</name>
    </author>
    <id>tag:thewebfellas.com,2010-07-15:16466</id>
    <published>2010-07-15T17:25:00Z</published>
    <updated>2010-07-15T22:36:12Z</updated>
    <category term="Blog"/>
    <category term="bug"/>
    <category term="params"/>
    <category term="patch"/>
    <category term="quotes"/>
    <category term="rack"/>
    <category term="rails"/>
    <link href="http://thewebfellas.com/blog/2010/7/15/rails-2-3-8-rack-1-1-and-the-curious-case-of-the-missing-quotes" rel="alternate" type="text/html"/>
    <title>Rails 2.3.8, Rack 1.1 and the curious case of the missing quotes</title>
<summary type="html">&lt;p&gt;If you're using Rails 2.3.8 for your application and thought that you were safe after May's comedy of errors produced &lt;a href=&quot;http://weblog.rubyonrails.org/2010/5/23/ruby-on-rails-2-3-6-released&quot; title=&quot;See Rails 2.3.6 release blog post&quot;&gt;three&lt;/a&gt; &lt;a href=&quot;http://weblog.rubyonrails.org/2010/5/24/ruby-on-rails-2-3-7-released&quot; title=&quot;See Rails 2.3.7 release blog post&quot;&gt;point&lt;/a&gt; &lt;a href=&quot;http://weblog.rubyonrails.org/2010/5/25/ruby-on-rails-2-3-8-released&quot; title=&quot;See Rails 2.3.8 release blog post&quot;&gt;updates&lt;/a&gt; in as many days, think again. Unfortunately there's a little bug that can lead to parameters being altered or potentially even truncated without warning.&lt;/p&gt;</summary><content type="html">
            &lt;p&gt;If you're using Rails 2.3.8 for your application and thought that you were safe after May's comedy of errors produced &lt;a href=&quot;http://weblog.rubyonrails.org/2010/5/23/ruby-on-rails-2-3-6-released&quot; title=&quot;See Rails 2.3.6 release blog post&quot;&gt;three&lt;/a&gt; &lt;a href=&quot;http://weblog.rubyonrails.org/2010/5/24/ruby-on-rails-2-3-7-released&quot; title=&quot;See Rails 2.3.7 release blog post&quot;&gt;point&lt;/a&gt; &lt;a href=&quot;http://weblog.rubyonrails.org/2010/5/25/ruby-on-rails-2-3-8-released&quot; title=&quot;See Rails 2.3.8 release blog post&quot;&gt;updates&lt;/a&gt; in as many days, think again. Unfortunately there's a little bug that can lead to parameters being altered or potentially even truncated without warning.&lt;/p&gt;
&lt;p&gt;As Rails is a Rack application it delegates the parsing of request parameters to the &lt;a href=&quot;http://rack.rubyforge.org/doc/classes/Rack/Utils.html&quot; title=&quot;Read the rdocs&quot;&gt;Rack::Utils&lt;/a&gt; module. This worked fine until for some bizarre reason the Rack team decided that the &lt;code&gt;parse_query&lt;/code&gt; and &lt;code&gt;normalize_params&lt;/code&gt; methods should “properly” &lt;a href=&quot;http://github.com/rack/rack/commit/fb4f2b5fe26a0e3821ac0f6361a3885bd88b42ca&quot; title=&quot;See the commit&quot;&gt;parse quoted string values&lt;/a&gt;. I haven't been able to find the discussion that led to this commit, so I don't know what the original intention was.&lt;/p&gt;
&lt;p&gt;Although this change was made way back in December last year and was released as part of Rack 1.1 in January, it didn't cause a problem for Rails applications until version 2.3.6 bumped the Rack dependency from 1.0 to 1.1. Applications using other Rack frameworks like &lt;a href=&quot;http://www.sinatrarb.com/&quot; title=&quot;Take to the stage with Sinatra&quot;&gt;Sinatra&lt;/a&gt; would likely have been affected sooner as they're able to take advantage of a new Rack release more easily.&lt;/p&gt;
&lt;p&gt;We first noticed a problem when deploying a Rails 2.3.5 app with JRuby (which was a real pain to track down as something in JRuby was activating Rack 1.1 even though Rails 2.3.5 uses 1.0) and then it cropped up again in one of our Sinatra apps.&lt;/p&gt;
&lt;h3&gt;So what's the problem?&lt;/h3&gt;
&lt;p&gt;It's probably best to show a couple of examples:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;Rack::Utils.parse_query(&quot;foo=bar&quot;)
=&amp;gt; {&quot;foo&quot;=&amp;gt;&quot;bar&quot;} # correct
Rack::Utils.parse_query(&quot;foo=\&quot;bar\&quot;&quot;)
=&amp;gt; {&quot;foo&quot;=&amp;gt;&quot;bar&quot;} # incorrect! should be {&quot;foo&quot;=&amp;gt;&quot;\&quot;bar\&quot;&quot;}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It may not be immediately obvious why this removal of quotes could be a problem. For us, our application was providing search functionality using &lt;a href=&quot;http://freelancing-god.github.com/ts/en/&quot; title=&quot;Read about the best Rails search plugin in the world!&quot;&gt;ThinkingSphinx&lt;/a&gt; and we wanted to allow users to search for phrases. Rack's interference meant this wasn't possible:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;Rack::Utils.parse_query(&quot;search=\&quot;I%20am%20a%20search%20phrase\&quot;&quot;)
{&quot;search&quot;=&amp;gt;&quot;I am a search phrase&quot;}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The quotes around the phrase are gone, changing the meaning of the ThinkingSphinx search and causing us headaches.&lt;/p&gt;
&lt;p&gt;A &lt;a href=&quot;https://rails.lighthouseapp.com/projects/8994/tickets/4808-textarea-input-silently-truncated-in-238&quot; title=&quot;See the ticket&quot;&gt;ticket&lt;/a&gt; has also been created on the Rails tracker that suggests that text submitted in a form can also be prematurely truncated as a result of this bug.&lt;/p&gt;
&lt;h3&gt;How can it be fixed?&lt;/h3&gt;
&lt;p&gt;The good news is that the changes that caused this bug were &lt;a href=&quot;http://github.com/rack/rack/commit/dae12e088592ee69545b5f2f81b87f4959859164&quot; title=&quot;See the commit&quot;&gt;reverted&lt;/a&gt; in time for Rack 1.2, the bad news is that at the time of writing Rails is still dependent on the broken 1.1 release. Hopefully this will be changed before Rails 2.3.9 is made available: if it isn't expect 2.3.10 to come out a day or so later!&lt;/p&gt;
&lt;p&gt;For existing Rails 2.3.8 applications you won't be able to change the Rack version used by Rails but you will be able to monkey-patch the code. Follow the steps below:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;p&gt;Create an initializer called &lt;kbd&gt;_run_first.rb&lt;/kbd&gt; in the &lt;kbd&gt;config/initializers&lt;/kbd&gt; directory of your app (note that the leading underscore is important as Rails loads initializers in alphabetical order, the underscore forces this one to the top of the list)&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Add the following code to it:&lt;/p&gt;
    &lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;Dir[File.join(Rails.root, &quot;lib&quot;, &quot;patches&quot;, &quot;**&quot;, &quot;*.rb&quot;)].sort.each { |patch| require(patch) }&lt;/code&gt;&lt;/pre&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;p&gt;Create a folder called &lt;kbd&gt;patches&lt;/kbd&gt; in the &lt;kbd&gt;lib&lt;/kbd&gt; directory of your Rails application (unless it already exists)&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;&lt;p&gt;Create a file in this &lt;kbd&gt;lib/patches&lt;/kbd&gt; directory called &lt;kbd&gt;rack.rb&lt;/kbd&gt;&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Add the following code (or &lt;a href=&quot;http://gist.github.com/477631&quot; title=&quot;Get the gist&quot;&gt;get the gist&lt;/a&gt;):&lt;/p&gt;
    &lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;module Rack
  module Utils
    def parse_query(qs, d = nil)
      params = {}

      (qs || '').split(d ? /[#{d}] */n : DEFAULT_SEP).each do |p|
        k, v = p.split('=', 2).map { |x| unescape(x) }
        if cur = params[k]
          if cur.class == Array
            params[k] &amp;lt;&amp;lt; v
          else
            params[k] = [cur, v]
          end
        else
          params[k] = v
        end
      end

      return params
    end
    module_function :parse_query

    def normalize_params(params, name, v = nil)
      name =~ %r(\A[\[\]]*([^\[\]]+)\]*)
      k = $1 || ''
      after = $' || ''

      return if k.empty?

      if after == &quot;&quot;
        params[k] = v
      elsif after == &quot;[]&quot;
        params[k] ||= []
        raise TypeError, &quot;expected Array (got #{params[k].class.name}) for param `#{k}'&quot; unless params[k].is_a?(Array)
        params[k] &amp;lt;&amp;lt; v
      elsif after =~ %r(^\[\]\[([^\[\]]+)\]$) || after =~ %r(^\[\](.+)$)
        child_key = $1
        params[k] ||= []
        raise TypeError, &quot;expected Array (got #{params[k].class.name}) for param `#{k}'&quot; unless params[k].is_a?(Array)
        if params[k].last.is_a?(Hash) &amp;amp;&amp;amp; !params[k].last.key?(child_key)
          normalize_params(params[k].last, child_key, v)
        else
          params[k] &amp;lt;&amp;lt; normalize_params({}, child_key, v)
        end
      else
        params[k] ||= {}
        raise TypeError, &quot;expected Hash (got #{params[k].class.name}) for param `#{k}'&quot; unless params[k].is_a?(Hash)
        params[k] = normalize_params(params[k], after, v)
      end

      return params
    end
    module_function :normalize_params
  end
end&lt;/code&gt;&lt;/pre&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;p&gt;Restart your server for the patch to take effect.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;There's two things going on here:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;You've now got a nice, simple pattern for adding patches to your Rails application. For example if you later need to patch a bug in ActionView then you could simply make a &lt;samp&gt;lib/patches/action_view.rb&lt;/samp&gt; file containing the code for the patch and it will automatically be loaded when you next start your app.&lt;/li&gt;
  &lt;li&gt;The patch file overwrites the broken &lt;code&gt;parse_query&lt;/code&gt; and &lt;code&gt;normalize_params&lt;/code&gt; methods using the fixed code from Rack 1.2.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can easily verify that the patch is working by firing up a Rails console (using &lt;kbd&gt;script/console&lt;/kbd&gt;) and trying one of the examples given above. You should get the correct results, if you don't then run through the above steps again making sure you've done everything exactly as described.&lt;/p&gt;
&lt;h3&gt;And the moral of the story is?&lt;/h3&gt;
&lt;p&gt;Even with an extensive test suite it is possible for bugs to slip through: in this case the Rack tests were changed to ensure they passed even though the behaviour was wrong, and the Rails tests didn't cover it because the code should already have been tested in the Rack tests. I guess the question is who tests the tests?!&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://thewebfellas.com/">
    <author>
      <name>Chris Anderton</name>
    </author>
    <id>tag:thewebfellas.com,2010-04-19:14791</id>
    <published>2010-04-19T20:43:00Z</published>
    <updated>2010-09-24T19:33:56Z</updated>
    <category term="Blog"/>
    <category term="ami"/>
    <category term="convert"/>
    <category term="ebs"/>
    <category term="ec2"/>
    <category term="s3"/>
    <link href="http://thewebfellas.com/blog/2010/4/19/create-an-ebs-boot-volume-from-a-running-instance" rel="alternate" type="text/html"/>
    <title>Create a bootable EBS AMI from a running instance</title>
<summary type="html">&lt;p&gt;A quick set of notes on how to create a bootable EBS snapshot from a running EC2 instance - for example, an instance that has been started from an S3 backed AMI.&lt;/p&gt;
&lt;p&gt;We've had to do this a few times over the last few months - for the benefit of others, we've outlined how we currently do it - this is based on a number of articles that were surfacing at the time of our research, but I don't have the links to hand. If there's a better way out there feel free to jump in!&lt;/p&gt;</summary><content type="html">
            &lt;p&gt;A quick set of notes on how to create a bootable EBS snapshot from a running EC2 instance - for example, an instance that has been started from an S3 backed AMI.&lt;/p&gt;
&lt;p&gt;We've had to do this a few times over the last few months - for the benefit of others, we've outlined how we currently do it - this is based on a number of articles that were surfacing at the time of our research, but I don't have the links to hand. If there's a better way out there feel free to jump in!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create an EBS volume (if converting from an S3 backed AMI then create it as 10Gb in size)&lt;/li&gt;
&lt;li&gt;Attach the EBS volume to your running instance&lt;/li&gt;
&lt;li&gt;Stop running services that may be locking files, etc, such as MySQL&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At this point, you're ready to copy the contents of your root drive onto the EBS volume. A couple of additional points here - it makes sense to stop any services possible to make the transfer as clean as possible. One of our readers has suggested an alternative approach to dd in the comments - i've included it here as an option for completeness.&lt;/p&gt;
&lt;h5&gt;Option 1: dd&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;Copy the root drive to the EBS volume - &lt;code&gt;dd bs=65536 if=SOURCE of=TARGET&lt;/code&gt;. Replace SOURCE with the boot volume, e.g. /dev/sda1 and TARGET with the EBS volume, e.g. /dev/sdj&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fsck TARGET&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mkdir /migrate&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mount TARGET /migrate&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;Option 2: rsync&lt;/h5&gt;
&lt;p&gt;I suspect with this route you'd need to exclude any directories used for mounting other volumes, otherwise your rsync will run out of space on the target device.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;mkfs.ext3 TARGET&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mount TARGET /migrate&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rsync -ax --progress / /migrate&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rm -rf /migrate/migrate&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;And then continue..&lt;/h5&gt;
&lt;li&gt;At this point, you can make any necessary modifications to the fstab to allow your EBS based AMI to boot - the file can be found in /migrate/etc/fstab&lt;/li&gt;
&lt;li&gt;&lt;code&gt;umount TARGET&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Create a snapshot of the EBS volume&lt;/li&gt;
&lt;li&gt;Register the snapshot as an AMI by running &lt;code&gt;ec2-register -n NAME --architecture ARCH --block-device-mapping /dev/sda1=SNAPSHOT:SIZE:false&lt;/code&gt;. There are a number of options here - some of which you can set when starting the instance if you prefer. NAME is the name of the AMI, ARCH is the architecture (i386 or x86_64). The block device mapping paramaters set the EBS snapshot to use as the boot device, the second parameter sets the size to 15Gb and final parameter sets the delete-on-termination attribute to false.&lt;/li&gt;
&lt;li&gt;Run an instance based on the newly registered AMI - if you've set the EBS volume size to anything greater then 10Gb then expand the filesystem to the size of the device.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;More details, as usual, can be found in the &lt;a href=&quot;http://docs.amazonwebservices.com/AWSEC2/latest/CommandLineReference/index.html?ApiReference-cmd-RunInstances.html&quot; title=&quot;EC2 Command Line Reference&quot;&gt;command line reference&lt;/a&gt;.&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://thewebfellas.com/">
    <author>
      <name>Rob Anderton</name>
    </author>
    <id>tag:thewebfellas.com,2010-01-31:12699</id>
    <published>2010-01-31T09:48:00Z</published>
    <updated>2010-02-05T14:51:45Z</updated>
    <category term="Blog"/>
    <category term="aws"/>
    <category term="buckets"/>
    <category term="european"/>
    <category term="paperclip"/>
    <category term="patch"/>
    <category term="permanentredirect"/>
    <category term="rails development"/>
    <category term="s3"/>
    <link href="http://thewebfellas.com/blog/2010/1/31/paperclip-vs-amazon-s3-european-buckets" rel="alternate" type="text/html"/>
    <title>Ooh la la: Paperclip et les European S3 buckets</title>
<summary type="html">&lt;p&gt;At the end of my &lt;a href=&quot;http://thewebfellas.com/blog/2009/8/29/protecting-your-paperclip-downloads&quot; title=&quot;What do you mean you haven’t already read it?!&quot;&gt;last blog about Paperclip&lt;/a&gt; I mentioned that you need to do some patching if you want to use European S3 buckets to store your files. The problem was introduced when Paperclip made the move from &lt;a href=&quot;http://rightaws.rubyforge.org/&quot; title=&quot;See the code on RubyForge&quot;&gt;RightAWS&lt;/a&gt; to &lt;a href=&quot;http://vernix.org/marcel/&quot; title=&quot;Say hello to Marcel&quot;&gt;Marcel Molina’s&lt;/a&gt; &lt;a href=&quot;http://github.com/marcel/aws-s3&quot; title=&quot;See the code on GitHub&quot;&gt;AWS::S3&lt;/a&gt; gem. Unfortunately despite several forks containing patches to AWS::S3 and a &lt;a href=&quot;http://github.com/marcel/aws-s3/issues#issue/4&quot; title=&quot;See the ticket&quot;&gt;4 month old bug report&lt;/a&gt; nothing has been done to officially fix the problem.&lt;/p&gt;
&lt;p&gt;So my fellow Europeans, what are we to do?&lt;/p&gt;</summary><content type="html">
            &lt;p&gt;At the end of my &lt;a href=&quot;http://thewebfellas.com/blog/2009/8/29/protecting-your-paperclip-downloads&quot; title=&quot;What do you mean you haven’t already read it?!&quot;&gt;last blog about Paperclip&lt;/a&gt; I mentioned that you need to do some patching if you want to use European S3 buckets to store your files. The problem was introduced when Paperclip made the move from &lt;a href=&quot;http://rightaws.rubyforge.org/&quot; title=&quot;See the code on RubyForge&quot;&gt;RightAWS&lt;/a&gt; to &lt;a href=&quot;http://vernix.org/marcel/&quot; title=&quot;Say hello to Marcel&quot;&gt;Marcel Molina’s&lt;/a&gt; &lt;a href=&quot;http://github.com/marcel/aws-s3&quot; title=&quot;See the code on GitHub&quot;&gt;AWS::S3&lt;/a&gt; gem. Unfortunately despite several forks containing patches to AWS::S3 and a &lt;a href=&quot;http://github.com/marcel/aws-s3/issues#issue/4&quot; title=&quot;See the ticket&quot;&gt;4 month old bug report&lt;/a&gt; nothing has been done to officially fix the problem.&lt;/p&gt;
&lt;p&gt;So my fellow Europeans, what are we to do?&lt;/p&gt;
&lt;p&gt;At the moment there are a couple of options that you might like to try, the first is the quick (and slightly dirty) fix I put together a while back to get things working when a deadline was looming. The second involves dumping AWS::S3 and moving to a European-friendly S3 library, which is the direction I’d like to see Paperclip take.&lt;/p&gt;
&lt;h3&gt;Option #1 - the quick and dirty patches&lt;/h3&gt;
&lt;p&gt;Five months ago I first hit this problem when moving one of our Rails apps over to S3 and at that time there were only a few forks of AWS::S3 that attempted to support European buckets. For reasons that are hazy to me now, but probably simply because it worked, I chose to borrow a &lt;a href=&quot;http://github.com/vladr/aws-s3/commit/a13504ba581496eab253583f38d26aa475949b53&quot; title=&quot;See the code on GitHub&quot;&gt;patch from Vlad Romascanu&lt;/a&gt;. If you want to monkey-patch the AWS::S3 gem to use Vlad’s changes then do the following (if you prefer you could of course grab a copy of Vlad’s fork and build a Gem from it):&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;p&gt;Make sure AWS::S3 is in your &lt;kbd&gt;environment.rb&lt;/kbd&gt; file using &lt;code&gt;config.gem &quot;aws-s3&quot;, :lib =&gt; &quot;aws/s3&quot;&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;&lt;p&gt;Create a file called &lt;kbd&gt;aws.rb&lt;/kbd&gt; in &lt;kbd&gt;RAILS_ROOT/lib/patches&lt;/kbd&gt;&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Copy and paste the following code into it (or get the &lt;a href=&quot;http://gist.github.com/289745#file_aws.rb&quot; title=&quot;Get the gist&quot;&gt;gist&lt;/a&gt;):&lt;/p&gt;
    &lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;module AWS
  module S3
    class Authentication
      class CanonicalString
        def initialize(request, options = {})
          super()
          @request = request
          @headers = {}
          @options = options
          # &quot;For non-authenticated or anonymous requests. A NotImplemented error result code will be returned if
          # an authenticated (signed) request specifies a Host: header other than 's3.amazonaws.com'&quot;
          # (from http://docs.amazonwebservices.com/AmazonS3/2006-03-01/VirtualHosting.html)
          request['Host'] = AWS::S3::Base.connection.subdomain || DEFAULT_HOST
          build
        end

        private
          def build
            self &amp;lt;&amp;lt; &quot;#{request.method}\n&quot;
            ensure_date_is_valid

            initialize_headers
            set_expiry!

            headers.sort_by {|k, _| k}.each do |key, value|
              value = value.to_s.strip
              self &amp;lt;&amp;lt; (key =~ self.class.amazon_header_prefix ? &quot;#{key}:#{value}&quot; : value)
              self &amp;lt;&amp;lt; &quot;\n&quot;
            end
            self &amp;lt;&amp;lt; (AWS::S3::Base.connection.subdomain ? &quot;/#{AWS::S3::Base.connection.subdomain}#{path}&quot; : path)
          end
      end
    end

    class Bucket
      class &amp;lt;&amp;lt; self
        private
          def path(name, options = {})
            if name.is_a?(Hash)
              options = name
              name    = nil
            end
            bucket_name(name) == connection.subdomain ? &quot;/#{RequestOptions.process(options).to_query_string}&quot; : &quot;/#{bucket_name(name)}#{RequestOptions.process(options).to_query_string}&quot;
          end
      end
    end

    class Connection
      def subdomain
        http.address[/^(.+)\.#{DEFAULT_HOST}$/, 1]
      end
    end

    class S3Object
      class &amp;lt;&amp;lt; self
        def path!(bucket, name, options = {}) #:nodoc:
          # We're using the second argument for options
          if bucket.is_a?(Hash)
            options.replace(bucket)
            bucket = nil
          end
          bucket_name(bucket) == connection.subdomain ? &quot;/#{name}&quot; : &quot;/#{bucket_name(bucket)}/#{name}&quot;
        end
      end
    end
  end
end&lt;/code&gt;&lt;/pre&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Require the patch from an initializer. I typically do this using the following code which loads any Ruby file in the &lt;kbd&gt;RAILS_ROOT/lib/patches&lt;/kbd&gt; directory:&lt;/p&gt;
    &lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;Dir[File.join(Rails.root, 'lib', 'patches', '**', '*.rb')].sort.each { |patch| require(patch) }&lt;/code&gt;&lt;/pre&gt;
  &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The above code fixes AWS::S3, but Paperclip also needs some love: it needs to pass a &lt;code&gt;:server&lt;/code&gt; option when the storage module establishes a connection to S3, the &lt;code&gt;to_file&lt;/code&gt; method needs to be patched to use binary mode on Windows (ah that &lt;a href=&quot;http://github.com/thoughtbot/paperclip/commit/9c510875813ec40186a7210b8e2dd1ed2873f8b0&quot; title=&quot;See my previous patches for Windows&quot;&gt;old chestnut&lt;/a&gt; again!) and to handle non-existent objects more gracefully, making it more consistent with the file system storage module. To make the changes:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;p&gt;Create a file called &lt;kbd&gt;paperclip.rb&lt;/kbd&gt; in &lt;kbd&gt;RAILS_ROOT/lib/patches&lt;/kbd&gt;&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Copy and paste the following code into it (or get the &lt;a href=&quot;http://gist.github.com/289745#file_paperclip.rb&quot; title=&quot;Get the gist&quot;&gt;gist&lt;/a&gt;):&lt;/p&gt;
    &lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;module Paperclip
  module Storage
    module S3
      # Patch s3 storage initialisation to pass server name to aws/s3
      def self.extended(base)
        require 'aws/s3'
        base.instance_eval do
          @s3_credentials = parse_credentials(@options[:s3_credentials])
          @bucket         = @options[:bucket]         || @s3_credentials[:bucket]
          @bucket         = @bucket.call(self) if @bucket.is_a?(Proc)
          @s3_options     = @options[:s3_options]     || {}
          @s3_permissions = @options[:s3_permissions] || :public_read
          @s3_protocol    = @options[:s3_protocol]    || (@s3_permissions == :public_read ? 'http' : 'https')
          @s3_headers     = @options[:s3_headers]     || {}
          @s3_host_alias  = @options[:s3_host_alias]
          @url            = &quot;:s3_path_url&quot; unless @url.to_s.match(/^:s3.*url$/)
          AWS::S3::Base.establish_connection!( @s3_options.merge(
            :access_key_id =&amp;gt; @s3_credentials[:access_key_id],
            :secret_access_key =&amp;gt; @s3_credentials[:secret_access_key],
            :server =&amp;gt; &quot;#{@bucket}.s3.amazonaws.com&quot;
          ))
        end
        Paperclip.interpolates(:s3_alias_url) do |attachment, style|
          &quot;#{attachment.s3_protocol}://#{attachment.s3_host_alias}/#{attachment.path(style).gsub(%r{^/}, &quot;&quot;)}&quot;
        end
        Paperclip.interpolates(:s3_path_url) do |attachment, style|
          &quot;#{attachment.s3_protocol}://s3.amazonaws.com/#{attachment.bucket_name}/#{attachment.path(style).gsub(%r{^/}, &quot;&quot;)}&quot;
        end
        Paperclip.interpolates(:s3_domain_url) do |attachment, style|
          &quot;#{attachment.s3_protocol}://#{attachment.bucket_name}.s3.amazonaws.com/#{attachment.path(style).gsub(%r{^/}, &quot;&quot;)}&quot;
        end
      end

      # Patch to use binmode on Windows
      def to_file(style = default_style)
        return @queued_for_write[style] if @queued_for_write[style]
        begin
          file = Tempfile.new(path(style))
          file.binmode if file.respond_to?(:binmode)
          file.write(AWS::S3::S3Object.value(path(style), bucket_name))
          file.rewind
        rescue AWS::S3::NoSuchKey
          file.close if file.respond_to?(:close)
          file = nil
        end
        file
      end
    end
  end
end&lt;/code&gt;&lt;/pre&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;p&gt;If you’re using the patch loading code from earlier then that’s all you need to do, otherwise make sure you require this patch file from an initializer.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Having patched everything up, all that remains is to change your &lt;code&gt;has_attached_file&lt;/code&gt; definitions to configure them to use S3, for example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;has_attached_file :photo,
                  :styles =&amp;gt; { :small =&amp;gt; '105x', :large =&amp;gt; '415x' },
                  :storage =&amp;gt; :s3,
                  :s3_credentials =&amp;gt; File.join(Rails.root, 'config', 's3.yml'),
                  :url =&amp;gt; ':s3_domain_url',
                  :path =&amp;gt; ':attachment/:id/:style:extension'&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see the storage module is set to S3 and the &lt;code&gt;:s3_credentials&lt;/code&gt; option is configured to use the &lt;kbd&gt;s3.yml&lt;/kbd&gt; file, which should look something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;yaml&quot;&gt;common: &amp;amp;common
  access_key_id: yourkeyhere
  secret_access_key: your/secret+here

development:
  &amp;lt;&amp;lt;: *common
  bucket: app-name-development

test:
  &amp;lt;&amp;lt;: *common
  bucket: app-name-test

production:
  &amp;lt;&amp;lt;: *common
  bucket: app-name-production&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And finally the &lt;code&gt;:url&lt;/code&gt; option is set to use the &lt;code&gt;:s3_domain_url&lt;/code&gt; interpolation: this is important as European buckets can only be accessed using domain URLs (e.g. http://app-name-bucket.s3.amazonaws.com/object_path) or aliased URLs &lt;a href=&quot;http://docs.amazonwebservices.com/AmazonS3/latest/index.html?VirtualHosting.html#VirtualHostingCustomURLs&quot; title=&quot;Read about custom URLs using CNAME records&quot;&gt;using CNAMEs&lt;/a&gt; and the &lt;code&gt;:s3_alias_url&lt;/code&gt; interpolation.&lt;/p&gt;
&lt;p&gt;So there you have it: support for European buckets with Paperclip. As workarounds go this is fairly straight forward, but the good news is that there is a slightly simpler solution.&lt;/p&gt;
&lt;h3&gt;Option #2 - use a different S3 gem (and patch!)&lt;/h3&gt;
&lt;p&gt;Last week I needed to write some code to manage objects on S3 in a non-Paperclip scenario. Having immediately ruled out AWS::S3 I decided to see what, if any, newer gems were available. A quick search of GemCutter revealed the not-so-imaginatively named &lt;a href=&quot;http://gemcutter.org/gems/s3&quot; title=&quot;See S3 on GemCutter&quot;&gt;S3&lt;/a&gt; gem: with full support for European buckets and a nice, straightforward syntax I decided to give it a try.&lt;/p&gt;
&lt;p&gt;One of the first things I tend to do when trying a new gem or plugin is to have a poke about in the source code to get a feel for how nice the code is, and while doing this with S3 I came across a &lt;a href=&quot;http://github.com/qoobaa/s3/blob/master/extra/s3_paperclip.rb&quot; title=&quot;See the code on GitHub&quot;&gt;custom storage module for Paperclip&lt;/a&gt; (and there’s one for &lt;a href=&quot;http://github.com/qoobaa/s3/blob/master/extra/s3_attachment_fu.rb&quot; title=&quot;See the code on GitHub&quot;&gt;attachment_fu&lt;/a&gt; too).&lt;/p&gt;
&lt;p&gt;The current version of the S3 gem (0.2.4) doesn’t support time expiring URLs for accessing private objects so, until 0.2.5 is released, using the gem is a little more work than it would otherwise be:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;p&gt;Grab a copy of the gem source using &lt;kbd&gt;git clone git://github.com/qoobaa/s3.git&lt;/kbd&gt;&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;&lt;p&gt;Change to the source code directory and use Rake to build and install the gem with &lt;kbd&gt;rake install&lt;/kbd&gt;.&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;&lt;p&gt;Add the gem to your application’s &lt;kbd&gt;environment.rb&lt;/kbd&gt; using &lt;code&gt;config.gem &quot;s3&quot;&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;&lt;p&gt;Create a file called &lt;kbd&gt;paperclip.rb&lt;/kbd&gt; in &lt;kbd&gt;RAILS_ROOT/lib/patches&lt;/kbd&gt;&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;&lt;p&gt;Copy and paste the contents of &lt;a href=&quot;http://github.com/qoobaa/s3/blob/master/extra/s3_paperclip.rb&quot; title=&quot;See the code on GitHub&quot;&gt;the module file&lt;/a&gt; into the file you created in step 4 (or get the &lt;a href=&quot;http://gist.github.com/289766&quot; title=&quot;Get the gist&quot;&gt;gist&lt;/a&gt; which includes the changes from steps 6 and 7 so you can skip on to step 8).&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Add the &lt;code&gt;expiring_url&lt;/code&gt; method to the module, the code looks like this:&lt;/p&gt;
    &lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;def expiring_url(style_name = default_style, time = 3600)
  bucket.objects.build(path(style_name)).temporary_url(Time.current + time)
end&lt;/code&gt;&lt;/pre&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The &lt;code&gt;to_file&lt;/code&gt; method also needs patching to better handle non-existing objects and if you’re running on Windows you’ll also want to patch it to handle binary files:&lt;/p&gt;
    &lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;def to_file style_name = default_style
  return @queued_for_write[style_name] if @queued_for_write[style_name]
  begin
    file = Tempfile.new(path(style_name))
    file.binmode if file.respond_to?(:binmode)
    file.write(bucket.objects.find(path(style_name)).content)
    file.rewind
  rescue ::S3::Error::NoSuchKey
    file.close if file.respond_to?(:close)
    file = nil
  end
  file
end&lt;/code&gt;&lt;/pre&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Require the patch from an initializer (see option #1 for an explanation of this code).&lt;/p&gt;
    &lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;Dir[File.join(Rails.root, 'lib', 'patches', '**', '*.rb')].sort.each { |patch| require(patch) }&lt;/code&gt;&lt;/pre&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;p&gt;Configure your model in the same way as described in option #1 and your good to go!&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;The lesser of two evils&lt;/h3&gt;
&lt;p&gt;At the moment neither of the above options are ideal. The S3 gem has the advantage of being more actively maintained than AWS::S3, and some of the steps to get it working with Paperclip will be simplified when the next version of the gem is released. While both options involve patching, the patches for S3 are much more trivial when compared with those for AWS::S3, reducing the risk for breakage when Paperclip is updated.&lt;/p&gt;
&lt;p&gt;I’d really like to see S3 become the official gem for Paperclip so that it just works without all this hassle. I’ve made a start on a full patch for Paperclip that uses the new gem: I’ve just got to get my head around all the mocking and stubbing that goes on in the storage module tests and then it’ll be ready for the guys at &lt;a href=&quot;http://thoughtbot.com/community/&quot; title=&quot;Say hello to ThoughtBot&quot;&gt;ThoughtBot&lt;/a&gt; to have a look!&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://thewebfellas.com/">
    <author>
      <name>Rob Anderton</name>
    </author>
    <id>tag:thewebfellas.com,2010-01-18:12729</id>
    <published>2010-01-18T23:30:00Z</published>
    <updated>2011-10-05T15:33:53Z</updated>
    <category term="Blog"/>
    <category term="duplicate key"/>
    <category term="insert"/>
    <category term="mysql"/>
    <category term="query"/>
    <category term="update"/>
    <link href="http://thewebfellas.com/blog/2010/1/18/conditional-duplicate-key-updates-with-mysql" rel="alternate" type="text/html"/>
    <title>Conditional duplicate key updates with MySQL</title>
<summary type="html">&lt;p&gt;In one of our larger Rails apps the sheer volume of data we process means we’ve had to rely more and more on direct SQL queries, denormalised tables and summary tables to speed things up. When updating summary tables we typically use &lt;code&gt;&lt;a href=&quot;http://dev.mysql.com/doc/refman/5.1/en/insert-on-duplicate.html&quot; title=&quot;Read the documentation&quot;&gt;ON DUPLICATE KEY UPDATE&lt;/a&gt;&lt;/code&gt;, a MySQL extension to &lt;code&gt;INSERT&lt;/code&gt; statements since version 4.1, that allows a record to either be inserted or updated in one query.&lt;/p&gt;</summary><content type="html">
            &lt;p&gt;In one of our larger Rails apps the sheer volume of data we process means we’ve had to rely more and more on direct SQL queries, denormalised tables and summary tables to speed things up. When updating summary tables we typically use &lt;code&gt;&lt;a href=&quot;http://dev.mysql.com/doc/refman/5.1/en/insert-on-duplicate.html&quot; title=&quot;Read the documentation&quot;&gt;ON DUPLICATE KEY UPDATE&lt;/a&gt;&lt;/code&gt;, a MySQL extension to &lt;code&gt;INSERT&lt;/code&gt; statements since version 4.1, that allows a record to either be inserted or updated in one query.&lt;/p&gt;
&lt;p&gt;For example, with this table:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;sql&quot;&gt;CREATE TABLE daily_events (
  created_on DATE NOT NULL,
  last_event_id INT(11) UNSIGNED NOT NULL,
  last_event_created_at DATETIME NOT NULL,
  PRIMARY KEY (created_on)
);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The purpose of this table is to provide a daily summary of events storing the &lt;code&gt;id&lt;/code&gt; and &lt;code&gt;created_at&lt;/code&gt; timestamp of the most recent event for the day. When a new event is processed the daily summary table should also be updated, an operation which is ideally suited to an &lt;code&gt;INSERT ON DUPLICATE KEY UPDATE&lt;/code&gt; query.&lt;/p&gt;
&lt;h3&gt;Simple conditional updates&lt;/h3&gt;
&lt;p&gt;Unfortunately there’s a catch: the app can process events out-of-order meaning updates to the summary table won’t necessarily occur in the order that the events are created. This means that the &lt;code&gt;last_event_id&lt;/code&gt; and &lt;code&gt;last_event_created_at&lt;/code&gt; fields  should only be updated if the event is newer than the last one for the day. In SQL terms it should look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;sql&quot;&gt;INSERT INTO daily_events (created_on, last_event_id, last_event_created_at)
VALUES ('2010-01-19', 23, '2010-01-19 10:23:11')
ON DUPLICATE KEY UPDATE
last_event_id = VALUES(last_event_id),
last_event_created_at = VALUES(last_event_created_at)
WHERE last_event_created_at &amp;lt; VALUES(last_event_created_at);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The bad news is that this won’t work as MySQL doesn’t allow &lt;code&gt;WHERE&lt;/code&gt; clauses in the update portion of the query. In a simple case like this the easiest workaround is to use the &lt;code&gt;&lt;a href=&quot;http://dev.mysql.com/doc/refman/5.1/en/control-flow-functions.html#function_if&quot; title=&quot;Read the documenation&quot;&gt;IF&lt;/a&gt;&lt;/code&gt; function:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;sql&quot;&gt;INSERT INTO daily_events (created_on, last_event_id, last_event_created_at)
VALUES ('2010-01-19', 23, '2010-01-19 10:23:11')
ON DUPLICATE KEY UPDATE
last_event_id = IF(last_event_created_at &amp;lt; VALUES(last_event_created_at), VALUES(last_event_id), last_event_id),
last_event_created_at = IF(last_event_created_at &amp;lt; VALUES(last_event_created_at), VALUES(last_event_created_at), last_event_created_at);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This works by checking if the &lt;code&gt;last_event_created_at&lt;/code&gt; timestamp of the event being updated is newer than the current timestamp, if it is then the new value is assigned to the field in the update, otherwise the current value is used.&lt;/p&gt;
&lt;p&gt;An important thing to keep in mind when using this approach is that the order in which you update your fields is &lt;strong&gt;very important&lt;/strong&gt;. I was wrongly under the impression that the updates took place in one mass-assignment after the entire query had been interpreted by MySQL. But they’re not: the assignments happen &lt;strong&gt;in the order they appear in the query&lt;/strong&gt;. To give you an example, this query won’t produce the expected result:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;sql&quot;&gt;INSERT INTO daily_events (created_on, last_event_id, last_event_created_at)
VALUES ('2010-01-19', 23, '2010-01-19 10:23:11')
ON DUPLICATE KEY UPDATE
last_event_created_at = IF(last_event_created_at &amp;lt; VALUES(last_event_created_at), VALUES(last_event_created_at), last_event_created_at),
last_event_id = IF(last_event_created_at &amp;lt; VALUES(last_event_created_at), VALUES(last_event_id), last_event_id);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When the update is executed with a more recent event, the &lt;code&gt;last_event_created_at&lt;/code&gt; field will be updated, but the &lt;code&gt;last_event_id&lt;/code&gt; field won’t. This is because when the second &lt;code&gt;IF&lt;/code&gt; is evaluated &lt;code&gt;last_event_created_at&lt;/code&gt; has already been updated so that &lt;code&gt;last_event_created_at&lt;/code&gt; is equal to &lt;code&gt;VALUES(last_event_created_at)&lt;/code&gt;. Crazy huh?!&lt;/p&gt;
&lt;h3&gt;Conditions using multiple fields&lt;/h3&gt;
&lt;p&gt;Keeping in mind the importance of the order in which fields are updated, conditions that involve multiple fields are a little trickier. For example, a slightly different summary table could look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;sql&quot;&gt;CREATE TABLE last_session_user_events (
  user_id INT(11) UNSIGNED NOT NULL,
  last_session_id INT(11) UNSIGNED NOT NULL,
  last_session_created_at DATETIME NOT NULL,
  last_event_id INT(11) UNSIGNED NOT NULL,
  last_event_created_at DATETIME NOT NULL,
  PRIMARY KEY (user_id)
);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This summary table tracks the most recent event for the most recent session for a user. Updates to this table should only be made if the session is newer, or if the session is the same and the event is newer. Again events can be processed out of order and it is possible for multiple sessions to be active at the same time. The query to insert/update looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;sql&quot;&gt;INSERT INTO last_session_user_events (user_id, last_session_id, last_session_created_at, last_event_id, last_event_created_at)
VALUES (6, 45, '2010-01-18 23:31:44', 453, '2010-01-19 10:23:11')
ON DUPLICATE KEY UPDATE
last_session_id = IF((last_session_created_at &amp;lt; VALUES(last_session_created_at) OR (last_session_id = VALUES(last_session_id) AND last_event_created_at &amp;lt; VALUES(last_event_created_at))), VALUES(last_session_id), last_session_id),
last_session_created_at = IF((last_session_created_at &amp;lt; VALUES(last_session_created_at) OR (last_session_id = VALUES(last_session_id) AND
last_event_created_at &amp;lt; VALUES(last_event_created_at))), VALUES(last_session_created_at), last_session_created_at),
last_event_id = IF((last_session_created_at &amp;lt; VALUES(last_session_created_at) OR (last_session_id = VALUES(last_session_id) AND last_event_created_at &amp;lt; VALUES(last_event_created_at))), VALUES(last_event_id), last_event_id),
last_event_created_at = IF((last_session_created_at &amp;lt; VALUES(last_session_created_at) OR (last_session_id = VALUES(last_session_id) AND last_event_created_at &amp;lt; VALUES(last_event_created_at))), VALUES(last_event_created_at), last_event_created_at);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Aside from the fact this looks a bit crazy (people who obsess about DRY code will be weeping) it also suffers from the order of evaluation problem I described earlier. In certain situations the result of the &lt;code&gt;IF&lt;/code&gt; statement will change within a single query as the updates are made, leading to inconsistent data. To make this work it is necessary evaluate the &lt;code&gt;IF&lt;/code&gt; condition once, before any updates occur, and to store the result in a &lt;a href=&quot;http://dev.mysql.com/doc/refman/5.1/en/user-variables.html&quot; title=&quot;Read the documentation&quot;&gt;user defined variable&lt;/a&gt; that can be referenced in the rest of the query. Here’s the updated query:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;sql&quot;&gt;INSERT INTO last_session_user_events (user_id, last_session_id, last_session_created_at, last_event_id, last_event_created_at)
VALUES (6, 45, '2010-01-18 23:31:44', 453, '2010-01-19 10:23:11')
ON DUPLICATE KEY UPDATE
last_session_id = IF((@update_record := (last_session_created_at &amp;lt; VALUES(last_session_created_at) OR (last_session_id = VALUES(last_session_id) AND last_event_created_at &amp;lt; VALUES(last_event_created_at)))), VALUES(last_session_id), last_session_id),
last_session_created_at = IF(@update_record, VALUES(last_session_created_at), last_session_created_at),
last_event_id = IF(@update_record, VALUES(last_event_id), last_event_id),
last_event_created_at = IF(@update_record, VALUES(last_event_created_at), last_event_created_at);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, not only does this ensure the &lt;code&gt;IF&lt;/code&gt; statement remains consistent throughout the update, it also cleans up the query making it a little easier on the eyes.&lt;/p&gt;
&lt;p&gt;This is just one of many MySQLisms we’ve had to deal with over the last 12 months, no doubt we’ll blog about more of them in the future. Our lawyers have asked us to point out that no DBAs were harmed in the making of the post!&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://thewebfellas.com/">
    <author>
      <name>Chris Anderton</name>
    </author>
    <id>tag:thewebfellas.com,2009-11-26:11118</id>
    <published>2009-11-26T20:30:00Z</published>
    <updated>2010-10-04T17:33:57Z</updated>
    <category term="Blog"/>
    <category term="data warehouse"/>
    <category term="greenplum"/>
    <category term="infinidb"/>
    <category term="infobright"/>
    <category term="monetdb"/>
    <category term="rails"/>
    <category term="ruby"/>
    <link href="http://thewebfellas.com/blog/2009/11/26/d3" rel="alternate" type="text/html"/>
    <title>InfiniDB, Infobright and MonetDB - Day 3: MonetDB</title>
<content type="html">
            &lt;p&gt;Day 3 of my database exploration mission brings me to &lt;a href=&quot;http://monetdb.cwi.nl/&quot; title=&quot;MonetDB&quot;&gt;MonetDB&lt;/a&gt;.  Binary downloads are available for Debian, Fedora, Ubuntu and (strangely!) Windows! If we still had any Windows users left here at HQ then it'd be a rare treat, but instead (as usual) our platform of choice (Centos 5) isn't directly available in binary form. We downloaded the Fedora source RPMs and built our own - in case they're of any use then i've put them up on a &lt;a href=&quot;http://code.google.com/p/monetdb-rhel-5&quot; title=&quot;Centos 5 MonetDB RPMs&quot;&gt;Google Code site&lt;/a&gt; for others to download.&lt;/p&gt;
&lt;p&gt;After installing the RPMs then you're ready to get started - before you can do anything you have to start the &lt;code&gt;merovingian&lt;/code&gt; process (you could either setup an init script, or run the binary manually for now). For information, the instructions say:&lt;/p&gt;
&lt;blockquote&gt;merovingian is a daemon process that controls a collection of database servers, i.e. mserver5 processes, each looking after a single physical database. Start this program to gain access to your MonetDB database farm. merovingian is designed to be used in a system initialisation script in production environments.&lt;/blockquote&gt;
&lt;p&gt;With merovingian running then you're ready to create a database - for this you use &lt;code&gt;monetdb&lt;/code&gt; - and then start the database using the same command for example:&lt;/p&gt;
&lt;pre&gt;&lt;kbd&gt;&amp;gt; monetdb create twf
successfully created database 'twf'

&amp;gt; monetdb status
     name        state     
twf            stopped              
  
&amp;gt; monetdb start twf
starting database 'twf'... done

&amp;gt; monetdb status
     name        state     
twf            running&lt;/kbd&gt;&lt;/pre&gt;
&lt;p&gt;You now have a running database and can connect to it using &lt;code&gt;mclient&lt;/code&gt;. This is similar to most command line clients where you can perform changes to your database as well as query for data.&lt;/p&gt;
&lt;p&gt;The first step to transferring the database was as usual - inspect the schema on our MySQL database and update it to make the correct use of the &lt;a href=&quot;http://monetdb.cwi.nl/projects/monetdb//SQL/Documentation/Data-Types.html#Data-Types&quot; title=&quot;MonetDB data types&quot;&gt;supported data types&lt;/a&gt;. As with the other systems, there's no support for unsigned values, it also wasn't immediately obvious to me what the maximum length of a varchar is.&lt;/p&gt;
&lt;p&gt;With the tables created it was time to try and migrate some data. Given MonetDB has been around for quite a while then there seemed to be pretty scarce resources with any detailed instructions - I couldn't, for example, find any simple migration tools or documentation detailing the best path for migration. I guess this could be because MonetDB is more often tackled by people with bigger brains or with more time to figure things out.&lt;/p&gt;
&lt;p&gt;I attempted to use the following to dump data from MySQL:&lt;/p&gt;
&lt;pre&gt;&lt;kbd&gt;select * from h into outfile '/dbtmp/tmp/h' fields terminated by &quot;|&quot; enclosed by '&quot;';&lt;/kbd&gt;&lt;/pre&gt;
&lt;p&gt;And then the following to import into my MonetDB table:&lt;/p&gt;
&lt;pre&gt;&lt;kbd&gt;copy 1000000 records into h from '/dbtmp/tmp/h' using delimiters '|','\n', '&quot;'  null as '';&lt;/kbd&gt;&lt;/pre&gt;
&lt;p&gt;This yielded reasonable results - though I did have to do some tidying up in the middle with &lt;code&gt;sed&lt;/code&gt; - in the end I gave up as there were some string values causing me problems, so I decided to rest on it and went to bed!&lt;/p&gt;
&lt;p&gt;In the morning I came back to find the merovingian process was dead, and the status of the database was showing as crashed. I started up the processes and took a look at the status - it said the health was 67% so i'm not really sure what's going on with it!&lt;/p&gt;
&lt;h4&gt;Performance&lt;/h4&gt;
&lt;p&gt;In the time I had available I was only able to get a 1 million row table imported successfully to play with - a shocking performance I know, but MonetDB was being quite fussy and I wasn't pressing the right buttons! I did run a few tests and also ran them against the same dataset in MySQL for comparison, all are run from cold - i.e. MySQL and MonetDB are both restarted before each query. I don't expect these queries to be representative of real world cases, I was just thinking of some nasty queries that I could throw at a single table in order to cause some pain.&lt;/p&gt;
&lt;h5&gt;Query 1&lt;/h5&gt;
&lt;p&gt;MySQL takes 250msec:&lt;/p&gt;
&lt;pre&gt;&lt;kbd&gt;sql&amp;gt;select count(*) from h;
+---------+
| L1      |
+=========+
| 1000000 |
+---------+
1 tuple
Timer       1.532 msec 1 rows&lt;/kbd&gt;&lt;/pre&gt;
&lt;h5&gt;Query 2&lt;/h5&gt;
&lt;p&gt;MySQL takes 420msec:&lt;/p&gt;
&lt;pre&gt;&lt;kbd&gt;sql&amp;gt;select count(*) from h group by intcolumn;
+-------+
| L2    |
+=======+
+-------+
65 tuples
Timer     142.260 msec 65 rows&lt;/kbd&gt;&lt;/pre&gt;
&lt;h5&gt;Query 3&lt;/h5&gt;
&lt;p&gt;MySQL takes 44,000msec:&lt;/p&gt;
&lt;pre&gt;&lt;kbd&gt;sql&amp;gt;select count(*) from h group by varcharcolumn;
+-------+
| L1    |
+=======+
+-------+
12743 tuples
Timer    1464.389 msec 12743 rows&lt;/kbd&gt;&lt;/pre&gt;
&lt;h5&gt;Query 4&lt;/h5&gt;
&lt;p&gt;MySQL takes 37,500msec:&lt;/p&gt;
&lt;pre&gt;&lt;kbd&gt;sql&amp;gt;select count(*) as total from h group by varcharcolumn order by total;
+-------+
| L1    |
+=======+
+-------+
12743 tuples
Timer    1496.537 msec 12743 rows&lt;/kbd&gt;&lt;/pre&gt;
&lt;h5&gt;Query 5&lt;/h5&gt;
&lt;p&gt;MySQL takes 373,000msec (not a typo, it's more than 6 minutes):&lt;/p&gt;
&lt;pre&gt;&lt;kbd&gt;sql&amp;gt;select count(*) as total from h group by varcharcolumn, anothervarcharcolumn order by total;
+-------+
| L1    |
+=======+
+-------+
69696 tuples
Timer    4170.520 msec 69696 rows&lt;/kbd&gt;&lt;/pre&gt;
&lt;h4&gt;Summary&lt;/h4&gt;
&lt;p&gt;Obviously this quick trial of each of these is not comprehensive enough to make any solid comparisons of performance - the next step will be for me to go through and come up with a proper test plan in order to be a little more methodical about things. However, it has given me a good grounding in how the 3 systems compare with respect to installing and getting started. I'll be keeping a close eye on InfiniDB - while not stable enough right now, i'm sure they'll keep things rolling and I look forward to taking another look. If I can overcome the import obstacles and also the different 'feel' of MonetDB then the basic query results make a compelling case for taking a further look - there's also more to learn here with respect to architecture, deployment techniques, monitoring, etc. Finally, Infobright - it would make my life easier if we could use it on an insert/update/delete basis - as it is I think we'd have a tough time getting clients to pay the license fee - perhaps if bundled with something like EC2 instances with a smaller incremental cost then it may be more palletable and help to increase adoption (it may be that Infobright have lots of customers with open wallets - in which case please share them!). In terms of immediate ease of use, with some visible performance improvements, Infobright fits the bill - but until i've had a chance to compare MonetDB and Infobright in a bit more detail then i'll reserve my final judgement!&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://thewebfellas.com/">
    <author>
      <name>Chris Anderton</name>
    </author>
    <id>tag:thewebfellas.com,2009-11-25:11079</id>
    <published>2009-11-25T11:32:00Z</published>
    <updated>2010-01-18T23:40:06Z</updated>
    <category term="Blog"/>
    <category term="data warehouse"/>
    <category term="greenplum"/>
    <category term="infinidb"/>
    <category term="infobright"/>
    <category term="monetdb"/>
    <category term="rails"/>
    <category term="ruby"/>
    <link href="http://thewebfellas.com/blog/2009/11/25/infinidb-infobright-and-monetdb-day-2-infobright" rel="alternate" type="text/html"/>
    <title>InfiniDB, Infobright and MonetDB - Day 2: Infobright</title>
<summary type="html">&lt;p&gt;Day 2 of my tour of column based storage brings me on to &lt;a href=&quot;http://www.infobright.org&quot; title=&quot;Infobright&quot;&gt;Infobright Community Edition (ICE)&lt;/a&gt;. The first impressive point was that based on my blog post of yesterday then I already had an email from Mark in Community Relations at Infobright offering help and advice - despite me calling him the wrong name (I was having a bad day!) then he was immediately helpful and also offered to get some of his team to look into my queries.&lt;/p&gt;
&lt;p&gt;As an aside, John from Calpont was also kind enough to drop by to respond to some of my points - to me this gives me a warm fuzzy feeling that both Infobright and Calpont are taking the community seriously - I guess for these products to gain traction they need to make sure people can get motoring with them to improve adoption.&lt;/p&gt;</summary><content type="html">
            &lt;p&gt;Day 2 of my tour of column based storage brings me on to &lt;a href=&quot;http://www.infobright.org&quot; title=&quot;Infobright&quot;&gt;Infobright Community Edition (ICE)&lt;/a&gt;. The first impressive point was that based on my blog post of yesterday then I already had an email from Mark in Community Relations at Infobright offering help and advice - despite me calling him the wrong name (I was having a bad day!) then he was immediately helpful and also offered to get some of his team to look into my queries.&lt;/p&gt;
&lt;p&gt;As an aside, John from Calpont was also kind enough to drop by to respond to some of my points - to me this gives me a warm fuzzy feeling that both Infobright and Calpont are taking the community seriously - I guess for these products to gain traction they need to make sure people can get motoring with them to improve adoption.&lt;/p&gt;

&lt;h3&gt;Infobright&lt;/h3&gt;
&lt;p&gt;Infobright is &quot;designed to deliver a scalable data warehouse optimized for analytic queries&quot; - which is exactly what we're looking for. The main shortcoming at first glance for us is the lack of DML in the Community Edition (it is available in the Enterprise edition, a free trial is available). Similar to InfiniDB then there is a slightly more restricted range of data types compared to MySQL - but currently more flexible than InfiniDB. For example - Infobright can support longer varchars - a feature that John from Calpont says is on it's way in InfiniDB.&lt;/p&gt;
&lt;p&gt;To get started we downloaded the 64bit RPM direct from the Infobright website - at the time of testing this was ICE v3.2.2. Install was straightforward with the RPM installing first go. Infobright installed onto a different port to MySQL by default, so there was no conflict with the existing MySQL installation. I guess it's also worth pointing out that we're just running with the default configurations for these systems to begin with.&lt;/p&gt;
&lt;p&gt;On my Centos 5 system I had to manually do &lt;code&gt;chkconfig --add mysql-ib&lt;/code&gt; and was then able to use &lt;code&gt;service mysql-ib start&lt;/code&gt; to crank up Infobright. After tweaking our table definitions to take account of the &lt;a href=&quot;http://www.infobright.org/wiki/Supported_Data_Types_and_Values/&quot; title=&quot;Supported data types and values&quot;&gt;supported data types and values&lt;/a&gt; then we were able to create our tables. We were also able to dump the data from our MySQL tables and import them using the Infobright loader with relative ease.&lt;/p&gt;
&lt;h4&gt;Performance&lt;/h4&gt;
&lt;p&gt;With our tables populated we were ready to get querying. It's only fair to point out that this is based on taking our MySQL tables straight into Infobright with no tweakage - as such it probably won't take full advantage of the features on offer. Our cold run figures are based on an average of 5 hopefully cold runs - i.e. we restart MySQL/Infobright in the hope of clearing the caches.&lt;/p&gt;
&lt;h5&gt;Query 1&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;SELECT   soh.co, 
         h.ca, 
         o.i, 
         o.n, 
         so.r, 
         so.a, 
         so.w, 
         soh.lhi, 
         soh.lca, 
         soh.lsfhi, 
         soh.lsfhca, 
         h.c, 
         h.r, 
         h.c, 
         h.sti, 
         h.st 
FROM     soh 
         INNER JOIN so 
           ON (so.si = soh.si 
               AND so.oi = soh.oi) 
         INNER JOIN o 
           ON o.i = soh.oi 
         INNER JOIN h 
           ON soh.lsfhi = h.i 
WHERE    soh.si = 125 
         AND soh.co BETWEEN &quot;2009-01-01&quot; AND &quot;2009-10-10&quot; 
ORDER BY soh.co DESC, 
         soh.lsfhca DESC
LIMIT    0,30;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I've removed the column names to protect the innocent, on a cold run our InnoDB (XtraDB) table responded in 1.84 secs vs 8.94 secs for Infobright. A second (warm) run gave 1.51 secs vs 4.40 secs.&lt;/p&gt;
&lt;h5&gt;Query 2&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;SELECT 
  SQL_CALC_FOUND_ROWS soh.co, 
                      soh.lca, 
                      soh.oi, 
                      soh.lhi, 
                      soh.lsfhi, 
                      soh.lsfhca 
FROM     soh 
WHERE    soh.si = 125 
         AND soh.co BETWEEN &quot;2009-01-01&quot; AND &quot;2009-10-10&quot; 
ORDER BY soh.co DESC, 
         soh.lsfhca DESC
LIMIT    0,30;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A cold run clocked MySQL at 1.62 secs vs 0.08 secs for Infobright. It's also worth pointing out that SQL_CALC_FOUND_ROWS is here to make a note - i.e. Infobright doesn't support it although it doesn't choke on it if included. Removing it made no significant difference to the times in either database.&lt;/p&gt;
&lt;h5&gt;Query 3&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;SELECT   soh.co, 
         soh.lca, 
         soh.oi, 
         soh.lhi, 
         soh.lca, 
         soh.lsfhi, 
         soh.lsfhca 
FROM     soh 
WHERE    soh.si = 125 
         AND soh.co BETWEEN &quot;2009-01-01&quot; AND &quot;2009-10-10&quot; 
         AND soh.oi NOT IN (68633,4) 
ORDER BY soh.co DESC, 
         soh.lsfhca DESC 
LIMIT    0,30;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cold runs of MySQL gave 1.93 secs vs 0.53 for Infobright.&lt;/p&gt;
&lt;h5&gt;Query 4&lt;/h5&gt;
&lt;pre&gt;&lt;code&gt;SELECT t1.oi, 
       t1.co, 
       t1.pv, 
       t1.rv, 
       t1.tpv, 
       t1.tnv, 
       t1.trv 
FROM   soh AS t1 
       INNER JOIN (SELECT   oi, 
                            Max(co) AS mco 
                   FROM     soh 
                   WHERE    si = 125 
                            AND co &amp;lt;= '2009-10-24 08:12:23' 
                   GROUP BY oi) AS t2 
         ON t1.oi = t2.oi 
            AND t1.co = t2.mco 
WHERE  t1.oi IN (1420,13501,85778,49753, 
                                  288095,21883,21623,1198, 
                                  123438,54699,145,50293, 
                                  38747,63918,1997,35299, 
                                  97864,321139,138088,38578, 
                                  105140,5440,73439,132415, 
                                  452,180846,4384,29049, 
                                  206296,113606) 
       AND t1.si = 125;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;MySQL finished in 1.39 secs vs 0.57 secs for Infobright&lt;/p&gt;
&lt;h4&gt;Summary&lt;/h4&gt;
&lt;p&gt;It was a real bonus to be able to get up and running with minimal effort - queries were pretty much able to run unmodified for testing, although both the schema and queries may benefit from optimisation to get the best out of Infobright. As a drop in replacement for a struggling MySQL DB it also holds promise - most of the queries we ran were at least twice as quick as MySQL - the exception being the first one which was slower, so we'll need to work out why.&lt;/p&gt;
&lt;p&gt;These current tests are in no way exhaustive - as a first pass i'm just getting to the point of getting things installed and in the position to play with so I can see what they're like to work with, and also to work out which ones are worth investing more time in. For a more thorough review on a more easily explainable dataset then checkout &lt;a href=&quot;http://www.mysqlperformanceblog.com/2009/09/29/quick-comparison-of-myisam-infobright-and-monetdb/&quot; title=&quot;Infobright comparison&quot;&gt;Baron's post on the MySQL Performance Blog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The only downside for us right now is the lack of insert/update/delete in the community edition - it means we'd have to move toward a batch data load and re-architecting to support this would of course involve effort - which then brings questions around using Hadoop back into the thought processes.&lt;/p&gt;
&lt;p&gt;I'm also considering taking a look at &lt;a href=&quot;http://www.greenplum.com/&quot; title=&quot;Greenplum&quot;&gt;Greenplum&lt;/a&gt; as they now offer the single node edition for free. Otherwise, it's off to MonetDB-land today and my RPMs have just finished compiling for Centos 5 so i'm ready to install!&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://thewebfellas.com/">
    <author>
      <name>Chris Anderton</name>
    </author>
    <id>tag:thewebfellas.com,2009-11-24:11023</id>
    <published>2009-11-24T09:48:00Z</published>
    <updated>2010-01-18T23:42:12Z</updated>
    <category term="Blog"/>
    <category term="data warehouse"/>
    <category term="infinidb"/>
    <category term="infobright"/>
    <category term="monetdb"/>
    <category term="rails"/>
    <category term="ruby"/>
    <link href="http://thewebfellas.com/blog/2009/11/24/infinidb-infobright-and-monetdb" rel="alternate" type="text/html"/>
    <title>InfiniDB, Infobright and MonetDB - Day 1: InfiniDB</title>
<summary type="html">&lt;p&gt;We're taking a whistlestop tour of some of the column based storage systems out there for a project we're working on (where the use case seems to fit better with this form of storage rather than straight MySQL). After reading through the &lt;a href=&quot;http://www.mysqlperformanceblog.com/2009/11/02/air-traffic-queries-in-infinidb-early-alpha/&quot; title=&quot;InfiniDB&quot;&gt;series&lt;/a&gt; &lt;a href=&quot;http://www.mysqlperformanceblog.com/2009/10/02/analyzing-air-traffic-performance-with-infobright-and-monetdb/&quot; title=&quot;Infobright and MonetDB&quot;&gt;of&lt;/a&gt; &lt;a href=&quot;http://www.mysqlperformanceblog.com/2009/10/26/air-traffic-queries-in-luciddb/&quot; title=&quot;LucidDB&quot;&gt;articles&lt;/a&gt; on the MySQL Performance Blog then we chose to look at InfiniDB, Infobright and MonetDB - with the two that talk MySQL coming first for ease of integration right now. I'm also going to do this as a three parter - so first up is InfiniDB.&lt;/p&gt;</summary><content type="html">
            &lt;p&gt;We're taking a whistlestop tour of some of the column based storage systems out there for a project we're working on (where the use case seems to fit better with this form of storage rather than straight MySQL). After reading through the &lt;a href=&quot;http://www.mysqlperformanceblog.com/2009/11/02/air-traffic-queries-in-infinidb-early-alpha/&quot; title=&quot;InfiniDB&quot;&gt;series&lt;/a&gt; &lt;a href=&quot;http://www.mysqlperformanceblog.com/2009/10/02/analyzing-air-traffic-performance-with-infobright-and-monetdb/&quot; title=&quot;Infobright and MonetDB&quot;&gt;of&lt;/a&gt; &lt;a href=&quot;http://www.mysqlperformanceblog.com/2009/10/26/air-traffic-queries-in-luciddb/&quot; title=&quot;LucidDB&quot;&gt;articles&lt;/a&gt; on the MySQL Performance Blog then we chose to look at InfiniDB, Infobright and MonetDB - with the two that talk MySQL coming first for ease of integration right now. I'm also going to do this as a three parter - so first up is InfiniDB.&lt;/p&gt;
&lt;h3&gt;InfiniDB&lt;/h3&gt;
&lt;p&gt;
&lt;a href=&quot;http://infinidb.org/&quot; title=&quot;InfiniDB&quot;&gt;InfiniDB&lt;/a&gt; was recently released as Open Source by &lt;a href=&quot;http://www.calpont.com/&quot; title=&quot;Calpont&quot;&gt;Calpont&lt;/a&gt;. The science bit says:&lt;/p&gt;
&lt;blockquote&gt;InfiniDB Community Edition provides a scale-up analytics database engine for your data warehousing, business intelligence and read-intensive application needs. Enabled via MySQL and purpose-built for an analytical workload with column-oriented technology at its core, the multi-threaded capabilities of InfiniDB Community Edition fully encompass query, transactional support and bulk load operations. &lt;/blockquote&gt;
&lt;p&gt;Handily for us, there are RHEL 5 RPMS available to download so we were able to suck them down and install them - for this trial I used version 0.9.5.1. Following the instructions (we believe fully!) yielded a system that wouldn't start (and yes, we had disabled the existing MySQL install to ensure no port conflict!). Wiping everything and reinstalling then presented a working system.&lt;/p&gt;
&lt;p&gt;There are some limitations to the data types supported - there is no support for unsigned numbers, and varchars are limited to 255. I expect this is a design decision as in a database structured around using InfiniDB then the strings would be stored in a MyISAM table and referenced.&lt;/p&gt;
&lt;p&gt;A nice feature is support for DML operations - so you can do insert, update and delete.&lt;/p&gt;
&lt;p&gt;After successfully exporting a table from MySQL and importing it into InfiniDB using their colxml and cpimport tools we had a table of data and were able to run some simple queries, select count(*) for example. However, after a teabreak the queries were no longer running and giving an error:&lt;/p&gt;
&lt;samp&gt;ERROR 122 (HY000): There was an internal error encountered in the Calpont Engine while processing this query. The query was cancelled. You may resubmit it if you like. The error is lost Connection to ExeMgr. If the problem continues, please contact your system administrator.&lt;/samp&gt;
&lt;p&gt;Not having the time or patience to dig deeper, I blitzed it once more and did another clean install. I then went to create the same table as earlier and boom! I saw the load climb up and then typically my wireless dropped out, when it came back I was unable to connect back in and our monitors were showing load sitting at 10. We left it 10 minutes, then rebooted. After a reboot, creating the same table took 20 seconds.&lt;/p&gt;
&lt;p&gt;We then successfully imported 4 tables of data and proceeded to start our list of queries to test - the first one caused load to rise to 10 again and a reboot was required. This one has been raised as a &lt;a href=&quot;https://bugs.launchpad.net/infinidb/+bug/487346&quot; title=&quot;InfiniDB bug&quot;&gt;bug&lt;/a&gt;!.&lt;/p&gt;
&lt;p&gt;I decided to leave it there for the day. The InfiniDB system looks promising on paper, and has been written by people infinitely more intelligent than I am, but right now it's not stable enough for me to want to go any further. It is only in Alpha right now so bugs are to be expected - i'll definately be revisiting it when it's a bit more mature or when i've got time to go through and explore the bugs properly so I can contribute back.&lt;/p&gt;
&lt;p&gt;Next up is Infobright and MonetDB - i'm tackling Infobright today so will see how things go!&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://thewebfellas.com/">
    <author>
      <name>Rob Anderton</name>
    </author>
    <id>tag:thewebfellas.com,2009-09-13:8601</id>
    <published>2009-09-13T23:10:00Z</published>
    <updated>2010-08-03T11:20:04Z</updated>
    <category term="Blog"/>
    <category term="apple"/>
    <category term="mac"/>
    <category term="schwarzenegger"/>
    <category term="windows"/>
    <link href="http://thewebfellas.com/blog/2009/9/13/i-need-your-designer-glasses-your-blue-jeans-and-your-black-turtle-neck-sweater" rel="alternate" type="text/html"/>
    <title>I need your designer glasses, your blue jeans and your black turtle-neck sweater</title>
<summary type="html">&lt;p&gt;&lt;a href=&quot;http://www.flickr.com/photos/thewebfellas/sets/72157622360722110/&quot; title=&quot;Take a look at the obligatory unboxing photos&quot; class=&quot;image-link&quot;&gt;&lt;img title=&quot;New MacBook Pro&quot; src=&quot;/assets/2009/9/13/new_mac.jpg&quot; alt=&quot;Picture of a new MacBook Pro&quot; /&gt;&lt;/a&gt;Ok so it’s not quite &lt;a href=&quot;http://www.youtube.com/watch?v=PBxzMOXGLsE&quot; title=&quot;See Arnie in action&quot;&gt;Schwarzenegger&lt;/a&gt; but last week I terminated a twenty year relationship with Microsoft and bought a Mac. Now I just need to get hold of the &lt;a href=&quot;http://www.stevesoutfit.com/&quot; title=&quot;Get your own Apple uniform&quot;&gt;Apple uniform&lt;/a&gt; and I’ll officially be part of the club!&lt;/p&gt;
&lt;p&gt;After more than a year of pontification on what exactly to buy as a replacement for my ageing Dell Inspiron laptop, I finally settled on a shiny new &lt;a href=&quot;http://www.apple.com/macbookpro/&quot; title=&quot;Take a look at the MacBook&quot;&gt;13” MacBook Pro&lt;/a&gt; and an even shinier 24” Cinema Display. So far I’m pretty chuffed with my choice.&lt;/p&gt;</summary><content type="html">
            &lt;p&gt;&lt;a href=&quot;http://www.flickr.com/photos/thewebfellas/sets/72157622360722110/&quot; title=&quot;Take a look at the obligatory unboxing photos&quot; class=&quot;image-link&quot;&gt;&lt;img title=&quot;New MacBook Pro&quot; src=&quot;/assets/2009/9/13/new_mac.jpg&quot; alt=&quot;Picture of a new MacBook Pro&quot; /&gt;&lt;/a&gt;Ok so it’s not quite &lt;a href=&quot;http://www.youtube.com/watch?v=PBxzMOXGLsE&quot; title=&quot;See Arnie in action&quot;&gt;Schwarzenegger&lt;/a&gt; but last week I terminated a twenty year relationship with Microsoft and bought a Mac. Now I just need to get hold of the &lt;a href=&quot;http://www.stevesoutfit.com/&quot; title=&quot;Get your own Apple uniform&quot;&gt;Apple uniform&lt;/a&gt; and I’ll officially be part of the club!&lt;/p&gt;
&lt;p&gt;After more than a year of pontification on what exactly to buy as a replacement for my ageing Dell Inspiron laptop, I finally settled on a shiny new &lt;a href=&quot;http://www.apple.com/macbookpro/&quot; title=&quot;Take a look at the MacBook&quot;&gt;13” MacBook Pro&lt;/a&gt; and an even shinier 24” Cinema Display. So far I’m pretty chuffed with my choice.&lt;/p&gt;
&lt;p&gt;Here are a few observations after my first week as a Mac user:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;p&gt;Apple know how to do packaging: OK so it doesn’t make the product any more useable but it does add to the warm and fuzzy feeling you get when you unwrap the new toys that you’ve just spent a big wedge of your hard-earned on.&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;&lt;p&gt;The laptop itself looks and feels really expensive and its light weight makes it perfect for carrying to meetings or working on the move. My old Dell felt heavy before but now it feels like it weighs a tonne.&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;&lt;p&gt;OS X is a much nicer place to work than Vista (which I’ve never liked which is why I stayed with XP on my laptop) although I am surprised at how much more I have to do in the terminal. There are a lot of handy utilities that I got used to having in Windows that either don’t exist or aren’t as fully featured on the Mac.&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;&lt;p&gt;After spending so many hours of my life staring at a 15.4” screen with a fairly high 1680x1050 resolution, the 24” screen makes everything look huge. The picture quality really is something to behold: super deep blacks and vibrant colour and it’s bright enough to light up a small town!&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;&lt;p&gt;I have to wonder which joker decided to move the keys around on the keyboard: I keep ending up with @ signs when I want double quotes. And could it have been the same joker who thought the hash key was so rarely used that it didn’t need to even appear on the keyboard? Whoever it was they’ve obviously never done any programming work. It could easily have replaced either the § or ± symbols that do get to share a key between them and I can't recall needing either of them recently. Oh and to get a hash you need ALT+3…easy eh?&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;&lt;p&gt;I gave &lt;a href=&quot;http://code.google.com/p/macvim/&quot; title=&quot;Take a look at MacVim&quot;&gt;MacVim&lt;/a&gt; a quick tryout for my source code editing as I’ve read a &lt;a href=&quot;http://weblog.jamisbuck.org/2008/10/10/coming-home-to-vim&quot; title=&quot;Jamis Buck went home to Vim&quot;&gt;few&lt;/a&gt; &lt;a href=&quot;http://robots.thoughtbot.com/post/166073596/intro-rails-vim&quot; title=&quot;See toughtbot’s screencast&quot;&gt;blogs&lt;/a&gt; recently about how great Vim is. I’ve not spent nearly long enough with it to make a fair assessment, but it’s a bit too alien for me at the moment. I’m so used to the &lt;a href=&quot;http://www.e-texteditor.com/&quot; title=&quot;Find out about E text editor&quot;&gt;E text editor&lt;/a&gt; on Windows that I think I’ll have to stick with &lt;a href=&quot;http://macromates.com/&quot; title=&quot;Find out about TextMate&quot;&gt;TextMate&lt;/a&gt; on the Mac for now.&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;&lt;p&gt;Ruby and Rails are both much quicker: sometimes I was able to go and make a cup of tea in the time it took Webrick to boot on my Dell, although this is partly because the Dell was so full of junk that it frequently ran out of both disk space and memory. It is also nice to be able to install gems that require compilation without all the hassle that this usually involves on Windows. In theory I should now be able to get more work done and drink less tea.&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;&lt;p&gt;I was initially worried that having to compile a lot of the tools I use, like Ruby, MySQL and ImageMagick, from source would be a daunting task, but thanks to some &lt;a href=&quot;http://hivelogic.com/articles/compiling-ruby-rubygems-and-rails-on-snow-leopard/&quot; title=&quot;Take a look at the tutorials&quot;&gt;excellent tutorials&lt;/a&gt; by Dan Benjamin on &lt;a href=&quot;http://hivelogic.com/&quot; title=&quot;Take a look at Hivelogic&quot;&gt;HiveLogic&lt;/a&gt; I’ve been able to do it with no real problems at all.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So far I’ve been really pleased with the experience and the transition has been a lot less painful than I imagined. I’m now faced with the daunting task of sorting through several years of accumulated files and then moving everything off the Dell and onto the Mac, although if my wallet can take the strain I might have to splash out on one of those new fangled SSDs first…&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://thewebfellas.com/">
    <author>
      <name>Rob Anderton</name>
    </author>
    <id>tag:thewebfellas.com,2009-08-29:8374</id>
    <published>2009-08-29T09:20:00Z</published>
    <updated>2011-05-16T16:09:59Z</updated>
    <category term="Blog"/>
    <category term="attachments"/>
    <category term="aws"/>
    <category term="mp3"/>
    <category term="paperclip"/>
    <category term="rails"/>
    <category term="s3"/>
    <category term="streaming"/>
    <link href="http://thewebfellas.com/blog/2009/8/29/protecting-your-paperclip-downloads" rel="alternate" type="text/html"/>
    <title>Protecting your Paperclip downloads</title>
<summary type="html">&lt;p&gt;Way back last November when I first &lt;a href=&quot;/blog/2008/11/2/goodbye-attachment_fu-hello-paperclip&quot; title=&quot;Read my first blog on Paperclip&quot;&gt;blogged about Paperclip&lt;/a&gt; I included a brief mention of hiding files behind a controller rather than simply putting them in the public directory for all to see. Since then I’ve noticed that the question of how to actually do this has come up regularly over on &lt;a href=&quot;http://www.railsforum.com/&quot; title=&quot;Visit RailsForum&quot;&gt;Rails Forum&lt;/a&gt; and a couple of weeks ago I had to figure out how to update some of our code to protect assets that we had migrated from local file system to &lt;a href=&quot;http://aws.amazon.com/s3/&quot; title=&quot;Read about Amazon S3&quot;&gt;Amazon S3&lt;/a&gt; storage. So I figured it’s probably a worthwhile technique to share.&lt;/p&gt;</summary><content type="html">
            &lt;p&gt;Way back last November when I first &lt;a href=&quot;/blog/2008/11/2/goodbye-attachment_fu-hello-paperclip&quot; title=&quot;Read my first blog on Paperclip&quot;&gt;blogged about Paperclip&lt;/a&gt; I included a brief mention of hiding files behind a controller rather than simply putting them in the public directory for all to see. Since then I’ve noticed that the question of how to actually do this has come up regularly over on &lt;a href=&quot;http://www.railsforum.com/&quot; title=&quot;Visit RailsForum&quot;&gt;Rails Forum&lt;/a&gt; and a couple of weeks ago I had to figure out how to update some of our code to protect assets that we had migrated from local file system to &lt;a href=&quot;http://aws.amazon.com/s3/&quot; title=&quot;Read about Amazon S3&quot;&gt;Amazon S3&lt;/a&gt; storage. So I figured it’s probably a worthwhile technique to share.&lt;/p&gt;
&lt;h3&gt;The model&lt;/h3&gt;
&lt;p&gt;I’ll use a slightly updated version of the &lt;code&gt;Track&lt;/code&gt; model from the &lt;a href=&quot;/blog/2008/11/2/goodbye-attachment_fu-hello-paperclip#getting_clever_with_validations&quot; title=&quot;See the model&quot;&gt;Getting clever with validations&lt;/a&gt; section of my original Paperclip blog. Here’s the code:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;class Track &amp;lt; ActiveRecord::Base

  has_attached_file :mp3,
                    :url =&amp;gt; '/:class/:id/:style.:extension',
                    :path =&amp;gt; ':rails_root/assets/:class/:id_partition/:style.:extension'

  validates_attachment_presence :mp3
  validates_attachment_content_type :mp3, :content_type =&amp;gt; [ 'application/mp3', 'application/x-mp3', 'audio/mpeg', 'audio/mp3' ]
  validates_attachment_size :mp3, :less_than =&amp;gt; 20.megabytes

  def downloadable?(user)
    user != :guest
  end
end&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This does the following:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Defines a &lt;code&gt;Track&lt;/code&gt; model that has a Paperclip attachment called &lt;code&gt;mp3&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Configures the URL used to access the mp3 asset, for example &lt;samp&gt;/track/1/original.mp3&lt;/samp&gt;. Leaving the style attribute in the URL makes it possible for future versions of the code to do things like generating 10 second previews of the track (using a custom Paperclip processor) that could then be accessed using a URL like &lt;samp&gt;/track/1/preview.mp3&lt;/samp&gt;.&lt;/li&gt;
  &lt;li&gt;Configures the path where Paperclip will store uploaded files (for example &lt;samp&gt;RAILS_ROOT/assets/tracks/000/000/001/original.mp3&lt;/samp&gt; where &lt;code&gt;RAILS_ROOT&lt;/code&gt; is the path to the root directory of the Rails app) – the important thing here is that the files are stored outside of the &lt;samp&gt;/public&lt;/samp&gt; directory.&lt;/li&gt;
  &lt;li&gt;Defines validations to ensure an mp3 is uploaded, it has a valid content type and it isn’t too big.&lt;/li&gt;
  &lt;li&gt;Defines a &lt;code&gt;downloadable?&lt;/code&gt; method that can be used to implement user access rights to the track. For simplicity it just allows all logged in users to access the track, however you can replace this with whatever logic your app requires.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Routes and controllers&lt;/h3&gt;
&lt;p&gt;Now if you try to provide a link to download the mp3 in your view with something like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;link_to('Listen', track.mp3.url)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You’ll get a routing error like this when you click it:&lt;/p&gt;
&lt;pre&gt;&lt;samp&gt;Routing Error
No route matches &quot;/tracks/1/original.mp3&quot; with {:method=&amp;gt;:get}&lt;/samp&gt;&lt;/pre&gt;
&lt;p&gt;I typically map the Paperclip URL to a &lt;code&gt;download&lt;/code&gt; action in a controller using &lt;code&gt;map.connect&lt;/code&gt; in my &lt;kbd&gt;routes.rb&lt;/kbd&gt; file like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;ActionController::Routing::Routes.draw do |map|
  map.connect 'tracks/:id/:style.:format', :controller =&amp;gt; 'tracks', :action =&amp;gt; 'download', :conditions =&amp;gt; { :method =&amp;gt; :get }
end&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There’s no need to use a named route as it’s unlikely you’ll need to reference the route by name, and by mapping to a custom download action you can also provide the standard resourceful CRUD actions in the controller if needed. Puritanical REST fans might insist that downloads are mapped to a separate resource and that you &lt;em&gt;create&lt;/em&gt; a new download resource using a POST request: this is probably worth considering if you're intending doing more than simply streaming a file to the client (like logging statistics, updating billing information) otherwise why complicate things?&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;TracksController&lt;/code&gt; then needs a &lt;code&gt;download&lt;/code&gt; action:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;class TracksController &amp;lt; ApplicationController

  SEND_FILE_METHOD = :default

  def download
    head(:not_found) and return if (track = Track.find_by_id(params[:id])).nil?
    head(:forbidden) and return unless track.downloadable?(current_user)

    path = track.mp3.path(params[:style])
    head(:bad_request) and return unless File.exist?(path) &amp;amp;&amp;amp; params[:format].to_s == File.extname(path).gsub(/^\.+/, '')

    send_file_options = { :type =&amp;gt; File.mime_type?(path) }

    case SEND_FILE_METHOD
      when :apache then send_file_options[:x_sendfile] = true
      when :nginx then head(:x_accel_redirect =&amp;gt; path.gsub(Rails.root, ''), :content_type =&amp;gt; send_file_options[:type]) and return
    end

    send_file(path, send_file_options)
  end

end&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There’s quite a lot going on in the controller, here’s a breakdown of the &lt;code&gt;download&lt;/code&gt; action:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;p&gt;An attempt is made to find the &lt;code&gt;Track&lt;/code&gt; model using the &lt;code&gt;:id&lt;/code&gt; parameter, returning a &lt;samp&gt;404 Not Found&lt;/samp&gt; response if the track doesn’t exist.&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The current user is passed to the &lt;code&gt;downloadable?&lt;/code&gt; method to determine if the user is allowed to download the track.&lt;/p&gt;
    &lt;p&gt;Assume here that &lt;code&gt;current_user&lt;/code&gt; is a method provided by the application’s authentication code and returns either a &lt;code&gt;User&lt;/code&gt; object or the &lt;samp&gt;:guest&lt;/samp&gt; symbol if no user is logged in. The actual code you’ll use here and in the &lt;code&gt;downloadable?&lt;/code&gt; method will probably need to be adapted to suit your own authentication code.&lt;/p&gt;
    &lt;p&gt;The controller returns a &lt;samp&gt;403 Forbidden&lt;/samp&gt; response if the user is not allowed to download the track.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;p&gt;The path to the mp3 file on the server is then generated for the passed &lt;code&gt;:style&lt;/code&gt; parameter.&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;&lt;p&gt;The controller then ensures that the file exists (it wouldn’t if, for example, an invalid &lt;code&gt;:style&lt;/code&gt; parameter was used) and that the requested file extension matches the actual extension of the file. It returns a &lt;samp&gt;400 Bad Request&lt;/samp&gt; if necessary.&lt;/p&gt;&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;A hash of options to be used with the &lt;a href=&quot;http://apidock.com/rails/ActionController/Streaming/send_file&quot; title=&quot;Read the send_file documentation&quot;&gt;send_file&lt;/a&gt; method is initialised with the MIME type of the mp3 file.&lt;/p&gt;
    &lt;p&gt;I’m using the &lt;a href=&quot;http://github.com/mattetti/mimetype-fu&quot; title=&quot;Read about the plugin&quot;&gt;mimetype-fu plugin&lt;/a&gt; here (installed using &lt;kbd&gt;ruby script/plugin install git://github.com/mattetti/mimetype-fu.git&lt;/kbd&gt;) rather than the &lt;code&gt;content_type&lt;/code&gt; attribute of the mp3 attachment to allow for future functionality where different styles may use different file types.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The controller supports standard streaming, using the Lighttpd/Apache &lt;a href=&quot;http://blog.lighttpd.net/articles/2006/07/02/x-sendfile&quot; title=&quot;Read about X-Sendfile&quot;&gt;X-Sendfile&lt;/a&gt; or the Nginx &lt;a href=&quot;http://wiki.nginx.org/NginxXSendfile&quot; title=&quot;Read about X-Accel-Redirect&quot;&gt;X-Accel-Redirect&lt;/a&gt; methods for downloading the file.&lt;/p&gt;
    &lt;p&gt;For simplicity the &lt;code&gt;SEND_FILE_METHOD&lt;/code&gt; constant is used here to configure the method to use, but in a real application it should be stored in a configuration setting of some sort (I recommend 
Luke Redpath’s &lt;a href=&quot;http://github.com/lukeredpath/simpleconfig/tree/master&quot; title=&quot;Take a look at SimpleConfig&quot;&gt;SimpleConfig&lt;/a&gt; gemplugin for this kind of thing).&lt;/p&gt;
    &lt;p&gt;Depending on the configured streaming method the controller then either uses &lt;code&gt;send_file&lt;/code&gt; (for standard and X-Sendfile streaming) or responds with a header (for X-Accel-Redirect streaming).&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We usually use Nginx for our Rails apps so we use the X-Accel-Redirect method for streaming files with something like this in our Nginx configuration:&lt;/p&gt;
&lt;pre&gt;&lt;samp&gt;
location /assets/ {
  root /path/to/rails_root;
  internal;
}&lt;/samp&gt;&lt;/pre&gt;
&lt;p&gt;where &lt;samp&gt;/path/to/rails_root&lt;/samp&gt; contains the full path to our Rails application root.&lt;/p&gt;
&lt;h3&gt;The view&lt;/h3&gt;
&lt;p&gt;With the &lt;code&gt;Track&lt;/code&gt; model providing access control logic via the &lt;code&gt;downloadable?&lt;/code&gt; method and a &lt;code&gt;download&lt;/code&gt; action in the &lt;code&gt;TracksController&lt;/code&gt; handling the streaming of the mp3 file, it is now possible to provide download links in views using the &lt;code&gt;link_to&lt;/code&gt; helper with the &lt;code&gt;url&lt;/code&gt; method provided by Paperclip. For example, an index view could look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&amp;lt;ul&amp;gt;
&amp;lt;% @tracks.each do |track| %&amp;gt;
  &amp;lt;li&amp;gt;&amp;lt;%= link_to('Listen', track.mp3.url) %&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;% end %&amp;gt;
&amp;lt;/ul&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Scaling up to Amazon S3&lt;/h3&gt;
&lt;p&gt;The code above works well if your mp3s are being stored on the local file system, but if your site starts to grow and you need to scale both in terms of storage space and download capacity you’ll probably want to move to S3.&lt;/p&gt;
&lt;p&gt;Paperclip provides an S3 storage module that until recently made use of the &lt;a href=&quot;http://rightscale.rubyforge.org/right_aws_gem_doc/&quot; title=&quot;Find out about RightAWS&quot;&gt;RightAWS gem&lt;/a&gt; however version 2.3.1 and newer use &lt;a href=&quot;http://amazon.rubyforge.org/&quot; title=&quot;Read about AWS::S3&quot;&gt;AWS::S3&lt;/a&gt; instead. Be warned that this change does &lt;a href=&quot;http://github.com/thoughtbot/paperclip/commit/ac86bb9dfbe3ed8dabe2f76cd4a3199966a1899f#comments&quot; title=&quot;Read about the move to AWS::S3&quot;&gt;cause some problems&lt;/a&gt; if you want to use S3 storage buckets located in Europe, so you might want to stick with 2.3.0 if this will be a problem for you. I'll cover both versions in the code below.&lt;/p&gt;
&lt;h3&gt;Changing the storage module&lt;/h3&gt;
&lt;p&gt;The first thing to change is the &lt;code&gt;has_attached_file&lt;/code&gt; definition in the &lt;code&gt;Track&lt;/code&gt; model:&lt;/p&gt; 
&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;has_attached_file :mp3,
                  :url =&amp;gt; ':s3_domain_url',
                  :path =&amp;gt; 'assets/:class/:id/:style.:extension',
                  :storage =&amp;gt; :s3,
                  :s3_credentials =&amp;gt; File.join(Rails.root, 'config', 's3.yml'),
                  :s3_permissions =&amp;gt; 'authenticated-read',
                  :s3_protocol =&amp;gt; 'http'&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The most obvious changes here are the new &lt;code&gt;:storage&lt;/code&gt;, &lt;code&gt;:s3_credentials&lt;/code&gt;, &lt;code&gt;:s3_permissions&lt;/code&gt; and &lt;code&gt;:s3_protocol&lt;/code&gt; options which specify the storage module, credentials file location, permissions for uploaded files and communications protocol respectively.&lt;/p&gt;
&lt;p&gt;The credentials YAML file is used to specify the access key, secret access key and bucket name for your S3 accounts and should look something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;yaml&quot;&gt;common: &amp;amp;common
  access_key_id: your_access_key_id_goes_here
  secret_access_key: your_secret_access_key_goes_here

development:
  &amp;lt;&amp;lt;: *common
  bucket: app-name-development

test:
  &amp;lt;&amp;lt;: *common
  bucket: app-name-test

production:
  &amp;lt;&amp;lt;: *common
  bucket: app-name-production&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This configures separate &lt;a href=&quot;http://docs.amazonwebservices.com/AmazonS3/latest/index.html?UsingBucket.html&quot; title=&quot;Find out what a bucket is&quot;&gt;S3 buckets&lt;/a&gt; for each environment which is a good idea to prevent accidently mixing up your test files with your production files!&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;:s3_permissions&lt;/code&gt; option allows you to specify one of four &lt;a href=&quot;http://docs.amazonwebservices.com/AmazonS3/latest/RESTAccessPolicy.html#RESTCannedAccessPolicies&quot; title=&quot;Read about canned access policies&quot;&gt;canned access policies&lt;/a&gt;, in this case &lt;code&gt;authenticated-read&lt;/code&gt; is the one to use so that read access is only available to the object owner or an authenticated user.&lt;/p&gt;
&lt;p&gt;Slightly less obvious are the changes to the &lt;code&gt;:url&lt;/code&gt; and &lt;code&gt;:path&lt;/code&gt; options: &lt;code&gt;:url&lt;/code&gt; is set to &lt;code&gt;':s3_domain_url'&lt;/code&gt; a Paperclip interpolation that uses &lt;a href=&quot;http://docs.amazonwebservices.com/AmazonS3/latest/VirtualHosting.html&quot; title=&quot;Read about virtual hosted style URLs&quot;&gt;virtual hosted style&lt;/a&gt; bucket access (especially important if using European buckets as this is the preferred method of access); and the &lt;code&gt;:path&lt;/code&gt; is used by Paperclip to generate a key name for the object being stored on S3.&lt;/p&gt;
&lt;p&gt;With these changes in place new files uploaded to the &lt;code&gt;Track&lt;/code&gt; model will be stored in the appropriate S3 bucket using a key generated from the &lt;code&gt;:path&lt;/code&gt; option, for example &lt;samp&gt;http://app-name-development.s3.amazonaws.com/assets/tracks/1/original.mp3&lt;/samp&gt;.&lt;/p&gt;
&lt;h3&gt;No more streaming, time for a redirection&lt;/h3&gt;
&lt;p&gt;One of the good things about moving to S3 is that your server no longer has to concern itself with the streaming of data to the clients (using techniques like X-SendFile and X-Accel-Redirect help unburden your Rails processes but the server still has to do all the work). Instead of sending file data to the client the &lt;code&gt;download&lt;/code&gt; action in the controller now needs to handle permissions checking but then redirect the client to the file on S3 for download.&lt;/p&gt;
&lt;p&gt;This does however present a problem as the uploaded files are using the &lt;code&gt;authenticated-read&lt;/code&gt; canned access policy which prevents public access to them. Fortunately S3 provides a way to generate an &lt;em&gt;authenticated&lt;/em&gt; URL for private content that only works for a specified period of time: our controller can then redirect the client to this temporary URL to initiate the download but if a someone was to obtain the details of the URL and try to access the file at a later date then they’d be out of luck as the URL would have expired.&lt;/p&gt;
&lt;p&gt;The updated code looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;def download
  head(:not_found) and return if (track = Track.find_by_id(params[:id])).nil?
  head(:forbidden) and return unless track.downloadable?(current_user)

  path = track.mp3.path(params[:style])
  head(:bad_request) and return unless params[:format].to_s == File.extname(path).gsub(/^\.+/, '')

  redirect_to(AWS::S3::S3Object.url_for(path, track.mp3.bucket_name, :expires_in =&amp;gt; 10.seconds))
end&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are two main differences in the controller:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;No check is made to ensure that the object exists as this would add the extra overhead of an additional request to S3 – let S3 worry about returning an appropriate response for non-existent objects.&lt;/li&gt;
  &lt;li&gt;Instead of streaming the file it redirects to a temporary URL generated using the &lt;a href=&quot;http://amazon.rubyforge.org/doc/classes/AWS/S3/S3Object.html#M000045&quot; title=&quot;Read the method documentation&quot;&gt;url_for&lt;/a&gt; method. The temporary URL is set to expire after 10 seconds which should be long enough for the redirect to initiate the download: it doesn't matter if the download takes longer than 10 seconds as long as it has started it will continue until it’s done.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you’re using Paperclip 2.3.0 or older with RightAWS the controller redirect should look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;redirect_to(track.mp3.s3.interface.get_link(track.mp3.s3_bucket.to_s, path, 10.seconds))&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;The view revisited&lt;/h3&gt;
&lt;p&gt;When using local file system storage the &lt;code&gt;url&lt;/code&gt; method of the Paperclip attachment could be used to link to the &lt;code&gt;download&lt;/code&gt; action of the &lt;code&gt;TracksController&lt;/code&gt; but now that the files are hosted on S3 the &lt;code&gt;url&lt;/code&gt; method now returns the URL of the S3 bucket, which is not what we want.&lt;/p&gt;
&lt;p&gt;There are a couple of choices here, the route could be changed to a named route and then the view could use something like &lt;code&gt;link_to('Listen', download_track_path(track.id, 'original', 'mp3')&lt;/code&gt; but I think a nicer approach is to add a new method to the &lt;code&gt;Track&lt;/code&gt; model that uses Paperclip interpolations to generate the download URL. And while I’m at it, I can also move the authenticated S3 URL generation out of the controller and into the model too. Here's the complete &lt;code&gt;Track&lt;/code&gt; model:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;class Track &amp;lt; ActiveRecord::Base

  has_attached_file :mp3,
                    :url =&amp;gt; ':s3_domain_url',
                    :path =&amp;gt; 'assets/:class/:id/:style.:extension',
                    :storage =&amp;gt; :s3,
                    :s3_credentials =&amp;gt; File.join(Rails.root, 'config', 's3.yml'),
                    :s3_permissions =&amp;gt; 'authenticated-read',
                    :s3_protocol =&amp;gt; 'http'

  validates_attachment_presence :mp3
  validates_attachment_content_type :mp3, :content_type =&amp;gt; [ 'application/mp3', 'application/x-mp3', 'audio/mpeg', 'audio/mp3' ]
  validates_attachment_size :mp3, :less_than =&amp;gt; 20.megabytes

  def downloadable?(user)
    user != :guest
  end

  def download_url(style = nil, include_updated_timestamp = true)
    url = Paperclip::Interpolations.interpolate('/:class/:id/:style.:extension', mp3, style || mp3.default_style)
    include_updated_timestamp &amp;amp;&amp;amp; mp3.updated_at ? [url, mp3.updated_at].compact.join(url.include?(&quot;?&quot;) ? &quot;&amp;amp;&quot; : &quot;?&quot;) : url
  end

  def authenticated_url(style = nil, expires_in = 10.seconds)
    AWS::S3::S3Object.url_for(mp3.path(style || mp3.default_style), mp3.bucket_name, :expires_in =&amp;gt; expires_in, :use_ssl =&amp;gt; mp3.s3_protocol == 'https')
  end

end&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Again for RightAWS (Paperclip &amp;lt;= 2.3.0) the authenticated URL generation is slightly different:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;def authenticated_url(style = nil, expires_in = 10.seconds)
  mp3.s3.interface.get_link(mp3.s3_bucket.to_s, mp3.path(style || mp3.default_style), expires_in)
end&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This then tidies up the &lt;code&gt;download&lt;/code&gt; action, the redirect changes to:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;redirect_to(track.authenticated_url(params[:style]))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And the view can now link to downloads:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;link_to('Listen', track.download_url)&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;The end&lt;/h3&gt;
&lt;p&gt;Hopefully I’ve given you enough here for you to start implementing protected downloads in your own Rails apps. Don’t forget that if you’re using European S3 buckets you’ll need to &lt;a href=&quot;http://github.com/thoughtbot/paperclip/commit/ac86bb9dfbe3ed8dabe2f76cd4a3199966a1899f#comments&quot; title=&quot;Read about the move to AWS::S3&quot;&gt;do some patching&lt;/a&gt; otherwise you’ll get strange errors like:&lt;/p&gt;
&lt;pre&gt;&lt;samp&gt;AWS::S3::PermanentRedirect: The bucket you are attempting to access must be addressed using the specified endpoint. Please send all future requests to this endpoint.&lt;/samp&gt;&lt;/pre&gt;
&lt;p&gt;when you try and upload – fixing that properly is a job for another day!&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://thewebfellas.com/">
    <author>
      <name>Rob Anderton</name>
    </author>
    <id>tag:thewebfellas.com,2009-08-09:8080</id>
    <published>2009-08-09T14:13:00Z</published>
    <updated>2009-08-09T14:11:55Z</updated>
    <category term="Blog"/>
    <category term="bugmash"/>
    <category term="patch"/>
    <category term="rails"/>
    <link href="http://thewebfellas.com/blog/2009/8/9/bugmash" rel="alternate" type="text/html"/>
    <title>Bugmash!</title>
<content type="html">
            &lt;p&gt;&lt;img title=&quot;Bugmash!&quot; src=&quot;/assets/2009/8/9/bugmash.png&quot; alt=&quot;Picture of a masher mashing a bug&quot; /&gt;Well it’s day two of the first ever &lt;a href=&quot;http://weblog.rubyonrails.org/2009/7/28/rails-bugmash&quot; title=&quot;Read about the event&quot;&gt;Rails BugMash&lt;/a&gt; and so far I’ve managed to score a sneaky &lt;a href=&quot;http://bugmash.heroku.com/&quot; title=&quot;What are the scores George Doors?&quot;&gt;1,000 points&lt;/a&gt; just by updating my one-line &lt;a href=&quot;http://rails.lighthouseapp.com/projects/8994/tickets/2597&quot; title=&quot;See the ticket&quot;&gt;binary fixtures test patch&lt;/a&gt;. Meanwhile &lt;a href=&quot;http://bugmash.heroku.com/participants/16&quot; title=&quot;See Matt’s contributions&quot;&gt;Matt Duncan&lt;/a&gt; and &lt;a href=&quot;http://bugmash.heroku.com/participants/8&quot; title=&quot;See Rizwan’s contributions&quot;&gt;Rizwan Reza&lt;/a&gt; are on fire with 4,350 and 4,000 points at the time of writing.&lt;/p&gt;
&lt;p&gt;Unfortunately the event has coincided with what may be the only nice weekend of the Great British summer, so I’ve been torn between the chance to mash bugs or to enjoy the sunshine. I’m currently trying to combine the two sat out in the garden squinting to see my laptop screen in the glare of the sun!&lt;/p&gt;
&lt;p&gt;My next attempt to score some points is an &lt;a href=&quot;http://rails.lighthouseapp.com/projects/8994/tickets/2600-quote-table-name-in-joinassociation&quot; title=&quot;See the ticket&quot;&gt;updated patch&lt;/a&gt;, now improved and including a test case, for a lack of quoting of aliased table names in SQL joins which has been (too eagerly) marked as resolved even though it’s still broken. If you get the opportunity please do take a look and comment on the ticket as it’d be nice to get it fixed.&lt;/p&gt;
&lt;p&gt;After that, I’m hoping to try and sneak my &lt;a href=&quot;https://rails.lighthouseapp.com/projects/8994/tickets/2186-anonymous-extension-modules-for-all-associations&quot; title=&quot;See the ticket&quot;&gt;patch for anonymous extension modules&lt;/a&gt; for belongs_to and has_one associations into the bugmash as it has been sat on Lighthouse since March and already has three +1s. Even if it isn’t eligible for the bugmash, I still think it’s a worthy patch so again please take a look and comment on the ticket if you get chance.&lt;/p&gt;
&lt;p&gt;And of course there are still &lt;a href=&quot;https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/bins/41908&quot; title=&quot;See tickets for the bugmash&quot;&gt;plenty more tickets tagged with bugmash&lt;/a&gt; to be looked at so even if you’ve never contributed to Rails before, now is a pretty good time to start!&lt;/p&gt;
          </content>  </entry>
  <entry xml:base="http://thewebfellas.com/">
    <author>
      <name>Chris Anderton</name>
    </author>
    <id>tag:thewebfellas.com,2009-07-15:7770</id>
    <published>2009-07-15T22:54:00Z</published>
    <updated>2010-01-18T23:45:57Z</updated>
    <category term="Blog"/>
    <category term="actioncontrollerdispatcher (nameerror)"/>
    <category term="rails"/>
    <category term="thin"/>
    <link href="http://thewebfellas.com/blog/2009/7/15/thin-rails-1-2-3-1-2-6-and-actioncontroller-dispatcher-nameerror" rel="alternate" type="text/html"/>
    <title>Thin, Rails 1.2.3/1.2.6 and ActionController::Dispatcher (NameError)</title>
<summary type="html">&lt;p&gt;&lt;strong&gt;Please note: This patch &lt;a href=&quot;http://github.com/macournoyer/thin/commit/dacb5df8bfe9aad74bc79db640b3ae4c2683db27&quot; title=&quot;applied to master&quot;&gt;has now been applied&lt;/a&gt; to the &lt;a href=&quot;http://github.com/macournoyer/thin&quot; title=&quot;Thin on Github&quot;&gt;Thin master repository&lt;/a&gt; so will be fixed in all future releases.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Whilst trying to get an old Rails app up and running with Thin (Gem version 1.2.2) then I encountered a spot of bother:&lt;/p&gt;
&lt;p&gt;&lt;samp&gt;load_missing_constant: uninitialized constant ActionController::Dispatcher (NameError)&lt;/samp&gt;&lt;/p&gt;</summary><content type="html">
            &lt;p&gt;&lt;strong&gt;Please note: This patch &lt;a href=&quot;http://github.com/macournoyer/thin/commit/dacb5df8bfe9aad74bc79db640b3ae4c2683db27&quot; title=&quot;applied to master&quot;&gt;has now been applied&lt;/a&gt; to the &lt;a href=&quot;http://github.com/macournoyer/thin&quot; title=&quot;Thin on Github&quot;&gt;Thin master repository&lt;/a&gt; so will be fixed in all future releases.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Whilst trying to get an old Rails app up and running with Thin (Gem version 1.2.2) then I encountered a spot of bother:&lt;/p&gt;
&lt;p&gt;&lt;samp&gt;load_missing_constant: uninitialized constant ActionController::Dispatcher (NameError)&lt;/samp&gt;&lt;/p&gt;
&lt;p&gt;I can't remember the exact point of change, but remember that Dispatcher changed into ActionController::Dispatcher somewhere along the line - obviously after Rails 1.2.3 and 1.2.6!&lt;/p&gt;
&lt;p&gt;There was already a check in place within Thin to see if the call method could be used on the Dispatcher - the trouble being that because ActionController::Dispatcher isn't defined then it fails!&lt;/p&gt;
&lt;p&gt;I've submitted a ticket - a simple check to make sure ActionController::Dispatcher is defined - in the meantime then i've also put a &lt;a href=&quot;http://gist.github.com/145594&quot; title=&quot;Thin patch&quot;&gt;patch up as a gist&lt;/a&gt; to get things running.&lt;/p&gt;
          </content>  </entry>
</feed>

