I needed to access some data from another app and display it to users. The api side was a Rails 2.3.11 app and used will_paginate. It returned the json of a model collection and an association along with paginator information. The client side was a Rails 3.1 app using HTTParty and Kaminari.
API Controller
Rails 2.3.11 doesn’t include associations in the json, which is a known bug. So I returned a collection with the two models’ information. I used an API key to authenticate, which I won’t describe in detail since it’s outside the scope of this post.
class Api::FoosController < ApplicationController
before_filter :authenticate_api_key!
def foos_index
@foos = Foo.paginate(:order => "created_at desc", :per_page => 40, :page => params[:page],
:include => :bar)
@records = @foos.collect { |foo| {:foo => foo, :bar => bar} }
render :json => {:records => @records, :total_entries => @foos.total_entries,
:per_page => @foo.per_page }
end
end
API Route
The route requires the page number.
map.connect '/api/foos/foos_index/:page', :controller => "api/foos", :action => :foos_index
Client Model
FooIndex.foos_index returns itself, along with accessors for the response, records, total_count, current_page, and limit_value. The last three are important for Kaminari. When I parsed the JSON, I symbolized the keys. Much prettier than strings.
class FooIndex
include HTTParty
base_uri ENV['FOO_APP_URL']
default_params :api_key => ENV['FOO_API_KEY']
format :json
attr_accessor :response, :records, :total_count, :current_page, :limit_value
def initialize(response, records, total_count, current_page, limit_value)
self.response = response
self.records = records
self.total_count = total_count
self.current_page = current_page
self.limit_value = limit_value
end
def self.foos_index(page)
@current_page = page || 1
response = get("/api/foos/foos_index/#{@current_page}")
if response.success?
json = JSON.parse(response.body, symbolize_names: true)
self.new(response, json[:records], json[:total_entries], page, json[:per_page])
else
raise response.response
end
end
end
Client Controller
I had to be creative with Kaminari using the paginate_array method. The options total_count and limit are used because the api doesn’t return the whole set of data, just the data for one page.
class FoosController < ApplicationController
def index
params[:page] ||= 1
@foos_index = FooIndex.foos_index(params[:page])
@paginated_array = Kaminari.paginate_array(@foos_index.records,
total_count: @foos_index.total_count,
limit: @foos_index.limit_value).page(@foos_index.current_page)
end
end
Client Route
Page number is optional in the route. The controller defaults it to page 1.
match '/foos(/:page)', :controller => :foos, :action => :index
Client View in HAML
The json returned by the api doubles up the first part of the hash. I’m not sure of an elegant way to handle that.
= paginate @paginated_array
%table
%thead
%th Foo Name
%th Bar Name
- @foos_index.records.each do |record|
%tr
%td= record[:foo][:foo][:name]
%td= record[:bar][:bar][:name]
= paginate @paginated_array
And that’s all it takes. Please comment with any comments or questions.
DevChix
