mislav / will_paginate

Adaptive pagination plugin for web frameworks and other applications

will_paginate / lib / will_paginate / collection.rb
ac36b026 » mislav 2007-05-28 Slight finder rewrite (yeah... 1 module WillPaginate
44fe4cc2 » mislav 2008-04-04 doc love all around 2 # = Invalid page number error
138f0049 » mislav 2007-12-24 Will Paginate Christmas doc... 3 # This is an ArgumentError raised in case a page was requested that is either
4 # zero or negative number. You should decide how do deal with such errors in
5 # the controller.
6 #
44fe4cc2 » mislav 2008-04-04 doc love all around 7 # If you're using Rails 2, then this error will automatically get handled like
8 # 404 Not Found. The hook is in "will_paginate.rb":
9 #
10 # ActionController::Base.rescue_responses['WillPaginate::InvalidPage'] = :not_found
11 #
12 # If you don't like this, use your preffered method of rescuing exceptions in
13 # public from your controllers to handle this differently. The +rescue_from+
14 # method is a nice addition to Rails 2.
15 #
138f0049 » mislav 2007-12-24 Will Paginate Christmas doc... 16 # This error is *not* raised when a page further than the last page is
0e5528b0 » mislav 2007-12-24 Will Paginate now has the o... 17 # requested. Use <tt>WillPaginate::Collection#out_of_bounds?</tt> method to
18 # check for those cases and manually deal with them as you see fit.
1c6d4237 » mislav 2007-12-23 Will Paginate now frowns on... 19 class InvalidPage < ArgumentError
20 def initialize(page, page_num)
21 super "#{page.inspect} given as value, which translates to '#{page_num}' as page number"
22 end
23 end
24
44fe4cc2 » mislav 2008-04-04 doc love all around 25 # = The key to pagination
26 # Arrays returned from paginating finds are, in fact, instances of this little
27 # class. You may think of WillPaginate::Collection as an ordinary array with
28 # some extra properties. Those properties are used by view helpers to generate
8ee293e0 » mislav 2007-06-13 Bunch of nice stuff: bugfix... 29 # correct page links.
ac36b026 » mislav 2007-05-28 Slight finder rewrite (yeah... 30 #
ba2d2cc0 » mislav 2007-10-07 Will Paingate dox spellchec... 31 # WillPaginate::Collection also assists in rolling out your own pagination
d5dee204 » mislav 2007-10-07 Will Paginate: more dox for... 32 # solutions: see +create+.
44fe4cc2 » mislav 2008-04-04 doc love all around 33 #
34 # If you are writing a library that provides a collection which you would like
35 # to conform to this API, you don't have to copy these methods over; simply
48d409a2 » mislav 2008-08-13 RDoc love (now live at http... 36 # make your plugin/gem dependant on the "mislav-will_paginate" gem:
d5dee204 » mislav 2007-10-07 Will Paginate: more dox for... 37 #
48d409a2 » mislav 2008-08-13 RDoc love (now live at http... 38 # gem 'mislav-will_paginate'
44fe4cc2 » mislav 2008-04-04 doc love all around 39 # require 'will_paginate/collection'
40 #
48d409a2 » mislav 2008-08-13 RDoc love (now live at http... 41 # # WillPaginate::Collection is now available for use
ac36b026 » mislav 2007-05-28 Slight finder rewrite (yeah... 42 class Collection < Array
9158a472 » mislav 2008-04-03 Rename WillPaginate::Collec... 43 attr_reader :current_page, :per_page, :total_entries, :total_pages
ac36b026 » mislav 2007-05-28 Slight finder rewrite (yeah... 44
44fe4cc2 » mislav 2008-04-04 doc love all around 45 # Arguments to the constructor are the current page number, per-page limit
d5dee204 » mislav 2007-10-07 Will Paginate: more dox for... 46 # and the total number of entries. The last argument is optional because it
47 # is best to do lazy counting; in other words, count *conditionally* after
48 # populating the collection using the +replace+ method.
406c6117 » mislav 2007-09-26 Will Paginate lazy counting... 49 def initialize(page, per_page, total = nil)
50 @current_page = page.to_i
1c6d4237 » mislav 2007-12-23 Will Paginate now frowns on... 51 raise InvalidPage.new(page, @current_page) if @current_page < 1
0fc17eb5 » mislav 2008-04-01 don't require 'will_paginat... 52 @per_page = per_page.to_i
1c6d4237 » mislav 2007-12-23 Will Paginate now frowns on... 53 raise ArgumentError, "`per_page` setting cannot be less than 1 (#{@per_page} given)" if @per_page < 1
406c6117 » mislav 2007-09-26 Will Paginate lazy counting... 54
55 self.total_entries = total if total
56 end
57
d5dee204 » mislav 2007-10-07 Will Paginate: more dox for... 58 # Just like +new+, but yields the object after instantiation and returns it
59 # afterwards. This is very useful for manual pagination:
60 #
61 # @entries = WillPaginate::Collection.create(1, 10) do |pager|
62 # result = Post.find(:all, :limit => pager.per_page, :offset => pager.offset)
63 # # inject the result array into the paginated collection:
64 # pager.replace(result)
65 #
66 # unless pager.total_entries
67 # # the pager didn't manage to guess the total count, do it manually
68 # pager.total_entries = Post.count
69 # end
70 # end
71 #
72 # The possibilities with this are endless. For another example, here is how
0e5528b0 » mislav 2007-12-24 Will Paginate now has the o... 73 # WillPaginate used to define pagination for Array instances:
d5dee204 » mislav 2007-10-07 Will Paginate: more dox for... 74 #
75 # Array.class_eval do
76 # def paginate(page = 1, per_page = 15)
77 # WillPaginate::Collection.create(page, per_page, size) do |pager|
78 # pager.replace self[pager.offset, pager.per_page].to_a
79 # end
80 # end
81 # end
82 #
44fe4cc2 » mislav 2008-04-04 doc love all around 83 # The Array#paginate API has since then changed, but this still serves as a
84 # fine example of WillPaginate::Collection usage.
029964fe » Darrick Wiebe 2009-02-09 Removed all unnecessary &bl... 85 def self.create(page, per_page, total = nil)
406c6117 » mislav 2007-09-26 Will Paginate lazy counting... 86 pager = new(page, per_page, total)
87 yield pager
88 pager
ac36b026 » mislav 2007-05-28 Slight finder rewrite (yeah... 89 end
90
1c6d4237 » mislav 2007-12-23 Will Paginate now frowns on... 91 # Helper method that is true when someone tries to fetch a page with a
92 # larger number than the last page. Can be used in combination with flashes
93 # and redirecting.
b44d79e0 » mislav 2007-10-06 Don't guess total count whe... 94 def out_of_bounds?
9158a472 » mislav 2008-04-03 Rename WillPaginate::Collec... 95 current_page > total_pages
b44d79e0 » mislav 2007-10-06 Don't guess total count whe... 96 end
97
8ee293e0 » mislav 2007-06-13 Bunch of nice stuff: bugfix... 98 # Current offset of the paginated collection. If we're on the first page,
99 # it is always 0. If we're on the 2nd page and there are 30 entries per page,
100 # the offset is 30. This property is useful if you want to render ordinals
48d409a2 » mislav 2008-08-13 RDoc love (now live at http... 101 # side by side with records in the view: simply start with offset + 1.
97d63b11 » mislav 2007-06-06 first_index is renamed to "... 102 def offset
ac36b026 » mislav 2007-05-28 Slight finder rewrite (yeah... 103 (current_page - 1) * per_page
104 end
97d63b11 » mislav 2007-06-06 first_index is renamed to "... 105
8ee293e0 » mislav 2007-06-13 Bunch of nice stuff: bugfix... 106 # current_page - 1 or nil if there is no previous page
97d63b11 » mislav 2007-06-06 first_index is renamed to "... 107 def previous_page
108 current_page > 1 ? (current_page - 1) : nil
109 end
110
8ee293e0 » mislav 2007-06-13 Bunch of nice stuff: bugfix... 111 # current_page + 1 or nil if there is no next page
97d63b11 » mislav 2007-06-06 first_index is renamed to "... 112 def next_page
9158a472 » mislav 2008-04-03 Rename WillPaginate::Collec... 113 current_page < total_pages ? (current_page + 1) : nil
97d63b11 » mislav 2007-06-06 first_index is renamed to "... 114 end
48d409a2 » mislav 2008-08-13 RDoc love (now live at http... 115
116 # sets the <tt>total_entries</tt> property and calculates <tt>total_pages</tt>
406c6117 » mislav 2007-09-26 Will Paginate lazy counting... 117 def total_entries=(number)
118 @total_entries = number.to_i
119 @total_pages = (@total_entries / per_page.to_f).ceil
120 end
121
d5dee204 » mislav 2007-10-07 Will Paginate: more dox for... 122 # This is a magic wrapper for the original Array#replace method. It serves
123 # for populating the paginated collection after initialization.
124 #
ba2d2cc0 » mislav 2007-10-07 Will Paingate dox spellchec... 125 # Why magic? Because it tries to guess the total number of entries judging
d5dee204 » mislav 2007-10-07 Will Paginate: more dox for... 126 # by the size of given array. If it is shorter than +per_page+ limit, then we
127 # know we're on the last page. This trick is very useful for avoiding
ba2d2cc0 » mislav 2007-10-07 Will Paingate dox spellchec... 128 # unnecessary hits to the database to do the counting after we fetched the
d5dee204 » mislav 2007-10-07 Will Paginate: more dox for... 129 # data for the current page.
130 #
131 # However, after using +replace+ you should always test the value of
132 # +total_entries+ and set it to a proper value if it's +nil+. See the example
133 # in +create+.
406c6117 » mislav 2007-09-26 Will Paginate lazy counting... 134 def replace(array)
61c6ddeb » mislav 2008-04-01 don't use `returning`. now ... 135 result = super
136
137 # The collection is shorter then page limit? Rejoice, because
138 # then we know that we are on the last page!
b4c00a7a » mislav 2008-04-02 Add paginated_each method f... 139 if total_entries.nil? and length < per_page and (current_page == 1 or length > 0)
61c6ddeb » mislav 2008-04-01 don't use `returning`. now ... 140 self.total_entries = offset + length
406c6117 » mislav 2007-09-26 Will Paginate lazy counting... 141 end
61c6ddeb » mislav 2008-04-01 don't use `returning`. now ... 142
143 result
406c6117 » mislav 2007-09-26 Will Paginate lazy counting... 144 end
ac36b026 » mislav 2007-05-28 Slight finder rewrite (yeah... 145 end
146 end