gcirne-will_paginate 2.3.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. data/CHANGELOG.rdoc +110 -0
  2. data/LICENSE +18 -0
  3. data/README.rdoc +107 -0
  4. data/Rakefile +53 -0
  5. data/examples/apple-circle.gif +0 -0
  6. data/examples/index.haml +69 -0
  7. data/examples/index.html +92 -0
  8. data/examples/pagination.css +90 -0
  9. data/examples/pagination.sass +91 -0
  10. data/init.rb +1 -0
  11. data/lib/will_paginate.rb +80 -0
  12. data/lib/will_paginate/array.rb +16 -0
  13. data/lib/will_paginate/collection.rb +146 -0
  14. data/lib/will_paginate/core_ext.rb +32 -0
  15. data/lib/will_paginate/finder.rb +264 -0
  16. data/lib/will_paginate/named_scope.rb +170 -0
  17. data/lib/will_paginate/named_scope_patch.rb +37 -0
  18. data/lib/will_paginate/version.rb +9 -0
  19. data/lib/will_paginate/view_helpers.rb +402 -0
  20. data/test/boot.rb +21 -0
  21. data/test/collection_test.rb +143 -0
  22. data/test/console +8 -0
  23. data/test/database.yml +22 -0
  24. data/test/finder_test.rb +476 -0
  25. data/test/fixtures/admin.rb +3 -0
  26. data/test/fixtures/developer.rb +14 -0
  27. data/test/fixtures/developers_projects.yml +13 -0
  28. data/test/fixtures/project.rb +15 -0
  29. data/test/fixtures/projects.yml +6 -0
  30. data/test/fixtures/replies.yml +29 -0
  31. data/test/fixtures/reply.rb +7 -0
  32. data/test/fixtures/schema.rb +38 -0
  33. data/test/fixtures/topic.rb +10 -0
  34. data/test/fixtures/topics.yml +30 -0
  35. data/test/fixtures/user.rb +2 -0
  36. data/test/fixtures/users.yml +35 -0
  37. data/test/helper.rb +40 -0
  38. data/test/lib/activerecord_test_case.rb +43 -0
  39. data/test/lib/activerecord_test_connector.rb +75 -0
  40. data/test/lib/load_fixtures.rb +11 -0
  41. data/test/lib/view_test_process.rb +178 -0
  42. data/test/tasks.rake +59 -0
  43. data/test/view_test.rb +365 -0
  44. metadata +124 -0
@@ -0,0 +1,9 @@
1
+ module WillPaginate
2
+ module VERSION
3
+ MAJOR = 2
4
+ MINOR = 3
5
+ TINY = 8
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
@@ -0,0 +1,402 @@
1
+ require 'will_paginate/core_ext'
2
+
3
+ module WillPaginate
4
+ # = Will Paginate view helpers
5
+ #
6
+ # The main view helper, #will_paginate, renders
7
+ # pagination links for the given collection. The helper itself is lightweight
8
+ # and serves only as a wrapper around LinkRenderer instantiation; the
9
+ # renderer then does all the hard work of generating the HTML.
10
+ #
11
+ # == Global options for helpers
12
+ #
13
+ # Options for pagination helpers are optional and get their default values from the
14
+ # <tt>WillPaginate::ViewHelpers.pagination_options</tt> hash. You can write to this hash to
15
+ # override default options on the global level:
16
+ #
17
+ # WillPaginate::ViewHelpers.pagination_options[:previous_label] = 'Previous page'
18
+ #
19
+ # By putting this into "config/initializers/will_paginate.rb" (or simply environment.rb in
20
+ # older versions of Rails) you can easily translate link texts to previous
21
+ # and next pages, as well as override some other defaults to your liking.
22
+ module ViewHelpers
23
+ # default options that can be overridden on the global level
24
+ @@pagination_options = {
25
+ :class => 'pagination',
26
+ :previous_label => '&laquo; Previous',
27
+ :next_label => 'Next &raquo;',
28
+ :inner_window => 4, # links around the current page
29
+ :outer_window => 1, # links around beginning and end
30
+ :separator => ' ', # single space is friendly to spiders and non-graphic browsers
31
+ :param_name => :page,
32
+ :params => nil,
33
+ :renderer => 'WillPaginate::LinkRenderer',
34
+ :page_links => true,
35
+ :container => true
36
+ }
37
+ mattr_reader :pagination_options
38
+
39
+ # Renders Digg/Flickr-style pagination for a WillPaginate::Collection
40
+ # object. Nil is returned if there is only one page in total; no point in
41
+ # rendering the pagination in that case...
42
+ #
43
+ # ==== Options
44
+ # Display options:
45
+ # * <tt>:previous_label</tt> -- default: "« Previous" (this parameter is called <tt>:prev_label</tt> in versions <b>2.3.2</b> and older!)
46
+ # * <tt>:next_label</tt> -- default: "Next »"
47
+ # * <tt>:page_links</tt> -- when false, only previous/next links are rendered (default: true)
48
+ # * <tt>:inner_window</tt> -- how many links are shown around the current page (default: 4)
49
+ # * <tt>:outer_window</tt> -- how many links are around the first and the last page (default: 1)
50
+ # * <tt>:separator</tt> -- string separator for page HTML elements (default: single space)
51
+ #
52
+ # HTML options:
53
+ # * <tt>:class</tt> -- CSS class name for the generated DIV (default: "pagination")
54
+ # * <tt>:container</tt> -- toggles rendering of the DIV container for pagination links, set to
55
+ # false only when you are rendering your own pagination markup (default: true)
56
+ # * <tt>:id</tt> -- HTML ID for the container (default: nil). Pass +true+ to have the ID
57
+ # automatically generated from the class name of objects in collection: for example, paginating
58
+ # ArticleComment models would yield an ID of "article_comments_pagination".
59
+ #
60
+ # Advanced options:
61
+ # * <tt>:param_name</tt> -- parameter name for page number in URLs (default: <tt>:page</tt>)
62
+ # * <tt>:params</tt> -- additional parameters when generating pagination links
63
+ # (eg. <tt>:controller => "foo", :action => nil</tt>)
64
+ # * <tt>:renderer</tt> -- class name, class or instance of a link renderer (default:
65
+ # <tt>WillPaginate::LinkRenderer</tt>)
66
+ #
67
+ # All options not recognized by will_paginate will become HTML attributes on the container
68
+ # element for pagination links (the DIV). For example:
69
+ #
70
+ # <%= will_paginate @posts, :style => 'font-size: small' %>
71
+ #
72
+ # ... will result in:
73
+ #
74
+ # <div class="pagination" style="font-size: small"> ... </div>
75
+ #
76
+ # ==== Using the helper without arguments
77
+ # If the helper is called without passing in the collection object, it will
78
+ # try to read from the instance variable inferred by the controller name.
79
+ # For example, calling +will_paginate+ while the current controller is
80
+ # PostsController will result in trying to read from the <tt>@posts</tt>
81
+ # variable. Example:
82
+ #
83
+ # <%= will_paginate :id => true %>
84
+ #
85
+ # ... will result in <tt>@post</tt> collection getting paginated:
86
+ #
87
+ # <div class="pagination" id="posts_pagination"> ... </div>
88
+ #
89
+ def will_paginate(collection = nil, options = {})
90
+ options, collection = collection, nil if collection.is_a? Hash
91
+ unless collection or !controller
92
+ collection_name = "@#{controller.controller_name}"
93
+ collection = instance_variable_get(collection_name)
94
+ raise ArgumentError, "The #{collection_name} variable appears to be empty. Did you " +
95
+ "forget to pass the collection object for will_paginate?" unless collection
96
+ end
97
+ # early exit if there is nothing to render
98
+ return nil unless WillPaginate::ViewHelpers.total_pages_for_collection(collection) > 1
99
+
100
+ options = options.symbolize_keys.reverse_merge WillPaginate::ViewHelpers.pagination_options
101
+ if options[:prev_label]
102
+ WillPaginate::Deprecation::warn(":prev_label view parameter is now :previous_label; the old name has been deprecated", caller)
103
+ options[:previous_label] = options.delete(:prev_label)
104
+ end
105
+
106
+ # get the renderer instance
107
+ renderer = case options[:renderer]
108
+ when String
109
+ options[:renderer].to_s.constantize.new
110
+ when Class
111
+ options[:renderer].new
112
+ else
113
+ options[:renderer]
114
+ end
115
+ # render HTML for pagination
116
+ renderer.prepare collection, options, self
117
+ renderer.to_html
118
+ end
119
+
120
+ # Wrapper for rendering pagination links at both top and bottom of a block
121
+ # of content.
122
+ #
123
+ # <% paginated_section @posts do %>
124
+ # <ol id="posts">
125
+ # <% for post in @posts %>
126
+ # <li> ... </li>
127
+ # <% end %>
128
+ # </ol>
129
+ # <% end %>
130
+ #
131
+ # will result in:
132
+ #
133
+ # <div class="pagination"> ... </div>
134
+ # <ol id="posts">
135
+ # ...
136
+ # </ol>
137
+ # <div class="pagination"> ... </div>
138
+ #
139
+ # Arguments are passed to a <tt>will_paginate</tt> call, so the same options
140
+ # apply. Don't use the <tt>:id</tt> option; otherwise you'll finish with two
141
+ # blocks of pagination links sharing the same ID (which is invalid HTML).
142
+ def paginated_section(*args, &block)
143
+ pagination = will_paginate(*args).to_s
144
+
145
+ unless ActionView::Base.respond_to? :erb_variable
146
+ concat pagination
147
+ yield
148
+ concat pagination
149
+ else
150
+ content = pagination + capture(&block) + pagination
151
+ concat(content, block.binding)
152
+ end
153
+ end
154
+
155
+ # Renders a helpful message with numbers of displayed vs. total entries.
156
+ # You can use this as a blueprint for your own, similar helpers.
157
+ #
158
+ # <%= page_entries_info @posts %>
159
+ # #-> Displaying posts 6 - 10 of 26 in total
160
+ #
161
+ # By default, the message will use the humanized class name of objects
162
+ # in collection: for instance, "project types" for ProjectType models.
163
+ # Override this with the <tt>:entry_name</tt> parameter:
164
+ #
165
+ # <%= page_entries_info @posts, :entry_name => 'item' %>
166
+ # #-> Displaying items 6 - 10 of 26 in total
167
+ def page_entries_info(collection, options = {})
168
+ entry_name = options[:entry_name] ||
169
+ (collection.empty?? 'entry' : collection.first.class.name.underscore.sub('_', ' '))
170
+
171
+ if collection.total_pages < 2
172
+ case collection.size
173
+ when 0; "No #{entry_name.pluralize} found"
174
+ when 1; "Displaying <b>1</b> #{entry_name}"
175
+ else; "Displaying <b>all #{collection.size}</b> #{entry_name.pluralize}"
176
+ end
177
+ else
178
+ %{Displaying #{entry_name.pluralize} <b>%d&nbsp;-&nbsp;%d</b> of <b>%d</b> in total} % [
179
+ collection.offset + 1,
180
+ collection.offset + collection.length,
181
+ collection.total_entries
182
+ ]
183
+ end
184
+ end
185
+
186
+ def self.total_pages_for_collection(collection) #:nodoc:
187
+ if collection.respond_to?('page_count') and !collection.respond_to?('total_pages')
188
+ WillPaginate::Deprecation.warn %{
189
+ You are using a paginated collection of class #{collection.class.name}
190
+ which conforms to the old API of WillPaginate::Collection by using
191
+ `page_count`, while the current method name is `total_pages`. Please
192
+ upgrade yours or 3rd-party code that provides the paginated collection}, caller
193
+ class << collection
194
+ def total_pages; page_count; end
195
+ end
196
+ end
197
+ collection.total_pages
198
+ end
199
+ end
200
+
201
+ # This class does the heavy lifting of actually building the pagination
202
+ # links. It is used by the <tt>will_paginate</tt> helper internally.
203
+ class LinkRenderer
204
+
205
+ # The gap in page links is represented by:
206
+ #
207
+ # <span class="gap">&hellip;</span>
208
+ attr_accessor :gap_marker
209
+
210
+ def initialize
211
+ @gap_marker = '<span class="gap">&hellip;</span>'
212
+ end
213
+
214
+ # * +collection+ is a WillPaginate::Collection instance or any other object
215
+ # that conforms to that API
216
+ # * +options+ are forwarded from +will_paginate+ view helper
217
+ # * +template+ is the reference to the template being rendered
218
+ def prepare(collection, options, template)
219
+ @collection = collection
220
+ @options = options
221
+ @template = template
222
+
223
+ # reset values in case we're re-using this instance
224
+ @total_pages = @param_name = @url_string = nil
225
+ end
226
+
227
+ # Process it! This method returns the complete HTML string which contains
228
+ # pagination links. Feel free to subclass LinkRenderer and change this
229
+ # method as you see fit.
230
+ def to_html
231
+ links = @options[:page_links] ? windowed_links : []
232
+ # previous/next buttons
233
+ links.unshift page_link_or_span(@collection.previous_page, 'disabled prev_page', @options[:previous_label])
234
+ links.push page_link_or_span(@collection.next_page, 'disabled next_page', @options[:next_label])
235
+
236
+ html = links.join(@options[:separator])
237
+ @options[:container] ? @template.content_tag(:div, html, html_attributes) : html
238
+ end
239
+
240
+ # Returns the subset of +options+ this instance was initialized with that
241
+ # represent HTML attributes for the container element of pagination links.
242
+ def html_attributes
243
+ return @html_attributes if @html_attributes
244
+ @html_attributes = @options.except *(WillPaginate::ViewHelpers.pagination_options.keys - [:class])
245
+ # pagination of Post models will have the ID of "posts_pagination"
246
+ if @options[:container] and @options[:id] === true
247
+ @html_attributes[:id] = @collection.first.class.name.underscore.pluralize + '_pagination'
248
+ end
249
+ @html_attributes
250
+ end
251
+
252
+ protected
253
+
254
+ # Collects link items for visible page numbers.
255
+ def windowed_links
256
+ prev = nil
257
+
258
+ visible_page_numbers.inject [] do |links, n|
259
+ # detect gaps:
260
+ links << gap_marker if prev and n > prev + 1
261
+ links << page_link_or_span(n, 'current')
262
+ prev = n
263
+ links
264
+ end
265
+ end
266
+
267
+ # Calculates visible page numbers using the <tt>:inner_window</tt> and
268
+ # <tt>:outer_window</tt> options.
269
+ def visible_page_numbers
270
+ inner_window, outer_window = @options[:inner_window].to_i, @options[:outer_window].to_i
271
+ window_from = current_page - inner_window
272
+ window_to = current_page + inner_window
273
+
274
+ # adjust lower or upper limit if other is out of bounds
275
+ if window_to > total_pages
276
+ window_from -= window_to - total_pages
277
+ window_to = total_pages
278
+ end
279
+ if window_from < 1
280
+ window_to += 1 - window_from
281
+ window_from = 1
282
+ window_to = total_pages if window_to > total_pages
283
+ end
284
+
285
+ visible = (1..total_pages).to_a
286
+ left_gap = (2 + outer_window)...window_from
287
+ right_gap = (window_to + 1)...(total_pages - outer_window)
288
+ visible -= left_gap.to_a if left_gap.last - left_gap.first > 1
289
+ visible -= right_gap.to_a if right_gap.last - right_gap.first > 1
290
+
291
+ visible
292
+ end
293
+
294
+ def page_link_or_span(page, span_class, text = nil)
295
+ text ||= page.to_s
296
+
297
+ if page and page != current_page
298
+ classnames = span_class && span_class.index(' ') && span_class.split(' ', 2).last
299
+ page_link page, text, :rel => rel_value(page), :class => classnames
300
+ else
301
+ page_span page, text, :class => span_class
302
+ end
303
+ end
304
+
305
+ def page_link(page, text, attributes = {})
306
+ @template.link_to text, url_for(page), attributes
307
+ end
308
+
309
+ def page_span(page, text, attributes = {})
310
+ @template.content_tag :span, text, attributes
311
+ end
312
+
313
+ # Returns URL params for +page_link_or_span+, taking the current GET params
314
+ # and <tt>:params</tt> option into account.
315
+ def url_for(page)
316
+ page_one = page == 1
317
+ unless @url_string and !page_one
318
+ @url_params = {}
319
+ # page links should preserve GET parameters
320
+ stringified_merge @url_params, @template.params if @template.request.get?
321
+ stringified_merge @url_params, @options[:params] if @options[:params]
322
+
323
+ if complex = param_name.index(/[^\w-]/)
324
+ page_param = parse_query_parameters("#{param_name}=#{page}")
325
+
326
+ stringified_merge @url_params, page_param
327
+ else
328
+ @url_params[param_name] = page_one ? 1 : 2
329
+ end
330
+
331
+ url = @template.url_for(@url_params)
332
+ return url if page_one
333
+
334
+ if complex
335
+ @url_string = url.sub(%r!((?:\?|&amp;)#{CGI.escape param_name}=)#{page}!, '\1@')
336
+ return url
337
+ else
338
+ @url_string = url
339
+ @url_params[param_name] = 3
340
+ @template.url_for(@url_params).split(//).each_with_index do |char, i|
341
+ if char == '3' and url[i, 1] == '2'
342
+ @url_string[i] = '@'
343
+ break
344
+ end
345
+ end
346
+ end
347
+ end
348
+ # finally!
349
+ @url_string.sub '@', page.to_s
350
+ end
351
+
352
+ private
353
+
354
+ def rel_value(page)
355
+ case page
356
+ when @collection.previous_page; 'prev' + (page == 1 ? ' start' : '')
357
+ when @collection.next_page; 'next'
358
+ when 1; 'start'
359
+ end
360
+ end
361
+
362
+ def current_page
363
+ @collection.current_page
364
+ end
365
+
366
+ def total_pages
367
+ @total_pages ||= WillPaginate::ViewHelpers.total_pages_for_collection(@collection)
368
+ end
369
+
370
+ def param_name
371
+ @param_name ||= @options[:param_name].to_s
372
+ end
373
+
374
+ # Recursively merge into target hash by using stringified keys from the other one
375
+ def stringified_merge(target, other)
376
+ other.each do |key, value|
377
+ key = key.to_s # this line is what it's all about!
378
+ existing = target[key]
379
+
380
+ if value.is_a?(Hash) and (existing.is_a?(Hash) or existing.nil?)
381
+ stringified_merge(existing || (target[key] = {}), value)
382
+ else
383
+ target[key] = value
384
+ end
385
+ end
386
+ end
387
+
388
+ def parse_query_parameters(params)
389
+ if defined?(CGIMethods)
390
+ CGIMethods.parse_query_parameters(params)
391
+ elsif defined?(ActionController::AbstractRequest)
392
+ ActionController::AbstractRequest.parse_query_parameters(params)
393
+ elsif defined?(ActionController::UrlEncodedPairParser)
394
+ # For Rails > 2.2
395
+ ActionController::UrlEncodedPairParser.parse_query_parameters(params)
396
+ else
397
+ # For Rails > 2.3
398
+ Rack::Utils.parse_nested_query(params)
399
+ end
400
+ end
401
+ end
402
+ end
@@ -0,0 +1,21 @@
1
+ plugin_root = File.join(File.dirname(__FILE__), '..')
2
+ version = ENV['RAILS_VERSION']
3
+ version = nil if version and version == ""
4
+
5
+ # first look for a symlink to a copy of the framework
6
+ if !version and framework_root = ["#{plugin_root}/rails", "#{plugin_root}/../../rails"].find { |p| File.directory? p }
7
+ puts "found framework root: #{framework_root}"
8
+ # this allows for a plugin to be tested outside of an app and without Rails gems
9
+ $:.unshift "#{framework_root}/activesupport/lib", "#{framework_root}/activerecord/lib", "#{framework_root}/actionpack/lib"
10
+ else
11
+ # simply use installed gems if available
12
+ puts "using Rails#{version ? ' ' + version : nil} gems"
13
+ require 'rubygems'
14
+
15
+ if version
16
+ gem 'rails', version
17
+ else
18
+ gem 'actionpack'
19
+ gem 'activerecord'
20
+ end
21
+ end
@@ -0,0 +1,143 @@
1
+ require 'helper'
2
+ require 'will_paginate/array'
3
+
4
+ class ArrayPaginationTest < Test::Unit::TestCase
5
+
6
+ def setup ; end
7
+
8
+ def test_simple
9
+ collection = ('a'..'e').to_a
10
+
11
+ [{ :page => 1, :per_page => 3, :expected => %w( a b c ) },
12
+ { :page => 2, :per_page => 3, :expected => %w( d e ) },
13
+ { :page => 1, :per_page => 5, :expected => %w( a b c d e ) },
14
+ { :page => 3, :per_page => 5, :expected => [] },
15
+ ].
16
+ each do |conditions|
17
+ expected = conditions.delete :expected
18
+ assert_equal expected, collection.paginate(conditions)
19
+ end
20
+ end
21
+
22
+ def test_defaults
23
+ result = (1..50).to_a.paginate
24
+ assert_equal 1, result.current_page
25
+ assert_equal 30, result.size
26
+ end
27
+
28
+ def test_deprecated_api
29
+ assert_raise(ArgumentError) { [].paginate(2) }
30
+ assert_raise(ArgumentError) { [].paginate(2, 10) }
31
+ end
32
+
33
+ def test_total_entries_has_precedence
34
+ result = %w(a b c).paginate :total_entries => 5
35
+ assert_equal 5, result.total_entries
36
+ end
37
+
38
+ def test_argument_error_with_params_and_another_argument
39
+ assert_raise ArgumentError do
40
+ [].paginate({}, 5)
41
+ end
42
+ end
43
+
44
+ def test_paginated_collection
45
+ entries = %w(a b c)
46
+ collection = create(2, 3, 10) do |pager|
47
+ assert_equal entries, pager.replace(entries)
48
+ end
49
+
50
+ assert_equal entries, collection
51
+ assert_respond_to_all collection, %w(total_pages each offset size current_page per_page total_entries)
52
+ assert_kind_of Array, collection
53
+ assert_instance_of Array, collection.entries
54
+ assert_equal 3, collection.offset
55
+ assert_equal 4, collection.total_pages
56
+ assert !collection.out_of_bounds?
57
+ end
58
+
59
+ def test_previous_next_pages
60
+ collection = create(1, 1, 3)
61
+ assert_nil collection.previous_page
62
+ assert_equal 2, collection.next_page
63
+
64
+ collection = create(2, 1, 3)
65
+ assert_equal 1, collection.previous_page
66
+ assert_equal 3, collection.next_page
67
+
68
+ collection = create(3, 1, 3)
69
+ assert_equal 2, collection.previous_page
70
+ assert_nil collection.next_page
71
+ end
72
+
73
+ def test_out_of_bounds
74
+ entries = create(2, 3, 2){}
75
+ assert entries.out_of_bounds?
76
+
77
+ entries = create(1, 3, 2){}
78
+ assert !entries.out_of_bounds?
79
+ end
80
+
81
+ def test_guessing_total_count
82
+ entries = create do |pager|
83
+ # collection is shorter than limit
84
+ pager.replace array
85
+ end
86
+ assert_equal 8, entries.total_entries
87
+
88
+ entries = create(2, 5, 10) do |pager|
89
+ # collection is shorter than limit, but we have an explicit count
90
+ pager.replace array
91
+ end
92
+ assert_equal 10, entries.total_entries
93
+
94
+ entries = create do |pager|
95
+ # collection is the same as limit; we can't guess
96
+ pager.replace array(5)
97
+ end
98
+ assert_equal nil, entries.total_entries
99
+
100
+ entries = create do |pager|
101
+ # collection is empty; we can't guess
102
+ pager.replace array(0)
103
+ end
104
+ assert_equal nil, entries.total_entries
105
+
106
+ entries = create(1) do |pager|
107
+ # collection is empty and we're on page 1,
108
+ # so the whole thing must be empty, too
109
+ pager.replace array(0)
110
+ end
111
+ assert_equal 0, entries.total_entries
112
+ end
113
+
114
+ def test_invalid_page
115
+ bad_inputs = [0, -1, nil, '', 'Schnitzel']
116
+
117
+ bad_inputs.each do |bad|
118
+ assert_raise(WillPaginate::InvalidPage) { create bad }
119
+ end
120
+ end
121
+
122
+ def test_invalid_per_page_setting
123
+ assert_raise(ArgumentError) { create(1, -1) }
124
+ end
125
+
126
+ def test_page_count_was_removed
127
+ assert_raise(NoMethodError) { create.page_count }
128
+ # It's `total_pages` now.
129
+ end
130
+
131
+ private
132
+ def create(page = 2, limit = 5, total = nil, &block)
133
+ if block_given?
134
+ WillPaginate::Collection.create(page, limit, total, &block)
135
+ else
136
+ WillPaginate::Collection.new(page, limit, total)
137
+ end
138
+ end
139
+
140
+ def array(size = 3)
141
+ Array.new(size)
142
+ end
143
+ end