kaminari 0.12.4 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of kaminari might be problematic. Click here for more details.

Files changed (49) hide show
  1. data/.travis.yml +6 -0
  2. data/CHANGELOG +62 -0
  3. data/{LICENSE.txt → MIT-LICENSE} +0 -0
  4. data/README.rdoc +81 -14
  5. data/kaminari.gemspec +18 -5
  6. data/lib/generators/kaminari/templates/kaminari_config.rb +1 -0
  7. data/lib/generators/kaminari/views_generator.rb +1 -1
  8. data/lib/kaminari.rb +73 -2
  9. data/lib/kaminari/config.rb +3 -1
  10. data/lib/kaminari/helpers/action_view_extension.rb +74 -20
  11. data/lib/kaminari/helpers/paginator.rb +23 -5
  12. data/lib/kaminari/helpers/sinatra_helpers.rb +119 -0
  13. data/lib/kaminari/hooks.rb +35 -0
  14. data/lib/kaminari/models/active_record_extension.rb +12 -15
  15. data/lib/kaminari/models/active_record_model_extension.rb +20 -0
  16. data/lib/kaminari/models/active_record_relation_methods.rb +23 -13
  17. data/lib/kaminari/models/array_extension.rb +33 -15
  18. data/lib/kaminari/models/data_mapper_collection_methods.rb +15 -0
  19. data/lib/kaminari/models/data_mapper_extension.rb +48 -0
  20. data/lib/kaminari/models/mongo_mapper_extension.rb +3 -3
  21. data/lib/kaminari/models/mongoid_criteria_methods.rb +15 -10
  22. data/lib/kaminari/models/mongoid_extension.rb +7 -5
  23. data/lib/kaminari/models/page_scope_methods.rb +27 -26
  24. data/lib/kaminari/models/plucky_criteria_methods.rb +9 -12
  25. data/lib/kaminari/railtie.rb +2 -31
  26. data/lib/kaminari/sinatra.rb +13 -0
  27. data/lib/kaminari/version.rb +1 -1
  28. data/spec/config/config_spec.rb +1 -1
  29. data/spec/fake_app.rb +4 -0
  30. data/spec/fake_gem.rb +6 -0
  31. data/spec/helpers/action_view_extension_spec.rb +105 -10
  32. data/spec/helpers/helpers_spec.rb +1 -1
  33. data/spec/helpers/sinatra_helpers_spec.rb +174 -0
  34. data/spec/helpers/tags_spec.rb +1 -1
  35. data/spec/models/active_record_relation_methods_spec.rb +1 -1
  36. data/spec/models/array_spec.rb +17 -1
  37. data/spec/models/data_mapper_spec.rb +181 -0
  38. data/spec/models/default_per_page_spec.rb +1 -1
  39. data/spec/models/mongo_mapper_spec.rb +14 -11
  40. data/spec/models/mongoid_spec.rb +63 -6
  41. data/spec/models/scopes_spec.rb +154 -140
  42. data/spec/{acceptance → requests}/users_spec.rb +3 -2
  43. data/spec/spec_helper.rb +3 -1
  44. data/spec/spec_helper_for_sinatra.rb +13 -0
  45. data/spec/support/matchers.rb +6 -0
  46. metadata +263 -170
  47. data/spec/acceptance/acceptance_helper.rb +0 -5
  48. data/spec/acceptance/support/helpers.rb +0 -5
  49. data/spec/acceptance/support/paths.rb +0 -9
@@ -1,4 +1,4 @@
1
- require File.join(File.dirname(__FILE__), 'plucky_criteria_methods')
1
+ require 'kaminari/models/plucky_criteria_methods'
2
2
 
3
3
  module Kaminari
4
4
  module MongoMapperExtension
@@ -9,10 +9,10 @@ module Kaminari
9
9
  included do
10
10
  # Fetch the values at the specified page number
11
11
  # Model.page(5)
12
- scope :page, Proc.new {|num|
12
+ scope Kaminari.config.page_method_name, Proc.new {|num|
13
13
  limit(default_per_page).offset(default_per_page * ([num.to_i, 1].max - 1))
14
14
  }
15
15
  end
16
16
  end
17
17
  end
18
- end
18
+ end
@@ -1,17 +1,22 @@
1
1
  module Kaminari
2
2
  module MongoidCriteriaMethods
3
- extend ActiveSupport::Concern
4
- module InstanceMethods
5
- def limit_value #:nodoc:
6
- options[:limit]
7
- end
3
+ def limit_value #:nodoc:
4
+ options[:limit]
5
+ end
8
6
 
9
- def offset_value #:nodoc:
10
- options[:skip]
11
- end
7
+ def offset_value #:nodoc:
8
+ options[:skip]
9
+ end
10
+
11
+ def total_count #:nodoc:
12
+ embedded? ? unpage.count : count
13
+ end
12
14
 
13
- def total_count #:nodoc:
14
- count
15
+ private
16
+ def unpage
17
+ clone.tap do |crit|
18
+ crit.options.delete :limit
19
+ crit.options.delete :skip
15
20
  end
16
21
  end
17
22
  end
@@ -1,4 +1,4 @@
1
- require File.join(File.dirname(__FILE__), 'mongoid_criteria_methods')
1
+ require 'kaminari/models/mongoid_criteria_methods'
2
2
 
3
3
  module Kaminari
4
4
  module MongoidExtension
@@ -6,9 +6,11 @@ module Kaminari
6
6
  extend ActiveSupport::Concern
7
7
 
8
8
  included do
9
- def page(*args)
10
- self.klass.page(*args).criteria.merge(self)
11
- end
9
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
10
+ def #{Kaminari.config.page_method_name}(*args)
11
+ super(*args).criteria.merge(self)
12
+ end
13
+ RUBY
12
14
  end
13
15
  end
14
16
 
@@ -19,7 +21,7 @@ module Kaminari
19
21
  included do
20
22
  # Fetch the values at the specified page number
21
23
  # Model.page(5)
22
- scope :page, Proc.new {|num|
24
+ scope Kaminari.config.page_method_name, Proc.new {|num|
23
25
  limit(default_per_page).offset(default_per_page * ([num.to_i, 1].max - 1))
24
26
  } do
25
27
  include Kaminari::MongoidCriteriaMethods
@@ -1,36 +1,37 @@
1
1
  module Kaminari
2
2
  module PageScopeMethods
3
- extend ActiveSupport::Concern
4
- module InstanceMethods
5
- # Specify the <tt>per_page</tt> value for the preceding <tt>page</tt> scope
6
- # Model.page(3).per(10)
7
- def per(num)
8
- if (n = num.to_i) <= 0
9
- self
10
- else
11
- limit(n).offset(offset_value / limit_value * n)
12
- end
3
+ # Specify the <tt>per_page</tt> value for the preceding <tt>page</tt> scope
4
+ # Model.page(3).per(10)
5
+ def per(num)
6
+ if (n = num.to_i) <= 0
7
+ self
8
+ else
9
+ limit(n).offset(offset_value / limit_value * n)
13
10
  end
11
+ end
14
12
 
15
- # Total number of pages
16
- def num_pages
17
- (total_count.to_f / limit_value).ceil
18
- end
13
+ def padding(num)
14
+ offset(offset_value + num.to_i)
15
+ end
19
16
 
20
- # Current page number
21
- def current_page
22
- (offset_value / limit_value) + 1
23
- end
17
+ # Total number of pages
18
+ def num_pages
19
+ (total_count.to_f / limit_value).ceil
20
+ end
24
21
 
25
- # First page of the collection ?
26
- def first_page?
27
- current_page == 1
28
- end
22
+ # Current page number
23
+ def current_page
24
+ (offset_value / limit_value) + 1
25
+ end
29
26
 
30
- # Last page of the collection?
31
- def last_page?
32
- current_page >= num_pages
33
- end
27
+ # First page of the collection ?
28
+ def first_page?
29
+ current_page == 1
30
+ end
31
+
32
+ # Last page of the collection?
33
+ def last_page?
34
+ current_page >= num_pages
34
35
  end
35
36
  end
36
37
  end
@@ -1,18 +1,15 @@
1
1
  module Kaminari
2
2
  module PluckyCriteriaMethods
3
- extend ActiveSupport::Concern
4
- module InstanceMethods
5
- def limit_value #:nodoc:
6
- options[:limit]
7
- end
3
+ def limit_value #:nodoc:
4
+ options[:limit]
5
+ end
8
6
 
9
- def offset_value #:nodoc:
10
- options[:skip]
11
- end
7
+ def offset_value #:nodoc:
8
+ options[:skip]
9
+ end
12
10
 
13
- def total_count #:nodoc:
14
- count
15
- end
11
+ def total_count #:nodoc:
12
+ count
16
13
  end
17
14
  end
18
- end
15
+ end
@@ -1,36 +1,7 @@
1
- require 'rails'
2
- # ensure ORMs are loaded *before* initializing Kaminari
3
- begin; require 'mongoid'; rescue LoadError; end
4
- begin; require 'mongo_mapper'; rescue LoadError; end
5
-
6
- require File.join(File.dirname(__FILE__), 'config')
7
- require File.join(File.dirname(__FILE__), 'helpers/action_view_extension')
8
- require File.join(File.dirname(__FILE__), 'helpers/paginator')
9
- require File.join(File.dirname(__FILE__), 'models/page_scope_methods')
10
- require File.join(File.dirname(__FILE__), 'models/configuration_methods')
11
-
12
1
  module Kaminari
13
2
  class Railtie < ::Rails::Railtie #:nodoc:
14
3
  initializer 'kaminari' do |app|
15
- ActiveSupport.on_load(:active_record) do
16
- require File.join(File.dirname(__FILE__), 'models/active_record_extension')
17
- ::ActiveRecord::Base.send :include, Kaminari::ActiveRecordExtension
18
- end
19
- if defined? ::Mongoid
20
- require File.join(File.dirname(__FILE__), 'models/mongoid_extension')
21
- ::Mongoid::Document.send :include, Kaminari::MongoidExtension::Document
22
- ::Mongoid::Criteria.send :include, Kaminari::MongoidExtension::Criteria
23
- end
24
- if defined? ::MongoMapper
25
- require File.join(File.dirname(__FILE__), 'models/mongo_mapper_extension')
26
- ::MongoMapper::Document.send :include, Kaminari::MongoMapperExtension::Document
27
- ::Plucky::Query.send :include, Kaminari::PluckyCriteriaMethods
28
- ::Plucky::Query.send :include, Kaminari::PageScopeMethods
29
- end
30
- require File.join(File.dirname(__FILE__), 'models/array_extension')
31
- ActiveSupport.on_load(:action_view) do
32
- ::ActionView::Base.send :include, Kaminari::ActionViewExtension
33
- end
4
+ Kaminari::Hooks.init!
34
5
  end
35
6
  end
36
- end
7
+ end
@@ -0,0 +1,13 @@
1
+ begin
2
+ require 'sinatra/base'
3
+ rescue LoadError
4
+ raise LoadError, "couldn't load `sinatra/base`, check out if appropriately bundled sinatra gem?"
5
+ end
6
+
7
+ require 'kaminari'
8
+ module Kaminari::Helpers
9
+ end
10
+ require 'kaminari/helpers/sinatra_helpers'
11
+
12
+ Kaminari::Hooks.init!
13
+
@@ -1,3 +1,3 @@
1
1
  module Kaminari
2
- VERSION = '0.12.4'
2
+ VERSION = '0.13.0'
3
3
  end
@@ -1,4 +1,4 @@
1
- require File.expand_path('../spec_helper', File.dirname(__FILE__))
1
+ require 'spec_helper'
2
2
 
3
3
  describe Kaminari::Configuration do
4
4
  subject { Kaminari.config }
data/spec/fake_app.rb CHANGED
@@ -53,6 +53,9 @@ class Book < ActiveRecord::Base
53
53
  has_many :authors, :through => :authorships, :source => :user
54
54
  has_many :readers, :through => :readerships, :source => :user
55
55
  end
56
+ # a model that is a descendant of AR::Base but doesn't directly inherit AR::Base
57
+ class Admin < User
58
+ end
56
59
 
57
60
  # controllers
58
61
  class ApplicationController < ActionController::Base; end
@@ -72,6 +75,7 @@ Object.const_set(:ApplicationHelper, Module.new)
72
75
  #migrations
73
76
  class CreateAllTables < ActiveRecord::Migration
74
77
  def self.up
78
+ create_table(:gem_defined_models) { |t| t.string :name; t.integer :age }
75
79
  create_table(:users) {|t| t.string :name; t.integer :age}
76
80
  create_table(:books) {|t| t.string :title}
77
81
  create_table(:readerships) {|t| t.integer :user_id; t.integer :book_id }
data/spec/fake_gem.rb ADDED
@@ -0,0 +1,6 @@
1
+ # Simulate a gem providing a subclass of ActiveRecord::Base before the Railtie is loaded.
2
+
3
+ require 'active_record'
4
+
5
+ class GemDefinedModel < ActiveRecord::Base
6
+ end
@@ -1,19 +1,114 @@
1
- require File.expand_path('../spec_helper', File.dirname(__FILE__))
2
- include Kaminari::ActionViewExtension
1
+ require 'spec_helper'
3
2
 
4
3
  describe 'Kaminari::ActionViewExtension' do
5
4
  describe '#paginate' do
6
5
  before do
7
- @author = User.create! :name => 'author'
8
- @books = 2.times.map { @author.books_authored.create! }
9
- @books = Book.page(1)
6
+ 50.times {|i| User.create! :name => "user#{i}"}
7
+ @users = User.page(1)
10
8
  end
11
- subject { paginate( @books ) }
12
- it { should be_a(String) }
9
+ subject { helper.paginate @users, :params => {:controller => 'users', :action => 'index'} }
10
+ it { should be_a String }
13
11
 
14
- context "escaping the pagination for javascript" do
15
- it "should escape for javascript" do
16
- lambda { escape_javascript( paginate( @books ) ) }.should_not raise_error
12
+ context 'escaping the pagination for javascript' do
13
+ it 'should escape for javascript' do
14
+ lambda { escape_javascript(helper.paginate @users, :params => {:controller => 'users', :action => 'index'}) }.should_not raise_error
15
+ end
16
+ end
17
+ end
18
+
19
+ describe '#link_to_next_page' do
20
+ before do
21
+ 50.times {|i| User.create! :name => "user#{i}"}
22
+ end
23
+ context 'having more page' do
24
+ before do
25
+ @users = User.page(1)
26
+ end
27
+ context 'the default behaviour' do
28
+ subject { helper.link_to_next_page @users, 'More', :params => {:controller => 'users', :action => 'index'} }
29
+ it { should be_a String }
30
+ it { should match /rel="next"/ }
31
+ end
32
+ context 'overriding rel=' do
33
+ subject { helper.link_to_next_page @users, 'More', :rel => 'external', :params => {:controller => 'users', :action => 'index'} }
34
+ it { should match /rel="external"/ }
35
+ end
36
+ end
37
+ context 'the last page' do
38
+ before do
39
+ @users = User.page(2)
40
+ end
41
+ subject { helper.link_to_next_page @users, 'More', :params => {:controller => 'users', :action => 'index'} }
42
+ it { should_not be }
43
+ end
44
+ end
45
+
46
+ describe '#page_entries_info' do
47
+ before do
48
+ @users = User.page(1).per(25)
49
+ end
50
+ context 'having no entries' do
51
+ subject { helper.page_entries_info @users, :params => {:controller => 'users', :action => 'index'} }
52
+ it { should == 'No entries found' }
53
+ end
54
+
55
+ context 'having 1 entry' do
56
+ before do
57
+ User.create!
58
+ @users = User.page(1).per(25)
59
+ end
60
+ subject { helper.page_entries_info @users, :params => {:controller => 'users', :action => 'index'} }
61
+ it { should == 'Displaying <b>1</b> user' }
62
+
63
+ context 'setting the entry name option to "member"' do
64
+ subject { helper.page_entries_info @users, :entry_name => 'member', :params => {:controller => 'users', :action => 'index'} }
65
+ it { should == 'Displaying <b>1</b> member' }
66
+ end
67
+ end
68
+
69
+ context 'having more than 1 but less than a page of entries' do
70
+ before do
71
+ 10.times {|i| User.create!}
72
+ @users = User.page(1).per(25)
73
+ end
74
+ subject { helper.page_entries_info @users, :params => {:controller => 'users', :action => 'index'} }
75
+ it { should == 'Displaying <b>all 10</b> users' }
76
+
77
+ context 'setting the entry name option to "member"' do
78
+ subject { helper.page_entries_info @users, :entry_name => 'member', :params => {:controller => 'users', :action => 'index'} }
79
+ it { should == 'Displaying <b>all 10</b> members' }
80
+ end
81
+ end
82
+
83
+ context 'having more than one page of entries' do
84
+ before do
85
+ 50.times {|i| User.create!}
86
+ end
87
+
88
+ describe 'the first page' do
89
+ before do
90
+ @users = User.page(1).per(25)
91
+ end
92
+ subject { helper.page_entries_info @users, :params => {:controller => 'users', :action => 'index'} }
93
+ it { should == 'Displaying users <b>1&nbsp;-&nbsp;25</b> of <b>50</b> in total' }
94
+
95
+ context 'setting the entry name option to "member"' do
96
+ subject { helper.page_entries_info @users, :entry_name => 'member', :params => {:controller => 'users', :action => 'index'} }
97
+ it { should == 'Displaying members <b>1&nbsp;-&nbsp;25</b> of <b>50</b> in total' }
98
+ end
99
+ end
100
+
101
+ describe 'the next page' do
102
+ before do
103
+ @users = User.page(2).per(25)
104
+ end
105
+ subject { helper.page_entries_info @users, :params => {:controller => 'users', :action => 'index'} }
106
+ it { should == 'Displaying users <b>26&nbsp;-&nbsp;50</b> of <b>50</b> in total' }
107
+
108
+ context 'setting the entry name option to "member"' do
109
+ subject { helper.page_entries_info @users, :entry_name => 'member', :params => {:controller => 'users', :action => 'index'} }
110
+ it { should == 'Displaying members <b>26&nbsp;-&nbsp;50</b> of <b>50</b> in total' }
111
+ end
17
112
  end
18
113
  end
19
114
  end
@@ -1,4 +1,4 @@
1
- require File.expand_path('../spec_helper', File.dirname(__FILE__))
1
+ require 'spec_helper'
2
2
  include Kaminari::Helpers
3
3
 
4
4
  describe 'Kaminari::Helpers::Paginator' do
@@ -0,0 +1,174 @@
1
+ require 'spec_helper'
2
+ require 'spec_helper_for_sinatra'
3
+
4
+ ERB_TEMPLATE_FOR_PAGINATE = <<EOT
5
+ <div>
6
+ <ul>
7
+ <% @users.each do |user| %>
8
+ <li class="user_info"><%= user.id %></li>
9
+ <% end %>
10
+ </ul>
11
+ <%= paginate @users, @options %>
12
+ </div>
13
+ EOT
14
+
15
+ ERB_TEMPLATE_FOR_NEXT_PAGE = <<EOT
16
+ <div>
17
+ <ul>
18
+ <% @users.each do |user| %>
19
+ <li class="user_info"><%= user.id %></li>
20
+ <% end %>
21
+ </ul>
22
+ <%= link_to_next_page(@users, "Next!", {:id => 'next_page_link'}.merge(@options || {})) %>
23
+ </div>
24
+ EOT
25
+
26
+ describe 'Kaminari::Helpers::SinatraHelper' do
27
+ before do
28
+ 50.times {|i| User.create! :name => "user#{i}"}
29
+ end
30
+
31
+ describe '#paginate' do
32
+ before do
33
+ mock_app do
34
+ register Kaminari::Helpers::SinatraHelpers
35
+ get '/users' do
36
+ @page = params[:page] || 1
37
+ @users = User.page(@page)
38
+ @options = {}
39
+ erb ERB_TEMPLATE_FOR_PAGINATE
40
+ end
41
+ end
42
+ end
43
+
44
+ context 'normal paginations with Sinatra' do
45
+ before { get '/users' }
46
+
47
+ it 'should have a navigation tag' do
48
+ last_document.search('nav.pagination').should_not be_empty
49
+ end
50
+
51
+ it 'should have pagination links' do
52
+ last_document.search('.page a').should have_at_least(1).items
53
+ last_document.search('.next a').should have_at_least(1).items
54
+ last_document.search('.last a').should have_at_least(1).items
55
+ end
56
+
57
+ it 'should point to current page' do
58
+ last_document.search('.current').text.should match /1/
59
+
60
+ get '/users?page=2'
61
+ last_document.search('.current').text.should match /2/
62
+ end
63
+
64
+ it 'should load 25 users' do
65
+ last_document.search('li.user_info').should have(25).items
66
+ end
67
+
68
+ it 'should preserve params' do
69
+ get '/users?foo=bar'
70
+ last_document.search('.page a').should(be_all do |elm|
71
+ elm.attribute('href').value =~ /foo=bar/
72
+ end)
73
+ end
74
+ end
75
+
76
+ context 'optional paginations with Sinatra' do
77
+ it 'should have 5 windows with 1 gap' do
78
+ mock_app do
79
+ register Kaminari::Helpers::SinatraHelpers
80
+ get '/users' do
81
+ @page = params[:page] || 1
82
+ @users = User.page(@page).per(5)
83
+ @options = {}
84
+ erb ERB_TEMPLATE_FOR_PAGINATE
85
+ end
86
+ end
87
+
88
+ get '/users'
89
+ last_document.search('.page').should have(6).items
90
+ last_document.search('.gap').should have(1).item
91
+ end
92
+
93
+ it 'should controll the inner window size' do
94
+ mock_app do
95
+ register Kaminari::Helpers::SinatraHelpers
96
+ get '/users' do
97
+ @page = params[:page] || 1
98
+ @users = User.page(@page).per(3)
99
+ @options = {:window => 10}
100
+ erb ERB_TEMPLATE_FOR_PAGINATE
101
+ end
102
+ end
103
+
104
+ get '/users'
105
+ last_document.search('.page').should have(12).items
106
+ last_document.search('.gap').should have(1).item
107
+ end
108
+
109
+ it 'should specify a page param name' do
110
+ mock_app do
111
+ register Kaminari::Helpers::SinatraHelpers
112
+ get '/users' do
113
+ @page = params[:page] || 1
114
+ @users = User.page(@page).per(3)
115
+ @options = {:param_name => :user_page}
116
+ erb ERB_TEMPLATE_FOR_PAGINATE
117
+ end
118
+ end
119
+
120
+ get '/users'
121
+ last_document.search('.page a').should(be_all do |elm|
122
+ elm.attribute('href').value =~ /user_page=\d+/
123
+ end)
124
+ end
125
+ end
126
+ end
127
+
128
+ describe '#link_to_next_page' do
129
+ before do
130
+ mock_app do
131
+ register Kaminari::Helpers::SinatraHelpers
132
+ get '/users' do
133
+ @page = params[:page] || 1
134
+ @users = User.page(@page)
135
+ erb ERB_TEMPLATE_FOR_NEXT_PAGE
136
+ end
137
+
138
+ get '/users_placeholder' do
139
+ @page = params[:page] || 1
140
+ @options = {:placeholder => %{<span id='no_next_page'>No Next Page</span>}}
141
+ @users = User.page(@page)
142
+ erb ERB_TEMPLATE_FOR_NEXT_PAGE
143
+ end
144
+ end
145
+ end
146
+
147
+ context 'having more page' do
148
+ it 'should have a more page link' do
149
+ get '/users'
150
+ last_document.search('a#next_page_link').should be_present
151
+ last_document.search('a#next_page_link').text.should match /Next!/
152
+ end
153
+ end
154
+
155
+ context 'the last page' do
156
+ before do
157
+ User.delete_all
158
+ 50.times {|i| User.create! :name => "user#{i}"}
159
+ end
160
+
161
+ it 'should not have a more page link' do
162
+ get '/users?page=2'
163
+ last_document.search('a#next_page_link').should be_empty
164
+ end
165
+
166
+ it 'should have a no more page notation using placeholder' do
167
+ get '/users_placeholder?page=2'
168
+ last_document.search('a#next_page_link').should be_empty
169
+ last_document.search('span#no_next_page').should be_present
170
+ last_document.search('span#no_next_page').text.should match /No Next Page/
171
+ end
172
+ end
173
+ end
174
+ end