agnostic-will_paginate 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. data/.autotest +54 -0
  2. data/.gitignore +4 -0
  3. data/.gitmodules +3 -0
  4. data/.manifest +61 -0
  5. data/CHANGELOG.rdoc +105 -0
  6. data/LICENSE +18 -0
  7. data/README.rdoc +125 -0
  8. data/Rakefile +58 -0
  9. data/init.rb +1 -0
  10. data/lib/will_paginate.rb +45 -0
  11. data/lib/will_paginate/array.rb +33 -0
  12. data/lib/will_paginate/collection.rb +145 -0
  13. data/lib/will_paginate/core_ext.rb +69 -0
  14. data/lib/will_paginate/deprecation.rb +50 -0
  15. data/lib/will_paginate/finders.rb +9 -0
  16. data/lib/will_paginate/finders/active_record.rb +192 -0
  17. data/lib/will_paginate/finders/active_record/named_scope.rb +170 -0
  18. data/lib/will_paginate/finders/active_record/named_scope_patch.rb +39 -0
  19. data/lib/will_paginate/finders/active_resource.rb +51 -0
  20. data/lib/will_paginate/finders/base.rb +112 -0
  21. data/lib/will_paginate/finders/data_mapper.rb +30 -0
  22. data/lib/will_paginate/finders/sequel.rb +22 -0
  23. data/lib/will_paginate/version.rb +9 -0
  24. data/lib/will_paginate/view_helpers.rb +42 -0
  25. data/lib/will_paginate/view_helpers/action_view.rb +158 -0
  26. data/lib/will_paginate/view_helpers/base.rb +126 -0
  27. data/lib/will_paginate/view_helpers/link_renderer.rb +130 -0
  28. data/lib/will_paginate/view_helpers/link_renderer_base.rb +83 -0
  29. data/lib/will_paginate/view_helpers/merb.rb +13 -0
  30. data/spec/collection_spec.rb +147 -0
  31. data/spec/console +8 -0
  32. data/spec/console_fixtures.rb +8 -0
  33. data/spec/database.yml +22 -0
  34. data/spec/finders/active_record_spec.rb +461 -0
  35. data/spec/finders/active_resource_spec.rb +52 -0
  36. data/spec/finders/activerecord_test_connector.rb +108 -0
  37. data/spec/finders/data_mapper_spec.rb +62 -0
  38. data/spec/finders/data_mapper_test_connector.rb +20 -0
  39. data/spec/finders/sequel_spec.rb +53 -0
  40. data/spec/finders/sequel_test_connector.rb +9 -0
  41. data/spec/finders_spec.rb +76 -0
  42. data/spec/fixtures/admin.rb +3 -0
  43. data/spec/fixtures/developer.rb +13 -0
  44. data/spec/fixtures/developers_projects.yml +13 -0
  45. data/spec/fixtures/project.rb +15 -0
  46. data/spec/fixtures/projects.yml +6 -0
  47. data/spec/fixtures/replies.yml +29 -0
  48. data/spec/fixtures/reply.rb +7 -0
  49. data/spec/fixtures/schema.rb +38 -0
  50. data/spec/fixtures/topic.rb +6 -0
  51. data/spec/fixtures/topics.yml +30 -0
  52. data/spec/fixtures/user.rb +2 -0
  53. data/spec/fixtures/users.yml +35 -0
  54. data/spec/rcov.opts +2 -0
  55. data/spec/spec.opts +2 -0
  56. data/spec/spec_helper.rb +75 -0
  57. data/spec/tasks.rake +60 -0
  58. data/spec/view_helpers/action_view_spec.rb +344 -0
  59. data/spec/view_helpers/base_spec.rb +64 -0
  60. data/spec/view_helpers/link_renderer_base_spec.rb +84 -0
  61. data/spec/view_helpers/view_example_group.rb +111 -0
  62. metadata +152 -0
@@ -0,0 +1,13 @@
1
+ require 'will_paginate/view_helpers/base'
2
+ require 'will_paginate/view_helpers/link_renderer'
3
+
4
+ WillPaginate::ViewHelpers::LinkRenderer.class_eval do
5
+ protected
6
+
7
+ def url(page)
8
+ params = @template.request.params.except(:action, :controller).merge(param_name => page)
9
+ @template.url(:this, params)
10
+ end
11
+ end
12
+
13
+ Merb::AbstractController.send(:include, WillPaginate::ViewHelpers::Base)
@@ -0,0 +1,147 @@
1
+ require 'will_paginate/array'
2
+ require 'spec_helper'
3
+
4
+ describe WillPaginate::Collection do
5
+
6
+ before :all do
7
+ @simple = ('a'..'e').to_a
8
+ end
9
+
10
+ it "should be a subset of original collection" do
11
+ @simple.paginate(:page => 1, :per_page => 3).should == %w( a b c )
12
+ end
13
+
14
+ it "can be shorter than per_page if on last page" do
15
+ @simple.paginate(:page => 2, :per_page => 3).should == %w( d e )
16
+ end
17
+
18
+ it "should include whole collection if per_page permits" do
19
+ @simple.paginate(:page => 1, :per_page => 5).should == @simple
20
+ end
21
+
22
+ it "should be empty if out of bounds" do
23
+ @simple.paginate(:page => 2, :per_page => 5).should be_empty
24
+ end
25
+
26
+ it "should default to 1 as current page and 30 per-page" do
27
+ result = (1..50).to_a.paginate
28
+ result.current_page.should == 1
29
+ result.size.should == 30
30
+ end
31
+
32
+ describe "old API" do
33
+ it "should fail with numeric params" do
34
+ Proc.new { [].paginate(2) }.should raise_error(ArgumentError)
35
+ Proc.new { [].paginate(2, 10) }.should raise_error(ArgumentError)
36
+ end
37
+
38
+ it "should fail with both options and numeric param" do
39
+ Proc.new { [].paginate({}, 5) }.should raise_error(ArgumentError)
40
+ end
41
+ end
42
+
43
+ it "should give total_entries precedence over actual size" do
44
+ %w(a b c).paginate(:total_entries => 5).total_entries.should == 5
45
+ end
46
+
47
+ it "should be an augmented Array" do
48
+ entries = %w(a b c)
49
+ collection = create(2, 3, 10) do |pager|
50
+ pager.replace(entries).should == entries
51
+ end
52
+
53
+ collection.should == entries
54
+ for method in %w(total_pages each offset size current_page per_page total_entries)
55
+ collection.should respond_to(method)
56
+ end
57
+ collection.should be_kind_of(Array)
58
+ collection.entries.should be_instance_of(Array)
59
+ # TODO: move to another expectation:
60
+ collection.offset.should == 3
61
+ collection.total_pages.should == 4
62
+ collection.should_not be_out_of_bounds
63
+ end
64
+
65
+ describe "previous/next pages" do
66
+ it "should have previous_page nil when on first page" do
67
+ collection = create(1, 1, 3)
68
+ collection.previous_page.should be_nil
69
+ collection.next_page.should == 2
70
+ end
71
+
72
+ it "should have both prev/next pages" do
73
+ collection = create(2, 1, 3)
74
+ collection.previous_page.should == 1
75
+ collection.next_page.should == 3
76
+ end
77
+
78
+ it "should have next_page nil when on last page" do
79
+ collection = create(3, 1, 3)
80
+ collection.previous_page.should == 2
81
+ collection.next_page.should be_nil
82
+ end
83
+ end
84
+
85
+ it "should show out of bounds when page number is too high" do
86
+ create(2, 3, 2).should be_out_of_bounds
87
+ end
88
+
89
+ it "should not show out of bounds when inside collection" do
90
+ create(1, 3, 2).should_not be_out_of_bounds
91
+ end
92
+
93
+ describe "guessing total count" do
94
+ it "can guess when collection is shorter than limit" do
95
+ collection = create { |p| p.replace array }
96
+ collection.total_entries.should == 8
97
+ end
98
+
99
+ it "should allow explicit total count to override guessed" do
100
+ collection = create(2, 5, 10) { |p| p.replace array }
101
+ collection.total_entries.should == 10
102
+ end
103
+
104
+ it "should not be able to guess when collection is same as limit" do
105
+ collection = create { |p| p.replace array(5) }
106
+ collection.total_entries.should be_nil
107
+ end
108
+
109
+ it "should not be able to guess when collection is empty" do
110
+ collection = create { |p| p.replace array(0) }
111
+ collection.total_entries.should be_nil
112
+ end
113
+
114
+ it "should be able to guess when collection is empty and this is the first page" do
115
+ collection = create(1) { |p| p.replace array(0) }
116
+ collection.total_entries.should == 0
117
+ end
118
+ end
119
+
120
+ it "should raise WillPaginate::InvalidPage on invalid input" do
121
+ for bad_input in [0, -1, nil, '', 'Schnitzel']
122
+ Proc.new { create bad_input }.should raise_error(WillPaginate::InvalidPage)
123
+ end
124
+ end
125
+
126
+ it "should raise Argument error on invalid per_page setting" do
127
+ Proc.new { create(1, -1) }.should raise_error(ArgumentError)
128
+ end
129
+
130
+ it "should not respond to page_count anymore" do
131
+ Proc.new { create.page_count }.should raise_error(NoMethodError)
132
+ end
133
+
134
+ private
135
+
136
+ def create(page = 2, limit = 5, total = nil, &block)
137
+ if block_given?
138
+ WillPaginate::Collection.create(page, limit, total, &block)
139
+ else
140
+ WillPaginate::Collection.new(page, limit, total)
141
+ end
142
+ end
143
+
144
+ def array(size = 3)
145
+ Array.new(size)
146
+ end
147
+ end
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
3
+ libs = []
4
+
5
+ libs << 'irb/completion'
6
+ libs << 'console_fixtures'
7
+
8
+ exec "#{irb} -Ilib:spec#{libs.map{ |l| " -r #{l}" }.join} --simple-prompt"
@@ -0,0 +1,8 @@
1
+ require 'will_paginate/finders/active_record'
2
+ require 'finders/activerecord_test_connector'
3
+ ActiverecordTestConnector.setup
4
+
5
+ # load all fixtures
6
+ Fixtures.create_fixtures(ActiverecordTestConnector::FIXTURES_PATH, ActiveRecord::Base.connection.tables)
7
+
8
+
@@ -0,0 +1,22 @@
1
+ sqlite3:
2
+ database: ":memory:"
3
+ adapter: sqlite3
4
+ timeout: 500
5
+
6
+ sqlite2:
7
+ database: ":memory:"
8
+ adapter: sqlite2
9
+
10
+ mysql:
11
+ adapter: mysql
12
+ username: rails
13
+ password: mislav
14
+ encoding: utf8
15
+ database: will_paginate_unittest
16
+
17
+ postgres:
18
+ adapter: postgresql
19
+ username: mislav
20
+ password: mislav
21
+ database: will_paginate_unittest
22
+ min_messages: warning
@@ -0,0 +1,461 @@
1
+ require 'spec_helper'
2
+ require 'will_paginate/finders/active_record'
3
+ require File.dirname(__FILE__) + '/activerecord_test_connector'
4
+
5
+ require 'will_paginate'
6
+ WillPaginate::enable_named_scope
7
+
8
+ class ArProject < ActiveRecord::Base
9
+ def self.column_names
10
+ ["id"]
11
+ end
12
+
13
+ named_scope :distinct, :select => "DISTINCT #{table_name}.*"
14
+ end
15
+
16
+ gem 'sqlite3-ruby'
17
+ ActiverecordTestConnector.setup
18
+
19
+ describe WillPaginate::Finders::ActiveRecord do
20
+
21
+ extend ActiverecordTestConnector::FixtureSetup
22
+
23
+ it "should integrate with ActiveRecord::Base" do
24
+ ActiveRecord::Base.should respond_to(:paginate)
25
+ end
26
+
27
+ it "should paginate" do
28
+ ArProject.expects(:find).with(:all, { :limit => 5, :offset => 0 }).returns([])
29
+ ArProject.paginate(:page => 1, :per_page => 5)
30
+ end
31
+
32
+ it "should respond to paginate_by_sql" do
33
+ ArProject.should respond_to(:paginate_by_sql)
34
+ end
35
+
36
+ it "should support explicit :all argument" do
37
+ ArProject.expects(:find).with(:all, instance_of(Hash)).returns([])
38
+ ArProject.paginate(:all, :page => nil)
39
+ end
40
+
41
+ it "should put implicit all in dynamic finders" do
42
+ ArProject.expects(:find_all_by_foo).returns([])
43
+ ArProject.expects(:count).returns(0)
44
+ ArProject.paginate_by_foo :page => 2
45
+ end
46
+
47
+ it "should leave extra parameters intact" do
48
+ ArProject.expects(:find).with(:all, {:foo => 'bar', :limit => 4, :offset => 0 }).returns(Array.new(5))
49
+ ArProject.expects(:count).with({:foo => 'bar'}).returns(1)
50
+
51
+ ArProject.paginate :foo => 'bar', :page => 1, :per_page => 4
52
+ end
53
+
54
+ describe "counting" do
55
+ it "should ignore nil in :count parameter" do
56
+ ArProject.expects(:find).returns([])
57
+ lambda { ArProject.paginate :page => nil, :count => nil }.should_not raise_error
58
+ end
59
+
60
+ it "should guess the total count" do
61
+ ArProject.expects(:find).returns(Array.new(2))
62
+ ArProject.expects(:count).never
63
+
64
+ result = ArProject.paginate :page => 2, :per_page => 4
65
+ result.total_entries.should == 6
66
+ end
67
+
68
+ it "should guess that there are no records" do
69
+ ArProject.expects(:find).returns([])
70
+ ArProject.expects(:count).never
71
+
72
+ result = ArProject.paginate :page => 1, :per_page => 4
73
+ result.total_entries.should == 0
74
+ end
75
+ end
76
+
77
+ it "should not ignore :select parameter when it says DISTINCT" do
78
+ ArProject.stubs(:find).returns([])
79
+ ArProject.expects(:count).with(:select => 'DISTINCT salary').returns(0)
80
+ ArProject.paginate :select => 'DISTINCT salary', :page => 2
81
+ end
82
+
83
+ it "should count with scoped select when :select => DISTINCT" do
84
+ ArProject.stubs(:find).returns([])
85
+ ArProject.expects(:count).with(:select => 'DISTINCT ar_projects.id').returns(0)
86
+ ArProject.distinct.paginate :page => 2
87
+ end
88
+
89
+ it "should use :with_foo for scope-out compatibility" do
90
+ ArProject.expects(:find_best).returns(Array.new(5))
91
+ ArProject.expects(:with_best).returns(1)
92
+
93
+ ArProject.paginate_best :page => 1, :per_page => 4
94
+ end
95
+
96
+ describe "paginate_by_sql" do
97
+ it "should paginate" do
98
+ ArProject.expects(:find_by_sql).with(regexp_matches(/sql LIMIT 3(,| OFFSET) 3/)).returns([])
99
+ ArProject.expects(:count_by_sql).with('SELECT COUNT(*) FROM (sql) AS count_table').returns(0)
100
+
101
+ ArProject.paginate_by_sql 'sql', :page => 2, :per_page => 3
102
+ end
103
+
104
+ it "should respect total_entrier setting" do
105
+ ArProject.expects(:find_by_sql).returns([])
106
+ ArProject.expects(:count_by_sql).never
107
+
108
+ entries = ArProject.paginate_by_sql 'sql', :page => 1, :total_entries => 999
109
+ entries.total_entries.should == 999
110
+ end
111
+
112
+ it "should strip the order when counting" do
113
+ ArProject.expects(:find_by_sql).returns([])
114
+ ArProject.expects(:count_by_sql).with("SELECT COUNT(*) FROM (sql\n ) AS count_table").returns(0)
115
+
116
+ ArProject.paginate_by_sql "sql\n ORDER\nby foo, bar, `baz` ASC", :page => 2
117
+ end
118
+
119
+ it "shouldn't change the original query string" do
120
+ query = 'SQL QUERY'
121
+ original_query = query.dup
122
+ ArProject.expects(:find_by_sql).returns([])
123
+
124
+ ArProject.paginate_by_sql(query, :page => 1)
125
+ query.should == original_query
126
+ end
127
+ end
128
+
129
+ # TODO: counts would still be wrong!
130
+ it "should be able to paginate custom finders" do
131
+ # acts_as_taggable defines find_tagged_with(tag, options)
132
+ ArProject.expects(:find_tagged_with).with('will_paginate', :offset => 5, :limit => 5).returns([])
133
+ ArProject.expects(:count).with({}).returns(0)
134
+
135
+ ArProject.paginate_tagged_with 'will_paginate', :page => 2, :per_page => 5
136
+ end
137
+
138
+ it "should not skip count when given an array argument to a finder" do
139
+ ids = (1..8).to_a
140
+ ArProject.expects(:find_all_by_id).returns([])
141
+ ArProject.expects(:count).returns(0)
142
+
143
+ ArProject.paginate_by_id(ids, :per_page => 3, :page => 2, :order => 'id')
144
+ end
145
+
146
+ it "doesn't mangle options" do
147
+ ArProject.expects(:find).returns([])
148
+ options = { :page => 1 }
149
+ options.expects(:delete).never
150
+ options_before = options.dup
151
+
152
+ ArProject.paginate(options)
153
+ options.should == options_before
154
+ end
155
+
156
+ if ::ActiveRecord::Calculations::CALCULATIONS_OPTIONS.include?(:from)
157
+ # for ActiveRecord 2.1 and newer
158
+ it "keeps the :from parameter in count" do
159
+ ArProject.expects(:find).returns([1])
160
+ ArProject.expects(:count).with {|options| options.key?(:from) }.returns(0)
161
+ ArProject.paginate(:page => 2, :per_page => 1, :from => 'projects')
162
+ end
163
+ else
164
+ it "excludes :from parameter from count" do
165
+ ArProject.expects(:find).returns([1])
166
+ ArProject.expects(:count).with {|options| !options.key?(:from) }.returns(0)
167
+ ArProject.paginate(:page => 2, :per_page => 1, :from => 'projects')
168
+ end
169
+ end
170
+
171
+ if ActiverecordTestConnector.able_to_connect
172
+ fixtures :topics, :replies, :users, :projects, :developers_projects
173
+
174
+ it "should get first page of Topics with a single query" do
175
+ lambda {
176
+ result = Topic.paginate :page => nil
177
+ result.current_page.should == 1
178
+ result.total_pages.should == 1
179
+ result.size.should == 4
180
+ }.should run_queries(1)
181
+ end
182
+
183
+ it "should get second (inexistent) page of Topics, requiring 2 queries" do
184
+ lambda {
185
+ result = Topic.paginate :page => 2
186
+ result.total_pages.should == 1
187
+ result.should be_empty
188
+ }.should run_queries(2)
189
+ end
190
+
191
+ it "should paginate with :order" do
192
+ result = Topic.paginate :page => 1, :order => 'created_at DESC'
193
+ result.should == topics(:futurama, :harvey_birdman, :rails, :ar).reverse
194
+ result.total_pages.should == 1
195
+ end
196
+
197
+ it "should paginate with :conditions" do
198
+ result = Topic.paginate :page => 1, :conditions => ["created_at > ?", 30.minutes.ago]
199
+ result.should == topics(:rails, :ar)
200
+ result.total_pages.should == 1
201
+ end
202
+
203
+ it "should paginate with :include and :conditions" do
204
+ result = Topic.paginate \
205
+ :page => 1,
206
+ :include => :replies,
207
+ :conditions => "replies.content LIKE 'Bird%' ",
208
+ :per_page => 10
209
+
210
+ expected = Topic.find :all,
211
+ :include => 'replies',
212
+ :conditions => "replies.content LIKE 'Bird%' ",
213
+ :limit => 10
214
+
215
+ result.should == expected
216
+ result.total_entries.should == 1
217
+ end
218
+
219
+ it "should paginate with :include and :order" do
220
+ result = nil
221
+ lambda {
222
+ result = Topic.paginate \
223
+ :page => 1,
224
+ :include => :replies,
225
+ :order => 'replies.created_at asc, topics.created_at asc',
226
+ :per_page => 10
227
+ }.should run_queries(2)
228
+
229
+ expected = Topic.find :all,
230
+ :include => 'replies',
231
+ :order => 'replies.created_at asc, topics.created_at asc',
232
+ :limit => 10
233
+
234
+ result.should == expected
235
+ result.total_entries.should == 4
236
+ end
237
+
238
+ # detect ActiveRecord 2.1
239
+ if ActiveRecord::Base.private_methods.include_method?(:references_eager_loaded_tables?)
240
+ it "should remove :include for count" do
241
+ Developer.expects(:find).returns([1])
242
+ Developer.expects(:count).with({}).returns(0)
243
+
244
+ Developer.paginate :page => 1, :per_page => 1, :include => :projects
245
+ end
246
+
247
+ it "should keep :include for count when they are referenced in :conditions" do
248
+ Developer.expects(:find).returns([1])
249
+ Developer.expects(:count).with({ :include => :projects, :conditions => 'projects.id > 2' }).returns(0)
250
+
251
+ Developer.paginate :page => 1, :per_page => 1,
252
+ :include => :projects, :conditions => 'projects.id > 2'
253
+ end
254
+ end
255
+
256
+ describe "associations" do
257
+ it "should paginate with include" do
258
+ project = projects(:active_record)
259
+
260
+ result = project.topics.paginate \
261
+ :page => 1,
262
+ :include => :replies,
263
+ :conditions => ["replies.content LIKE ?", 'Nice%'],
264
+ :per_page => 10
265
+
266
+ expected = Topic.find :all,
267
+ :include => 'replies',
268
+ :conditions => ["project_id = #{project.id} AND replies.content LIKE ?", 'Nice%'],
269
+ :limit => 10
270
+
271
+ result.should == expected
272
+ end
273
+
274
+ it "should paginate" do
275
+ dhh = users(:david)
276
+ expected_name_ordered = projects(:action_controller, :active_record)
277
+ expected_id_ordered = projects(:active_record, :action_controller)
278
+
279
+ lambda {
280
+ # with association-specified order
281
+ result = dhh.projects.paginate(:page => 1)
282
+ result.should == expected_name_ordered
283
+ result.total_entries.should == 2
284
+ }.should run_queries(2)
285
+
286
+ # with explicit order
287
+ result = dhh.projects.paginate(:page => 1, :order => 'projects.id')
288
+ result.should == expected_id_ordered
289
+ result.total_entries.should == 2
290
+
291
+ lambda {
292
+ dhh.projects.find(:all, :order => 'projects.id', :limit => 4)
293
+ }.should_not raise_error
294
+
295
+ result = dhh.projects.paginate(:page => 1, :order => 'projects.id', :per_page => 4)
296
+ result.should == expected_id_ordered
297
+
298
+ # has_many with implicit order
299
+ topic = Topic.find(1)
300
+ expected = replies(:spam, :witty_retort)
301
+ # FIXME: wow, this is ugly
302
+ topic.replies.paginate(:page => 1).map(&:id).sort.should == expected.map(&:id).sort
303
+ topic.replies.paginate(:page => 1, :order => 'replies.id ASC').should == expected.reverse
304
+ end
305
+
306
+ it "should paginate through association extension" do
307
+ project = Project.find(:first)
308
+ expected = [replies(:brave)]
309
+
310
+ lambda {
311
+ result = project.replies.paginate_recent :page => 1
312
+ result.should == expected
313
+ }.should run_queries(1)
314
+ end
315
+ end
316
+
317
+ it "should paginate with joins" do
318
+ result = nil
319
+ join_sql = 'LEFT JOIN developers_projects ON users.id = developers_projects.developer_id'
320
+
321
+ lambda {
322
+ result = Developer.paginate :page => 1, :joins => join_sql, :conditions => 'project_id = 1'
323
+ result.size.should == 2
324
+ developer_names = result.map(&:name)
325
+ developer_names.should include('David')
326
+ developer_names.should include('Jamis')
327
+ }.should run_queries(1)
328
+
329
+ lambda {
330
+ expected = result.to_a
331
+ result = Developer.paginate :page => 1, :joins => join_sql,
332
+ :conditions => 'project_id = 1', :count => { :select => "users.id" }
333
+ result.should == expected
334
+ result.total_entries.should == 2
335
+ }.should run_queries(1)
336
+ end
337
+
338
+ it "should paginate with group" do
339
+ result = nil
340
+ lambda {
341
+ result = Developer.paginate :page => 1, :per_page => 10,
342
+ :group => 'salary', :select => 'salary', :order => 'salary'
343
+ }.should run_queries(1)
344
+
345
+ expected = users(:david, :jamis, :dev_10, :poor_jamis).map(&:salary).sort
346
+ result.map(&:salary).should == expected
347
+ end
348
+
349
+ it "should paginate with dynamic finder" do
350
+ expected = replies(:witty_retort, :spam)
351
+ Reply.paginate_by_topic_id(1, :page => 1).should == expected
352
+
353
+ result = Developer.paginate :conditions => { :salary => 100000 }, :page => 1, :per_page => 5
354
+ result.total_entries.should == 8
355
+ Developer.paginate_by_salary(100000, :page => 1, :per_page => 5).should == result
356
+ end
357
+
358
+ it "should paginate with dynamic finder and conditions" do
359
+ result = Developer.paginate_by_salary(100000, :page => 1, :conditions => ['id > ?', 6])
360
+ result.total_entries.should == 4
361
+ result.map(&:id).should == (7..10).to_a
362
+ end
363
+
364
+ it "should raise error when dynamic finder is not recognized" do
365
+ lambda {
366
+ Developer.paginate_by_inexistent_attribute 100000, :page => 1
367
+ }.should raise_error(NoMethodError)
368
+ end
369
+
370
+ it "should paginate with_scope" do
371
+ result = Developer.with_poor_ones { Developer.paginate :page => 1 }
372
+ result.size.should == 2
373
+ result.total_entries.should == 2
374
+ end
375
+
376
+ describe "named_scope" do
377
+ it "should paginate" do
378
+ result = Developer.poor.paginate :page => 1, :per_page => 1
379
+ result.size.should == 1
380
+ result.total_entries.should == 2
381
+ end
382
+
383
+ it "should paginate on habtm association" do
384
+ project = projects(:active_record)
385
+ lambda {
386
+ result = project.developers.poor.paginate :page => 1, :per_page => 1
387
+ result.size.should == 1
388
+ result.total_entries.should == 1
389
+ }.should run_queries(2)
390
+ end
391
+
392
+ it "should paginate on hmt association" do
393
+ project = projects(:active_record)
394
+ expected = [replies(:brave)]
395
+
396
+ lambda {
397
+ result = project.replies.recent.paginate :page => 1, :per_page => 1
398
+ result.should == expected
399
+ result.total_entries.should == 1
400
+ }.should run_queries(2)
401
+ end
402
+
403
+ it "should paginate on has_many association" do
404
+ project = projects(:active_record)
405
+ expected = [topics(:ar)]
406
+
407
+ lambda {
408
+ result = project.topics.mentions_activerecord.paginate :page => 1, :per_page => 1
409
+ result.should == expected
410
+ result.total_entries.should == 1
411
+ }.should run_queries(2)
412
+ end
413
+ end
414
+
415
+ it "should paginate with :readonly option" do
416
+ lambda { Developer.paginate :readonly => true, :page => 1 }.should_not raise_error
417
+ end
418
+
419
+ # detect ActiveRecord 2.0
420
+ unless ActiveRecord::Base.respond_to? :find_all
421
+ it "should paginate array of IDs" do
422
+ # AR finders also accept arrays of IDs
423
+ # (this was broken in Rails before [6912])
424
+ lambda {
425
+ result = Developer.paginate((1..8).to_a, :per_page => 3, :page => 2, :order => 'id')
426
+ result.map(&:id).should == (4..6).to_a
427
+ result.total_entries.should == 8
428
+ }.should run_queries(1)
429
+ end
430
+ end
431
+
432
+ end
433
+
434
+ protected
435
+
436
+ def run_queries(num)
437
+ QueryCountMatcher.new(num)
438
+ end
439
+
440
+ end
441
+
442
+ class QueryCountMatcher
443
+ def initialize(num)
444
+ @queries = num
445
+ @old_query_count = $query_count
446
+ end
447
+
448
+ def matches?(block)
449
+ block.call
450
+ @queries_run = $query_count - @old_query_count
451
+ @queries == @queries_run
452
+ end
453
+
454
+ def failure_message
455
+ "expected #{@queries} queries, got #{@queries_run}"
456
+ end
457
+
458
+ def negative_failure_message
459
+ "expected query count not to be #{$queries}"
460
+ end
461
+ end