kaminari 0.10.4 → 0.11.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.
- data/CHANGELOG +31 -0
- data/Gemfile +1 -30
- data/Gemfile.lock +66 -82
- data/README.rdoc +16 -0
- data/Rakefile +12 -2
- data/VERSION +1 -1
- data/app/views/kaminari/_first_page.html.erb +11 -0
- data/app/views/kaminari/_first_page.html.haml +9 -0
- data/app/views/kaminari/{_truncated_span.html.erb → _gap.html.erb} +2 -2
- data/app/views/kaminari/{_truncated_span.html.haml → _gap.html.haml} +2 -2
- data/app/views/kaminari/_last_page.html.erb +11 -0
- data/app/views/kaminari/_last_page.html.haml +9 -0
- data/app/views/kaminari/{_next_link.html.erb → _next_page.html.erb} +3 -3
- data/app/views/kaminari/{_next_link.html.haml → _next_page.html.haml} +3 -3
- data/app/views/kaminari/_page.html.erb +12 -0
- data/app/views/kaminari/_page.html.haml +10 -0
- data/app/views/kaminari/_paginator.html.erb +8 -14
- data/app/views/kaminari/_paginator.html.haml +8 -13
- data/app/views/kaminari/{_prev_link.html.erb → _prev_page.html.erb} +3 -3
- data/app/views/kaminari/_prev_page.html.haml +9 -0
- data/config/locales/kaminari.yml +4 -2
- data/kaminari.gemspec +88 -59
- data/lib/generators/kaminari/views_generator.rb +13 -9
- data/lib/kaminari.rb +1 -0
- data/lib/kaminari/helpers/action_view_extension.rb +5 -5
- data/lib/kaminari/helpers/paginator.rb +153 -0
- data/lib/kaminari/helpers/tags.rb +32 -200
- data/lib/kaminari/models/active_record_extension.rb +1 -8
- data/lib/kaminari/models/active_record_relation_methods.rb +8 -1
- data/lib/kaminari/models/mongoid_extension.rb +1 -0
- data/lib/kaminari/railtie.rb +1 -1
- data/lib/kaminari/version.rb +3 -0
- data/spec/acceptance/users_spec.rb +13 -4
- data/spec/fake_app.rb +34 -2
- data/spec/helpers/helpers_spec.rb +13 -4
- data/spec/helpers/tags_spec.rb +32 -50
- data/spec/models/active_record_relation_methods_spec.rb +18 -0
- data/spec/models/scopes_spec.rb +12 -1
- data/spec/spec_helper.rb +1 -0
- data/spec/support/database_cleaner.rb +13 -0
- metadata +248 -127
- data/app/views/kaminari/_current_page.html.erb +0 -9
- data/app/views/kaminari/_current_page.html.haml +0 -9
- data/app/views/kaminari/_first_page_link.html.erb +0 -12
- data/app/views/kaminari/_first_page_link.html.haml +0 -10
- data/app/views/kaminari/_last_page_link.html.erb +0 -12
- data/app/views/kaminari/_last_page_link.html.haml +0 -10
- data/app/views/kaminari/_next_span.html.erb +0 -8
- data/app/views/kaminari/_next_span.html.haml +0 -7
- data/app/views/kaminari/_page_link.html.erb +0 -12
- data/app/views/kaminari/_page_link.html.haml +0 -10
- data/app/views/kaminari/_prev_link.html.haml +0 -9
- data/app/views/kaminari/_prev_span.html.erb +0 -8
- data/app/views/kaminari/_prev_span.html.haml +0 -7
- data/lib/kaminari/helpers/helpers.rb +0 -75
@@ -9,42 +9,19 @@ module Kaminari
|
|
9
9
|
# class, so _tag parital is not needed).
|
10
10
|
# e.g.) PrevLink -> app/views/kaminari/_prev_link.html.erb
|
11
11
|
#
|
12
|
-
#
|
13
|
-
# e.g.) FirstPageLink -> app/views/kaminari/_first_page_link.html.erb
|
14
|
-
# -> app/views/kaminari/_page_link.html.erb
|
15
|
-
#
|
16
|
-
# When no matching template were found in your app, finally the engine's pre
|
12
|
+
# When no matching template were found in your app, the engine's pre
|
17
13
|
# installed template will be used.
|
18
14
|
# e.g.) Paginator -> $GEM_HOME/kaminari-x.x.x/app/views/kaminari/_paginator.html.erb
|
19
15
|
class Tag
|
20
16
|
def initialize(template, options = {}) #:nodoc:
|
21
|
-
@template, @options = template,
|
22
|
-
@param_name = @options.delete
|
17
|
+
@template, @options = template, options.dup
|
18
|
+
@param_name = @options.delete(:param_name)
|
19
|
+
@theme = @options[:theme] ? "#{@options.delete(:theme)}/" : ''
|
20
|
+
@params = @options[:params] ? template.params.merge(@options.delete :params) : template.params
|
23
21
|
end
|
24
22
|
|
25
23
|
def to_s(locals = {}) #:nodoc:
|
26
|
-
@template.render :partial =>
|
27
|
-
end
|
28
|
-
|
29
|
-
private
|
30
|
-
def self.ancestor_renderables
|
31
|
-
arr = []
|
32
|
-
ancestors.each do |klass|
|
33
|
-
return arr if klass == Tag
|
34
|
-
arr << klass if klass != Renderable
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
# OMG yet another super dirty hack
|
39
|
-
# this method finds
|
40
|
-
# 1. a template for the given class from app/views
|
41
|
-
# 2. a template for its parent class from app/views
|
42
|
-
# 3. the default one inside the engine
|
43
|
-
def find_template
|
44
|
-
self.class.ancestor_renderables.each do |klass|
|
45
|
-
return "kaminari/#{klass.template_filename}" if @template.partial_exists? klass.template_filename
|
46
|
-
end
|
47
|
-
"kaminari/#{self.class.template_filename}"
|
24
|
+
@template.render :partial => "kaminari/#{@theme}#{self.class.name.demodulize.underscore}", :locals => @options.merge(locals)
|
48
25
|
end
|
49
26
|
|
50
27
|
def page_url_for(page)
|
@@ -52,129 +29,12 @@ module Kaminari
|
|
52
29
|
end
|
53
30
|
end
|
54
31
|
|
55
|
-
|
56
|
-
|
57
|
-
base.extend ClassMethods
|
58
|
-
end
|
59
|
-
module ClassMethods #:nodoc:
|
60
|
-
def template_filename #:nodoc:
|
61
|
-
name.demodulize.underscore
|
62
|
-
end
|
63
|
-
def included(base) #:nodoc:
|
64
|
-
base.extend Renderable::ClassMethods
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
# The container tag
|
70
|
-
class Paginator < Tag
|
71
|
-
include Renderable
|
72
|
-
attr_reader :options
|
73
|
-
|
74
|
-
def initialize(template, window_options) #:nodoc:
|
75
|
-
@template, @options = template, window_options.reverse_merge(template.options)
|
76
|
-
# so that this instance can actually "render". Black magic?
|
77
|
-
@output_buffer = ActionView::OutputBuffer.new
|
78
|
-
end
|
79
|
-
|
80
|
-
# render given block as a view template
|
81
|
-
def render(&block)
|
82
|
-
instance_eval &block if @options[:num_pages] > 1
|
83
|
-
@output_buffer
|
84
|
-
end
|
85
|
-
|
86
|
-
# enumerate each page providing PageProxy object as the block parameter
|
87
|
-
def each_page
|
88
|
-
1.upto(@options[:num_pages]) do |i|
|
89
|
-
@page = i
|
90
|
-
yield PageProxy.new(options, i, @last)
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
%w[current_page first_page_link last_page_link page_link].each do |tag|
|
95
|
-
eval <<-DEF
|
96
|
-
def #{tag}_tag
|
97
|
-
@last = #{tag.classify}.new @template, :page => @page
|
98
|
-
end
|
99
|
-
DEF
|
100
|
-
end
|
101
|
-
|
102
|
-
%w[prev_link prev_span next_link next_span truncated_span].each do |tag|
|
103
|
-
eval <<-DEF
|
104
|
-
def #{tag}_tag
|
105
|
-
@last = #{tag.classify}.new @template
|
106
|
-
end
|
107
|
-
DEF
|
108
|
-
end
|
109
|
-
|
110
|
-
def to_s(window_options = {}) #:nodoc:
|
111
|
-
super window_options.merge :paginator => self
|
112
|
-
end
|
113
|
-
|
114
|
-
# Wraps a "page number" and provides some utility methods
|
115
|
-
class PageProxy
|
116
|
-
def initialize(options, page, last) #:nodoc:
|
117
|
-
@options, @page, @last = options, page, last
|
118
|
-
end
|
119
|
-
|
120
|
-
# the page number
|
121
|
-
def number
|
122
|
-
@page
|
123
|
-
end
|
124
|
-
|
125
|
-
# current page or not
|
126
|
-
def current?
|
127
|
-
@page == @options[:current_page]
|
128
|
-
end
|
129
|
-
|
130
|
-
# the first page or not
|
131
|
-
def first?
|
132
|
-
@page == 1
|
133
|
-
end
|
134
|
-
|
135
|
-
# the last page or not
|
136
|
-
def last?
|
137
|
-
@page == @options[:num_pages]
|
138
|
-
end
|
139
|
-
|
140
|
-
# within the left outer window or not
|
141
|
-
def left_outer?
|
142
|
-
@page <= @options[:left] + 1
|
143
|
-
end
|
144
|
-
|
145
|
-
# within the right outer window or not
|
146
|
-
def right_outer?
|
147
|
-
@options[:num_pages] - @page <= @options[:right]
|
148
|
-
end
|
149
|
-
|
150
|
-
# inside the inner window or not
|
151
|
-
def inside_window?
|
152
|
-
(@page - @options[:current_page]).abs <= @options[:window]
|
153
|
-
end
|
154
|
-
|
155
|
-
# The last rendered tag was "truncated" or not
|
156
|
-
def was_truncated?
|
157
|
-
@last.is_a? TruncatedSpan
|
158
|
-
end
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
|
-
# A page
|
163
|
-
module Page
|
164
|
-
include Renderable
|
32
|
+
# Tag that contains a link
|
33
|
+
module Link
|
165
34
|
# target page number
|
166
35
|
def page
|
167
36
|
raise 'Override page with the actual page value to be a Page.'
|
168
37
|
end
|
169
|
-
def to_s(locals = {}) #:nodoc:
|
170
|
-
super locals.merge(:page => page)
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
|
-
# Tag that contains a link
|
175
|
-
module Link
|
176
|
-
include Renderable
|
177
|
-
include Page
|
178
38
|
# the link's href
|
179
39
|
def url
|
180
40
|
page_url_for page
|
@@ -184,80 +44,52 @@ module Kaminari
|
|
184
44
|
end
|
185
45
|
end
|
186
46
|
|
187
|
-
#
|
188
|
-
|
189
|
-
include
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
# "Previous" without link
|
198
|
-
class PrevSpan < Tag
|
199
|
-
include NonLink
|
200
|
-
include Prev
|
47
|
+
# A page
|
48
|
+
class Page < Tag
|
49
|
+
include Link
|
50
|
+
# target page number
|
51
|
+
def page
|
52
|
+
@options[:page]
|
53
|
+
end
|
54
|
+
def to_s(locals = {}) #:nodoc:
|
55
|
+
super locals.merge(:page => page)
|
56
|
+
end
|
201
57
|
end
|
202
58
|
|
203
|
-
#
|
204
|
-
class
|
59
|
+
# Link with page number that appears at the leftmost
|
60
|
+
class FirstPage < Tag
|
205
61
|
include Link
|
206
|
-
include Prev
|
207
62
|
def page #:nodoc:
|
208
|
-
|
63
|
+
1
|
209
64
|
end
|
210
65
|
end
|
211
66
|
|
212
|
-
#
|
213
|
-
|
214
|
-
include Renderable
|
215
|
-
end
|
216
|
-
|
217
|
-
# "Next" without link
|
218
|
-
class NextSpan < Tag
|
219
|
-
include NonLink
|
220
|
-
include Next
|
221
|
-
end
|
222
|
-
|
223
|
-
# "Next" with link
|
224
|
-
class NextLink < Tag
|
67
|
+
# Link with page number that appears at the rightmost
|
68
|
+
class LastPage < Tag
|
225
69
|
include Link
|
226
|
-
include Next
|
227
70
|
def page #:nodoc:
|
228
|
-
@options[:
|
71
|
+
@options[:num_pages]
|
229
72
|
end
|
230
73
|
end
|
231
74
|
|
232
|
-
#
|
233
|
-
class
|
234
|
-
include Page
|
75
|
+
# The "previous" page of the current page
|
76
|
+
class PrevPage < Tag
|
235
77
|
include Link
|
236
78
|
def page #:nodoc:
|
237
|
-
@options[:
|
79
|
+
@options[:current_page] - 1
|
238
80
|
end
|
239
81
|
end
|
240
82
|
|
241
|
-
#
|
242
|
-
class
|
243
|
-
include
|
244
|
-
include NonLink
|
83
|
+
# The "next" page of the current page
|
84
|
+
class NextPage < Tag
|
85
|
+
include Link
|
245
86
|
def page #:nodoc:
|
246
|
-
@options[:
|
87
|
+
@options[:current_page] + 1
|
247
88
|
end
|
248
89
|
end
|
249
90
|
|
250
|
-
# Link with page number that appears at the leftmost
|
251
|
-
class FirstPageLink < PageLink
|
252
|
-
end
|
253
|
-
|
254
|
-
# Link with page number that appears at the rightmost
|
255
|
-
class LastPageLink < PageLink
|
256
|
-
end
|
257
|
-
|
258
91
|
# Non-link tag that stands for skipped pages...
|
259
|
-
class
|
260
|
-
include NonLink
|
92
|
+
class Gap < Tag
|
261
93
|
end
|
262
94
|
end
|
263
95
|
end
|
@@ -1,17 +1,10 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), 'active_record_relation_methods')
|
2
|
+
|
2
3
|
module Kaminari
|
3
4
|
module ActiveRecordExtension
|
4
5
|
extend ActiveSupport::Concern
|
5
6
|
included do
|
6
7
|
def self.inherited(kls) #:nodoc:
|
7
|
-
# TERRIBLE HORRIBLE NO GOOD VERY BAD HACK: inheritable_attributes is not yet set here on AR 3.0
|
8
|
-
unless kls.default_scoping
|
9
|
-
new_inheritable_attributes = Hash[inheritable_attributes.map do |key, value|
|
10
|
-
[key, value.duplicable? ? value.dup : value]
|
11
|
-
end]
|
12
|
-
kls.instance_variable_set('@inheritable_attributes', new_inheritable_attributes)
|
13
|
-
end
|
14
|
-
|
15
8
|
kls.class_eval do
|
16
9
|
include Kaminari::ConfigurationMethods
|
17
10
|
|
@@ -2,8 +2,15 @@ module Kaminari
|
|
2
2
|
module ActiveRecordRelationMethods
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
module InstanceMethods
|
5
|
+
if Rails.version < '3.1'
|
6
|
+
def count #:nodoc:
|
7
|
+
limit_value == 0 ? 0 : length
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
5
11
|
def total_count #:nodoc:
|
6
|
-
|
12
|
+
# #count overrides the #select which could include generated columns referenced in #order, so skip #order here, where it's irrelevant to the result anyway
|
13
|
+
c = except(:offset, :limit, :includes, :order).count
|
7
14
|
# .group returns an OrderdHash that responds to #count
|
8
15
|
c.respond_to?(:count) ? c.count : c
|
9
16
|
end
|
data/lib/kaminari/railtie.rb
CHANGED
@@ -3,7 +3,7 @@ require 'rails'
|
|
3
3
|
begin; require 'mongoid'; rescue LoadError; end
|
4
4
|
|
5
5
|
require File.join(File.dirname(__FILE__), 'helpers/action_view_extension')
|
6
|
-
require File.join(File.dirname(__FILE__), 'helpers/
|
6
|
+
require File.join(File.dirname(__FILE__), 'helpers/paginator')
|
7
7
|
require File.join(File.dirname(__FILE__), 'models/page_scope_methods')
|
8
8
|
require File.join(File.dirname(__FILE__), 'models/configuration_methods')
|
9
9
|
|
@@ -13,7 +13,7 @@ feature 'Users' do
|
|
13
13
|
page.should have_content '1'
|
14
14
|
end
|
15
15
|
within 'span.next' do
|
16
|
-
click_link 'Next
|
16
|
+
click_link 'Next ›'
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
@@ -21,8 +21,8 @@ feature 'Users' do
|
|
21
21
|
within 'span.page.current' do
|
22
22
|
page.should have_content '2'
|
23
23
|
end
|
24
|
-
within 'span.
|
25
|
-
click_link '
|
24
|
+
within 'span.last' do
|
25
|
+
click_link 'Last »'
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
@@ -31,7 +31,7 @@ feature 'Users' do
|
|
31
31
|
page.should have_content '4'
|
32
32
|
end
|
33
33
|
within 'span.prev' do
|
34
|
-
click_link '
|
34
|
+
click_link '‹ Prev'
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
@@ -39,6 +39,15 @@ feature 'Users' do
|
|
39
39
|
within 'span.page.current' do
|
40
40
|
page.should have_content '3'
|
41
41
|
end
|
42
|
+
within 'span.first' do
|
43
|
+
click_link '« First'
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
within 'nav.pagination' do
|
48
|
+
within 'span.page.current' do
|
49
|
+
page.should have_content '1'
|
50
|
+
end
|
42
51
|
end
|
43
52
|
end
|
44
53
|
end
|
data/spec/fake_app.rb
CHANGED
@@ -20,9 +20,39 @@ end
|
|
20
20
|
|
21
21
|
# models
|
22
22
|
class User < ActiveRecord::Base
|
23
|
-
|
23
|
+
has_many :authorships
|
24
|
+
has_many :readerships
|
25
|
+
has_many :books_authored, :through => :authorships, :source => :book
|
26
|
+
has_many :books_read, :through => :readerships, :source => :book
|
27
|
+
|
28
|
+
def readers
|
29
|
+
User.joins(:books_read => :authors).where(:authors_books => {:id => self})
|
30
|
+
end
|
31
|
+
|
32
|
+
scope :by_name, order(:name)
|
33
|
+
scope :by_read_count, lambda {
|
34
|
+
cols = if connection.adapter_name == "PostgreSQL"
|
35
|
+
column_names.map { |column| %{"users"."#{column}"} }.join(", ")
|
36
|
+
else
|
37
|
+
'"users"."id"'
|
38
|
+
end
|
39
|
+
group(cols).select("count(readerships.id) AS read_count, #{cols}").order('read_count DESC')
|
40
|
+
}
|
41
|
+
end
|
42
|
+
class Authorship < ActiveRecord::Base
|
43
|
+
belongs_to :user
|
44
|
+
belongs_to :book
|
45
|
+
end
|
46
|
+
class Readership < ActiveRecord::Base
|
47
|
+
belongs_to :user
|
48
|
+
belongs_to :book
|
49
|
+
end
|
50
|
+
class Book < ActiveRecord::Base
|
51
|
+
has_many :authorships
|
52
|
+
has_many :readerships
|
53
|
+
has_many :authors, :through => :authorships, :source => :user
|
54
|
+
has_many :readers, :through => :readerships, :source => :user
|
24
55
|
end
|
25
|
-
class Book < ActiveRecord::Base; end
|
26
56
|
|
27
57
|
# controllers
|
28
58
|
class ApplicationController < ActionController::Base; end
|
@@ -44,5 +74,7 @@ class CreateAllTables < ActiveRecord::Migration
|
|
44
74
|
def self.up
|
45
75
|
create_table(:users) {|t| t.string :name; t.integer :age}
|
46
76
|
create_table(:books) {|t| t.string :title}
|
77
|
+
create_table(:readerships) {|t| t.integer :user_id; t.integer :book_id }
|
78
|
+
create_table(:authorships) {|t| t.integer :user_id; t.integer :book_id }
|
47
79
|
end
|
48
80
|
end
|
@@ -1,11 +1,12 @@
|
|
1
1
|
require File.expand_path('../spec_helper', File.dirname(__FILE__))
|
2
2
|
include Kaminari::Helpers
|
3
3
|
|
4
|
-
describe 'Kaminari::Helpers::
|
4
|
+
describe 'Kaminari::Helpers::Paginator' do
|
5
5
|
let :template do
|
6
6
|
stub(r = Object.new) do
|
7
7
|
render.with_any_args
|
8
8
|
params { {} }
|
9
|
+
options { {} }
|
9
10
|
url_for {|h| "/foo?page=#{h[:page]}"}
|
10
11
|
end
|
11
12
|
r
|
@@ -13,10 +14,18 @@ describe 'Kaminari::Helpers::PaginationRenderer' do
|
|
13
14
|
|
14
15
|
describe '#params' do
|
15
16
|
before do
|
16
|
-
@
|
17
|
+
@paginator = Paginator.new(template, :params => {:controller => 'foo', :action => 'bar'})
|
17
18
|
end
|
18
|
-
subject { @
|
19
|
-
|
19
|
+
subject { @paginator.page_tag(template).instance_variable_get('@params') }
|
20
|
+
it { should == {:controller => 'foo', :action => 'bar'} }
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#param_name' do
|
24
|
+
before do
|
25
|
+
@paginator = Paginator.new(template, :param_name => :pagina)
|
26
|
+
end
|
27
|
+
subject { @paginator.page_tag(template).instance_variable_get('@param_name') }
|
28
|
+
it { should == :pagina }
|
20
29
|
end
|
21
30
|
|
22
31
|
#TODO test somehow...
|