public
Rubygem
Fork of mislav/will_paginate
Description: Most awesome pagination solution for Rails
Homepage: http://github.com/mislav/will_paginate/wikis
Clone URL: git://github.com/chriseppstein/will_paginate.git
method_missing_with_paginate now DEFINES paginating finders on self, then 
calls them. for multiple pagination calls to the same resource, this can 
improve performance since method_missing is never hit


git-svn-id: svn://errtheblog.com/svn/plugins/will_paginate@447 
1eaa51fe-a21a-0410-9c2e-ae7a00a434c4
mislav (author)
Sat Feb 23 12:38:59 -0800 2008
commit  6a3be1e668b5326c4131ad0fbe0258755f12b6d7
tree    1429cadce4f9f4ebed8a2b067ec940c211ff4cf7
parent  843bf7c164378da3d07059256fa07530862c5ae1
...
11
12
13
 
14
15
16
...
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
127
128
...
143
144
145
146
 
147
148
 
149
150
151
 
 
 
 
152
153
154
...
174
175
176
177
178
179
180
181
182
183
184
185
 
 
 
186
187
188
...
11
12
13
14
15
16
17
...
101
102
103
 
 
 
 
 
104
105
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
...
158
159
160
 
161
162
 
163
164
 
 
165
166
167
168
169
170
171
...
191
192
193
 
 
 
 
 
 
 
 
 
194
195
196
197
198
199
0
@@ -11,6 +11,7 @@ module WillPaginate
0
       base.extend ClassMethods
0
       class << base
0
         alias_method_chain :method_missing, :paginate
0
+ alias_method_chain :find_every, :paginate
0
         define_method(:per_page) { 30 } unless respond_to?(:per_page)
0
       end
0
     end
0
@@ -100,29 +101,43 @@ module WillPaginate
0
         unless method.to_s.index('paginate') == 0
0
           return method_missing_without_paginate(method, *args, &block)
0
         end
0
-
0
- options = args.pop
0
- page, per_page, total_entries = wp_parse_options!(options)
0
- # an array of IDs may have been given:
0
- total_entries ||= (Array === args.first and args.first.size)
0
         
0
         # paginate finders are really just find_* with limit and offset
0
- finder = method.to_s.sub /^paginate/, 'find'
0
-
0
- # :all is implicit
0
- if finder == 'find'
0
- args.unshift(:all) if args.empty?
0
- elsif finder.index('find_by_') == 0
0
- finder.sub! /^find/, 'find_all'
0
- end
0
-
0
- WillPaginate::Collection.create(page, per_page, total_entries) do |pager|
0
- args << options.except(:count).merge(:offset => pager.offset, :limit => pager.per_page)
0
- pager.replace send(finder, *args)
0
-
0
- # magic counting for user convenience:
0
- pager.total_entries = wp_count!(options, args, finder) unless pager.total_entries
0
- end
0
+ finder = method.to_s.sub('paginate', 'find')
0
+ finder.sub!('find', 'find_all') if finder.index('find_by_') == 0
0
+
0
+ if @owner and @reflection
0
+ unless @wp_extension_module
0
+ @wp_extension_module = Module.new
0
+ self.proxy_extend @wp_extension_module
0
+ end
0
+ eval_mode = 'module_eval'
0
+ @wp_extension_module
0
+ else
0
+ eval_mode = 'instance_eval'
0
+ self
0
+ end.send eval_mode, %{
0
+ def #{method}(*args, &block)
0
+ options = args.pop
0
+ page, per_page, total_entries = wp_parse_options!(options)
0
+ # an array of IDs may have been given:
0
+ total_entries ||= (Array === args.first and args.first.size)
0
+ # :all is implicit
0
+ #{finder == 'find' ? 'args.unshift(:all) if args.empty?' : ''}
0
+
0
+ WillPaginate::Collection.create(page, per_page, total_entries) do |pager|
0
+ args << options.except(:count).merge(:offset => pager.offset, :limit => pager.per_page)
0
+
0
+ @options_from_last_find = nil
0
+ pager.replace #{finder}(*args, &block)
0
+
0
+ # magic counting for user convenience:
0
+ pager.total_entries = wp_count!(options, args, '#{finder}') unless pager.total_entries
0
+ end
0
+ end
0
+ }, __FILE__, __LINE__
0
+ # paginating finder is now defined
0
+ __send__(method, *args, &block)
0
       end
0
 
0
       def wp_count!(options, args, finder)
0
@@ -143,12 +158,14 @@ module WillPaginate
0
         # we may be in a model or an association proxy!
0
         klass = (@owner and @reflection) ? @reflection.klass : self
0
 
0
- count = if finder =~ /^find_/ and klass.respond_to?(scoper = finder.sub(/^find_/, 'with_'))
0
+ count = if finder.index('find_') == 0 and klass.respond_to?(scoper = finder.sub('find', 'with'))
0
                   # scope_out adds a 'with_finder' method which acts like with_scope, if it's present
0
- # then execute the count with the scoping provided by the with_finder
0
+ # then execute the count with the scoping provided by the with_finder
0
                   send(scoper, &counter)
0
- elsif conditions = wp_extract_finder_conditions(finder, args)
0
- # extracted the conditions from calls like "paginate_by_foo_and_bar"
0
+ elsif match = /^find_(all_by|by)_([_a-zA-Z]\w*)$/.match(finder)
0
+ # extract conditions from calls like "paginate_by_foo_and_bar"
0
+ attribute_names = extract_attribute_names_from_match(match)
0
+ conditions = construct_attributes_from_arguments(attribute_names, args)
0
                   with_scope(:find => { :conditions => conditions }, &counter)
0
                 else
0
                   counter.call
0
@@ -174,15 +191,9 @@ module WillPaginate
0
 
0
     private
0
 
0
- # thanks to active record for making us duplicate this code
0
- def wp_extract_finder_conditions(finder, arguments)
0
- return unless match = /^find_(all_by|by)_([_a-zA-Z]\w*)$/.match(finder.to_s)
0
-
0
- attribute_names = extract_attribute_names_from_match(match)
0
- unless all_attributes_exists?(attribute_names)
0
- raise "I can't make sense of `#{finder}`. Try doing the count manually"
0
- end
0
- construct_attributes_from_arguments(attribute_names, arguments)
0
+ def find_every_with_paginate(options)
0
+ @options_from_last_find = options
0
+ find_every_without_paginate(options)
0
       end
0
     end
0
   end
...
205
206
207
 
 
 
 
 
 
 
 
 
208
209
210
...
219
220
221
222
223
 
224
225
226
...
233
234
235
236
237
 
 
238
239
240
241
242
243
244
245
246
 
 
 
 
 
 
 
 
 
 
247
248
249
...
254
255
256
257
258
259
260
261
262
263
264
265
266
267
...
285
286
287
288
289
290
291
292
293
294
295
296
297
 
 
 
 
 
 
 
298
299
300
...
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
...
228
229
230
 
 
231
232
233
234
...
241
242
243
 
 
244
245
246
247
248
249
 
 
 
 
 
250
251
252
253
254
255
256
257
258
259
260
261
262
...
267
268
269
 
 
 
 
 
 
 
 
270
271
272
...
290
291
292
 
 
 
 
 
 
 
 
 
 
293
294
295
296
297
298
299
300
301
302
0
@@ -205,6 +205,15 @@ class FinderTest < ActiveRecordTestCase
0
     assert_nothing_raised { Developer.paginate :readonly => true, :page => 1 }
0
   end
0
 
0
+ def test_pagination_defines_method
0
+ pager = "paginate_by_created_at"
0
+ assert !User.methods.include?(pager), "User methods should not include `#{pager}` method"
0
+ # paginate!
0
+ assert 0, User.send(pager, nil, :page => 1).total_entries
0
+ # the paging finder should now be defined
0
+ assert User.methods.include?(pager), "`#{pager}` method should be defined on User"
0
+ end
0
+
0
   # Is this Rails 2.0? Find out by testing find_all which was removed in [6998]
0
   unless Developer.respond_to? :find_all
0
     def test_paginate_array_of_ids
0
@@ -219,8 +228,7 @@ class FinderTest < ActiveRecordTestCase
0
   uses_mocha 'internals' do
0
     def test_implicit_all_with_dynamic_finders
0
       Topic.expects(:find_all_by_foo).returns([])
0
- Topic.expects(:wp_extract_finder_conditions)
0
- Topic.expects(:count)
0
+ Topic.expects(:count).returns(0)
0
       Topic.paginate_by_foo :page => 1
0
     end
0
     
0
@@ -233,17 +241,22 @@ class FinderTest < ActiveRecordTestCase
0
     end
0
     
0
     def test_extra_parameters_stay_untouched
0
- Topic.expects(:find).with() { |*args| args.last.key? :foo }.returns(Array.new(5))
0
- Topic.expects(:count).with(){ |*args| args.last.key? :foo }.returns(1)
0
+ Topic.expects(:find).with(:all, {:foo => 'bar', :limit => 4, :offset => 0 }).returns(Array.new(5))
0
+ Topic.expects(:count).with({:foo => 'bar'}).returns(1)
0
 
0
       Topic.paginate :foo => 'bar', :page => 1, :per_page => 4
0
     end
0
 
0
- def test_count_doesnt_use_select_options
0
- Developer.expects(:find).with() { |*args| args.last.key? :select }.returns(Array.new(5))
0
- Developer.expects(:count).with(){ |*args| !args.last.key? :select }.returns(1)
0
-
0
- Developer.paginate :select => 'users.*', :page => 1, :per_page => 4
0
+ def test_count_skips_select
0
+ Developer.stubs(:find).returns([])
0
+ Developer.expects(:count).with({}).returns(0)
0
+ Developer.paginate :select => 'salary', :page => 1
0
+ end
0
+
0
+ def test_count_select_when_distinct
0
+ Developer.stubs(:find).returns([])
0
+ Developer.expects(:count).with(:select => 'DISTINCT salary').returns(0)
0
+ Developer.paginate :select => 'DISTINCT salary', :page => 1
0
     end
0
 
0
     def test_should_use_scoped_finders_if_present
0
@@ -254,14 +267,6 @@ class FinderTest < ActiveRecordTestCase
0
       Topic.paginate_best :page => 1, :per_page => 4
0
     end
0
 
0
- def test_ability_to_use_with_custom_finders
0
- # acts_as_taggable defines `find_tagged_with(tag, options)`
0
- Topic.expects(:find_tagged_with).with('will_paginate', :offset => 0, :limit => 5).returns([])
0
- Topic.expects(:count).with({}).returns(0)
0
-
0
- Topic.paginate_tagged_with 'will_paginate', :page => 1, :per_page => 5
0
- end
0
-
0
     def test_paginate_by_sql
0
       assert_respond_to Developer, :paginate_by_sql
0
       Developer.expects(:find_by_sql).with(regexp_matches(/sql LIMIT 3(,| OFFSET) 3/)).returns([])
0
@@ -285,16 +290,13 @@ class FinderTest < ActiveRecordTestCase
0
       entries = Developer.paginate_by_sql "sql\n ORDER\nby foo, bar, `baz` ASC", :page => 1
0
     end
0
 
0
- def test_count_skips_select
0
- Developer.stubs(:find).returns([])
0
- Developer.expects(:count).with({}).returns(0)
0
- Developer.paginate :select => 'salary', :page => 1
0
- end
0
-
0
- def test_count_select_when_distinct
0
- Developer.stubs(:find).returns([])
0
- Developer.expects(:count).with(:select => 'DISTINCT salary').returns(0)
0
- Developer.paginate :select => 'DISTINCT salary', :page => 1
0
+ # TODO: counts are still wrong
0
+ def test_ability_to_use_with_custom_finders
0
+ # acts_as_taggable defines find_tagged_with(tag, options)
0
+ Topic.expects(:find_tagged_with).with('will_paginate', :offset => 0, :limit => 5).returns([])
0
+ Topic.expects(:count).with({}).returns(0)
0
+
0
+ Topic.paginate_tagged_with 'will_paginate', :page => 1, :per_page => 5
0
     end
0
   end
0
 end

Comments

    No one has commented yet.