Adding initial pagination support based on kaminari

This commit is contained in:
Sam Lown 2011-02-27 19:06:37 +01:00
parent 6a896c27b3
commit 6723564969
4 changed files with 143 additions and 21 deletions

View file

@ -21,6 +21,8 @@ module CouchRest
module ClassMethods module ClassMethods
# Add views and other design document features
# to the current model.
def design(*args, &block) def design(*args, &block)
mapper = DesignMapper.new(self) mapper = DesignMapper.new(self)
mapper.create_view_method(:all) mapper.create_view_method(:all)
@ -30,6 +32,23 @@ module CouchRest
req_design_doc_refresh req_design_doc_refresh
end end
# Override the default page pagination value:
#
# class Person < CouchRest::Model::Base
# paginates_per 10
# end
#
def paginates_per(val)
@_default_per_page = val
end
# The models number of documents to return
# by default when performing pagination.
# Returns 25 unless explicitly overridden via <tt>paginates_per</tt>
def default_per_page
@_default_per_page || 25
end
end end
# #

View file

@ -14,7 +14,7 @@ module CouchRest
class View class View
include Enumerable include Enumerable
attr_accessor :model, :name, :query, :result attr_accessor :owner, :model, :name, :query, :result
# Initialize a new View object. This method should not be called from # Initialize a new View object. This method should not be called from
# outside CouchRest Model. # outside CouchRest Model.
@ -22,11 +22,13 @@ module CouchRest
if parent.is_a?(Class) && parent < CouchRest::Model::Base if parent.is_a?(Class) && parent < CouchRest::Model::Base
raise "Name must be provided for view to be initialized" if name.nil? raise "Name must be provided for view to be initialized" if name.nil?
self.model = parent self.model = parent
self.owner = parent
self.name = name.to_s self.name = name.to_s
# Default options: # Default options:
self.query = { :reduce => false } self.query = { :reduce => false }
elsif parent.is_a?(self.class) elsif parent.is_a?(self.class)
self.model = (new_query.delete(:proxy) || parent.model) self.model = (new_query.delete(:proxy) || parent.model)
self.owner = parent.owner
self.name = parent.name self.name = parent.name
self.query = parent.query.dup self.query = parent.query.dup
else else
@ -175,7 +177,6 @@ module CouchRest
# are combined in an incorrect fashion. # are combined in an incorrect fashion.
# #
# Find all entries in the index whose key matches the value provided. # Find all entries in the index whose key matches the value provided.
# #
# Cannot be used when the +#startkey+ or +#endkey+ have been set. # Cannot be used when the +#startkey+ or +#endkey+ have been set.
@ -300,6 +301,46 @@ module CouchRest
@docs = nil @docs = nil
end end
# == Kaminari compatible pagination support
#
# Based on the really simple support for scoped pagination in the
# the Kaminari gem, we provide compatible methods here to perform
# the same actions you'd expect.
#
alias :total_count :count
def page(page)
limit(owner.default_per_page).skip(owner.default_per_page * ([page.to_i, 1].max - 1))
end
def per(num)
raise "View#page must be called before #per!" if limit_value.nil? || offset_value.nil?
if (n = num.to_i) <= 0
self
else
limit(num).skip(offset_value / limit_value * n)
end
end
def offset_value
query[:skip]
end
def limit_value
query[:limit]
end
def num_pages
(total_count.to_f / limit_value).ceil
end
def current_page
(offset_value / limit_value) + 1
end
protected protected
def include_docs! def include_docs!

View file

@ -541,6 +541,53 @@ describe "Design View" do
end end
describe "pagination methods" do
describe "#total_count" do
it "should be an alias for count" do
@obj.method(:total_count).should eql(@obj.method(:count))
end
end
describe "#page" do
it "should call limit and skip" do
@obj.should_receive(:limit).with(25).and_return(@obj)
@obj.should_receive(:skip).with(25).and_return(@obj)
@obj.page(2)
end
end
describe "#per" do
it "should raise an error if page not called before hand" do
lambda { @obj.per(12) }.should raise_error
end
it "should not do anything if number less than or eql 0" do
view = @obj.page(1)
view.per(0).should eql(view)
end
it "should set limit and update skip" do
view = @obj.page(2).per(10)
view.query[:skip].should eql(10)
view.query[:limit].should eql(10)
end
end
describe "#num_pages" do
it "should use total_count and limit_value" do
@obj.should_receive(:total_count).and_return(200)
@obj.should_receive(:limit_value).and_return(25)
@obj.num_pages.should eql(8)
end
end
describe "#current_page" do
it "should use offset and limit" do
@obj.should_receive(:offset_value).and_return(25)
@obj.should_receive(:limit_value).and_return(25)
@obj.current_page.should eql(2)
end
end
end
end end
end end

View file

@ -10,8 +10,9 @@ describe "Design" do
DesignModel.respond_to?(:design).should be_true DesignModel.respond_to?(:design).should be_true
end end
describe ".design" do describe "class methods" do
describe ".design" do
before :each do before :each do
@mapper = mock('DesignMapper') @mapper = mock('DesignMapper')
@mapper.stub!(:create_view_method) @mapper.stub!(:create_view_method)
@ -37,6 +38,20 @@ describe "Design" do
end end
describe "default_per_page" do
it "should return 25 default" do
DesignModel.default_per_page.should eql(25)
end
end
describe ".paginates_per" do
it "should set the default per page value" do
DesignModel.paginates_per(21)
DesignModel.default_per_page.should eql(21)
end
end
end
describe "DesignMapper" do describe "DesignMapper" do
before :all do before :all do