Update 2020-01-19: this tutorial is based on old version Will Paginate and Sinatra, I will update this next time.

I’m new to Sinatra and I want to use it together with Active Record and Will Paginate. After hours searching, I’ve finally found out to use active_record and will_paginate for non Rails application. Before I explain use Active Record and Will Paginate for non-Rails app, I assume that you use bundler and your database migration is already setup and working.

Step 1. Gemfile

First thing first, edit your Gemfile.

# Gemfile
source :rubygems
gem 'sinatra', '1.0.0', :require => 'sinatra/base'
gem 'activerecord', '3.0.0.rc', :require => 'active_record'
gem 'haml'
gem 'will_paginate', :git => 'git://github.com/mislav/will_paginate.git',
  :tag => 'v3.0.pre2',
  :require => 'will_paginate/finders/base'

Step 2. Edit Your Model

You must extend your class model with Will Paginate module.

# your_model.rb
class Article < ActiveRecord::Base
  extend WillPaginate::Finders::Base
end

Step 3. Edit your Sinatra Application

Because Will Paginate helper does not work with Sinatra Application, so you need to write your own helper.

# your_sinatra_app.rb
require 'rubygems'
require 'bundler/setup'
Bundler.require :default
require 'your_model.rb'

class Application < Sinatra::Base
  get '/' do
    @articles = Article.order("created_at desc").
      paginate :page => params[:page], :per_page => 10
    haml :articles
  end

  helpers do
    # helper for pagination
    def paginate(resources)
      if !resources.next_page.nil? and !resources.previous_page.nil?
        html = "« Prev "
        html += "#{params[:page]} of #{resources.total_pages} "
        html += "Next »"
      elsif !resources.next_page.nil? and resources.previous_page.nil?
        html = "Next »"
      elsif resources.next_page.nil? and !resources.previous_page.nil?
        html = "« Prev "
        html += "#{params[:page]} of #{resources.total_pages}"
      end
      return html
    end
  end
end

Step 4. Edit Your View

# views/articles.haml
%div
  %header
    %h2
      Article list
  .content
    %table
      %thead
        %tr
          %th
            Title
          %th
            Content
          %th
            Author
      %tbody
        - @articles.each do |article|
          %tr
            %td
              = article.title
            %td
              = article.content
            %td
              = article.author
        = paginate @articles

Done. Sinatra, Will Paginate, and Active Record are ready to use. The above code is extracted from my Sinatrails project, feel free to use it.

Update in 2012:

New code for step 3.

# your_sinatra_app.rb
require 'rubygems'
require 'bundler/setup'
Bundler.require :default
require 'your_model.rb'

class Application < Sinatra::Base
  get '/' do
    @articles = Article.order("created_at desc").
      paginate :page => params[:page], :per_page => 10
    haml :articles
  end

  # inspired from http://pastie.org/1192729 by krishnaprasad
  helpers do
    def to_params(params_hash)
      new_params = ''
      stack = []

      params_hash.each do |k, v|
        unless k == "page"
          if v.is_a?(Hash)
            stack << [k,v]
          else
            new_params << "#{k}=#{v}&"
          end
        end
      end

      stack.each do |parent, hash|
        hash.each do |k, v|
          unless k == "page"
            if v.is_a?(Hash)
              stack << ["#{parent}[#{k}]", v]
            else
              new_params << "#{parent}[#{k}]=#{v}&"
            end
          end
        end
      end

      new_params.chop! # trailing &
      "&" + new_params unless new_params.empty?
    end

    def paginate(resources)
      parameters = to_params(params.clone)

      if !resources.next_page.nil? and !resources.previous_page.nil?
        html = %^<a href="#{request.path_info}?page=#{resources.previous_page}#{parameters}">&laquo; Prev</a> ^
        html += "#{params[:page]} of #{resources.total_pages} "
        html += %^<a href="#{request.path_info}?page=#{resources.next_page}#{parameters}">Next &raquo;</a>^
      elsif !resources.next_page.nil? and resources.previous_page.nil?
        html = %^<a href="#{request.path_info}?page=#{resources.next_page}#{parameters}">Next &raquo;</a>^
      elsif resources.next_page.nil? and !resources.previous_page.nil?
        html = %^<a href="#{request.path_info}?page=#{resources.previous_page}#{parameters}">&laquo; Prev</a> ^
        html += "#{params[:page]} of #{resources.total_pages}"
      end

      html
    end
  end
end