Skip to content

Loading…

Cutomizable out of range, fixes #297 #306

Closed
wants to merge 10 commits into from

4 participants

@gs
gs commented

Added out_of_range functionality which allows user to set what page should
be displayed when he go out of range. Allowed settings are :blank (by
defualt), :first, :last page.

@herrtreas herrtreas commented on an outdated diff
lib/kaminari/models/mongo_mapper_extension.rb
((10 lines not shown))
}
end
end
+
+ module ClassMethods
+ def calculate_offset(num)

Parts of this method are repeated over different classes. Maybe something to refactor and move into one dedicated place?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@incubus

+1

@yuki24
Collaborator

Thank you for the PR! This out_of_rage option sounds good to me. However, this PR will introduce an unexpected query when we use #page. It should make an additional query only when the result is empty. Can you change that behaviour?

@yuki24 yuki24 added a commit that closed this pull request
@yuki24 yuki24 Add #out_of_range? method
closes #306, and closes #366
8e48e45
@yuki24 yuki24 closed this in 8e48e45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
View
6 README.rdoc
@@ -80,6 +80,7 @@ You can configure the following default values by overriding these values using
right # 0 by default
page_method_name # :page by default
param_name # :page by default
+ out_of_range # :blank by default
There's a handy generator that generates the default configuration file into config/initializers directory.
Run the following generator command, then edit the generated file.
@@ -90,6 +91,11 @@ Run the following generator command, then edit the generated file.
You can change the method name `page` to `bonzo` or `plant` or whatever you like, in order to play nice with existing `page` method or association or scope or any other plugin that defines `page` method on your models.
+* changing +out_of_range+
+
+ You can define the page loaded when you go out of range.
+ Possibilities are `:blank`, `:first`, `:last`.
+
=== Configuring default +per_page+ value for each model
* +paginates_per+
View
1 lib/generators/kaminari/templates/kaminari_config.rb
@@ -7,4 +7,5 @@
# config.right = 0
# config.page_method_name = :page
# config.param_name = :page
+ # config.out_of_range = :blank
end
View
2 lib/kaminari/config.rb
@@ -24,6 +24,7 @@ class Configuration #:nodoc:
config_accessor :left
config_accessor :right
config_accessor :page_method_name
+ config_accessor :out_of_range
def param_name
config.param_name.respond_to?(:call) ? config.param_name.call : config.param_name
@@ -45,5 +46,6 @@ def param_name
config.right = 0
config.page_method_name = :page
config.param_name = :page
+ config.out_of_range = :blank #:first, :last
end
end
View
5 lib/kaminari/models/active_record_model_extension.rb
@@ -1,4 +1,5 @@
require 'kaminari/models/active_record_relation_methods'
+require 'kaminari/models/page_extensions'
module Kaminari
module ActiveRecordModelExtension
@@ -6,15 +7,17 @@ module ActiveRecordModelExtension
included do
self.send(:include, Kaminari::ConfigurationMethods)
+ self.send(:extend, Kaminari::PageExtensions)
# Fetch the values at the specified page number
# Model.page(5)
self.scope Kaminari.config.page_method_name, Proc.new {|num|
- limit(default_per_page).offset(default_per_page * ([num.to_i, 1].max - 1))
+ limit(default_per_page).offset(calculate_offset(num))
} do
include Kaminari::ActiveRecordRelationMethods
include Kaminari::PageScopeMethods
end
end
+
end
end
View
15 lib/kaminari/models/array_extension.rb
@@ -43,8 +43,21 @@ def total_count
# returns another chunk of the original array
def offset(num)
- self.class.new @_original_array, :limit => @_limit_value, :offset => num, :total_count => @_total_count
+ self.class.new @_original_array, :limit => @_limit_value, :offset => calculate_offset(num), :total_count => @_total_count
end
+
+ def calculate_offset(num)
+ goto_page = Kaminari.config.out_of_range
+
+ return 0 if goto_page == :first && out_of_range?(num)
+ return total_count - @_limit_value if goto_page == :last && out_of_range?(num)
+ num
+ end
+
+ def out_of_range?(num)
+ num + 1 > total_count
+ end
+
end
# Wrap an Array object to make it paginatable
View
24 lib/kaminari/models/data_mapper_extension.rb
@@ -6,9 +6,23 @@ module Paginatable
class_eval <<-RUBY, __FILE__, __LINE__ + 1
def #{Kaminari.config.page_method_name}(num = 1)
num = [num.to_i, 1].max - 1
- all(:limit => default_per_page, :offset => default_per_page * num).extend Paginating
+ all(:limit => default_per_page, :offset => calculate_offset(num)).extend Paginating
end
RUBY
+
+ def calculate_offset(num)
+ num = default_per_page * num
+ goto_page = Kaminari.config.out_of_range
+
+ return 0 if goto_page == :first && out_of_range?(num)
+ return total_count - default_per_page if goto_page == :last && out_of_range?(num)
+ num
+ end
+
+ def out_of_range?(num)
+ num + 1 > total_count
+ end
+
end
module Paginating
@@ -21,7 +35,7 @@ def all(options={})
def per(num)
super.extend Paginating
end
- end
+ end
module Collection
extend ActiveSupport::Concern
@@ -43,6 +57,10 @@ def limit(val)
def offset(val)
all(:offset => val)
end
- end
+
+ def total_count
+ all.count
+ end
+ end
end
end
View
5 lib/kaminari/models/mongo_mapper_extension.rb
@@ -1,4 +1,5 @@
require 'kaminari/models/plucky_criteria_methods'
+require 'kaminari/models/page_extensions'
module Kaminari
module MongoMapperExtension
@@ -7,12 +8,14 @@ module Document
include Kaminari::ConfigurationMethods
included do
+ self.send(:extend, Kaminari::PageExtensions)
# Fetch the values at the specified page number
# Model.page(5)
scope Kaminari.config.page_method_name, Proc.new {|num|
- limit(default_per_page).offset(default_per_page * ([num.to_i, 1].max - 1))
+ limit(default_per_page).offset(calculate_offset(num))
}
end
end
+
end
end
View
5 lib/kaminari/models/mongoid_extension.rb
@@ -1,4 +1,5 @@
require 'kaminari/models/mongoid_criteria_methods'
+require 'kaminari/models/page_extensions'
module Kaminari
module MongoidExtension
@@ -19,15 +20,17 @@ module Document
include Kaminari::ConfigurationMethods
included do
+ self.send(:extend, Kaminari::PageExtensions)
# Fetch the values at the specified page number
# Model.page(5)
scope Kaminari.config.page_method_name, Proc.new {|num|
- limit(default_per_page).offset(default_per_page * ([num.to_i, 1].max - 1))
+ limit(default_per_page).offset(calculate_offset(num))
} do
include Kaminari::MongoidCriteriaMethods
include Kaminari::PageScopeMethods
end
end
+
end
end
end
View
16 lib/kaminari/models/page_extensions.rb
@@ -0,0 +1,16 @@
+module Kaminari
+ module PageExtensions
+ def calculate_offset(num)
+ num = default_per_page * ([num.to_i, 1].max - 1)
+ goto_page = Kaminari.config.out_of_range
+
+ return 0 if goto_page == :first && out_of_range?(num)
+ return count - default_per_page if goto_page == :last && out_of_range?(num)
+ num
+ end
+
+ def out_of_range?(num)
+ num + 1 > count
+ end
+ end
+end
View
2 lib/kaminari/models/page_scope_methods.rb
@@ -4,7 +4,7 @@ module PageScopeMethods
# Model.page(3).per(10)
def per(num)
if (n = num.to_i) <= 0
- self
+ limit(0)
elsif max_per_page && max_per_page < n
limit(max_per_page).offset(offset_value / limit_value * max_per_page)
else
View
16 spec/config/config_spec.rb
@@ -32,6 +32,22 @@
end
end
+ describe 'out_of_range' do
+ context 'by default' do
+ its(:out_of_range) { should == :blank }
+ end
+
+ context "configure via config block" do
+ before do
+ Kaminari.configure {|c| c.out_of_range = :first}
+ end
+ its(:out_of_range) { should == :first }
+ after do
+ Kaminari.configure {|c| c.out_of_range = :blank}
+ end
+ end
+ end
+
describe 'window' do
context 'by default' do
its(:window) { should == 4 }
View
49 spec/models/active_record/scopes_spec.rb
@@ -11,6 +11,13 @@
it { should have(0).users }
end
+ shared_examples_for 'the last page' do
+ it { should have(25).users }
+ its(:current_page) { should == 4 }
+ its('first.name') { should == 'user076' }
+
+ end
+
describe Kaminari::ActiveRecordExtension do
before do
1.upto(100) {|i| User.create! :name => "user#{'%03d' % i}", :age => (i / 10)}
@@ -41,9 +48,34 @@
it_should_behave_like 'the first page'
end
- context 'page > max page' do
- subject { model_class.page 5 }
- it_should_behave_like 'blank page'
+ describe 'with out of range configuration' do
+ context 'set to :blank' do
+ subject { model_class.page 5 }
+ it { should == [] }
+ it_should_behave_like 'blank page'
+ end
+
+ context 'set to :first' do
+ before do
+ Kaminari.configure {|c| c.out_of_range = :first}
+ end
+ subject { model_class.page 5 }
+ it_should_behave_like 'the first page'
+ after do
+ Kaminari.configure {|c| c.out_of_range = :blank}
+ end
+ end
+
+ context 'set to :last' do
+ before do
+ Kaminari.configure {|c| c.out_of_range = :last}
+ end
+ subject { model_class.page 5 }
+ it_should_behave_like 'the last page'
+ after do
+ Kaminari.configure {|c| c.out_of_range = :blank}
+ end
+ end
end
describe 'ensure #order_values is preserved' do
@@ -66,6 +98,11 @@
it { should have(5).users }
its('first.name') { should == 'user002' }
end
+
+ context 'page 1 per 0 padding 1' do
+ subject { model_class.page(1).per(0).padding(1) }
+ it { should == [] }
+ end
end
describe '#total_pages' do
@@ -86,17 +123,17 @@
context 'per 0 (using default)' do
subject { model_class.page(50).per(0) }
- its(:total_pages) { should == 4 }
+ it { should == [] }
end
context 'per -1 (using default)' do
subject { model_class.page(5).per(-1) }
- its(:total_pages) { should == 4 }
+ it { should == [] }
end
context 'per "String value that can not be converted into Number" (using default)' do
subject { model_class.page(5).per('aho') }
- its(:total_pages) { should == 4 }
+ it { should == [] }
end
end
View
44 spec/models/array_spec.rb
@@ -42,11 +42,39 @@
it_should_behave_like 'the first page of array'
end
- context 'page > max page' do
- subject { array.page 5 }
- it_should_behave_like 'blank array page'
- end
- end
+ describe 'with out of range configuration' do
+ context 'set to :blank' do
+ subject { array.page 5 }
+ it { should == [] }
+ its(:current_page) { should == 5 }
+ end
+
+ context 'set to :first' do
+ before do
+ Kaminari.configure {|c| c.out_of_range = :first}
+ end
+ subject { array.page 5 }
+ it_should_behave_like 'the first page of array'
+ after do
+ Kaminari.configure {|c| c.out_of_range = :blank}
+ end
+ end
+
+ context 'set to :last' do
+ before do
+ Kaminari.configure {|c| c.out_of_range = :last}
+ end
+ subject { array.page 5 }
+ it { should have(25).users }
+ its(:current_page) { should == 4 }
+ its(:first) { should == 76 }
+ after do
+ Kaminari.configure {|c| c.out_of_range = :blank}
+ end
+ end
+ end
+
+ end
describe '#per' do
context 'page 1 per 5' do
@@ -74,17 +102,17 @@
context 'per 0 (using default)' do
subject { array.page(50).per(0) }
- its(:total_pages) { should == 4 }
+ it { should == [] }
end
context 'per -1 (using default)' do
subject { array.page(5).per(-1) }
- its(:total_pages) { should == 4 }
+ it { should == [] }
end
context 'per "String value that can not be converted into Number" (using default)' do
subject { array.page(5).per('aho') }
- its(:total_pages) { should == 4 }
+ it { should == [] }
end
end
View
39 spec/models/data_mapper/data_mapper_spec.rb
@@ -88,6 +88,45 @@
its(:total_count) { should == User.count(:age.gt => 60) }
its(:total_pages) { should == 2 }
end
+
+ describe 'with out of range configuration' do
+ context 'set to :blank' do
+ subject { User.page(5).all }
+ it { should == [] }
+ its(:current_page) { should == 5 }
+ its('query.limit') { should == 25 }
+ its('query.offset') { should == 100 }
+ end
+
+ context 'set to :first' do
+ before do
+ Kaminari.configure {|c| c.out_of_range = :first}
+ end
+ subject { User.page(5).all }
+ its(:current_page) { should == 1 }
+ its('query.limit') { should == 25 }
+ its('query.offset') { should == 0 }
+ after do
+ Kaminari.configure {|c| c.out_of_range = :blank}
+ end
+ end
+
+ context 'set to :last' do
+ before do
+ Kaminari.configure {|c| c.out_of_range = :last}
+ end
+ subject { User.page(5).all }
+ its(:current_page) { should == 4 }
+ its('query.limit') { should == 25 }
+ its('query.offset') { should == 75 }
+ after do
+ Kaminari.configure {|c| c.out_of_range = :blank}
+ end
+ end
+ end
+
+
+
end
describe '#per' do
View
48 spec/models/mongo_mapper/mongo_mapper_spec.rb
@@ -4,7 +4,7 @@
describe Kaminari::MongoMapperExtension do
before(:each) do
User.destroy_all
- 41.times { User.create!({:salary => 1}) }
+ 100.times { User.create!({:salary => 1}) }
end
describe '#page' do
@@ -13,7 +13,7 @@
it { should be_a Plucky::Query }
its(:current_page) { should == 1 }
its(:limit_value) { should == 25 }
- its(:total_pages) { should == 2 }
+ its(:total_pages) { should == 4 }
it { should skip(0) }
end
@@ -22,7 +22,7 @@
it { should be_a Plucky::Query }
its(:current_page) { should == 2 }
its(:limit_value) { should == 25 }
- its(:total_pages) { should == 2 }
+ its(:total_pages) { should == 4 }
it { should skip 25 }
end
@@ -31,7 +31,7 @@
it { should be_a Plucky::Query }
its(:current_page) { should == 1 }
its(:limit_value) { should == 25 }
- its(:total_pages) { should == 2 }
+ its(:total_pages) { should == 4 }
it { should skip 0 }
end
@@ -43,7 +43,7 @@
subject { User.where(:salary => 1).page 2 }
its(:current_page) { should == 2 }
its(:limit_value) { should == 25 }
- its(:total_pages) { should == 2 }
+ its(:total_pages) { should == 4 }
it { should skip 25 }
end
@@ -55,17 +55,51 @@
subject { User.page(2).where(:salary => 1) }
its(:current_page) { should == 2 }
its(:limit_value) { should == 25 }
- its(:total_pages) { should == 2 }
+ its(:total_pages) { should == 4 }
it { should skip 25 }
end
end
+ describe 'with out of range configuration' do
+ context 'set to :blank' do
+ subject { User.page(5) }
+ # not sure how to return empty array when out of range
+ #it { should == [] }
+ its(:current_page) { should == 5 }
+ it { should skip 100 }
+ end
+
+ context 'set to :first' do
+ before do
+ Kaminari.configure {|c| c.out_of_range = :first}
+ end
+ subject { User.page(5) }
+ its(:current_page) { should == 1 }
+ it { should skip 0 }
+ after do
+ Kaminari.configure {|c| c.out_of_range = :blank}
+ end
+ end
+
+ context 'set to :last' do
+ before do
+ Kaminari.configure {|c| c.out_of_range = :last}
+ end
+ subject { User.page(5) }
+ its(:current_page) { should == 4 }
+ it { should skip 75 }
+ after do
+ Kaminari.configure {|c| c.out_of_range = :blank}
+ end
+ end
+ end
+
describe '#per' do
subject { User.page(2).per(10) }
it { should be_a Plucky::Query }
its(:current_page) { should == 2 }
its(:limit_value) { should == 10 }
- its(:total_pages) { should == 5 }
+ its(:total_pages) { should == 10 }
it { should skip 10 }
end
end
View
45 spec/models/mongoid/mongoid_spec.rb
@@ -3,7 +3,7 @@
if defined? Mongoid
describe Kaminari::MongoidExtension do
before(:each) do
- 41.times do
+ 100.times do
User.create!({:salary => 1})
end
end
@@ -15,7 +15,7 @@
it { should be_a Mongoid::Criteria }
its(:current_page) { should == 1 }
its(:limit_value) { should == 25 }
- its(:total_pages) { should == 2 }
+ its(:total_pages) { should == 4 }
it { should skip(0) }
end
@@ -24,7 +24,7 @@
it { should be_a Mongoid::Criteria }
its(:current_page) { should == 2 }
its(:limit_value) { should == 25 }
- its(:total_pages) { should == 2 }
+ its(:total_pages) { should == 4 }
it { should skip 25 }
end
@@ -33,7 +33,7 @@
it { should be_a Mongoid::Criteria }
its(:current_page) { should == 1 }
its(:limit_value) { should == 25 }
- its(:total_pages) { should == 2 }
+ its(:total_pages) { should == 4 }
it { should skip 0 }
end
@@ -45,7 +45,7 @@
end
its(:current_page) { should == 2 }
its(:limit_value) { should == 25 }
- its(:total_pages) { should == 2 }
+ its(:total_pages) { should == 4 }
it { should skip 25 }
end
@@ -58,6 +58,39 @@
subject { User.page(2).where(:salary => 1) }
it_should_behave_like 'complete valid pagination'
end
+
+ describe 'with out of range configuration' do
+ context 'set to :blank' do
+ subject { User.page(5) }
+ it { should == [] }
+ its(:current_page) { should == 5 }
+ end
+
+ context 'set to :first' do
+ before do
+ Kaminari.configure {|c| c.out_of_range = :first}
+ end
+ subject { User.page(5) }
+ its(:current_page) { should == 1 }
+ it { should skip 0 }
+ after do
+ Kaminari.configure {|c| c.out_of_range = :blank}
+ end
+ end
+
+ context 'set to :last' do
+ before do
+ Kaminari.configure {|c| c.out_of_range = :last}
+ end
+ subject { User.page(5) }
+ its(:current_page) { should == 4 }
+ it { should skip 75 }
+ after do
+ Kaminari.configure {|c| c.out_of_range = :blank}
+ end
+ end
+ end
+
end
describe '#per' do
@@ -65,7 +98,7 @@
it { should be_a Mongoid::Criteria }
its(:current_page) { should == 2 }
its(:limit_value) { should == 10 }
- its(:total_pages) { should == 5 }
+ its(:total_pages) { should == 10 }
it { should skip 10 }
end
Something went wrong with that request. Please try again.