Skip to content

Commit

Permalink
add count
Browse files Browse the repository at this point in the history
  • Loading branch information
dorren committed Jun 3, 2010
1 parent 7d7ab9d commit 3efa0c8
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 37 deletions.
42 changes: 30 additions & 12 deletions README.rdoc
@@ -1,17 +1,35 @@
= active_resource_pagination

Description goes here.
This gem adds pagination support to Active Resource.

== Note on Patches/Pull Requests

* Fork the project.
* Make your feature addition or bug fix.
* Add tests for it. This is important so I don't break it in a
future version unintentionally.
* Commit, do not mess with rakefile, version, or history.
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
* Send me a pull request. Bonus points for topic branches.
== Sample Usage
# Article is a resource model

Article.paginate
Article.paginate(:page => 2, :per_page => 20)
Article.paginate(:page => 2, :per_page => 20, :total_entries => 123)
Article.paginate(:page => 2, :per_page => 20, :params => {:year => 2010})

== Copyright

Copyright (c) 2010 Dorren Chen. See LICENSE for details.

== Configuration

To set default per_page value for all resources. you can do
ActiveResource::Base.per_page = 20 # in config/environment or initializers

or to implement per_page() in your resource class.


== Detail

When doing the pagination query, it converts :page and :per_page parameters to :offset and :limit to the actual find method, assure your backend honors :offset and :limit parameters.

Article.paginate(:page => 2, :per_page => 20, :params => {:year => 2010}) # is translated into 2 request calls

Article.find(:all, :params => {:year => 2010, :offset => 40, :limit => 20})
Article.find(:one, :from => :count, :params => {:year => 2010})

* If you pass in the :total_entries parameter, Model.count() will not be called.
* You can always override count() if default doesn't suit your need.
* If you don't pass in the :total_entries parameter, Model.count() will be called,
unless result count > per_page, then this method automatically sets the count from the result.
2 changes: 1 addition & 1 deletion VERSION
@@ -1 +1 @@
0.0.5
0.0.6
50 changes: 30 additions & 20 deletions lib/active_resource_pagination.rb
@@ -1,14 +1,15 @@
require 'active_resource'
require 'will_paginate'
require 'hash_ext'

module ActiveResource
# This adds pagination support to Active Resource. For example
#
# Article.paginate
# Article.paginate(:page => 2, :per_page => 20)
# Article.paginate(:page => 2, :per_page => 20, :total_entries => 123)
# Article.paginate(:from => :most_popular,
# :params => {:year => 2010, :page => 1, :per_page => 20})
# Article.paginate(:page => 2, :per_page => 20,
# :from => :most_popular, :params => {:year => 2010})
#
# To set default per_page value for all resources. you can do
# ActiveResource::Base.per_page = 20 # do this in config/environment or initializers
Expand All @@ -22,7 +23,18 @@ def self.included(base)
base.extend ClassMethods
end

module ClassMethods
module ClassMethods
# returns the total_entries count for the paginated result.
#
# method expects the returned xml to be in the format of:
# <?xml version="1.0" encoding="UTF-8"?>
# <hash>
# <count type="integer">5</count>
# </hash>
def count(options)
find(:one, :from => :count, :params => options).count.to_i
end

# use same method signatures as find(), optional additional parameters:
# page - current page
# per_pape - entries per page
Expand All @@ -32,30 +44,28 @@ module ClassMethods
# sets the total_entry count from the result. Otherwise, you have to pass in the
# :total_entries count value manually.
def paginate(options={})
if options[:params]
pg_params = options[:params] = with_default_params(options[:params])
else
pg_params = options = with_default_params(options)
end

page = pg_params[:page]
per_page = pg_params[:per_page]
pg_options, find_options = options.partition{|k,v| [:page, :per_page, :total_entries].include?(k)}

arr = find(:all, options) || []
total_entries = options[:total_entries] || arr.size
WillPaginate::Collection.create(page, per_page, total_entries) do |pager|
pg_options[:page] ||= 1
pg_options[:per_page] ||= per_page

WillPaginate::Collection.create(pg_options[:page], pg_options[:per_page], pg_options[:total_entries]) do |pager|
find_options[:params] = (find_options[:params] || {}).merge(:offset => pager.offset, :limit => pager.per_page)

arr = find(:all, find_options) || []
if pg_options[:total_entries]
pager.total_entries = pg_options[:total_entries]
else
pager.total_entries = arr.size > pager.per_page ? arr.size : count(find_options[:params])
end

if arr.size > per_page
pager.replace arr[pager.offset, pager.per_page]
else
pager.replace arr
end
end
end

protected
def with_default_params(options)
{:page => 1, :per_page => per_page}.merge(options.reject{|k, v| v.blank?})
end
end
end
end
end
Expand Down
11 changes: 11 additions & 0 deletions lib/hash_ext.rb
@@ -0,0 +1,11 @@
class Hash
# similar to Enumerable partition() method, separate one hash into 2 based on the passed in test condition.
def partition(&block)
h = dup
h2 = {}
each{|k, v|
h2[k] = h.delete(k) if block.call(k,v)
}
[h2, h]
end
end
31 changes: 27 additions & 4 deletions spec/active_resource_pagination_spec.rb
Expand Up @@ -53,11 +53,14 @@ def self.from_xml(xml)
end

describe "when backend does not paginate" do
before(:each) do
Article.should_receive(:find).and_return(@articles)
before(:each) do
end

it "should paginate with no params" do
pg_params = {:offset => 0, :limit => Article.per_page}
Article.should_receive(:find).with(:all, :params => pg_params).and_return(@articles)
Article.should_not_receive(:count).with(pg_params) # since articles count > per_page

col = Article.paginate
col.current_page.should == 1
col.per_page.should == Article.per_page
Expand All @@ -66,16 +69,36 @@ def self.from_xml(xml)
end

it "should paginate with params" do
pg_params = {:offset => Article.per_page, :limit => Article.per_page}
Article.should_receive(:find).with(:all, :params => pg_params).and_return(@articles)
Article.should_not_receive(:count).with(pg_params)

col = Article.paginate(:page => 2)
col.current_page.should == 2
col.first.should == @articles[3]
end
end

describe "when backend do paginate" do
it "should paginate when backend returns paginated entries" do
it "should paginate without :total_entries param" do
@articles = @articles[2,2] # returns 2nd page result
pg_params = {:offset => Article.per_page, :limit => Article.per_page}
Article.should_receive(:find).with(:all, :params => pg_params).and_return(@articles)
Article.should_receive(:count).with(pg_params).and_return(5)

col = Article.paginate(:page => 2)
col.current_page.should == 2
col.per_page.should == Article.per_page
col.total_entries.should == 5
col.total_pages.should == 3 # (col.total_entries / col.per_page.to_f).ceil
col.first.should == @articles.first
end

it "should paginate with :total_entries param" do
@articles = @articles[2,2] # returns 2nd page result
Article.should_receive(:find).and_return(@articles)
pg_params = {:offset => Article.per_page, :limit => Article.per_page}
Article.should_receive(:find).with(:all, :params => pg_params).and_return(@articles)
Article.should_not_receive(:count)

col = Article.paginate(:page => 2, :total_entries => 5)
col.current_page.should == 2
Expand Down
11 changes: 11 additions & 0 deletions spec/hash_ext_spec.rb
@@ -0,0 +1,11 @@
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')

describe Hash do
describe "#partition" do
it "should partition" do
h1, h2 = {'a' => 1, 'b' => 2, 'c' => 10, 'd' => 20}.partition{|k, v| v >= 10}
h1.should == {'c' => 10, 'd' => 20}
h2.should == {'a' => 1, 'b' => 2}
end
end
end

0 comments on commit 3efa0c8

Please sign in to comment.