/
index.html
489 lines (397 loc) · 39.9 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="chrome=1" />
<meta name="viewport" content="width=device-width, minimum-scale=1.0" />
<link rel="stylesheet" href="/css/style-53d352ed04ac62e6bbc36307da3b41cb.css" type="text/css" />
<link rel="alternate" type="application/atom+xml" href="http://tomafro.net/atom.xml" />
<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 · tomafro.net</title>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-1103395-2']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script');
ga.src = ('https:' == document.location.protocol ? 'https://ssl' :
'http://www') + '.google-analytics.com/ga.js';
ga.setAttribute('async', 'true');
document.documentElement.firstChild.appendChild(ga);
})();
</script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body>
<nav>
<ul>
<li><a href='/'>tomafro.net</a><span class='byline'>, a blog by Tom Ward</span></li>
<li><a href='/about'>about</a></li>
<li><a href='https://github.com/tomafro'>github</a></li>
<li><a href='https://twitter.com/tomafro'>twitter</a></li>
</ul>
</nav>
<article>
<header>
<h1><a href="/2012/06/tip-bundler-with-binstubs">Tip: Bundler with --binstubs</a></h1>
</header>
<div class="content">
<p>In <a href="/2011/09/tip-automatic-bundle-exec-for-rake-and-more">a previous blog</a>, I wrote how I'd aliased commands such as <code>rake</code>, <code>cap</code> and <code>rspec</code> to run either with or without <code>bundle exec</code>, based on the presence of a <code>Gemfile</code>. I gave up on that a while ago. Instead, I've started installing all my bundles like this:</p>
<div class="highlight"><pre>bundle install --path .bundle/gems --binstubs .bundle/bin
</pre>
</div>
<p>I often use features like <code>bundle open <gem></code> to debug and edit failing gems, so I like to keep each application's gems isolated. The <code>--path .bundle/gems</code> installs them within an application's <code>.bundle</code> directory. As well as isolating my gems, it has the added benefit that I can blow away the gemset with <code>rm -rf .bundle</code></p>
<p>The <code>--binstubs .bundle/bin</code> option installs bundle-aware scripts for each command provided by a bundled gem. For example, a bundle including <code>rake</code> will generate a <code>.bundle/bin/rake</code> script. By adding <code>./.bundle/bin</code> to the front of my environment <code>PATH</code>, the bundled version of <code>rake</code> will run when I'm in the application folder. I never have to type <code>bundle exec</code>!</p>
<p>Obviously typing that long <code>bundle install</code> command each time is tedious, so I've aliased it to <code>bi</code>:</p>
<div class="highlight"><pre><span class="nb">alias </span><span class="nv">bi</span><span class="o">=</span><span class="s1">'bundle install --path .bundle/gems --binstubs .bundle/bin'</span>
</pre>
</div>
<p>I've been using these options for a few months, and so far I'm very happy with them.</p>
</div>
<footer>
<span class='author'><a rel='author' href='http://tomafro.net'>Tom Ward</a></span>
<span class='date'><a href="/2012/06">21st June 2012</a></span>
<ul>
<li><a href="/tags/ruby" rel="tag">ruby</a></li>
<li><a href="/tags/gem" rel="tag">gem</a></li>
<li><a href="/tags/rake" rel="tag">rake</a></li>
<li><a href="/tags/bundler" rel="tag">bundler</a></li>
<li><a href="/tags/tip" rel="tag">tip</a></li>
</ul>
</footer>
</article>
<article>
<header>
<h1><a href="/2012/02/working-inside-government">Working inside government</a></h1>
</header>
<div class="content">
<p>Since September, I and the other <a href="http://gofreerange.com">Go Free Range</a> guys have been working for the government. We've been helping the able and growing <a href="http://digital.cabinetoffice.gov.uk/">Government Digital Service</a> to build <a href="https://github.com/alphagov/whitehall">whitehall</a>, our code name for the <a href="https://www.gov.uk/government">Inside Government section of gov.uk</a>. Yesterday we removed the password and opened our doors to the public.</p>
<div class="browsershots">
<a href="https://www.gov.uk/government/organisations/department-for-international-development"><img class="browsershot" src="/images/dfid.thumb.png"/></a>
<a href="https://www.gov.uk/government/policies/human-rights"><img class="browsershot" src="/images/promoting-human-rights.thumb.png"/></a>
<a href="https://www.gov.uk/government/organisations"><img class="browsershot" src="/images/departments.thumb.png"/></a>
</div>
<p>For those who don't know, <a href="https://www.gov.uk">gov.uk</a> is the UK government's attempt to not only consolidate government websites to a single domain, but also build these sites in the right way. That means <a href="https://www.pivotaltracker.com/projects/367813">public</a>, <a href="https://github.com/alphagov/whitehall">open source</a>, continual delivery and more.</p>
<p>While the first part of <a href="https://www.gov.uk">gov.uk</a> focussed on helping citizens, <a href="https://www.gov.uk/government">Inside Government</a> is about the business of government. <a href="https://www.gov.uk/government/policy-topics">What policies</a> the government has, <a href="https://www.gov.uk/government/policies/launching-the-single-domain">how it's implementing them</a>, not to mention the <a href="https://www.gov.uk/government/news-and-speeches">news and speeches</a>, <a href="https://www.gov.uk/government/publications">publications</a>, <a href="https://www.gov.uk/government/consultations">consultations</a> and other things the government does each day.</p>
<p>It's only the first release of many, a small glimpse into what will one day be. How it ends up will be shaped by <a href="https://www.gov.uk/feedback">feedback from the people who use it</a>.</p>
</div>
<footer>
<span class='author'><a rel='author' href='http://tomafro.net'>Tom Ward</a></span>
<span class='date'><a href="/2012/02">29th February 2012</a></span>
<ul>
<li><a href="/tags/gds" rel="tag">gds</a></li>
<li><a href="/tags/government" rel="tag">government</a></li>
<li><a href="/tags/whitehall" rel="tag">whitehall</a></li>
<li><a href="/tags/freerange" rel="tag">freerange</a></li>
</ul>
</footer>
</article>
<article>
<header>
<h1><a href="/2011/09/geohash-toy-code-released">Geohash toy: code released</a></h1>
</header>
<div class="content">
<p>A couple of weeks ago I <a href="http://tomafro.net/2011/09/a-small-toy-to-explore-geohashes">wrote about a small toy app</a> I'd written to explore geohashes. Now I've cleaned the code up a little, upgraded it to rails 3.1 and released it <a href="https://github.com/tomafro/geohash-explorer">here on github</a>. Enjoy.</p>
</div>
<footer>
<span class='author'><a rel='author' href='http://tomafro.net'>Tom Ward</a></span>
<span class='date'><a href="/2011/09">24th September 2011</a></span>
<ul>
<li><a href="/tags/geohash" rel="tag">geohash</a></li>
<li><a href="/tags/toy" rel="tag">toy</a></li>
<li><a href="/tags/code" rel="tag">code</a></li>
<li><a href="/tags/app" rel="tag">app</a></li>
<li><a href="/tags/github" rel="tag">github</a></li>
<li><a href="/tags/tiny" rel="tag">tiny</a></li>
</ul>
</footer>
</article>
<article>
<header>
<h1><a href="/2011/09/a-small-toy-to-explore-geohashes">A small toy to explore geohashes</a></h1>
</header>
<div class="content">
<p>For an app I've been building, I've been looking into <a href="http://geohash.org/">geohashes</a>. For those who don't know, the <a href="http://en.wikipedia.org/wiki/Geohash">geohash</a> format is a simple way to encode latitude and longitude into a single string. As an example, Nelson's Column in London (51.507794, -0.127952) has the geohash <code>gcpvj0dyds</code>.</p>
<p>Geohashes have a couple of interesting features. First, as you remove characters, you lose precision. <code>gcpvj0dyds</code> fairly accurately points to Nelson's Column; <code>gcpvj0d</code> represents the South-West of Trafalgar Square and some of the Mall; and <code>gcpvj</code> covers most of Central London, as well as Islington and King's Cross. A geohash doesn't really represent a point, but rather a bounding area within which a point may lie. The longer the geohash, the smaller that bounding area.</p>
<p>The other interesting property geohashes have is that nearby locations usually (but not always) share similar prefixes. So much of North London is in <code>gcpv</code>, while much of South London is in <code>gcpu</code>. However, due to the <a href="http://en.wikipedia.org/wiki/Prime_Meridian">Prime Meridian</a> passing through Greenwhich, South East London has the geohash <code>u10h</code> - wildly different than the other two.</p>
<p>This probably sounds a bit complicated. I was having trouble getting my head around the concept, so to try and get to grips with geohashes I've written a toy app that draws them on a map. To try it out, go to <a href="http://geohash.gofreerange.com">http://geohash.gofreerange.com</a>, click the map, zoom and play. If you find it useful, let me know.</p>
<div class="update">
Update: This code is now available [on github](https://github.com/tomafro/geohash-explorer).
</div>
</div>
<footer>
<span class='author'><a rel='author' href='http://tomafro.net'>Tom Ward</a></span>
<span class='date'><a href="/2011/09">15th September 2011</a></span>
<ul>
<li><a href="/tags/geohash" rel="tag">geohash</a></li>
<li><a href="/tags/toy" rel="tag">toy</a></li>
<li><a href="/tags/app" rel="tag">app</a></li>
</ul>
</footer>
</article>
<article>
<header>
<h1><a href="/2011/09/tip-automatic-bundle-exec-for-rake-and-more">Tip: Automatic bundle exec for rake and other gems</a></h1>
</header>
<div class="content">
<p>It's irritating to run gem commands like <code>rake</code>, <code>cap</code>, <code>rspec</code> and others, only to find they needed to be executed via <code>bundle exec</code>. As a simple solution, I use a simple zsh function, combined with aliases for commonly used commands.</p>
<p>Here's the function (which I've named <code>be</code>):</p>
<div class="highlight"><pre><span class="k">if</span> <span class="o">[[</span> -a Gemfile <span class="o">]]</span>; <span class="k">then</span>
<span class="k"> </span>bundle <span class="nb">exec</span> <span class="nv">$*</span>
<span class="k">else</span>
<span class="k"> </span><span class="nb">command</span> <span class="nv">$*</span>
<span class="k">fi</span>
</pre>
</div>
<p>It's very simple. If there's a <code>Gemfile</code> in the pwd, it runs commands through bundle exec. Otherwise it just runs them.</p>
<p>I've combined this with some aliases for much less pain and less frustration:</p>
<div class="highlight"><pre><span class="nb">alias </span><span class="nv">rake</span><span class="o">=</span><span class="s1">'be rake'</span>
<span class="nb">alias </span><span class="nv">cap</span><span class="o">=</span><span class="s1">'be cap'</span>
<span class="nb">alias </span><span class="nv">rspec</span><span class="o">=</span><span class="s1">'be rspec'</span>
</pre>
</div>
</div>
<footer>
<span class='author'><a rel='author' href='http://tomafro.net'>Tom Ward</a></span>
<span class='date'><a href="/2011/09"> 1st September 2011</a></span>
<ul>
<li><a href="/tags/zsh" rel="tag">zsh</a></li>
<li><a href="/tags/ruby" rel="tag">ruby</a></li>
<li><a href="/tags/gem" rel="tag">gem</a></li>
<li><a href="/tags/rake" rel="tag">rake</a></li>
<li><a href="/tags/bundler" rel="tag">bundler</a></li>
<li><a href="/tags/tip" rel="tag">tip</a></li>
</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"><</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">"contact"</span><span class="o">:</span> <span class="p">{</span>
<span class="s2">"uri"</span><span class="o">:</span> <span class="s2">"https://api.example.com/contacts/ccpwjc"</span><span class="p">,</span>
<span class="s2">"name"</span><span class="o">:</span> <span class="s2">"George"</span><span class="p">,</span>
<span class="s2">"email"</span><span class="o">:</span> <span class="s2">"george@handmade.org"</span><span class="p">,</span>
<span class="s2">"msisdn"</span><span class="o">:</span> <span class="s2">"447897897899"</span><span class="p">,</span>
<span class="s2">"phone_number"</span><span class="o">:</span> <span class="s2">"07897897899"</span><span class="p">,</span>
<span class="s2">"messages"</span><span class="o">:</span> <span class="s2">"https://api.example.com/contacts/ccpwjc/messages"</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">=></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">=></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">=></span> <span class="p">{</span>
<span class="ss">:uri</span> <span class="o">=></span> <span class="n">uri</span><span class="p">,</span>
<span class="ss">:email</span> <span class="o">=></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">=></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">=></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">=></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">=></span> <span class="n">api_contact_messages_url</span><span class="p">(</span><span class="ss">:contact_id</span> <span class="o">=></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">=></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"><</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">=></span> <span class="n">uri</span><span class="p">,</span>
<span class="ss">:email</span> <span class="o">=></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">=></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">=></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">=></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">=></span> <span class="n">api_contact_messages_url</span><span class="p">(</span><span class="ss">:contact_id</span> <span class="o">=></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">=></span> <span class="n">as_json</span><span class="p">(</span><span class="ss">:partial</span> <span class="o">=></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">=></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">=></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">=></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">=></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"><</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">=></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">=></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"><</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">=></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">=></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">=></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"><</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">&&</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">=></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">=></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">"Controller"</span><span class="p">,</span> <span class="s2">""</span><span class="p">)</span><span class="o">.</span><span class="n">singularize</span> <span class="o">+</span> <span class="s2">"Presenter"</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">"Controller"</span><span class="p">,</span> <span class="s2">""</span><span class="p">)</span><span class="o">.</span><span class="n">singularize</span> <span class="o">+</span> <span class="s2">"PagePresenter"</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>
<section class="related-links">
<h1>Other articles:</h1>
<ul>
<li><a href="/2011/03/hashblue-opens-for-business">#blue opens for business</a><span class="meta"> - 3rd March 2011</span></li>
<li><a href="/2011/02/rails-mongo-instrumentation-gem">Mongo instrumentation released as a gem</a><span class="meta"> - 19th February 2011</span></li>
<li><a href="/2011/02/experimental-mongo-instrumentation">Experimental Mongo instrumentation (for Rails 3)</a><span class="meta"> - 18th February 2011</span></li>
<li><a href="/2011/02/rails-3-column-reader-tweak">A home for my Active Record column reader</a><span class="meta"> - 8th February 2011</span></li>
<li><a href="/2010/02/updated-rails-template-for-bundler">An updated rails template for gem bundler</a><span class="meta"> - 28th February 2010</span></li>
<li><a href="/2010/02/rails-3-direct-column-reader">Rails 3 direct column reader</a><span class="meta"> - 11th February 2010</span></li>
<li><a href="/2010/01/how-to-use-rails3-gems-now">How to easily use Rails 3 now</a><span class="meta"> - 24th January 2010</span></li>
<li><a href="/2010/01/tip-relative-paths-with-file-expand-path">Tip: Relative paths with File.expand_path</a><span class="meta"> - 23rd January 2010</span></li>
<li><a href="/2009/11/taking-screenshots-of-web-pages-with-macruby">Taking screenshots of web pages with macruby</a><span class="meta"> - 30th November 2009</span></li>
<li><a href="/2009/11/zoom-keyboard-shortcut-for-os-x">Tip: Zoom keyboard shortcut for OS X</a><span class="meta"> - 6th November 2009</span></li>
<li><a href="/2009/11/building-gems-from-a-rails-branch">Building rails gems from the 2-3-stable branch</a><span class="meta"> - 5th November 2009</span></li>
<li><a href="/2009/11/a-rails-template-for-gem-bundler">A rails template for gem bundler</a><span class="meta"> - 3rd November 2009</span></li>
<li><a href="/2009/10/tip-cdpath-am-i-the-last-to-know">Tip: cdpath - Am I the last to know?</a><span class="meta"> - 6th October 2009</span></li>
<li><a href="/2009/09/tip-the-case-for-from-param">Tip: The case for from_param</a><span class="meta"> - 23rd September 2009</span></li>
<li><a href="/2009/09/quickly-list-missing-foreign-key-indexes">Quickly list missing foreign key indexes</a><span class="meta"> - 22nd September 2009</span></li>
<li><a href="/2009/08/tip-open-new-tab-in-osx-terminal">Tip: Open new tab in OS X Terminal</a><span class="meta"> - 19th August 2009</span></li>
<li><a href="/2009/08/using-indexes-in-rails-choosing-additional-indexes">Using indexes in rails: Choosing additional indexes</a><span class="meta"> - 18th August 2009</span></li>
<li><a href="/2009/08/zsh-completion-for-gem-and-gem-open">ZSH Completion for gem and gem open</a><span class="meta"> - 14th August 2009</span></li>
<li><a href="/2009/08/tip-create-and-move-to-directory">Tip: Create and move to directory</a><span class="meta"> - 12th August 2009</span></li>
<li><a href="/2009/08/using-indexes-in-rails-index-your-associations">Using indexes in rails: Index your associations</a><span class="meta"> - 11th August 2009</span></li>
<li><a href="/2009/08/tip-move-to-directory-and-open-in-textmate">Tip: Move to directory and open in TextMate</a><span class="meta"> - 7th August 2009</span></li>
<li><a href="/2009/08/the-cost-of-explicit-returns-in-ruby">The cost of explicit returns in ruby</a><span class="meta"> - 4th August 2009</span></li>
<li><a href="/2009/08/kernel-specific-zsh-dotfiles">Kernel specific ZSH dotfiles</a><span class="meta"> - 3rd August 2009</span></li>
<li><a href="/2009/07/salvador-dali-on-whats-my-line">Salvador Dali on "What's My Line?"</a><span class="meta"> - 10th July 2009</span></li>
<li><a href="/2009/07/dscl-the-easy-way-to-add-hosts-on-osx">dscl - the easy way to add hosts on OS X</a><span class="meta"> - 8th July 2009</span></li>
<li><a href="/2009/06/pimp-my-script-console">Pimp my script/console</a><span class="meta"> - 22nd June 2009</span></li>
<li><a href="/2009/05/read-active-record-columns-directly-from-the-class">Read ActiveRecord columns directly from the class</a><span class="meta"> - 29th May 2009</span></li>
<li><a href="/2009/05/an-objective-c-implementation-of-active-supports-inflector">An Objective-C implementation of ActiveSupport's Inflector</a><span class="meta"> - 28th May 2009</span></li>
<li><a href="/2009/05/i-thought-we-were-going-to-kew-gardens">I thought we were going to Kew Gardens</a><span class="meta"> - 23rd May 2009</span></li>
<li><a href="/2009/05/using-rack-middleware-for-good-and-evil">Using Rack Middleware for good and evil</a><span class="meta"> - 22nd May 2009</span></li>
<li><a href="/2009/05/imitation-is-the-sincerest-form-of-flattery">If imitation is the sincerest form of flattery...</a><span class="meta"> - 14th May 2009</span></li>
<li><a href="/2009/05/automatching-rails-paths-in-cucumber">Automatching rails paths in cucumber</a><span class="meta"> - 13th May 2009</span></li>
<li><a href="/2009/05/adam-sandersons-open-gem">Adam Sanderson's open_gem</a><span class="meta"> - 6th May 2009</span></li>
</ul>
</section>
</body>
</html>