Docs for pagination, not including docs in reduce and raising errors when cannot include docs

This commit is contained in:
Sam Lown 2011-02-27 20:18:19 +01:00
parent 6723564969
commit 0a35be7167
3 changed files with 82 additions and 12 deletions

View file

@ -364,6 +364,21 @@ You'll see that this new syntax requires all views to be defined inside a design
puts "Tag: #{row.key} Uses: #{row.value}"
end
#### Pagination
The view objects have built in support for pagination based on the [kaminari](https://github.com/amatsuda/kaminari) gem. Methods are provided to match those required by the library to peform pagination.
For your view to support paginating the results, it must use a reduce function that provides a total count of the documents in the result set. By default, auto-generated views include a reduce function that supports this.
Use pagination as follows:
# Prepare a query:
@posts = Post.by_title.page(params[:page]).per(10)
# In your view, with the kaminari gem loaded:
paginate @posts
## Assocations
Two types at the moment:

View file

@ -249,7 +249,7 @@ module CouchRest
# will fail.
def reduce
raise "Cannot reduce a view without a reduce method" unless can_reduce?
update_query(:reduce => true)
update_query(:reduce => true, :include_docs => nil)
end
# Control whether the reduce function reduces to a set of distinct keys
@ -308,8 +308,6 @@ module CouchRest
# 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
@ -323,6 +321,10 @@ module CouchRest
end
end
def total_count
@total_count ||= limit(nil).skip(nil).count
end
def offset_value
query[:skip]
end
@ -344,6 +346,7 @@ module CouchRest
protected
def include_docs!
raise "Cannot include documents in view that has been reduced!" if query[:reduce]
reset! if result && !include_docs?
query[:include_docs] = true
self
@ -376,7 +379,7 @@ module CouchRest
# Remove the reduce value if its not needed
query.delete(:reduce) unless can_reduce?
begin
self.result = model.design_doc.view_on(use_database, name, query)
self.result = model.design_doc.view_on(use_database, name, query.reject{|k,v| v.nil?})
rescue RestClient::ResourceNotFound => e
if retryable
model.save_design_doc(use_database)
@ -474,7 +477,7 @@ module CouchRest
def doc
return model.build_from_database(self['doc']) if self['doc']
doc_id = (value.is_a?(Hash) && value['_id']) ? value['_id'] : self.id
model.get(doc_id)
doc_id ? model.get(doc_id) : nil
end
end

View file

@ -367,7 +367,7 @@ describe "Design View" do
describe "#reduce" do
it "should update query" do
@obj.should_receive(:can_reduce?).and_return(true)
@obj.should_receive(:update_query).with({:reduce => true})
@obj.should_receive(:update_query).with({:reduce => true, :include_docs => nil})
@obj.reduce
end
it "should raise error if query cannot be reduced" do
@ -442,6 +442,10 @@ describe "Design View" do
@obj.send(:include_docs!)
@obj.query[:include_docs].should be_true
end
it "should raise an error if view is reduced" do
@obj.query[:reduce] = true
lambda { @obj.send(:include_docs!) }.should raise_error
end
end
describe "#include_docs?" do
@ -538,17 +542,19 @@ describe "Design View" do
lambda { @obj.send(:execute) }.should raise_error(RestClient::ResourceNotFound)
end
it "should remove nil values from query" do
@obj.should_receive(:can_reduce?).and_return(true)
@obj.stub!(:use_database).and_return('database')
@obj.query = {:reduce => true, :limit => nil, :skip => nil}
@design_doc.should_receive(:view_on).with('database', 'test_view', {:reduce => true})
@obj.send(:execute)
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)
@ -572,6 +578,16 @@ describe "Design View" do
end
end
describe "#total_count" do
it "set limit and skip to nill and perform count" do
@obj.should_receive(:limit).with(nil).and_return(@obj)
@obj.should_receive(:skip).with(nil).and_return(@obj)
@obj.should_receive(:count).and_return(5)
@obj.total_count.should eql(5)
@obj.total_count.should eql(5) # Second to test caching
end
end
describe "#num_pages" do
it "should use total_count and limit_value" do
@obj.should_receive(:total_count).and_return(200)
@ -657,6 +673,15 @@ describe "Design View" do
obj.doc.should eql(doc)
end
it "should try to return nil for document if none available" do
hash = {'value' => 23} # simulate reduce
obj = @klass.new(hash, DesignViewModel)
doc = mock('DesignViewModel')
obj.model.should_not_receive(:get)
obj.doc.should be_nil
end
end
end
@ -708,6 +733,33 @@ describe "Design View" do
end
end
describe "pagination" do
before :all do
DesignViewModel.paginates_per 3
end
before :each do
@view = DesignViewModel.by_name.page(1)
end
it "should calculate number of pages" do
@view.num_pages.should eql(2)
end
it "should return results from first page" do
@view.all.first.name.should eql('Judith')
@view.all.last.name.should eql('Peter')
end
it "should return results from second page" do
@view.page(2).all.first.name.should eql('Sam')
@view.page(2).all.last.name.should eql('Vilma')
end
it "should allow overriding per page count" do
@view = @view.per(10)
@view.num_pages.should eql(1)
@view.all.last.name.should eql('Vilma')
end
end
end