Cheap Toto Pagination

February 23rd 2011

Toto is a great tiny blogging platform for Ruby/Rack. However, it doesn't expose much in the way of a MVC structure and it can be just annoying enough when you want to add some feature that isn't there. In this case, I wanted to add some simple older/newer pagination to the front page. To my chagrin, I couldn't find a way to pass variables to a Toto page without using the GET variable syntax (i.e. ?page=1) and I still wanted to hold onto the rails RESTful paradigm of /page/1, so I monkey patched the Toto::Site dispatcher, like so:

# in config.ru
class Toto::Site
    alias_method :old_go, :go

<span class="k">def</span> <span class="nf">go</span> <span class="n">route</span><span class="p">,</span> <span class="n">env</span> <span class="o">=</span> <span class="p">{},</span> <span class="n">type</span> <span class="o">=</span> <span class="ss">:html</span>
    <span class="k">if</span> <span class="n">not</span> <span class="n">route</span><span class="p">.</span><span class="nf">first</span> <span class="o">=~</span> <span class="sr">/\d{4}/</span> <span class="n">and</span> <span class="n">route</span><span class="p">.</span><span class="nf">size</span> <span class="o">==</span> <span class="mi">2</span> <span class="n">and</span> <span class="n">route</span><span class="p">.</span><span class="nf">last</span> <span class="o">=~</span> <span class="sr">/(\d+)/</span>
        <span class="vi">@config</span><span class="p">[</span><span class="ss">:id</span><span class="p">]</span> <span class="o">=</span> <span class="n">route</span><span class="p">.</span><span class="nf">last</span><span class="p">.</span><span class="nf">to_i</span>
        <span class="n">route</span><span class="p">.</span><span class="nf">pop</span>
    <span class="k">end</span>
    <span class="n">ret</span> <span class="o">=</span> <span class="n">old_go</span> <span class="n">route</span><span class="p">,</span> <span class="n">env</span><span class="p">,</span> <span class="n">type</span>
    <span class="vi">@config</span><span class="p">.</span><span class="nf">delete</span> <span class="ss">:id</span>
    <span class="n">ret</span>
<span class="k">end</span>

end ... # and in the config initializer block: set :root, "page" # page to load on /

You can see that we intercept routes that look like name/1234 and pass the numeric portion of the route in @config[:id], and then clear @config[:id] (because @config is persistent). This is pretty hacky and only really acceptable in the context of Heroku caching everything.

and in templates/pages/page.rhtml...

<%
    page = @config[:id]
    per_page = @config[:articles_per_page]
    page = 1 if (page.nil? or (page-1)  per_page > @articles.length) or page < 1
    page_results = @articles[(page-1)  per_page .. page  per_page - 1]
    prev_page = page > 1 ? page - 1 : nil
    next_page = @articles.length > page  per_page ? page + 1 : nil
%>
...
<p id="footer">
<% if prev_page %>
    <a href="/page/<%=prev_page%>">&laquo; newer</a>
    <% if next_page  %>|<% end %>
<% end %>
<% if next_page %>
    <a href="/page/<%=next_page%>">older &raquo;</a>
<% end %>
</p>

Also, syntax highlighting brought to you by highlight.js.