<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <id>http://arjanvandergaag.nl/</id>
  <title>arjanvandergaag.nl atom feed</title>
  <updated>2012-05-11T13:00:00Z</updated>
  <link rel="alternate" href="http://arjanvandergaag.nl/"/>
  <link rel="self" href="http://arjanvandergaag.nl/feed/"/>
  <author>
    <name>Arjan van der Gaag</name>
    <uri>http://arjanvandergaag.nl</uri>
  </author>
  <entry>
    <id>tag:arjanvandergaag.nl,2012-05-11:/blog/rails-decorator-patterns.html</id>
    <title type="html">Rails decorator patterns</title>
    <published>2012-05-11T13:00:00Z</published>
    <updated>2012-05-11T13:00:00Z</updated>
    <link rel="alternate" href="http://arjanvandergaag.nl/blog/rails-decorator-patterns.html"/>
    <content type="html">&lt;p class="leader"&gt;After spending some time refactoring a collection of decorators in a Rails app
recently, I found that most of the decorator methods followed one of a small
set of patterns.&lt;/p&gt;

&lt;p&gt;These examples use code examples based on the &lt;a href="https://github.com/jcasimir/draper"&gt;Draper&lt;/a&gt; gem, but the concepts
should apply to any decorator/presenter/exhibition pattern or library.&lt;/p&gt;

&lt;h2 id="linking"&gt;1. Linking&lt;/h2&gt;

&lt;p&gt;One scenario that keeps coming up is wanting to link to an object. Rather than
constructing your own using &lt;code&gt;link_to&lt;/code&gt;, the decorated model should know how to
generate a link to itself:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-ruby"&gt;post = &lt;span class="constant"&gt;PostDecorator&lt;/span&gt;.decorate(post)
post.link &lt;span class="comment"&gt;# =&amp;gt; "&amp;lt;a href='...'&amp;gt;...&amp;lt;/a&amp;gt;"&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This does not seem all that special for regular objects, but consider nested
resources or date-based archives requiring complex arguments:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-ruby"&gt;&lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="class"&gt;PostDecorator&lt;/span&gt; &amp;lt; &lt;span class="constant"&gt;ApplicationDecorator&lt;/span&gt;
  &lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function"&gt;link&lt;/span&gt;
    h.link_to title_with_comments, post_path(post, archive_params)
  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function"&gt;title_with_comments&lt;/span&gt;
    &lt;span class="string"&gt;&lt;span class="delimiter"&gt;"&lt;/span&gt;&lt;span class="inline"&gt;&lt;span class="inline-delimiter"&gt;#{&lt;/span&gt;title&lt;span class="inline-delimiter"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="content"&gt; (&lt;/span&gt;&lt;span class="inline"&gt;&lt;span class="inline-delimiter"&gt;#{&lt;/span&gt;comments_count&lt;span class="inline-delimiter"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class="content"&gt;)&lt;/span&gt;&lt;span class="delimiter"&gt;"&lt;/span&gt;&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function"&gt;archive_params&lt;/span&gt;
    { &lt;span class="symbol"&gt;:month&lt;/span&gt; =&amp;gt; created_at.month, &lt;span class="symbol"&gt;:year&lt;/span&gt; =&amp;gt; created_at.year }
  &lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This logic is best kept out of your view template. It is also better suited for
a decorator than a generic helper method. Compare:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-ruby"&gt;post.link
archive_post_link_to(post)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This becomes extra helpful when using delegation:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-ruby"&gt;&lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="class"&gt;AuthorDecorator&lt;/span&gt; &amp;lt; &lt;span class="constant"&gt;ApplicationDecorator&lt;/span&gt;
  &lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function"&gt;link&lt;/span&gt;
    h.link_to full_name, model
  &lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;

&lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="class"&gt;PostDecorator&lt;/span&gt; &amp;lt; &lt;span class="constant"&gt;ApplicationDecorator&lt;/span&gt;
  decorates &lt;span class="symbol"&gt;:post&lt;/span&gt;
  decorates_association &lt;span class="symbol"&gt;:author&lt;/span&gt;
  delegate &lt;span class="symbol"&gt;:link&lt;/span&gt;, &lt;span class="symbol"&gt;:to&lt;/span&gt; =&amp;gt; &lt;span class="symbol"&gt;:author&lt;/span&gt;, &lt;span class="symbol"&gt;:prefix&lt;/span&gt; =&amp;gt; &lt;span class="predefined-constant"&gt;true&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;

post = &lt;span class="constant"&gt;PostDecorator&lt;/span&gt;.decorate(post)
post.author_link &lt;span class="comment"&gt;# =&amp;gt; '&amp;lt;a href="/authors/1"&amp;gt;Arjan&amp;lt;/a&amp;gt;'&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It is a good convention to always have every model be able to generate a
sensible link to itself.&lt;/p&gt;

&lt;h2 id="filter-attribute-through-helper-method"&gt;2. Filter attribute through helper method&lt;/h2&gt;

&lt;p&gt;Most custom decorator methods apply a single helper method to the value of an
attribute, like applying &lt;code&gt;number_to_currency&lt;/code&gt; to a &lt;code&gt;product.price&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;It is easy to write a simple macro for that in the base decorator:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-ruby"&gt;&lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="class"&gt;ApplicationDecorator&lt;/span&gt; &amp;lt; &lt;span class="constant"&gt;Draper&lt;/span&gt;::&lt;span class="constant"&gt;Base&lt;/span&gt;
  &lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="predefined-constant"&gt;self&lt;/span&gt;.&lt;span class="function"&gt;filter_attributes&lt;/span&gt;(*args)
    options = args.extract_options!
    [*args].each &lt;span class="keyword"&gt;do&lt;/span&gt; |attr|
      define_method attr &lt;span class="keyword"&gt;do&lt;/span&gt;
        h.send(options.fetch(&lt;span class="symbol"&gt;:with&lt;/span&gt;), model.send(attr))
      &lt;span class="keyword"&gt;end&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This allows you to write a decorator like so:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-ruby"&gt;&lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="class"&gt;ProductDecorator&lt;/span&gt; &amp;lt; &lt;span class="constant"&gt;ApplicationDecorator&lt;/span&gt;
  filter_attributes &lt;span class="symbol"&gt;:price&lt;/span&gt;, &lt;span class="symbol"&gt;:with&lt;/span&gt; =&amp;gt; &lt;span class="symbol"&gt;:number_to_currency&lt;/span&gt;
  filter_attributes &lt;span class="symbol"&gt;:created_at&lt;/span&gt;,
                    &lt;span class="symbol"&gt;:published_at&lt;/span&gt;,
                    &lt;span class="symbol"&gt;:with&lt;/span&gt; =&amp;gt; &lt;span class="symbol"&gt;:relative_time_in_words&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This pattern allows you to write generic helper modules that can be used
anywhere containing the logic you wish to use. The decorator then takes
care of
consistently applying it.&lt;/p&gt;

&lt;p&gt;Note that it is probably best to get rid of your &lt;code&gt;ApplicationHelper&lt;/code&gt; and not
generate any helpers for your controllers either. Its best to group your
helpers in sensibly named modules rather than by controller or in one big junk
drawer.&lt;/p&gt;

&lt;h2 id="translate-attribute-values"&gt;3. Translate attribute values&lt;/h2&gt;

&lt;p&gt;Attributes commonly contain values that need to be localized, like a status
column (e.g. ‘published’, ‘draft’, etc.). A decorator method would then usually
look like this:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-ruby"&gt;&lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="class"&gt;PostDecorator&lt;/span&gt; &amp;lt; &lt;span class="constant"&gt;ApplicationDecorator&lt;/span&gt;
  &lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function"&gt;state&lt;/span&gt;
    h.t(&lt;span class="string"&gt;&lt;span class="delimiter"&gt;'&lt;/span&gt;&lt;span class="content"&gt;activerecord.attributes.post.states&lt;/span&gt;&lt;span class="delimiter"&gt;'&lt;/span&gt;&lt;/span&gt;).fetch state.to_sym
  &lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This is pretty tedious. A macro would help clarify intent, so we can create
something like the following:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-ruby"&gt;&lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="class"&gt;ApplicationDecorator&lt;/span&gt; &amp;lt; &lt;span class="constant"&gt;Draper&lt;/span&gt;::&lt;span class="constant"&gt;Base&lt;/span&gt;
  &lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="predefined-constant"&gt;self&lt;/span&gt;.&lt;span class="function"&gt;translate_attributes&lt;/span&gt;(translations = {})
    translations.each &lt;span class="keyword"&gt;do&lt;/span&gt; |attribute, key|
      define_method attribute &lt;span class="keyword"&gt;do&lt;/span&gt;
        h.t(key).fetch model.send(attribute).to_s.to_sym
      &lt;span class="keyword"&gt;end&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This allows us to declare that an attribute value should be translated:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-ruby"&gt;&lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="class"&gt;PostDecorator&lt;/span&gt; &amp;lt; &lt;span class="constant"&gt;ApplicationDecorator&lt;/span&gt;
  translate_attributes {
      &lt;span class="symbol"&gt;:state&lt;/span&gt; =&amp;gt; &lt;span class="string"&gt;&lt;span class="delimiter"&gt;'&lt;/span&gt;&lt;span class="content"&gt;activerecord.attributes.post.states&lt;/span&gt;&lt;span class="delimiter"&gt;'&lt;/span&gt;&lt;/span&gt;
  }
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;With the accompanying translation file:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-yaml"&gt;&lt;span class="key"&gt;nl&lt;/span&gt;:
  &lt;span class="key"&gt;activerecord&lt;/span&gt;:
    &lt;span class="key"&gt;attributes&lt;/span&gt;:
      &lt;span class="key"&gt;post&lt;/span&gt;:
        &lt;span class="key"&gt;states&lt;/span&gt;:
          &lt;span class="key"&gt;published&lt;/span&gt;: &lt;span class="string"&gt;&lt;span class="content"&gt;Gepubliceerd&lt;/span&gt;&lt;/span&gt;
          &lt;span class="key"&gt;draft&lt;/span&gt;: &lt;span class="string"&gt;&lt;span class="content"&gt;Concept&lt;/span&gt;&lt;/span&gt;
          &lt;span class="key"&gt;scheduled&lt;/span&gt;: &lt;span class="string"&gt;&lt;span class="content"&gt;Gepland&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id="mix-and-match"&gt;Mix and match&lt;/h2&gt;

&lt;p&gt;These macros do not cover all use cases and do not allow easy mixing and
matching. When you find yourself wanting to combine such macros on a single
attribute, you are probably better off writing an explicit method.&lt;/p&gt;</content>
  </entry>
  <entry>
    <id>tag:arjanvandergaag.nl,2012-05-01:/blog/introducing-rpub.html</id>
    <title type="html">Introducing Rpub, a simple ePub generation library</title>
    <published>2012-05-01T10:00:00Z</published>
    <updated>2012-05-01T10:00:00Z</updated>
    <link rel="alternate" href="http://arjanvandergaag.nl/blog/introducing-rpub.html"/>
    <content type="html">&lt;p class="leader"&gt;I am writing a book. A little book, but a book nonetheless. And when I started writing it, I knew I wanted to publish it as an ebook and write in Markdown. I found no easy solution to facilitate that, so I wrote my own.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://avdgaag.github.com/rpub"&gt;Rpub&lt;/a&gt; is a very simple Ruby gem for converting a set of &lt;a href="http://daringfireball.net/projects/markdown"&gt;markdown&lt;/a&gt; input files into an ebook in &lt;code&gt;.epub&lt;/code&gt; format. It is simple because it doesn’t do much:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it converts the markdown files to &lt;span class="caps"&gt;HTML&lt;/span&gt; using &lt;a href="http://kramdown.rubyforge.org"&gt;Kramdown&lt;/a&gt;;&lt;/li&gt;
  &lt;li&gt;it combines the &lt;span class="caps"&gt;HTML&lt;/span&gt; files together with any referenced images and fonts into a &lt;code&gt;.zip&lt;/code&gt; archive, together with some &lt;span class="caps"&gt;XML&lt;/span&gt; boilerplate files.&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;Additionally, it provides some helper functions to make life a little easier:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a package task to combine the &lt;code&gt;.epub&lt;/code&gt; file with an arbitrary number of other files (e.g. a license or &lt;span class="caps"&gt;README&lt;/span&gt;) into a single archive for online distribution;&lt;/li&gt;
  &lt;li&gt;a preview task for generating a single &lt;span class="caps"&gt;HTML&lt;/span&gt; page for easy previewing-as-you-write.&lt;/li&gt;
  &lt;li&gt;a statistics task for counting words and stuff.&lt;/li&gt;
  &lt;li&gt;it can generate a table of contents for you based on headers in your text.&lt;/li&gt;
&lt;/ul&gt;&lt;h2 id="configuration"&gt;Configuration&lt;/h2&gt;

&lt;p&gt;As valid &lt;code&gt;.epub&lt;/code&gt; files require some metadata, a &lt;code&gt;config.yml&lt;/code&gt; file is needed to generate your book. It is needed to specify book title, author, publisher, subject and all that malarkey. You can also use it to specify the cover image you want to use for the book or what files to include in the package.&lt;/p&gt;

&lt;p&gt;The gem comes with default &lt;span class="caps"&gt;HTML&lt;/span&gt; and &lt;span class="caps"&gt;CSS&lt;/span&gt; files for layout, but you can easily override them. Using the &lt;code&gt;generate&lt;/code&gt; task you can copy these files into your current project and tweak them to your taste.&lt;/p&gt;

&lt;h2 id="secret-features"&gt;Secret features&lt;/h2&gt;

&lt;p&gt;Because markdown files are converted using &lt;a href="http://kramdown.rubyforge.org"&gt;Kramdown&lt;/a&gt;, we get a few nice extra features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;footnotes&lt;/li&gt;
  &lt;li&gt;syntax highlighting of code samples&lt;/li&gt;
  &lt;li&gt;automatic header ids&lt;/li&gt;
  &lt;li&gt;abbreviations&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;Find out more about those in the &lt;a href="http://kramdown.rubyforge.org/quickref.html"&gt;Kramdown documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id="getting-started"&gt;Getting started&lt;/h2&gt;

&lt;p&gt;Rpub is distributed as a Ruby gem, so getting started is easy. Once you’ve got a working installation of Ruby and Ruby Gems set up, you can install Rpub:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ gem install rpub
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then create a new directory for your project, write your next great novel in a &lt;code&gt;.md&lt;/code&gt; file per chapter and set the title and author in a &lt;code&gt;config.yml&lt;/code&gt; file:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-yaml"&gt;&lt;span class="head"&gt;&lt;span class="head"&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span class="key"&gt;creator&lt;/span&gt;: &lt;span class="string"&gt;&lt;span class="content"&gt;Arjan van der Gaag&lt;/span&gt;&lt;/span&gt;
&lt;span class="key"&gt;language&lt;/span&gt;: &lt;span class="string"&gt;&lt;span class="content"&gt;en&lt;/span&gt;&lt;/span&gt;
&lt;span class="key"&gt;version&lt;/span&gt;: &lt;span class="string"&gt;&lt;span class="content"&gt;1.0.0&lt;/span&gt;&lt;/span&gt;
&lt;span class="key"&gt;title&lt;/span&gt;: &lt;span class="string"&gt;&lt;span class="content"&gt;Absolutism in 18th century Cleves&lt;/span&gt;&lt;/span&gt;
&lt;span class="key"&gt;publisher&lt;/span&gt;: &lt;span class="string"&gt;&lt;span class="content"&gt;Me!&lt;/span&gt;&lt;/span&gt;
&lt;span class="key"&gt;subject&lt;/span&gt;: &lt;span class="string"&gt;&lt;span class="content"&gt;History&lt;/span&gt;&lt;/span&gt;
&lt;span class="key"&gt;rights&lt;/span&gt;: &lt;span class="string"&gt;&lt;span class="content"&gt;copyright 2012. All rights reserved.&lt;/span&gt;&lt;/span&gt;
&lt;span class="key"&gt;description&lt;/span&gt;: &lt;span class="string"&gt;&lt;span class="content"&gt;My awesome history thesis.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That should do it. Compile your ebook:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ rpub compile
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;…and a new &lt;code&gt;absolutism-in-the-18th-centruy-1.0.0.epub&lt;/code&gt; file will appear in your current directory. Open it in iBooks on your iPad or use an ebook reader on your computer and bask in your self-publishing glory.&lt;/p&gt;

&lt;h3 id="contribute"&gt;Contribute&lt;/h3&gt;

&lt;p&gt;Take &lt;a href="https://rubygems.org/gems/rpub"&gt;the gem&lt;/a&gt; for a spin and report any issues you find on the &lt;a href="https://github.com/avdgaag/rpub/issues"&gt;Github issues tracker&lt;/a&gt;. Or better yet, &lt;a href="http://avdgaag.github.com/rpub"&gt;fork it&lt;/a&gt; and send it some pull request love.&lt;/p&gt;</content>
  </entry>
  <entry>
    <id>tag:arjanvandergaag.nl,2012-04-24:/blog/presentation-getting-started-with-ruby.html</id>
    <title type="html">Presentation: Getting started with ruby</title>
    <published>2012-04-24T10:00:00Z</published>
    <updated>2012-04-24T10:00:00Z</updated>
    <link rel="alternate" href="http://arjanvandergaag.nl/blog/presentation-getting-started-with-ruby.html"/>
    <content type="html">&lt;p class="leader"&gt;I gave a lightning talk about using the ruby program the other day at &lt;a href="http://eindhovenrb.nl"&gt;Eindhoven.rb&lt;/a&gt;, the Ruby user group for the Eindhoven area.&lt;/p&gt;

&lt;p&gt;Ruby is a programming language, and &lt;code&gt;ruby&lt;/code&gt; is the program you invoke it with. It takes some interesting arguments that enable you to use it as a general-purpose command line scripting solution – traditionally the domain of Perl.&lt;/p&gt;

&lt;script async="" class="speakerdeck-embed" data-id="4f7e98ffb2741a0022002a36" data-ratio="1.3333333333333333" src="//speakerdeck.com/assets/embed.js"&gt;&lt;/script&gt;&lt;p&gt;In my &lt;a href="http://speakerdeck.com/u/avdgaag/p/getting-started-with-ruby"&gt;slides&lt;/a&gt; I go over most of the options and some common use cases, so flip through them. Being a lightning talk, there wasn’t time to review these features in-depth, but &lt;a href="http://pragprog.com/book/ruby3/programming-ruby-1-9"&gt;Programming Ruby &lt;span class="caps"&gt;1.9&lt;/span&gt;&lt;/a&gt; is a great reference book to find out more.&lt;/p&gt;

&lt;p&gt;The &lt;a href="http://speakerdeck.com/u/avdgaag/p/getting-started-with-ruby"&gt;slides at Speakerdeck&lt;/a&gt; do not contain any presenter notes, but there is actually a &lt;a href="http://vimeo.com/39934412"&gt;video recording available&lt;/a&gt;. Check it out:&lt;/p&gt;

&lt;iframe src="http://player.vimeo.com/video/39934412" width="500" height="281" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen=""&gt;&lt;/iframe&gt;</content>
  </entry>
  <entry>
    <id>tag:arjanvandergaag.nl,2012-02-02:/blog/bol-gem.html</id>
    <title type="html">A Ruby gem for the bol.com developer API</title>
    <published>2012-02-02T12:30:00Z</published>
    <updated>2012-02-02T12:30:00Z</updated>
    <link rel="alternate" href="http://arjanvandergaag.nl/blog/bol-gem.html"/>
    <content type="html">&lt;p class="leader"&gt;It has bugged me for a long time that I could not access product information
from &lt;a href="http://bol.com"&gt;bol.com&lt;/a&gt; the same way I could from Amazon.com. When bol.com released
their public &lt;span class="caps"&gt;API&lt;/span&gt; some time ago, I &lt;em&gt;still&lt;/em&gt; couldn’t. Not easily. So I wrote a
Ruby library for &lt;a href="http://developers.bol.com"&gt;the Bol.com &lt;span class="caps"&gt;API&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The bol.com &lt;span class="caps"&gt;API&lt;/span&gt; allows you to fetch detailed information for common products,
such as books, DVDs and toys, search for products by keyword, or list
bestsellers. The &lt;span class="caps"&gt;API&lt;/span&gt; is not too complex: it’s just a handful of endpoints, a
couple of available query parameters and a request signature. &lt;/p&gt;

&lt;p&gt;I built a wrapper around this &lt;span class="caps"&gt;API&lt;/span&gt; that exposes the available operations and
handles the difficult parts of request signing and &lt;span class="caps"&gt;XML&lt;/span&gt; parsing. All you, as the
developer, have to do is provide the access credentials. All operations give
you nice, clean Ruby objects back, such as a &lt;code&gt;Bol::Product&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id="getting-started"&gt;Getting started&lt;/h2&gt;

&lt;p&gt;You need to &lt;a href="https://developers.bol.com/inloggen/?action=register"&gt;sign up for a developer account&lt;/a&gt; first. When your account
has been approved, you can request an &lt;span class="caps"&gt;API&lt;/span&gt; key. The key has two parts – one
public, one secret – that will be used by bol.com to make sure you really are
who you say you are when you make a request.&lt;/p&gt;

&lt;p&gt;Once you have both your access key and its secret, you can get started by
installing the Ruby gem:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ gem install bol
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id="an-example-application"&gt;An example application&lt;/h2&gt;

&lt;p&gt;Let’s create a very simple Sinatra application to search the bol.com store (see
&lt;a href="https://gist.github.com/1724664"&gt;the completed app in one file&lt;/a&gt;). Create an application file &lt;code&gt;app.rb&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-ruby"&gt;require &lt;span class="string"&gt;&lt;span class="delimiter"&gt;'&lt;/span&gt;&lt;span class="content"&gt;sinatra&lt;/span&gt;&lt;span class="delimiter"&gt;'&lt;/span&gt;&lt;/span&gt;

get &lt;span class="string"&gt;&lt;span class="delimiter"&gt;'&lt;/span&gt;&lt;span class="content"&gt;/&lt;/span&gt;&lt;span class="delimiter"&gt;'&lt;/span&gt;&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
  erb &lt;span class="symbol"&gt;:search&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Add a simple form view:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-html"&gt;&lt;span class="tag"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="attribute-name"&gt;method&lt;/span&gt;=&lt;span class="string"&gt;&lt;span class="delimiter"&gt;"&lt;/span&gt;&lt;span class="content"&gt;post&lt;/span&gt;&lt;span class="delimiter"&gt;"&lt;/span&gt;&lt;/span&gt; &lt;span class="attribute-name"&gt;action&lt;/span&gt;=&lt;span class="string"&gt;&lt;span class="delimiter"&gt;"&lt;/span&gt;&lt;span class="content"&gt;/search&lt;/span&gt;&lt;span class="delimiter"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;span class="tag"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="tag"&gt;&amp;lt;label&amp;gt;&lt;/span&gt;Query: &lt;span class="tag"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="attribute-name"&gt;type&lt;/span&gt;=&lt;span class="string"&gt;&lt;span class="delimiter"&gt;"&lt;/span&gt;&lt;span class="content"&gt;text&lt;/span&gt;&lt;span class="delimiter"&gt;"&lt;/span&gt;&lt;/span&gt; &lt;span class="attribute-name"&gt;name&lt;/span&gt;=&lt;span class="string"&gt;&lt;span class="delimiter"&gt;"&lt;/span&gt;&lt;span class="content"&gt;q&lt;/span&gt;&lt;span class="delimiter"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;span class="tag"&gt;&amp;gt;&lt;/span&gt;&lt;span class="tag"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
  &lt;span class="tag"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="attribute-name"&gt;type&lt;/span&gt;=&lt;span class="string"&gt;&lt;span class="delimiter"&gt;"&lt;/span&gt;&lt;span class="content"&gt;submit&lt;/span&gt;&lt;span class="delimiter"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;span class="tag"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="tag"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Run the app to confirm everything works:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ ruby -rubygems app.rb
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;When you browse to &lt;code&gt;http://localhost:4567&lt;/code&gt; you can see the search page, but it
doesn’t do anything yet. Let’s add a search action:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-ruby"&gt;post &lt;span class="string"&gt;&lt;span class="delimiter"&gt;'&lt;/span&gt;&lt;span class="content"&gt;/search&lt;/span&gt;&lt;span class="delimiter"&gt;'&lt;/span&gt;&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
  &lt;span class="instance-variable"&gt;@products&lt;/span&gt; = []
  erb &lt;span class="symbol"&gt;:results&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And a view to display results:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-rhtml"&gt;&lt;span class="tag"&gt;&amp;lt;ol&amp;gt;&lt;/span&gt;
&lt;span class="inline"&gt;&lt;span class="inline-delimiter"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="instance-variable"&gt;@products&lt;/span&gt;.each &lt;span class="keyword"&gt;do&lt;/span&gt; |product| &lt;span class="inline-delimiter"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class="tag"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;&lt;span class="inline"&gt;&lt;span class="inline-delimiter"&gt;&amp;lt;%=&lt;/span&gt; product.title &lt;span class="inline-delimiter"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="tag"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="inline"&gt;&lt;span class="inline-delimiter"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="keyword"&gt;end&lt;/span&gt; &lt;span class="inline-delimiter"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="tag"&gt;&amp;lt;/ol&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id="implementing-search"&gt;Implementing search&lt;/h2&gt;

&lt;p&gt;We’ve added a &lt;code&gt;/search&lt;/code&gt; route that will somehow set an array of products, and
then render them to an ordered list on the page. Simple enough. Now to actually
search some products:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-ruby"&gt;post &lt;span class="string"&gt;&lt;span class="delimiter"&gt;'&lt;/span&gt;&lt;span class="content"&gt;/search&lt;/span&gt;&lt;span class="delimiter"&gt;'&lt;/span&gt;&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
  &lt;span class="instance-variable"&gt;@products&lt;/span&gt; = &lt;span class="constant"&gt;Bol&lt;/span&gt;.search params[&lt;span class="symbol"&gt;:q&lt;/span&gt;]      
  erb &lt;span class="symbol"&gt;:results&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Searching the bol.com website is as easy as &lt;code&gt;Bol.search(params[:q])&lt;/code&gt;. Restart
the app, and try it out. You will get an error, complaining that the gem is not
properly configured. We need to provide our access key and its secret, so they
can be used to sign our requests:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-ruby"&gt;&lt;span class="constant"&gt;Bol&lt;/span&gt;.configure &lt;span class="keyword"&gt;do&lt;/span&gt; |c|
  c.access_key = &lt;span class="string"&gt;&lt;span class="delimiter"&gt;'&lt;/span&gt;&lt;span class="content"&gt;123456789&lt;/span&gt;&lt;span class="delimiter"&gt;'&lt;/span&gt;&lt;/span&gt;
  c.secret     = &lt;span class="string"&gt;&lt;span class="delimiter"&gt;'&lt;/span&gt;&lt;span class="content"&gt;abcdefghi&lt;/span&gt;&lt;span class="delimiter"&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;When you restart the application, you should be able to search bol.com by
keyword and get a list of titles back. &lt;/p&gt;

&lt;h2 id="showing-product-details"&gt;Showing product details&lt;/h2&gt;

&lt;p&gt;Let’s go one step further and add some product details. We’ll create a new
route for that:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-ruby"&gt;get &lt;span class="string"&gt;&lt;span class="delimiter"&gt;'&lt;/span&gt;&lt;span class="content"&gt;/product/:id&lt;/span&gt;&lt;span class="delimiter"&gt;'&lt;/span&gt;&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
  &lt;span class="instance-variable"&gt;@product&lt;/span&gt; = &lt;span class="constant"&gt;Bol&lt;/span&gt;::&lt;span class="constant"&gt;Product&lt;/span&gt;.find params[&lt;span class="symbol"&gt;:id&lt;/span&gt;]
  erb &lt;span class="symbol"&gt;:product&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We can use &lt;code&gt;Product#find&lt;/code&gt; to find a particular product on bol.com by its
internal &lt;span class="caps"&gt;ID.&lt;/span&gt; We’ll create a link to the detail page in our search results:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-rhtml"&gt;&lt;span class="tag"&gt;&amp;lt;ol&amp;gt;&lt;/span&gt;
&lt;span class="inline"&gt;&lt;span class="inline-delimiter"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="instance-variable"&gt;@products&lt;/span&gt;.each &lt;span class="keyword"&gt;do&lt;/span&gt; |product| &lt;span class="inline-delimiter"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class="tag"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;
    &lt;span class="tag"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="attribute-name"&gt;href&lt;/span&gt;=&lt;span class="string"&gt;&lt;span class="delimiter"&gt;"&lt;/span&gt;&lt;span class="content"&gt;/product/&lt;/span&gt;&lt;/span&gt;&lt;span class="inline"&gt;&lt;span class="inline-delimiter"&gt;&amp;lt;%=&lt;/span&gt; product.id &lt;span class="inline-delimiter"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="string"&gt;&lt;span class="delimiter"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;span class="tag"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="inline"&gt;&lt;span class="inline-delimiter"&gt;&amp;lt;%=&lt;/span&gt; product.title &lt;span class="inline-delimiter"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class="tag"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
  &lt;span class="tag"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class="inline"&gt;&lt;span class="inline-delimiter"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="keyword"&gt;end&lt;/span&gt; &lt;span class="inline-delimiter"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="tag"&gt;&amp;lt;/ol&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We also need a view for our new action:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-rhtml"&gt;&lt;span class="tag"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;&lt;span class="inline"&gt;&lt;span class="inline-delimiter"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="instance-variable"&gt;@product&lt;/span&gt;.title &lt;span class="inline-delimiter"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="tag"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class="tag"&gt;&amp;lt;dl&amp;gt;&lt;/span&gt;
  &lt;span class="inline"&gt;&lt;span class="inline-delimiter"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="instance-variable"&gt;@product&lt;/span&gt;.attributes.each_pair &lt;span class="keyword"&gt;do&lt;/span&gt; |k, v| &lt;span class="inline-delimiter"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class="tag"&gt;&amp;lt;dt&amp;gt;&lt;/span&gt;&lt;span class="inline"&gt;&lt;span class="inline-delimiter"&gt;&amp;lt;%=&lt;/span&gt; k &lt;span class="inline-delimiter"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="tag"&gt;&amp;lt;/dt&amp;gt;&lt;/span&gt;
  &lt;span class="tag"&gt;&amp;lt;dd&amp;gt;&lt;/span&gt;&lt;span class="tag"&gt;&amp;lt;pre&amp;gt;&lt;/span&gt;&lt;span class="inline"&gt;&lt;span class="inline-delimiter"&gt;&amp;lt;%=&lt;/span&gt; v &lt;span class="inline-delimiter"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="tag"&gt;&amp;lt;/pre&amp;gt;&lt;/span&gt;&lt;span class="tag"&gt;&amp;lt;/dd&amp;gt;&lt;/span&gt;
  &lt;span class="inline"&gt;&lt;span class="inline-delimiter"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="keyword"&gt;end&lt;/span&gt; &lt;span class="inline-delimiter"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class="tag"&gt;&amp;lt;/dl&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Restart the app, perform a search and click on a result. You should see all
available product details. Note that there are several sizes of cover image
available, and that the &lt;code&gt;author&lt;/code&gt; attribute is an &lt;code&gt;Array&lt;/code&gt; – there can be more
than one, after all.&lt;/p&gt;

&lt;h2 id="scoping-search-results-to-categories"&gt;Scoping search results to categories&lt;/h2&gt;

&lt;p&gt;Now we want to limit our search results to a particular category of products.
Bol.com products are extensively categorized and we can look up those
categories and search in them. Let’s add a drop-down list of categories to
search to our search form:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-ruby"&gt;get &lt;span class="string"&gt;&lt;span class="delimiter"&gt;'&lt;/span&gt;&lt;span class="content"&gt;/&lt;/span&gt;&lt;span class="delimiter"&gt;'&lt;/span&gt;&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
  &lt;span class="instance-variable"&gt;@categories&lt;/span&gt; = &lt;span class="constant"&gt;Bol&lt;/span&gt;.categories
  erb &lt;span class="symbol"&gt;:search&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;

post &lt;span class="string"&gt;&lt;span class="delimiter"&gt;'&lt;/span&gt;&lt;span class="content"&gt;/search&lt;/span&gt;&lt;span class="delimiter"&gt;'&lt;/span&gt;&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
  &lt;span class="instance-variable"&gt;@products&lt;/span&gt; = &lt;span class="constant"&gt;Bol&lt;/span&gt;::&lt;span class="constant"&gt;Scope&lt;/span&gt;.new(params[&lt;span class="symbol"&gt;:category_id&lt;/span&gt;])
    .search params[&lt;span class="symbol"&gt;:q&lt;/span&gt;]      
  erb &lt;span class="symbol"&gt;:results&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And in the view:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-rhtml"&gt;&lt;span class="tag"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="attribute-name"&gt;method&lt;/span&gt;=&lt;span class="string"&gt;&lt;span class="delimiter"&gt;"&lt;/span&gt;&lt;span class="content"&gt;post&lt;/span&gt;&lt;span class="delimiter"&gt;"&lt;/span&gt;&lt;/span&gt; &lt;span class="attribute-name"&gt;action&lt;/span&gt;=&lt;span class="string"&gt;&lt;span class="delimiter"&gt;"&lt;/span&gt;&lt;span class="content"&gt;/search&lt;/span&gt;&lt;span class="delimiter"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;span class="tag"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="tag"&gt;&amp;lt;select&lt;/span&gt; &lt;span class="attribute-name"&gt;name&lt;/span&gt;=&lt;span class="string"&gt;&lt;span class="delimiter"&gt;"&lt;/span&gt;&lt;span class="content"&gt;category_id&lt;/span&gt;&lt;span class="delimiter"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;span class="tag"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="inline"&gt;&lt;span class="inline-delimiter"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="instance-variable"&gt;@categories&lt;/span&gt;.each &lt;span class="keyword"&gt;do&lt;/span&gt; |category| &lt;span class="inline-delimiter"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class="tag"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="attribute-name"&gt;value&lt;/span&gt;=&lt;span class="string"&gt;&lt;span class="delimiter"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;span class="inline"&gt;&lt;span class="inline-delimiter"&gt;&amp;lt;%=&lt;/span&gt; category.id &lt;span class="inline-delimiter"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="string"&gt;&lt;span class="delimiter"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;span class="tag"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="inline"&gt;&lt;span class="inline-delimiter"&gt;&amp;lt;%=&lt;/span&gt; category.name &lt;span class="inline-delimiter"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt; (&lt;span class="inline"&gt;&lt;span class="inline-delimiter"&gt;&amp;lt;%=&lt;/span&gt; category.count &lt;span class="inline-delimiter"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;)
    &lt;span class="tag"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
    &lt;span class="inline"&gt;&lt;span class="inline-delimiter"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="keyword"&gt;end&lt;/span&gt; &lt;span class="inline-delimiter"&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class="tag"&gt;&amp;lt;/select&amp;gt;&lt;/span&gt;
  &lt;span class="tag"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="attribute-name"&gt;type&lt;/span&gt;=&lt;span class="string"&gt;&lt;span class="delimiter"&gt;"&lt;/span&gt;&lt;span class="content"&gt;text&lt;/span&gt;&lt;span class="delimiter"&gt;"&lt;/span&gt;&lt;/span&gt; &lt;span class="attribute-name"&gt;name&lt;/span&gt;=&lt;span class="string"&gt;&lt;span class="delimiter"&gt;"&lt;/span&gt;&lt;span class="content"&gt;q&lt;/span&gt;&lt;span class="delimiter"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;span class="tag"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="tag"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="attribute-name"&gt;type&lt;/span&gt;=&lt;span class="string"&gt;&lt;span class="delimiter"&gt;"&lt;/span&gt;&lt;span class="content"&gt;submit&lt;/span&gt;&lt;span class="delimiter"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;span class="tag"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="tag"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Once we’ve reloaded the application, we can choose a category to search in and
get results limited to that category. Categories are nested, so you can get
subcategories using &lt;code&gt;Bol::Scope.new(some_id).categories&lt;/code&gt;. And there are also
&lt;em&gt;refinements&lt;/em&gt;, such as groupings by price or brand and other attributes, but
they work similarly to categories.&lt;/p&gt;

&lt;h2 id="referral-links"&gt;Referral links&lt;/h2&gt;

&lt;p&gt;Then only referral links remain. It is a good idea to link to
products on bol.com, so you can get a kickback on any sales
resulting from the traffic you send over. This is not strictly a
feature of the developer &lt;span class="caps"&gt;API&lt;/span&gt;, but it is so commonly used, I just
threw it in here. You can simply ask a product for its referral
&lt;span class="caps"&gt;URL&lt;/span&gt;, given a specific referral &lt;span class="caps"&gt;ID&lt;/span&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-ruby"&gt;product = &lt;span class="constant"&gt;Bol&lt;/span&gt;.find(params[&lt;span class="symbol"&gt;:id&lt;/span&gt;])
product.referral_link(&lt;span class="string"&gt;&lt;span class="delimiter"&gt;'&lt;/span&gt;&lt;span class="content"&gt;my-site-id&lt;/span&gt;&lt;span class="delimiter"&gt;'&lt;/span&gt;&lt;/span&gt;)
&lt;span class="comment"&gt;# =&amp;gt; "http://..."&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id="other-features"&gt;Other features&lt;/h2&gt;

&lt;p&gt;This example application showcases the basics of the bol gem, but it includes
some more:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ordering and limiting of number of results&lt;/li&gt;
  &lt;li&gt;Automatic pagination&lt;/li&gt;
  &lt;li&gt;Joining and subtracting categories and refinements to create complex scopes&lt;/li&gt;
  &lt;li&gt;List popular or bestselling products&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;See the project &lt;a href="https://github.com/avdgaag/bol"&gt;&lt;span class="caps"&gt;README&lt;/span&gt;&lt;/a&gt; file for more information.&lt;/p&gt;

&lt;h2 id="example-application-and-source-code"&gt;Example application and source code&lt;/h2&gt;

&lt;p&gt;That’s all you need to know to get started. You can see the entire &lt;a href="https://gist.github.com/1724664"&gt;example
application&lt;/a&gt; in &lt;a href="https://gist.github.com/1724664"&gt;this gist&lt;/a&gt;. You can find &lt;a href="https://github.com/avdgaag/bol"&gt;the source for this Ruby gem
on Github&lt;/a&gt;, where I also keep the &lt;span class="caps"&gt;API&lt;/span&gt; documentation and track issues. The
gem is still in beta stage, so it can be field-tested before getting an actual
stamp-of-approval by releasing a version &lt;span class="caps"&gt;1.0.&lt;/span&gt; Try it out and do let me know if
you make anything awesome with it!&lt;/p&gt;</content>
  </entry>
  <entry>
    <id>tag:arjanvandergaag.nl,2012-01-19:/blog/shell-scripting.html</id>
    <title type="html">Shell scripting to the rescue</title>
    <published>2012-01-19T19:00:00Z</published>
    <updated>2012-01-19T19:00:00Z</updated>
    <link rel="alternate" href="http://arjanvandergaag.nl/blog/shell-scripting.html"/>
    <content type="html">&lt;p class="leader"&gt;I love &lt;a href="http://ruby-lang.org"&gt;Ruby&lt;/a&gt; and tend to use it for everything I &lt;em&gt;can&lt;/em&gt; use it for. But
I’ve &lt;a href="http://www.amazon.co.uk/gp/product/0596003307/ref=as_li_ss_tl?ie=UTF8&amp;amp;tag=arjanvandergaag-21&amp;amp;linkCode=as2&amp;amp;camp=1634&amp;amp;creative=19450&amp;amp;creativeASIN=0596003307"&gt;reading up on Unix&lt;/a&gt; recently, and I decided to test my newfound
knowledge by using standard unix programs to solve a problem. Those who do not
know Unix are doomed to re-implement it badly (or so I have been told).&lt;/p&gt;

&lt;p&gt;I needed to copy a lot of images from a remote server to my local machine.
Since images were constantly being added to the remote server, I wanted to have
a repeatable script to download only those images that were listed in a &lt;span class="caps"&gt;YAML&lt;/span&gt;
file from another application. So I needed to read the &lt;span class="caps"&gt;YAML&lt;/span&gt; file, find the
files listed inside it, and collect those in an archive for easy downloading.&lt;/p&gt;

&lt;h2 id="reading-input"&gt;01. Reading input&lt;/h2&gt;

&lt;p&gt;My input file was in &lt;span class="caps"&gt;YAML&lt;/span&gt;, so the first step is reading that. But since the
file is several thousand lines long, we pipe it into head to just print the
first few lines:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ cat images.yml | head
---
- http://host.tld/images/image1.jpg
- http://host.tld/images/image2.jpg
...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The first problem was the first line of three dashes, which I needed to get rid
of. Using &lt;code&gt;sed&lt;/code&gt; you can actually issue &lt;code&gt;ex&lt;/code&gt; commands like in Vim, so this was
easy:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ cat images.yml | sed '1d' | head
- http://host.tld/images/image1.jpg
- http://host.tld/images/image2.jpg
- http://flickr.com/images/image3.jpg
...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This deletes line one, but there’s a saying along the lines of: “if you &lt;code&gt;cat&lt;/code&gt; a
file and immediately pipe it into something else, something’s wrong”. So, I
rewrote it like so:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ sed 'd' images.yml | head
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id="parsing-yaml"&gt;02. “Parsing” &lt;span class="caps"&gt;YAML&lt;/span&gt;
&lt;/h2&gt;

&lt;p&gt;Then, I needed to get rid of the &lt;span class="caps"&gt;YAML&lt;/span&gt; array element indicators – the dashes
starting each line. I could have used &lt;code&gt;sed&lt;/code&gt; for that, but I chose &lt;code&gt;cut&lt;/code&gt;, which
extracts fields from a line, splitting the line on a given delimited into
columns. I wanted the second column with a space as delimiter:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ sed 'd' images.yml | cut -d' ' -f 2 | head
http://host.tld/images/image1.jpg
http://host.tld/images/image2.jpg
http://flickr.com/images/image3.jpg
…
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This was starting to look useful. &lt;/p&gt;

&lt;h2 id="getting-just-the-image-path"&gt;03. Getting just the image path&lt;/h2&gt;

&lt;p&gt;There was a problem with the images: all images contained the full &lt;span class="caps"&gt;URL&lt;/span&gt;, and I
wanted to get just the path. &lt;code&gt;sed&lt;/code&gt; to the rescue, again:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ sed 'd' images.yml | \
  cut -d' ' -f 2 | \
  sed 's|http://host.tld/||' |\
  head
images/image1.jpg
images/image2.jpg
http://flickr.com/images/image3.jpg
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This time, I used a replacement pattern as we would in Vim, only replacing the
standard &lt;code&gt;/&lt;/code&gt; separator with a &lt;code&gt;|&lt;/code&gt; to not have to escape every &lt;code&gt;/&lt;/code&gt; in the search
string.&lt;/p&gt;

&lt;h2 id="getting-rid-of-externally-hosted-images"&gt;04. Getting rid of externally hosted images&lt;/h2&gt;

&lt;p&gt;This left the problem of externally hosted images. I just gave up on those.
Getting rid of those sounded like a task for &lt;code&gt;grep&lt;/code&gt;, which can be used to
&lt;em&gt;exclude&lt;/em&gt; lines matching a pattern:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ sed 'd' images.yml | \
  cut -d' ' -f 2 | \
  sed 's|http://host.tld/||' |\
  grep -v "flickr" |\
  head
images/image1.jpg
images/image2.jpg
http://amazon.com/images/image4.jpg
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This gives a new problem: there are several different external hosts in the
file. I only wanted our own. I decided to rewrite the command and use &lt;code&gt;grep&lt;/code&gt; to
filter out all lines that &lt;em&gt;do&lt;/em&gt; contain our own host, and &lt;em&gt;then&lt;/em&gt; remove the
domain:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ sed 'd' images.yml | \
  cut -d' ' -f 2 | \
  grep "http://host.tld" |\
  sed 's|http://host.tld/||' |\
  head
images/image1.jpg
images/image2.jpg
images/image5.jpg
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id="combining-files-into-an-archive"&gt;05. Combining files into an archive&lt;/h2&gt;

&lt;p&gt;The next task was to zip up all those files into one big archive for easy
downloading from the server to my local machine.&lt;/p&gt;

&lt;p&gt;The first idea was to just dump the whole lot into &lt;code&gt;zip&lt;/code&gt;, like so:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ sed 'd' images.yml | \
  cut -d' ' -f 2 | \
  grep "http://host.tld" |\
  sed 's|http://host.tld/||' |\
  zip dump.zip
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Alas, that doesn’t work. I started investigating possible solutions, such as
using &lt;code&gt;xargs&lt;/code&gt; – which mashes a bunch of lines into a single line and feed them
as arguments to another program, with some intelligence about the number of
arguments a program accepts. After some fiddling, I got frustrated that &lt;code&gt;zip&lt;/code&gt;
just didn’t read filenames from standard input, so I &lt;em&gt;finally&lt;/em&gt; decided to open
the &lt;code&gt;zip&lt;/code&gt; manual with &lt;code&gt;man zip&lt;/code&gt;. Searching the manual for &lt;code&gt;stdin&lt;/code&gt;, I found out
&lt;code&gt;zip&lt;/code&gt; indeed does not read input filenames from standard input by default, but
On Mac &lt;span class="caps"&gt;OS&lt;/span&gt; X, there’s the &lt;code&gt;--names-stdin&lt;/code&gt; option, while on most other systems
there’s &lt;code&gt;-@&lt;/code&gt;. There you go, it pays to &lt;abbr title="Read The Fucking Manual"&gt;&lt;span class="caps"&gt;RTFM&lt;/span&gt;&lt;/abbr&gt;.&lt;/p&gt;

&lt;p&gt;So, the entire command now looks like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ sed 'd' images.yml | \
  cut -d' ' -f 2 | \
  grep "http://host.tld" |\
  sed 's|http://host.tld/||' |\
  zip dump.zip -@
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This does what I wanted it to do quite nicely, but I figured I could do
slightly better.&lt;/p&gt;

&lt;h2 id="duplicates-and-thumbnails"&gt;06. Duplicates and thumbnails&lt;/h2&gt;

&lt;p&gt;One problem was a lot of duplicate images; another was lots of different sizes
of the same image – with the original one the only I care about.&lt;/p&gt;

&lt;p&gt;Solving duplicates is easy enough using the &lt;code&gt;uniq&lt;/code&gt; program:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ sed 'd' images.yml | \
  cut -d' ' -f 2 | \
  grep "http://host.tld" |\
  sed 's|http://host.tld/||' |\
  uniq |\
  zip dump.zip -@
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then, I want to only use the original image, not the generated thumbnails. I
happened to know that generated thumbnails have filenames like
&lt;code&gt;original-filename-150x75.jpg&lt;/code&gt;. Removing the dimensions at the end of the
filename would give me the regular file. My list could very well contain that
original file already, but &lt;code&gt;uniq&lt;/code&gt; would sort that out. So, there’s one more
&lt;code&gt;sed&lt;/code&gt; to add:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ sed 'd' images.yml | \
  cut -d' ' -f 2 | \
  grep "http://host.tld" |\
  sed 's|http://host.tld/||' |\
  sed 's/-\d+x\d+\.jpg/.jpg/' |\
  uniq |\
  zip -9 dump.zip -@
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That gave me a dump archive file containing all my images. As I was happy with
the result, I tacked on a &lt;code&gt;-9&lt;/code&gt; to enable maximum compression for the archive,
shaving a couple of percentage points of the end result file size.&lt;/p&gt;

&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;This post might seem long, but the process of developing this command chain was
actually rather quick. Feedback is almost instant and there’s a rich collection
of tools to get the job done. I’m pretty sure developing a Ruby script doing
the same thing would have involved more manual tweaking and looking up
documentation.&lt;/p&gt;</content>
  </entry>
</feed>

