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.

Files changed (55) hide show
  1. data/CHANGELOG +31 -0
  2. data/Gemfile +1 -30
  3. data/Gemfile.lock +66 -82
  4. data/README.rdoc +16 -0
  5. data/Rakefile +12 -2
  6. data/VERSION +1 -1
  7. data/app/views/kaminari/_first_page.html.erb +11 -0
  8. data/app/views/kaminari/_first_page.html.haml +9 -0
  9. data/app/views/kaminari/{_truncated_span.html.erb → _gap.html.erb} +2 -2
  10. data/app/views/kaminari/{_truncated_span.html.haml → _gap.html.haml} +2 -2
  11. data/app/views/kaminari/_last_page.html.erb +11 -0
  12. data/app/views/kaminari/_last_page.html.haml +9 -0
  13. data/app/views/kaminari/{_next_link.html.erb → _next_page.html.erb} +3 -3
  14. data/app/views/kaminari/{_next_link.html.haml → _next_page.html.haml} +3 -3
  15. data/app/views/kaminari/_page.html.erb +12 -0
  16. data/app/views/kaminari/_page.html.haml +10 -0
  17. data/app/views/kaminari/_paginator.html.erb +8 -14
  18. data/app/views/kaminari/_paginator.html.haml +8 -13
  19. data/app/views/kaminari/{_prev_link.html.erb → _prev_page.html.erb} +3 -3
  20. data/app/views/kaminari/_prev_page.html.haml +9 -0
  21. data/config/locales/kaminari.yml +4 -2
  22. data/kaminari.gemspec +88 -59
  23. data/lib/generators/kaminari/views_generator.rb +13 -9
  24. data/lib/kaminari.rb +1 -0
  25. data/lib/kaminari/helpers/action_view_extension.rb +5 -5
  26. data/lib/kaminari/helpers/paginator.rb +153 -0
  27. data/lib/kaminari/helpers/tags.rb +32 -200
  28. data/lib/kaminari/models/active_record_extension.rb +1 -8
  29. data/lib/kaminari/models/active_record_relation_methods.rb +8 -1
  30. data/lib/kaminari/models/mongoid_extension.rb +1 -0
  31. data/lib/kaminari/railtie.rb +1 -1
  32. data/lib/kaminari/version.rb +3 -0
  33. data/spec/acceptance/users_spec.rb +13 -4
  34. data/spec/fake_app.rb +34 -2
  35. data/spec/helpers/helpers_spec.rb +13 -4
  36. data/spec/helpers/tags_spec.rb +32 -50
  37. data/spec/models/active_record_relation_methods_spec.rb +18 -0
  38. data/spec/models/scopes_spec.rb +12 -1
  39. data/spec/spec_helper.rb +1 -0
  40. data/spec/support/database_cleaner.rb +13 -0
  41. metadata +248 -127
  42. data/app/views/kaminari/_current_page.html.erb +0 -9
  43. data/app/views/kaminari/_current_page.html.haml +0 -9
  44. data/app/views/kaminari/_first_page_link.html.erb +0 -12
  45. data/app/views/kaminari/_first_page_link.html.haml +0 -10
  46. data/app/views/kaminari/_last_page_link.html.erb +0 -12
  47. data/app/views/kaminari/_last_page_link.html.haml +0 -10
  48. data/app/views/kaminari/_next_span.html.erb +0 -8
  49. data/app/views/kaminari/_next_span.html.haml +0 -7
  50. data/app/views/kaminari/_page_link.html.erb +0 -12
  51. data/app/views/kaminari/_page_link.html.haml +0 -10
  52. data/app/views/kaminari/_prev_link.html.haml +0 -9
  53. data/app/views/kaminari/_prev_span.html.erb +0 -8
  54. data/app/views/kaminari/_prev_span.html.haml +0 -7
  55. 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
- # If the template file does not exist, it falls back to ancestor classes.
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, template.options.merge(options)
22
- @param_name = @options.delete :param_name
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 => find_template, :locals => @options.merge(locals)
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
- module Renderable #:nodoc:
56
- def self.included(base) #:nodoc:
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
- # Tag that doesn't contain a link
188
- module NonLink
189
- include Renderable
190
- end
191
-
192
- # The "previous" page of the current page
193
- module Prev
194
- include Renderable
195
- end
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
- # "Previous" with link
204
- class PrevLink < Tag
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
- @options[:current_page] - 1
63
+ 1
209
64
  end
210
65
  end
211
66
 
212
- # The "next" page of the current page
213
- module Next
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[:current_page] + 1
71
+ @options[:num_pages]
229
72
  end
230
73
  end
231
74
 
232
- # Link showing page number
233
- class PageLink < Tag
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[:page]
79
+ @options[:current_page] - 1
238
80
  end
239
81
  end
240
82
 
241
- # Non-link tag showing the current page number
242
- class CurrentPage < Tag
243
- include Page
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[:page]
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 TruncatedSpan < Tag
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
- c = except(:offset, :limit).count
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
@@ -1,4 +1,5 @@
1
1
  require File.join(File.dirname(__FILE__), 'mongoid_criteria_methods')
2
+
2
3
  module Kaminari
3
4
  module MongoidExtension
4
5
  module Criteria
@@ -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/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
 
@@ -0,0 +1,3 @@
1
+ module Kaminari
2
+ VERSION = File.read File.join(File.dirname(__FILE__), '../../VERSION')
3
+ end
@@ -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.page.last' do
25
- click_link '4'
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 '« Prev'
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
@@ -20,9 +20,39 @@ end
20
20
 
21
21
  # models
22
22
  class User < ActiveRecord::Base
23
- default_scope order(:name)
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::PaginationRenderer' do
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
- @renderer = PaginationRenderer.new(template, :params => {:controller => 'foo', :action => 'bar'})
17
+ @paginator = Paginator.new(template, :params => {:controller => 'foo', :action => 'bar'})
17
18
  end
18
- subject { @renderer.instance_variable_get '@template' }
19
- its(:params) { should == {:controller => 'foo', :action => 'bar'} }
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...