merb_paginate 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. data/CHANGELOG.rdoc +105 -0
  2. data/LICENSE +18 -0
  3. data/README.rdoc +125 -0
  4. data/Rakefile +59 -0
  5. data/lib/merbtasks.rb +17 -0
  6. data/lib/will_paginate.rb +40 -0
  7. data/lib/will_paginate/array.rb +35 -0
  8. data/lib/will_paginate/collection.rb +145 -0
  9. data/lib/will_paginate/core_ext.rb +58 -0
  10. data/lib/will_paginate/deprecation.rb +50 -0
  11. data/lib/will_paginate/finders.rb +9 -0
  12. data/lib/will_paginate/finders/active_record.rb +192 -0
  13. data/lib/will_paginate/finders/active_record/named_scope.rb +170 -0
  14. data/lib/will_paginate/finders/active_record/named_scope_patch.rb +39 -0
  15. data/lib/will_paginate/finders/active_resource.rb +51 -0
  16. data/lib/will_paginate/finders/base.rb +112 -0
  17. data/lib/will_paginate/finders/data_mapper.rb +30 -0
  18. data/lib/will_paginate/finders/sequel.rb +21 -0
  19. data/lib/will_paginate/version.rb +9 -0
  20. data/lib/will_paginate/view_helpers.rb +42 -0
  21. data/lib/will_paginate/view_helpers/base.rb +126 -0
  22. data/lib/will_paginate/view_helpers/link_renderer.rb +130 -0
  23. data/lib/will_paginate/view_helpers/link_renderer_base.rb +83 -0
  24. data/lib/will_paginate/view_helpers/merb.rb +13 -0
  25. data/spec/collection_spec.rb +147 -0
  26. data/spec/console +8 -0
  27. data/spec/console_fixtures.rb +8 -0
  28. data/spec/database.yml +22 -0
  29. data/spec/finders/active_record_spec.rb +461 -0
  30. data/spec/finders/active_resource_spec.rb +52 -0
  31. data/spec/finders/activerecord_test_connector.rb +108 -0
  32. data/spec/finders/data_mapper_spec.rb +62 -0
  33. data/spec/finders/data_mapper_test_connector.rb +20 -0
  34. data/spec/finders/sequel_spec.rb +53 -0
  35. data/spec/finders/sequel_test_connector.rb +9 -0
  36. data/spec/finders_spec.rb +76 -0
  37. data/spec/fixtures/admin.rb +3 -0
  38. data/spec/fixtures/developer.rb +13 -0
  39. data/spec/fixtures/developers_projects.yml +13 -0
  40. data/spec/fixtures/project.rb +15 -0
  41. data/spec/fixtures/projects.yml +6 -0
  42. data/spec/fixtures/replies.yml +29 -0
  43. data/spec/fixtures/reply.rb +7 -0
  44. data/spec/fixtures/schema.rb +38 -0
  45. data/spec/fixtures/topic.rb +6 -0
  46. data/spec/fixtures/topics.yml +30 -0
  47. data/spec/fixtures/user.rb +2 -0
  48. data/spec/fixtures/users.yml +35 -0
  49. data/spec/rcov.opts +2 -0
  50. data/spec/spec.opts +2 -0
  51. data/spec/spec_helper.rb +75 -0
  52. data/spec/tasks.rake +60 -0
  53. data/spec/view_helpers/base_spec.rb +64 -0
  54. data/spec/view_helpers/link_renderer_base_spec.rb +84 -0
  55. data/spec/view_helpers/view_example_group.rb +111 -0
  56. metadata +126 -0
@@ -0,0 +1,105 @@
1
+ == "agnostic" branch
2
+
3
+ * added Sequel support
4
+ * added an initialization hook for Merb
5
+ * refactored URL generation
6
+ * BACKWARDS INCOMPATIBLE: refactored LinkRenderer; also markup changes
7
+ <span class="current">1</span> is now <em>1</em>
8
+ a.prev_page -> a.previous_page (for consistency)
9
+ * "prev_label" -> "previous_label"
10
+ * ported view tests to specs
11
+ * setup Autotest
12
+ * added per_page=(limit) attribute writer to set default per_page
13
+ * Remove :include option from count_all query when possible (Rails 2.1)
14
+ * added WP::ViewHelpers::ActionView and LinkRenderer
15
+ * specs for ViewHelpers::Base and LinkRendererBase
16
+ * created LinkRendererBase that implements windowed visible page numbers logic
17
+ * created WP::ViewHelpers::Base abstract module that implements generic view helpers
18
+ * ported finder tests to specs
19
+ * added WP::Finders::DataMapper
20
+ * added WP::Finders::ActiveRecord mixin for ActiveRecord::Base
21
+ * created WP::Finders::Base abstract module that implements generic pagination logic
22
+ * removed dependency to ActiveSupport
23
+
24
+ == 2.3.1, released 2008-05-04
25
+
26
+ * Fixed page numbers not showing with custom routes and implicit first page
27
+ * Try to use Hanna for documentation (falls back to default RDoc template if not)
28
+
29
+ == 2.3.0, released 2008-04-29
30
+
31
+ * Changed LinkRenderer to receive collection, options and reference to view template NOT in
32
+ constructor, but with the #prepare method. This is a step towards supporting passing of
33
+ LinkRenderer (or subclass) instances that may be preconfigured in some way
34
+ * LinkRenderer now has #page_link and #page_span methods for easier customization of output in
35
+ subclasses
36
+ * Changed page_entries_info() method to adjust its output according to humanized class name of
37
+ collection items. Override this with :entry_name parameter (singular).
38
+
39
+ page_entries_info(@posts)
40
+ #-> "Displaying all 12 posts"
41
+ page_entries_info(@posts, :entry_name => 'item')
42
+ #-> "Displaying all 12 items"
43
+
44
+ == 2.2.3, released 2008-04-26
45
+
46
+ * will_paginate gem is no longer published on RubyForge, but on
47
+ gems.github.com:
48
+
49
+ gem sources -a http://gems.github.com/ (you only need to do this once)
50
+ gem install mislav-will_paginate
51
+
52
+ * extract reusable pagination testing stuff into WillPaginate::View
53
+ * rethink the page URL construction mechanism to be more bulletproof when
54
+ combined with custom routing for page parameter
55
+ * test that anchor parameter can be used in pagination links
56
+
57
+ == 2.2.2, released 2008-04-21
58
+
59
+ * Add support for page parameter in custom routes like "/foo/page/2"
60
+ * Change output of "page_entries_info" on single-page collection and erroneous
61
+ output with empty collection as reported by Tim Chater
62
+
63
+ == 2.2.1, released 2008-04-08
64
+
65
+ * take less risky path when monkeypatching named_scope; fix that it no longer
66
+ requires ActiveRecord::VERSION
67
+ * use strings in "respond_to?" calls to work around a bug in acts_as_ferret
68
+ stable (ugh)
69
+ * add rake release task
70
+
71
+
72
+ == 2.2.0, released 2008-04-07
73
+
74
+ === API changes
75
+ * Rename WillPaginate::Collection#page_count to "total_pages" for consistency.
76
+ If you implemented this interface, change your implementation accordingly.
77
+ * Remove old, deprecated style of calling Array#paginate as "paginate(page,
78
+ per_page)". If you want to specify :page, :per_page or :total_entries, use a
79
+ parameter hash.
80
+ * Rename LinkRenderer#url_options to "url_for" and drastically optimize it
81
+
82
+ === View changes
83
+ * Added "prev_page" and "next_page" CSS classes on previous/next page buttons
84
+ * Add examples of pagination links styling in "examples/index.html"
85
+ * Change gap in pagination links from "..." to
86
+ "<span class="gap">&hellip;</span>".
87
+ * Add "paginated_section", a block helper that renders pagination both above and
88
+ below content in the block
89
+ * Add rel="prev|next|start" to page links
90
+
91
+ === Other
92
+
93
+ * Add ability to opt-in for Rails 2.1 feature "named_scope" by calling
94
+ WillPaginate.enable_named_scope (tested in Rails 1.2.6 and 2.0.2)
95
+ * Support complex page parameters like "developers[page]"
96
+ * Move Array#paginate definition to will_paginate/array.rb. You can now easily
97
+ use pagination on arrays outside of Rails:
98
+
99
+ gem 'will_paginate'
100
+ require 'will_paginate/array'
101
+
102
+ * Add "paginated_each" method for iterating through every record by loading only
103
+ one page of records at the time
104
+ * Rails 2: Rescue from WillPaginate::InvalidPage error with 404 Not Found by
105
+ default
data/LICENSE ADDED
@@ -0,0 +1,18 @@
1
+ Copyright (c) 2009 Mislav Marohnić
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7
+ the Software, and to permit persons to whom the Software is furnished to do so,
8
+ subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
16
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,125 @@
1
+ = The will_paginate Ruby library
2
+
3
+ Pagination is just limiting the number of records loaded and displayed. Why should you let it get in
4
+ your way while developing?
5
+
6
+ This is how you paginate on an ActiveRecord model:
7
+
8
+ Post.paginate :page => 1, :order => 'created_at DESC'
9
+
10
+ Most of the time it's as simple as replacing "find" with "paginate" and specifying the page you want.
11
+
12
+ Some resources to get you started:
13
+
14
+ * The {will_paginate project page}[http://mislav.github.com/will_paginate/];
15
+ * Your mind reels with questions? Join our {Google group}[http://groups.google.com/group/will_paginate];
16
+ * {How to report bugs}[http://github.com/mislav/will_paginate/wikis/report-bugs];
17
+ * {Watch the will_paginate screencast}[http://railscasts.com/episodes/51] by Ryan Bates.
18
+
19
+ == I'm not using Rails; can I still use will_paginate?
20
+
21
+ Absolutely -- although will_paginate started off as a Rails plugin, now it is a <em>completely
22
+ framework-agnostic</em> library with support for Rails and Merb built-in. The core library doesn't
23
+ have any dependences and you can safely use it in any Ruby code.
24
+
25
+ When will_paginate is loaded in an environment where ActiveRecord and ActionView are present, it
26
+ automatically hooks into these frameworks to provide easy pagination on your models and in your
27
+ views. The same mechanism works for Merb applications, too. But, if no known framework is present
28
+ then you have absolute control over what parts of will_paginate do you want to load and where you want
29
+ them mixed in.
30
+
31
+
32
+ == Installation
33
+
34
+ The recommended way is that you get the gem hosted on {gems.github.com}[http://gems.github.com/]:
35
+
36
+ gem install mislav-will_paginate
37
+
38
+ In <b>Rails 2.1</b>, add a gem dependency:
39
+
40
+ # for Rails 2.1 and newer
41
+ config.gem 'mislav-will_paginate', :lib => 'will_paginate', :version => '~> 3.0'
42
+
43
+ If you're using Rails 2.0 or older, or any other Ruby framework, just add a simple require to a file
44
+ that initializes your application. For example, in Rails you would put this at the end of
45
+ "config/environment.rb".
46
+
47
+ gem 'mislav-will_paginate', '~> 3.0'
48
+ require 'will_paginate'
49
+
50
+ That's it. Remember to install the gem on <strong>all</strong> machines that you are deploying to.
51
+
52
+ <i>There are extensive {installation
53
+ instructions}[http://github.com/mislav/will_paginate/wikis/installation] on {the
54
+ wiki}[http://github.com/mislav/will_paginate/wikis].</i>
55
+
56
+
57
+ == Example usage
58
+
59
+ Typical usage involves a paginating find in the controller:
60
+
61
+ @posts = Post.paginate :page => params[:page], :order => 'updated_at DESC'
62
+
63
+ It's true: +paginate+ works just like +find+ -- it just doesn't fetch all the records. Don't forget
64
+ to tell it which page you want, or it will complain! Read more in WillPaginate::Finders.
65
+
66
+ Render the posts in your view like you would normally do, and when you need to render pagination,
67
+ just stick this in:
68
+
69
+ <%= will_paginate @posts %>
70
+
71
+ You're done. Read more in WillPaginate::ViewHelpers::Base.
72
+
73
+ How does it know how much items to fetch per page? It asks your model by calling its
74
+ +per_page+ class method. You can define it like this:
75
+
76
+ class Post < ActiveRecord::Base
77
+ self.per_page = 50
78
+ end
79
+
80
+ ... or don't worry about it at all. WillPaginate defines it to be <strong>30</strong> by default. You can
81
+ always specify the count explicitly when calling +paginate+:
82
+
83
+ Post.paginate :page => params[:page], :per_page => 50
84
+
85
+ The +paginate+ finder wraps the original finder and returns your result set that now has some new
86
+ properties. You can use the collection as you would use any other array. WillPaginate view helpers
87
+ also need that collection object to be able to render pagination:
88
+
89
+ <ol>
90
+ <% for post in @posts -%>
91
+ <li>Render `post` in some nice way.</li>
92
+ <% end -%>
93
+ </ol>
94
+
95
+ <p>Now let's render us some pagination!</p>
96
+ <%= will_paginate @posts %>
97
+
98
+
99
+ == Authors and credits
100
+
101
+ The original author of will_paginate is PJ Hyett, who later handed over development to Mislav
102
+ Marohnić. The library was almost completely rewritten twice since then.
103
+
104
+ All these people helped making will_paginate what it is now with their code contributions or just
105
+ simply awesome ideas:
106
+
107
+ Chris Wanstrath, Dr. Nic Williams, K. Adam Christensen, Mike Garey, Bence Golda, Matt Aimonetti,
108
+ Charles Brian Quinn, Desi McAdam, James Coglan, Matijs van Zuijlen, Maria, Brendan Ribera, Todd
109
+ Willey, Bryan Helmkamp, Jan Berkel, Lourens Naudé, Rick Olson, Russell Norris, Piotr Usewicz, Chris
110
+ Eppstein, Brandon Arbini, Denis Barushev, Paul Barry, Ben Pickles, Ken Collins, Lida Tang and Pieter
111
+ Noordhuis.
112
+
113
+
114
+ == Usable pagination in the UI
115
+
116
+ There are example CSS styles to get you started on the will_paginate project page.
117
+
118
+ More reading about pagination as design pattern:
119
+
120
+ * {Pagination 101}[http://kurafire.net/log/archive/2007/06/22/pagination-101];
121
+ * {Pagination gallery}[http://www.smashingmagazine.com/2007/11/16/pagination-gallery-examples-and-good-practices/] featured on Smashing Magazine;
122
+ * {Pagination design pattern}[http://developer.yahoo.com/ypatterns/parent.php?pattern=pagination] on Yahoo Design Pattern Library.
123
+
124
+ Want to discuss, request features, ask questions? Join the {Google
125
+ group}[http://groups.google.com/group/will_paginate].
@@ -0,0 +1,59 @@
1
+ require 'rubygems'
2
+ require 'rake/gempackagetask'
3
+
4
+ require 'merb-core'
5
+ require 'merb-core/tasks/merb'
6
+
7
+ load 'spec/tasks.rake'
8
+
9
+ desc 'Default: run specs.'
10
+ task :default => :spec
11
+
12
+ GEM_NAME = "merb_paginate"
13
+ GEM_VERSION = "0.9.0"
14
+ AUTHORS = ['Jacques Crocker', 'Mislav Marohnić', 'PJ Hyett']
15
+ EMAIL = 'merbjedi@gmail.com'
16
+ HOMEPAGE = 'http://github.com/merbjedi/merb_paginate'
17
+ SUMMARY = "merb_paginate is a fork of will_paginate agnostic branch, refocused to work specifically with Merb"
18
+
19
+ spec = Gem::Specification.new do |s|
20
+ s.rubyforge_project = 'merb'
21
+ s.name = GEM_NAME
22
+ s.version = GEM_VERSION
23
+ s.platform = Gem::Platform::RUBY
24
+ s.has_rdoc = true
25
+ s.extra_rdoc_files = ["README.rdoc", "LICENSE"]
26
+ s.summary = SUMMARY
27
+ s.description = s.summary
28
+ s.authors = AUTHORS
29
+ s.email = EMAIL
30
+ s.homepage = HOMEPAGE
31
+
32
+ s.add_dependency('merb-core', '>= 1.0')
33
+
34
+ s.require_path = 'lib'
35
+ s.files = %w(LICENSE README.rdoc CHANGELOG.rdoc Rakefile) + Dir.glob("{lib,spec}/**/*")
36
+ end
37
+
38
+ Rake::GemPackageTask.new(spec) do |pkg|
39
+ pkg.gem_spec = spec
40
+ end
41
+
42
+ desc "install the plugin as a gem"
43
+ task :install do
44
+ Merb::RakeHelper.install(GEM_NAME, :version => GEM_VERSION)
45
+ end
46
+
47
+ desc "Uninstall the gem"
48
+ task :uninstall do
49
+ Merb::RakeHelper.uninstall(GEM_NAME, :version => GEM_VERSION)
50
+ end
51
+
52
+ desc "Create a gemspec file"
53
+ task :gemspec do
54
+ File.open("#{GEM_NAME}.gemspec", "w") do |file|
55
+ file.puts spec.to_ruby
56
+ end
57
+ end
58
+
59
+ require 'lib/merbtasks'
@@ -0,0 +1,17 @@
1
+ namespace :app_config do
2
+ desc "Create a blank config/app_config.yml file"
3
+ task :init do
4
+ puts "Setting up AppConfig files..."
5
+ `mkdir -p config/app_config`
6
+
7
+ ["config/app_config.yml",
8
+ "config/app_config/development.yml",
9
+ "config/app_config/production.yml"
10
+ ].each do |path|
11
+ `touch #{path}`
12
+ puts "Created: #{path}"
13
+ end
14
+ puts "Complete!"
15
+ puts "Add key/value pairs to your yaml file,\nthen access them in your Merb project via AppConfig.[key]"
16
+ end
17
+ end
@@ -0,0 +1,40 @@
1
+ require 'will_paginate/deprecation'
2
+
3
+ # = You *will* paginate!
4
+ #
5
+ # First read about WillPaginate::Finders::Base, then see
6
+ # WillPaginate::ViewHelpers. The magical array you're handling in-between is
7
+ # WillPaginate::Collection.
8
+ #
9
+ # Happy paginating!
10
+ module WillPaginate
11
+ # This method used to hook in ActiveRecord and ActionView at load time,
12
+ # but now doesn't do anything anymore and will be removed in future releases.
13
+ def self.enable
14
+ Deprecation.warn "WillPaginate::enable() doesn't do anything anymore"
15
+ end
16
+
17
+ # Enable named_scope, a feature of Rails 2.1, even if you have older Rails
18
+ # (tested on Rails 2.0.2 and 1.2.6).
19
+ #
20
+ # You can pass +false+ for +patch+ parameter to skip monkeypatching
21
+ # *associations*. Use this if you feel that <tt>named_scope</tt> broke
22
+ # has_many, has_many :through or has_and_belongs_to_many associations in
23
+ # your app. By passing +false+, you can still use <tt>named_scope</tt> in
24
+ # your models, but not through associations.
25
+ def self.enable_named_scope(patch = true)
26
+ return if defined? ActiveRecord::NamedScope
27
+ require 'will_paginate/finders/active_record/named_scope'
28
+ require 'will_paginate/finders/active_record/named_scope_patch' if patch
29
+
30
+ ActiveRecord::Base.send :include, WillPaginate::NamedScope
31
+ end
32
+ end
33
+
34
+ if defined?(Merb::Plugins)
35
+ require 'will_paginate/view_helpers/merb'
36
+ # auto-load the right ORM adapter
37
+ if adapter = { :datamapper => 'data_mapper', :activerecord => 'active_record', :sequel => 'sequel' }[Merb.orm]
38
+ require "will_paginate/finders/#{adapter}"
39
+ end
40
+ end
@@ -0,0 +1,35 @@
1
+ require 'will_paginate/collection'
2
+
3
+ class Array
4
+ # Paginates a static array (extracting a subset of it). The result is a
5
+ # WillPaginate::Collection instance, which is an array with a few more
6
+ # properties about its paginated state.
7
+ #
8
+ # Parameters:
9
+ # * <tt>:page</tt> - current page, defaults to 1
10
+ # * <tt>:per_page</tt> - limit of items per page, defaults to 30
11
+ # * <tt>:total_entries</tt> - total number of items in the array, defaults to
12
+ # <tt>array.length</tt> (obviously)
13
+ #
14
+ # Example:
15
+ # arr = ['a', 'b', 'c', 'd', 'e']
16
+ # paged = arr.paginate(:per_page => 2) #-> ['a', 'b']
17
+ # paged.total_entries #-> 5
18
+ # arr.paginate(:page => 2, :per_page => 2) #-> ['c', 'd']
19
+ # arr.paginate(:page => 3, :per_page => 2) #-> ['e']
20
+ #
21
+ # This method was originally {suggested by Desi
22
+ # McAdam}[http://www.desimcadam.com/archives/8] and later proved to be the
23
+ # most useful method of will_paginate library.
24
+ def paginate(options = {})
25
+ raise ArgumentError, "parameter hash expected (got #{options.inspect})" unless Hash === options
26
+
27
+ WillPaginate::Collection.create options[:page] || 1,
28
+ options[:per_page] || 30,
29
+ options[:total_entries] || self.length do |pager|
30
+ pager.replace self[pager.offset, pager.per_page].to_a
31
+ end
32
+ end
33
+
34
+ alias :total_entries :size
35
+ end
@@ -0,0 +1,145 @@
1
+ module WillPaginate
2
+ # = Invalid page number error
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
+ #
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
+ #
16
+ # This error is *not* raised when a page further than the last page is
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.
19
+ class InvalidPage < ArgumentError
20
+ def initialize(page, page_num) #:nodoc:
21
+ super "#{page.inspect} given as value, which translates to '#{page_num}' as page number"
22
+ end
23
+ end
24
+
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
29
+ # correct page links.
30
+ #
31
+ # WillPaginate::Collection also assists in rolling out your own pagination
32
+ # solutions: see +create+.
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
36
+ # make your plugin/gem dependant on the "will_paginate" gem:
37
+ #
38
+ # gem 'will_paginate'
39
+ # require 'will_paginate/collection'
40
+ #
41
+ # # now use WillPaginate::Collection directly or subclass it
42
+ class Collection < Array
43
+ attr_reader :current_page, :per_page, :total_entries, :total_pages
44
+
45
+ # Arguments to the constructor are the current page number, per-page limit
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.
49
+ def initialize(page, per_page, total = nil)
50
+ @current_page = page.to_i
51
+ raise InvalidPage.new(page, @current_page) if @current_page < 1
52
+ @per_page = per_page.to_i
53
+ raise ArgumentError, "`per_page` setting cannot be less than 1 (#{@per_page} given)" if @per_page < 1
54
+
55
+ self.total_entries = total if total
56
+ end
57
+
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
73
+ # WillPaginate used to define pagination for Array instances:
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
+ #
83
+ # The Array#paginate API has since then changed, but this still serves as a
84
+ # fine example of WillPaginate::Collection usage.
85
+ def self.create(page, per_page, total = nil, &block)
86
+ pager = new(page, per_page, total)
87
+ yield pager
88
+ pager
89
+ end
90
+
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.
94
+ def out_of_bounds?
95
+ current_page > total_pages
96
+ end
97
+
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
101
+ # besides your records: simply start with offset + 1.
102
+ def offset
103
+ (current_page - 1) * per_page
104
+ end
105
+
106
+ # current_page - 1 or nil if there is no previous page
107
+ def previous_page
108
+ current_page > 1 ? (current_page - 1) : nil
109
+ end
110
+
111
+ # current_page + 1 or nil if there is no next page
112
+ def next_page
113
+ current_page < total_pages ? (current_page + 1) : nil
114
+ end
115
+
116
+ def total_entries=(number)
117
+ @total_entries = number.to_i
118
+ @total_pages = (@total_entries / per_page.to_f).ceil
119
+ end
120
+
121
+ # This is a magic wrapper for the original Array#replace method. It serves
122
+ # for populating the paginated collection after initialization.
123
+ #
124
+ # Why magic? Because it tries to guess the total number of entries judging
125
+ # by the size of given array. If it is shorter than +per_page+ limit, then we
126
+ # know we're on the last page. This trick is very useful for avoiding
127
+ # unnecessary hits to the database to do the counting after we fetched the
128
+ # data for the current page.
129
+ #
130
+ # However, after using +replace+ you should always test the value of
131
+ # +total_entries+ and set it to a proper value if it's +nil+. See the example
132
+ # in +create+.
133
+ def replace(array)
134
+ result = super
135
+
136
+ # The collection is shorter then page limit? Rejoice, because
137
+ # then we know that we are on the last page!
138
+ if total_entries.nil? and length < per_page and (current_page == 1 or length > 0)
139
+ self.total_entries = offset + length
140
+ end
141
+
142
+ result
143
+ end
144
+ end
145
+ end