Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

The main index can be a normal mustache template

  • Loading branch information...
commit b19e66b1944239fec8e4f0a4fe6046e69a68bb5f 1 parent 5fddc13
@tomafro authored
View
16 lib/lanyon/site.rb
@@ -8,7 +8,6 @@ def initialize(source, destination)
Mustache.template_path = source + "/_templates"
Mustache.raise_on_context_miss = true
require 'post'
- require 'main_index'
require 'tag_index'
require 'month_index'
require 'stylesheet'
@@ -30,7 +29,7 @@ def posts
end
def recent_posts
- posts.compact.reverse.take(10)
+ posts.take(10)
end
def stylesheets
@@ -38,7 +37,7 @@ def stylesheets
end
def pages_with_path(&block)
- pages.select(&block).values
+ Collection.new(pages.select(&block).values.compact.reverse)
end
def updated_date_xml
@@ -77,7 +76,6 @@ def template_for(path, include_layouts = false)
when '.page' then ::Page
when '.post' then ::Post
when '.scss' then ::Stylesheet
- when '.main_index' then ::MainIndex
when '.tag_index' then ::TagIndex
when '.month_index' then ::MonthIndex
when '.txt' then ::Static
@@ -111,6 +109,16 @@ def in_source_folder(&block)
end
end
+ class Collection < Array
+ define_method "take-6" do
+ take(6)
+ end
+
+ define_method "leave-6" do
+ from(6)
+ end
+ end
+
class Destination
def initialize(directory)
@directory = directory
View
26 lib/main_index.rb
@@ -1,26 +0,0 @@
-require 'lanyon'
-
-class MainIndex < Lanyon::Template
- def url
- "/"
- end
-
- def title
- "Tom Ward's blog"
- end
-
- def description
- "Personal blog of Tom Ward, in which he writes about ruby, rails and web development, as well as other random ephemera"
- end
-
- def layout
- "_layouts/default.mustache"
- end
-
- def write_to(destination)
- posts = site.posts.reverse.take(6)
- push_context posts: posts do
- destination.write('index.html', render_page(context))
- end
- end
-end
View
2  lib/month_index.rb
@@ -16,7 +16,7 @@ def write_to(destination)
m = (month < 10) ? "0#{month}" : month.to_s
raise "#{month}" if m.blank?
- push_context posts: posts.reverse, title: "Posts from #{m}/#{year}", url: "/#{year}/#{m}" do
+ push_context posts: posts, title: "Posts from #{m}/#{year}", url: "/#{year}/#{m}" do
destination.write "/#{year}/#{m}.html", render_page(context)
end
end
View
2  lib/tag_index.rb
@@ -8,7 +8,7 @@ def layout
def write_to(destination)
tags = site.posts.compact.collect(&:tags).flatten.uniq
tags.each do |tag|
- posts = site.posts.reverse.select {|p| p && p.tags.include?(tag) }
+ posts = site.posts.select {|p| p && p.tags.include?(tag) }
#context = self.context.dup
push_context(
posts: posts,
View
2  public/atom.xml
@@ -2,7 +2,7 @@
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Tom Ward</title>
<link href="http://tomafro.net/atom.xml" rel="self"/>
- <updated>2012-06-24T10:13:20+01:00</updated>
+ <updated>2012-07-02T11:29:56+01:00</updated>
<id>http://tomafro.net/</id>
<author>
<name>Tom Ward</name>
View
251 public/index.html
@@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, minimum-scale=1.0" />
<link rel="stylesheet" href="/css/style-42a6c8dcda3ddbe3660a0126ea4a5670.css" type="text/css" />
<link rel="alternate" type="application/atom+xml" href="http://tomafro.net/atom.xml" />
- <link rel="canonical" href="http://tomafro.net/"/>
+ <link rel="canonical" href="http://tomafro.net"/>
<script type="text/javascript" src="http://use.typekit.com/brv6igt.js"></script>
<script type="text/javascript">try{Typekit.load();}catch(e){}</script>
<title>Tom Ward's blog &middot; tomafro.net</title>
@@ -36,8 +36,7 @@
<li><a href='https://twitter.com/tomafro'>twitter</a></li>
</ul>
</nav>
-
-<article>
+ <article>
<header>
<h1><a href="/2012/06/tip-bundler-with-binstubs">Tip: Bundler with --binstubs</a></h1>
</header>
@@ -202,6 +201,252 @@
</ul>
</footer>
</article>
+<article>
+ <header>
+ <h1><a href="/2011/08/presenting-the-hashblue-api">Presenting the #blue api</a></h1>
+ </header>
+ <div class="content">
+ <p>In building <a href="https://hashblue.com">#blue</a> (sign up now!), one of the problems we faced was how to build <code>json</code> data in response to requests to <a href="https://api.hashblue.com">our API</a>. The typical rails solution would be to override <code>#as_json</code> in a model class, then write a controller like this:</p>
+
+<div class="highlight"><pre><span class="k">class</span> <span class="nc">ContactsController</span> <span class="o">&lt;</span> <span class="no">ApiController</span>
+ <span class="n">responds_to</span> <span class="ss">:json</span>
+
+ <span class="k">def</span> <span class="nf">show</span>
+ <span class="n">respond_with</span> <span class="no">Contact</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:id</span><span class="o">]</span><span class="p">)</span>
+ <span class="k">end</span>
+<span class="k">end</span>
+</pre>
+</div>
+
+
+<p>I always prefer to keep my controllers as skinny as possible, so this looks like a great solution. The <code>respond_with</code> call takes care of converting the message to json and responding with the right <code>Content-Type</code>, all in a simple call. However it has a number of problems and disadvantages.</p>
+
+<p>The biggest issue for our API is that rather than expose the <code>id</code> of each model, we've tried to encourage the use of the <code>uri</code> instead. So the <code>json</code> returned for a single contact (for example) looks like this:</p>
+
+<div class="highlight"><pre><span class="p">{</span>
+ <span class="s2">&quot;contact&quot;</span><span class="o">:</span> <span class="p">{</span>
+ <span class="s2">&quot;uri&quot;</span><span class="o">:</span> <span class="s2">&quot;https://api.example.com/contacts/ccpwjc&quot;</span><span class="p">,</span>
+ <span class="s2">&quot;name&quot;</span><span class="o">:</span> <span class="s2">&quot;George&quot;</span><span class="p">,</span>
+ <span class="s2">&quot;email&quot;</span><span class="o">:</span> <span class="s2">&quot;george@handmade.org&quot;</span><span class="p">,</span>
+ <span class="s2">&quot;msisdn&quot;</span><span class="o">:</span> <span class="s2">&quot;447897897899&quot;</span><span class="p">,</span>
+ <span class="s2">&quot;phone_number&quot;</span><span class="o">:</span> <span class="s2">&quot;07897897899&quot;</span><span class="p">,</span>
+ <span class="s2">&quot;messages&quot;</span><span class="o">:</span> <span class="s2">&quot;https://api.example.com/contacts/ccpwjc/messages&quot;</span>
+ <span class="p">}</span>
+<span class="p">}</span>
+</pre>
+</div>
+
+
+<p>It doesn't just have a <code>uri</code> for the actual contact, but also for the messages belonging to that contact (and yes, I regret not calling that attribute <code>messages_uri</code>). Models can't generate uris, and shouldn't really be aware of them, so overriding <code>#as_json</code> doesn't work. In any case, the json structure is really presentation logic, not business logic. It doesn't belong in the model.</p>
+
+<h3>Presenting a single model</h3>
+
+<p>The solution we've used is to build a presenter for each model, solely responsible for building the json. Here's an example for a contact:</p>
+
+<div class="highlight"><pre><span class="k">class</span> <span class="nc">ContactPresenter</span>
+ <span class="kp">include</span> <span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">routes</span><span class="o">.</span><span class="n">url_helpers</span>
+
+ <span class="kp">attr_accessor</span> <span class="ss">:controller</span><span class="p">,</span> <span class="ss">:subject</span>
+ <span class="n">delegate</span> <span class="ss">:params</span><span class="p">,</span> <span class="ss">:url_options</span><span class="p">,</span> <span class="ss">:to</span> <span class="o">=&gt;</span> <span class="ss">:controller</span>
+ <span class="n">delegate</span> <span class="ss">:errors</span><span class="p">,</span> <span class="ss">:to</span> <span class="o">=&gt;</span> <span class="ss">:subject</span>
+
+ <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">controller</span><span class="p">,</span> <span class="n">subject</span><span class="p">)</span>
+ <span class="vi">@controller</span> <span class="o">=</span> <span class="n">controller</span>
+ <span class="vi">@subject</span> <span class="o">=</span> <span class="n">subject</span>
+ <span class="k">end</span>
+
+ <span class="k">def</span> <span class="nf">as_json</span><span class="p">(</span><span class="n">options</span> <span class="o">=</span> <span class="p">{})</span>
+ <span class="p">{</span><span class="ss">:contact</span> <span class="o">=&gt;</span> <span class="p">{</span>
+ <span class="ss">:uri</span> <span class="o">=&gt;</span> <span class="n">uri</span><span class="p">,</span>
+ <span class="ss">:email</span> <span class="o">=&gt;</span> <span class="n">subject</span><span class="o">.</span><span class="n">email</span><span class="p">,</span>
+ <span class="ss">:name</span> <span class="o">=&gt;</span> <span class="n">subject</span><span class="o">.</span><span class="n">name</span><span class="p">,</span>
+ <span class="ss">:msisdn</span> <span class="o">=&gt;</span> <span class="n">subject</span><span class="o">.</span><span class="n">msisdn</span><span class="p">,</span>
+ <span class="ss">:phone_number</span> <span class="o">=&gt;</span> <span class="n">subject</span><span class="o">.</span><span class="n">phone_number</span><span class="p">,</span>
+ <span class="ss">:messages</span> <span class="o">=&gt;</span> <span class="n">api_contact_messages_url</span><span class="p">(</span><span class="ss">:contact_id</span> <span class="o">=&gt;</span> <span class="n">subject</span><span class="o">.</span><span class="n">id</span><span class="p">)</span>
+ <span class="p">}</span>
+ <span class="k">end</span>
+
+ <span class="k">def</span> <span class="nf">uri</span>
+ <span class="n">api_contact_url</span><span class="p">(</span><span class="ss">:id</span> <span class="o">=&gt;</span> <span class="n">subject</span><span class="o">.</span><span class="n">id</span><span class="p">)</span>
+ <span class="k">end</span>
+<span class="k">end</span>
+</pre>
+</div>
+
+
+<p>It's now simple to rewrite our controller to use the new presenter:</p>
+
+<div class="highlight"><pre><span class="k">class</span> <span class="nc">ContactsController</span> <span class="o">&lt;</span> <span class="no">ApiController</span>
+ <span class="n">responds_to</span> <span class="ss">:json</span>
+
+ <span class="k">def</span> <span class="nf">show</span>
+ <span class="n">respond_with</span> <span class="no">ContactPresenter</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="nb">self</span><span class="p">,</span> <span class="no">Contact</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="n">params</span><span class="o">[</span><span class="ss">:id</span><span class="o">]</span><span class="p">))</span>
+ <span class="k">end</span>
+<span class="k">end</span>
+</pre>
+</div>
+
+
+<h3>Presenting pages of models</h3>
+
+<p>The presenter above works well for a single model, but many of our API calls return a page of results. The <a href="https://api.hashblue.com/doc/GET%3Acontacts">/contacts</a> for example returns all the contacts belonging to a user (of which there may be hundreds). Luckily it's simple to adapt this pattern to present pages like this. First, we change our original <code>#as_json</code> method slightly:</p>
+
+<div class="highlight"><pre><span class="k">def</span> <span class="nf">as_json</span><span class="p">(</span><span class="n">options</span> <span class="o">=</span> <span class="p">{})</span>
+ <span class="k">if</span> <span class="n">options</span><span class="o">[</span><span class="ss">:partial</span><span class="o">]</span>
+ <span class="p">{</span>
+ <span class="ss">:uri</span> <span class="o">=&gt;</span> <span class="n">uri</span><span class="p">,</span>
+ <span class="ss">:email</span> <span class="o">=&gt;</span> <span class="n">subject</span><span class="o">.</span><span class="n">email</span><span class="p">,</span>
+ <span class="ss">:name</span> <span class="o">=&gt;</span> <span class="n">subject</span><span class="o">.</span><span class="n">name</span><span class="p">,</span>
+ <span class="ss">:msisdn</span> <span class="o">=&gt;</span> <span class="n">subject</span><span class="o">.</span><span class="n">msisdn</span><span class="p">,</span>
+ <span class="ss">:phone_number</span> <span class="o">=&gt;</span> <span class="n">subject</span><span class="o">.</span><span class="n">phone_number</span><span class="p">,</span>
+ <span class="ss">:messages</span> <span class="o">=&gt;</span> <span class="n">api_contact_messages_url</span><span class="p">(</span><span class="ss">:contact_id</span> <span class="o">=&gt;</span> <span class="n">subject</span><span class="o">.</span><span class="n">id</span><span class="p">)</span>
+ <span class="p">}</span>
+ <span class="k">else</span>
+ <span class="p">{</span><span class="ss">:contact</span> <span class="o">=&gt;</span> <span class="n">as_json</span><span class="p">(</span><span class="ss">:partial</span> <span class="o">=&gt;</span> <span class="kp">true</span><span class="p">)}</span>
+ <span class="k">end</span>
+<span class="k">end</span>
+</pre>
+</div>
+
+
+<p>This change allows us to call as_json with the options <code>:partial</code>. With the option, a hash of data is returned. Without, the same hash is returned, wrapped in another hash.</p>
+
+<p>Next, add a page presenter:</p>
+
+<div class="highlight"><pre><span class="k">class</span> <span class="nc">ContactPagePresenter</span>
+ <span class="kp">include</span> <span class="no">Rails</span><span class="o">.</span><span class="n">application</span><span class="o">.</span><span class="n">routes</span><span class="o">.</span><span class="n">url_helpers</span>
+
+ <span class="kp">attr_accessor</span> <span class="ss">:controller</span><span class="p">,</span> <span class="ss">:subject</span>
+ <span class="n">delegate</span> <span class="ss">:params</span><span class="p">,</span> <span class="ss">:url_options</span><span class="p">,</span> <span class="ss">:to</span> <span class="o">=&gt;</span> <span class="ss">:controller</span>
+ <span class="n">delegate</span> <span class="ss">:errors</span><span class="p">,</span> <span class="ss">:to</span> <span class="o">=&gt;</span> <span class="ss">:subject</span>
+
+ <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="n">controller</span><span class="p">,</span> <span class="n">subject</span><span class="p">)</span>
+ <span class="vi">@controller</span> <span class="o">=</span> <span class="n">controller</span>
+ <span class="vi">@subject</span> <span class="o">=</span> <span class="n">subject</span>
+ <span class="k">end</span>
+
+ <span class="k">def</span> <span class="nf">as_json</span><span class="p">(</span><span class="n">options</span> <span class="o">=</span> <span class="p">{})</span>
+ <span class="n">contacts</span> <span class="o">=</span> <span class="n">subject</span><span class="o">.</span><span class="n">map</span> <span class="p">{</span><span class="o">|</span><span class="n">o</span><span class="o">|</span> <span class="no">ContactPresenter</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="n">controller</span><span class="p">,</span> <span class="n">o</span><span class="p">)</span><span class="o">.</span><span class="n">as_json</span><span class="p">(</span><span class="ss">:partial</span> <span class="o">=&gt;</span> <span class="kp">true</span><span class="p">)</span> <span class="p">}</span>
+
+ <span class="p">{</span><span class="ss">:contacts</span> <span class="o">=&gt;</span> <span class="n">contacts</span><span class="p">}</span><span class="o">.</span><span class="n">tap</span> <span class="k">do</span> <span class="o">|</span><span class="n">result</span><span class="o">|</span>
+ <span class="k">if</span> <span class="n">subject</span><span class="o">.</span><span class="n">previous_page</span>
+ <span class="n">result</span><span class="o">[</span><span class="ss">:previous_page_uri</span><span class="o">]</span> <span class="o">=</span> <span class="n">contacts_url</span><span class="p">(</span><span class="n">subject</span><span class="o">.</span><span class="n">previous_page</span><span class="p">)</span>
+ <span class="k">end</span>
+
+ <span class="k">if</span> <span class="n">subject</span><span class="o">.</span><span class="n">next_page</span>
+ <span class="n">result</span><span class="o">[</span><span class="ss">:next_page_uri</span><span class="o">]</span> <span class="o">=</span> <span class="n">contacts_url</span><span class="p">(</span><span class="n">subject</span><span class="o">.</span><span class="n">next_page</span><span class="p">)</span>
+ <span class="k">end</span>
+ <span class="k">end</span>
+ <span class="k">end</span>
+<span class="k">end</span>
+</pre>
+</div>
+
+
+<p>Finally, we can add an index action using this presenter:</p>
+
+<div class="highlight"><pre><span class="k">class</span> <span class="nc">ContactsController</span> <span class="o">&lt;</span> <span class="no">ApiController</span>
+ <span class="n">responds_to</span> <span class="ss">:json</span>
+
+ <span class="k">def</span> <span class="nf">index</span>
+ <span class="n">respond_with</span> <span class="no">ContactPagePresenter</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="nb">self</span><span class="p">,</span> <span class="no">Contact</span><span class="o">.</span><span class="n">paginate</span><span class="p">(</span>
+ <span class="ss">:page</span> <span class="o">=&gt;</span> <span class="n">params</span><span class="o">[</span><span class="ss">:page</span><span class="o">]</span><span class="p">,</span>
+ <span class="ss">:per_page</span> <span class="o">=&gt;</span> <span class="mi">50</span><span class="p">)</span>
+ <span class="p">)</span>
+ <span class="k">end</span>
+<span class="k">end</span>
+</pre>
+</div>
+
+
+<h3>Refactoring common logic</h3>
+
+<p>The code above is a very much simplified version of what we do in <a href="https://hashblue.com">#blue</a>. We have many controllers, and several different models, so in our actual code we've abstracted out as much common logic as possible. In reality, our contacts controller looks more like this:</p>
+
+<div class="highlight"><pre><span class="k">class</span> <span class="nc">ContactsController</span> <span class="o">&lt;</span> <span class="no">ApiController</span>
+ <span class="n">before_filter</span> <span class="ss">:find_contact</span><span class="p">,</span> <span class="ss">:only</span> <span class="o">=&gt;</span> <span class="o">[</span><span class="ss">:show</span><span class="p">,</span> <span class="ss">:update</span><span class="o">]</span>
+
+ <span class="k">def</span> <span class="nf">show</span>
+ <span class="n">present</span> <span class="vi">@contact</span>
+ <span class="k">end</span>
+
+ <span class="k">def</span> <span class="nf">index</span>
+ <span class="n">present_page_of</span> <span class="n">current_account</span><span class="o">.</span><span class="n">contacts</span>
+ <span class="k">end</span>
+
+ <span class="k">def</span> <span class="nf">create</span>
+ <span class="vi">@contact</span> <span class="o">=</span> <span class="n">current_account</span><span class="o">.</span><span class="n">contacts</span><span class="o">.</span><span class="n">build</span><span class="p">(</span><span class="n">attributes</span><span class="p">)</span>
+ <span class="vi">@contact</span><span class="o">.</span><span class="n">save</span>
+ <span class="n">present</span> <span class="vi">@contact</span>
+ <span class="k">end</span>
+
+ <span class="k">def</span> <span class="nf">update</span>
+ <span class="vi">@contact</span><span class="o">.</span><span class="n">update_attributes</span><span class="p">(</span><span class="n">attributes</span><span class="p">)</span>
+ <span class="n">present</span> <span class="vi">@contact</span>
+ <span class="k">end</span>
+
+ <span class="kp">private</span>
+
+ <span class="k">def</span> <span class="nf">find_contact</span>
+ <span class="vi">@contact</span> <span class="o">=</span> <span class="n">current_account</span><span class="o">.</span><span class="n">contacts</span><span class="o">.</span><span class="n">where</span><span class="p">(</span><span class="ss">:_id</span> <span class="o">=&gt;</span> <span class="n">params</span><span class="o">[</span><span class="ss">:id</span><span class="o">]</span><span class="p">)</span><span class="o">.</span><span class="n">first</span>
+ <span class="n">head</span> <span class="ss">:status</span> <span class="o">=&gt;</span> <span class="ss">:not_found</span> <span class="k">unless</span> <span class="vi">@contact</span>
+ <span class="k">end</span>
+<span class="k">end</span>
+</pre>
+</div>
+
+
+<p>I think the code looks pretty clean. The clever stuff happens in the <code>#present</code> and <code>#present_page_of</code> methods, defined in the superclass:</p>
+
+<div class="highlight"><pre><span class="k">class</span> <span class="nc">ApiController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span><span class="o">::</span><span class="no">Base</span>
+ <span class="kp">protected</span>
+
+ <span class="k">def</span> <span class="nf">present</span><span class="p">(</span><span class="n">instance</span><span class="p">,</span> <span class="n">options</span> <span class="o">=</span> <span class="p">{})</span>
+ <span class="n">presenter</span> <span class="o">=</span> <span class="n">presenter_class</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="nb">self</span><span class="p">,</span> <span class="n">instance</span><span class="p">)</span>
+ <span class="n">options</span><span class="o">[</span><span class="ss">:location</span><span class="o">]</span> <span class="o">||=</span> <span class="n">presenter</span><span class="o">.</span><span class="n">uri</span> <span class="k">if</span> <span class="n">request</span><span class="o">.</span><span class="n">post?</span> <span class="o">&amp;&amp;</span> <span class="n">subject</span><span class="o">.</span><span class="n">errors</span><span class="o">.</span><span class="n">empty?</span>
+ <span class="n">respond_with</span> <span class="n">presenter</span><span class="p">,</span> <span class="n">options</span>
+ <span class="k">end</span>
+
+ <span class="k">def</span> <span class="nf">present_page_of</span><span class="p">(</span><span class="n">collection</span><span class="p">,</span> <span class="n">options</span> <span class="o">=</span> <span class="p">{})</span>
+ <span class="n">presenter</span> <span class="o">=</span> <span class="n">page_presenter_class</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="nb">self</span><span class="p">,</span> <span class="n">page_of</span><span class="p">(</span><span class="n">collection</span><span class="p">))</span>
+ <span class="n">respond_with</span> <span class="n">presenter</span><span class="p">,</span> <span class="n">options</span>
+ <span class="k">end</span>
+
+ <span class="k">def</span> <span class="nf">page_of</span><span class="p">(</span><span class="n">collection</span><span class="p">)</span>
+ <span class="n">collection</span><span class="o">.</span><span class="n">paginate</span><span class="p">(</span><span class="ss">:page</span> <span class="o">=&gt;</span> <span class="n">params</span><span class="o">[</span><span class="ss">:page</span><span class="o">]</span><span class="p">,</span> <span class="ss">:per_page</span> <span class="o">=&gt;</span> <span class="mi">50</span><span class="p">)</span>
+ <span class="k">end</span>
+
+ <span class="k">def</span> <span class="nf">presenter_class</span>
+ <span class="p">(</span><span class="nb">self</span><span class="o">.</span><span class="n">class</span><span class="o">.</span><span class="n">name</span><span class="o">.</span><span class="n">gsub!</span><span class="p">(</span><span class="s2">&quot;Controller&quot;</span><span class="p">,</span> <span class="s2">&quot;&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">singularize</span> <span class="o">+</span> <span class="s2">&quot;Presenter&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">constantize</span>
+ <span class="k">end</span>
+
+ <span class="k">def</span> <span class="nf">page_presenter_class</span>
+ <span class="p">(</span><span class="nb">self</span><span class="o">.</span><span class="n">class</span><span class="o">.</span><span class="n">name</span><span class="o">.</span><span class="n">gsub!</span><span class="p">(</span><span class="s2">&quot;Controller&quot;</span><span class="p">,</span> <span class="s2">&quot;&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">singularize</span> <span class="o">+</span> <span class="s2">&quot;PagePresenter&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">constantize</span>
+ <span class="k">end</span>
+<span class="k">end</span>
+</pre>
+</div>
+
+
+<p>The <code>#present</code> and <code>#present_page_of</code> methods handle determining the correct presenter to us, as well as paginating the collection where required. They still use rails build in <code>#respond_with</code> method, which helps provide the correct response headers for each request. As the ContactPresenter delegates <code>#errors</code> to its subject, if there are validation errors, <code>#respond_with</code> correctly returns a 422.</p>
+
+<p>One further motivation for this pattern (other than moving presentation logic out of the model) is that should we want to release a new version of our API, we'll be able to get a lot of the way there simply by swapping which presenter is used. We started using this code about 8 months ago, and I'm still pretty happy with it. I hope you find something useful in it too.</p>
+
+<p>Any comments or suggestions, please get in touch with me <a href="http://twitter.com/tomafro">on twitter</a>.</p>
+
+ </div>
+ <footer>
+ <span class='author'><a rel='author' href='http://tomafro.net'>Tom Ward</a></span>
+ <span class='date'><a href="/2011/08"> 2nd August 2011</a></span>
+ <ul>
+ <li><a href="/tags/hashblue" rel="tag">hashblue</a></li>
+ <li><a href="/tags/gofreerange" rel="tag">gofreerange</a></li>
+ <li><a href="/tags/o2" rel="tag">o2</a></li>
+ <li><a href="/tags/api" rel="tag">api</a></li>
+ <li><a href="/tags/rails" rel="tag">rails</a></li>
+ </ul>
+ </footer>
+</article>
</body>
</html>
View
157 public/sitemap.xml
@@ -10,98 +10,98 @@
<changefreq>weekly</changefreq>
</url>
<url>
- <loc>http://tomafro.net/2009/05/adam-sandersons-open-gem</loc>
- <lastmod>2009-05-06</lastmod>
+ <loc>http://tomafro.net/2012/06/tip-bundler-with-binstubs</loc>
+ <lastmod>2012-06-21</lastmod>
<changefreq>monthly</changefreq>
</url>
<url>
- <loc>http://tomafro.net/2009/05/automatching-rails-paths-in-cucumber</loc>
- <lastmod>2009-05-13</lastmod>
+ <loc>http://tomafro.net/2012/02/working-inside-government</loc>
+ <lastmod>2012-02-29</lastmod>
<changefreq>monthly</changefreq>
</url>
<url>
- <loc>http://tomafro.net/2009/05/imitation-is-the-sincerest-form-of-flattery</loc>
- <lastmod>2009-05-14</lastmod>
+ <loc>http://tomafro.net/2011/09/geohash-toy-code-released</loc>
+ <lastmod>2011-09-24</lastmod>
<changefreq>monthly</changefreq>
</url>
<url>
- <loc>http://tomafro.net/2009/05/using-rack-middleware-for-good-and-evil</loc>
- <lastmod>2009-05-22</lastmod>
+ <loc>http://tomafro.net/2011/09/a-small-toy-to-explore-geohashes</loc>
+ <lastmod>2011-09-15</lastmod>
<changefreq>monthly</changefreq>
</url>
<url>
- <loc>http://tomafro.net/2009/05/i-thought-we-were-going-to-kew-gardens</loc>
- <lastmod>2009-05-23</lastmod>
+ <loc>http://tomafro.net/2011/09/tip-automatic-bundle-exec-for-rake-and-more</loc>
+ <lastmod>2011-09-01</lastmod>
<changefreq>monthly</changefreq>
</url>
<url>
- <loc>http://tomafro.net/2009/05/an-objective-c-implementation-of-active-supports-inflector</loc>
- <lastmod>2009-05-28</lastmod>
+ <loc>http://tomafro.net/2011/08/presenting-the-hashblue-api</loc>
+ <lastmod>2011-08-02</lastmod>
<changefreq>monthly</changefreq>
</url>
<url>
- <loc>http://tomafro.net/2009/05/read-active-record-columns-directly-from-the-class</loc>
- <lastmod>2009-05-29</lastmod>
+ <loc>http://tomafro.net/2011/03/hashblue-opens-for-business</loc>
+ <lastmod>2011-03-03</lastmod>
<changefreq>monthly</changefreq>
</url>
<url>
- <loc>http://tomafro.net/2009/06/pimp-my-script-console</loc>
- <lastmod>2009-06-22</lastmod>
+ <loc>http://tomafro.net/2011/02/rails-mongo-instrumentation-gem</loc>
+ <lastmod>2011-02-19</lastmod>
<changefreq>monthly</changefreq>
</url>
<url>
- <loc>http://tomafro.net/2009/07/dscl-the-easy-way-to-add-hosts-on-osx</loc>
- <lastmod>2009-07-08</lastmod>
+ <loc>http://tomafro.net/2011/02/experimental-mongo-instrumentation</loc>
+ <lastmod>2011-02-18</lastmod>
<changefreq>monthly</changefreq>
</url>
<url>
- <loc>http://tomafro.net/2009/07/salvador-dali-on-whats-my-line</loc>
- <lastmod>2009-07-10</lastmod>
+ <loc>http://tomafro.net/2011/02/rails-3-column-reader-tweak</loc>
+ <lastmod>2011-02-08</lastmod>
<changefreq>monthly</changefreq>
</url>
<url>
- <loc>http://tomafro.net/2009/08/kernel-specific-zsh-dotfiles</loc>
- <lastmod>2009-08-03</lastmod>
+ <loc>http://tomafro.net/2010/02/updated-rails-template-for-bundler</loc>
+ <lastmod>2010-02-28</lastmod>
<changefreq>monthly</changefreq>
</url>
<url>
- <loc>http://tomafro.net/2009/08/the-cost-of-explicit-returns-in-ruby</loc>
- <lastmod>2009-08-04</lastmod>
+ <loc>http://tomafro.net/2010/02/rails-3-direct-column-reader</loc>
+ <lastmod>2010-02-11</lastmod>
<changefreq>monthly</changefreq>
</url>
<url>
- <loc>http://tomafro.net/2009/08/tip-move-to-directory-and-open-in-textmate</loc>
- <lastmod>2009-08-07</lastmod>
+ <loc>http://tomafro.net/2010/01/how-to-use-rails3-gems-now</loc>
+ <lastmod>2010-01-24</lastmod>
<changefreq>monthly</changefreq>
</url>
<url>
- <loc>http://tomafro.net/2009/08/using-indexes-in-rails-index-your-associations</loc>
- <lastmod>2009-08-11</lastmod>
+ <loc>http://tomafro.net/2010/01/tip-relative-paths-with-file-expand-path</loc>
+ <lastmod>2010-01-23</lastmod>
<changefreq>monthly</changefreq>
</url>
<url>
- <loc>http://tomafro.net/2009/08/tip-create-and-move-to-directory</loc>
- <lastmod>2009-08-12</lastmod>
+ <loc>http://tomafro.net/2009/11/taking-screenshots-of-web-pages-with-macruby</loc>
+ <lastmod>2009-11-30</lastmod>
<changefreq>monthly</changefreq>
</url>
<url>
- <loc>http://tomafro.net/2009/08/zsh-completion-for-gem-and-gem-open</loc>
- <lastmod>2009-08-14</lastmod>
+ <loc>http://tomafro.net/2009/11/zoom-keyboard-shortcut-for-os-x</loc>
+ <lastmod>2009-11-06</lastmod>
<changefreq>monthly</changefreq>
</url>
<url>
- <loc>http://tomafro.net/2009/08/using-indexes-in-rails-choosing-additional-indexes</loc>
- <lastmod>2009-08-18</lastmod>
+ <loc>http://tomafro.net/2009/11/building-gems-from-a-rails-branch</loc>
+ <lastmod>2009-11-05</lastmod>
<changefreq>monthly</changefreq>
</url>
<url>
- <loc>http://tomafro.net/2009/08/tip-open-new-tab-in-osx-terminal</loc>
- <lastmod>2009-08-19</lastmod>
+ <loc>http://tomafro.net/2009/11/a-rails-template-for-gem-bundler</loc>
+ <lastmod>2009-11-03</lastmod>
<changefreq>monthly</changefreq>
</url>
<url>
- <loc>http://tomafro.net/2009/09/quickly-list-missing-foreign-key-indexes</loc>
- <lastmod>2009-09-22</lastmod>
+ <loc>http://tomafro.net/2009/10/tip-cdpath-am-i-the-last-to-know</loc>
+ <lastmod>2009-10-06</lastmod>
<changefreq>monthly</changefreq>
</url>
<url>
@@ -110,103 +110,98 @@
<changefreq>monthly</changefreq>
</url>
<url>
- <loc>http://tomafro.net/2009/10/tip-cdpath-am-i-the-last-to-know</loc>
- <lastmod>2009-10-06</lastmod>
- <changefreq>monthly</changefreq>
- </url>
- <url>
- <loc>http://tomafro.net/2009/11/a-rails-template-for-gem-bundler</loc>
- <lastmod>2009-11-03</lastmod>
+ <loc>http://tomafro.net/2009/09/quickly-list-missing-foreign-key-indexes</loc>
+ <lastmod>2009-09-22</lastmod>
<changefreq>monthly</changefreq>
</url>
<url>
- <loc>http://tomafro.net/2009/11/building-gems-from-a-rails-branch</loc>
- <lastmod>2009-11-05</lastmod>
+ <loc>http://tomafro.net/2009/08/tip-open-new-tab-in-osx-terminal</loc>
+ <lastmod>2009-08-19</lastmod>
<changefreq>monthly</changefreq>
</url>
<url>
- <loc>http://tomafro.net/2009/11/zoom-keyboard-shortcut-for-os-x</loc>
- <lastmod>2009-11-06</lastmod>
+ <loc>http://tomafro.net/2009/08/using-indexes-in-rails-choosing-additional-indexes</loc>
+ <lastmod>2009-08-18</lastmod>
<changefreq>monthly</changefreq>
</url>
<url>
- <loc>http://tomafro.net/2009/11/taking-screenshots-of-web-pages-with-macruby</loc>
- <lastmod>2009-11-30</lastmod>
+ <loc>http://tomafro.net/2009/08/zsh-completion-for-gem-and-gem-open</loc>
+ <lastmod>2009-08-14</lastmod>
<changefreq>monthly</changefreq>
</url>
<url>
- <loc>http://tomafro.net/2010/01/tip-relative-paths-with-file-expand-path</loc>
- <lastmod>2010-01-23</lastmod>
+ <loc>http://tomafro.net/2009/08/tip-create-and-move-to-directory</loc>
+ <lastmod>2009-08-12</lastmod>
<changefreq>monthly</changefreq>
</url>
<url>
- <loc>http://tomafro.net/2010/01/how-to-use-rails3-gems-now</loc>
- <lastmod>2010-01-24</lastmod>
+ <loc>http://tomafro.net/2009/08/using-indexes-in-rails-index-your-associations</loc>
+ <lastmod>2009-08-11</lastmod>
<changefreq>monthly</changefreq>
</url>
<url>
- <loc>http://tomafro.net/2010/02/rails-3-direct-column-reader</loc>
- <lastmod>2010-02-11</lastmod>
+ <loc>http://tomafro.net/2009/08/tip-move-to-directory-and-open-in-textmate</loc>
+ <lastmod>2009-08-07</lastmod>
<changefreq>monthly</changefreq>
</url>
<url>
- <loc>http://tomafro.net/2010/02/updated-rails-template-for-bundler</loc>
- <lastmod>2010-02-28</lastmod>
+ <loc>http://tomafro.net/2009/08/the-cost-of-explicit-returns-in-ruby</loc>
+ <lastmod>2009-08-04</lastmod>
<changefreq>monthly</changefreq>
</url>
<url>
- <loc>http://tomafro.net/2011/02/rails-3-column-reader-tweak</loc>
- <lastmod>2011-02-08</lastmod>
+ <loc>http://tomafro.net/2009/08/kernel-specific-zsh-dotfiles</loc>
+ <lastmod>2009-08-03</lastmod>
<changefreq>monthly</changefreq>
</url>
<url>
- <loc>http://tomafro.net/2011/02/experimental-mongo-instrumentation</loc>
- <lastmod>2011-02-18</lastmod>
+ <loc>http://tomafro.net/2009/07/salvador-dali-on-whats-my-line</loc>
+ <lastmod>2009-07-10</lastmod>
<changefreq>monthly</changefreq>
</url>
<url>
- <loc>http://tomafro.net/2011/02/rails-mongo-instrumentation-gem</loc>
- <lastmod>2011-02-19</lastmod>
+ <loc>http://tomafro.net/2009/07/dscl-the-easy-way-to-add-hosts-on-osx</loc>
+ <lastmod>2009-07-08</lastmod>
<changefreq>monthly</changefreq>
</url>
<url>
- <loc>http://tomafro.net/2011/03/hashblue-opens-for-business</loc>
- <lastmod>2011-03-03</lastmod>
+ <loc>http://tomafro.net/2009/06/pimp-my-script-console</loc>
+ <lastmod>2009-06-22</lastmod>
<changefreq>monthly</changefreq>
</url>
<url>
- <loc>http://tomafro.net/2011/08/presenting-the-hashblue-api</loc>
- <lastmod>2011-08-02</lastmod>
+ <loc>http://tomafro.net/2009/05/read-active-record-columns-directly-from-the-class</loc>
+ <lastmod>2009-05-29</lastmod>
<changefreq>monthly</changefreq>
</url>
<url>
- <loc>http://tomafro.net/2011/09/tip-automatic-bundle-exec-for-rake-and-more</loc>
- <lastmod>2011-09-01</lastmod>
+ <loc>http://tomafro.net/2009/05/an-objective-c-implementation-of-active-supports-inflector</loc>
+ <lastmod>2009-05-28</lastmod>
<changefreq>monthly</changefreq>
</url>
<url>
- <loc>http://tomafro.net/2011/09/a-small-toy-to-explore-geohashes</loc>
- <lastmod>2011-09-15</lastmod>
+ <loc>http://tomafro.net/2009/05/i-thought-we-were-going-to-kew-gardens</loc>
+ <lastmod>2009-05-23</lastmod>
<changefreq>monthly</changefreq>
</url>
<url>
- <loc>http://tomafro.net/2011/09/geohash-toy-code-released</loc>
- <lastmod>2011-09-24</lastmod>
+ <loc>http://tomafro.net/2009/05/using-rack-middleware-for-good-and-evil</loc>
+ <lastmod>2009-05-22</lastmod>
<changefreq>monthly</changefreq>
</url>
<url>
- <loc>http://tomafro.net/2012/02/working-inside-government</loc>
- <lastmod>2012-02-29</lastmod>
+ <loc>http://tomafro.net/2009/05/imitation-is-the-sincerest-form-of-flattery</loc>
+ <lastmod>2009-05-14</lastmod>
<changefreq>monthly</changefreq>
</url>
<url>
- <loc>http://tomafro.net/2012/06/tip-bundler-with-binstubs</loc>
- <lastmod>2012-06-21</lastmod>
+ <loc>http://tomafro.net/2009/05/automatching-rails-paths-in-cucumber</loc>
+ <lastmod>2009-05-13</lastmod>
<changefreq>monthly</changefreq>
</url>
<url>
- <loc>http://tomafro.net</loc>
- <lastmod></lastmod>
+ <loc>http://tomafro.net/2009/05/adam-sandersons-open-gem</loc>
+ <lastmod>2009-05-06</lastmod>
<changefreq>monthly</changefreq>
</url>
</urlset>
View
3  source/_templates/main_index.mustache
@@ -1,3 +0,0 @@
-{{#posts}}
-{{{article}}}
-{{/posts}}
View
0  source/index.main
No changes.
View
0  source/index.main_index
No changes.
View
9 source/index.mustache
@@ -0,0 +1,9 @@
+---
+title: "Tom Ward's blog"
+description: "Personal blog of Tom Ward, in which he writes about ruby, rails and web development, as well as other random ephemera"
+layout: "_layouts/default.mustache"
+destination_path: "index.html"
+---
+{{#site.posts.take-6}}
+{{{article}}}
+{{/site.posts.take-6}}
Please sign in to comment.
Something went wrong with that request. Please try again.