hobo_will_paginate 2.1.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 (55) hide show
  1. checksums.yaml +15 -0
  2. data/LICENSE +18 -0
  3. data/README.md +61 -0
  4. data/Rakefile +25 -0
  5. data/lib/will_paginate.rb +25 -0
  6. data/lib/will_paginate/active_record.rb +216 -0
  7. data/lib/will_paginate/array.rb +57 -0
  8. data/lib/will_paginate/collection.rb +149 -0
  9. data/lib/will_paginate/core_ext.rb +30 -0
  10. data/lib/will_paginate/data_mapper.rb +95 -0
  11. data/lib/will_paginate/deprecation.rb +55 -0
  12. data/lib/will_paginate/i18n.rb +22 -0
  13. data/lib/will_paginate/locale/en.yml +33 -0
  14. data/lib/will_paginate/page_number.rb +57 -0
  15. data/lib/will_paginate/per_page.rb +27 -0
  16. data/lib/will_paginate/railtie.rb +68 -0
  17. data/lib/will_paginate/sequel.rb +39 -0
  18. data/lib/will_paginate/version.rb +9 -0
  19. data/lib/will_paginate/view_helpers.rb +161 -0
  20. data/lib/will_paginate/view_helpers/action_view.rb +148 -0
  21. data/lib/will_paginate/view_helpers/link_renderer.rb +132 -0
  22. data/lib/will_paginate/view_helpers/link_renderer_base.rb +77 -0
  23. data/lib/will_paginate/view_helpers/merb.rb +26 -0
  24. data/lib/will_paginate/view_helpers/sinatra.rb +41 -0
  25. data/spec/ci.rb +29 -0
  26. data/spec/collection_spec.rb +139 -0
  27. data/spec/console +12 -0
  28. data/spec/console_fixtures.rb +28 -0
  29. data/spec/database.yml +22 -0
  30. data/spec/finders/active_record_spec.rb +543 -0
  31. data/spec/finders/activerecord_test_connector.rb +113 -0
  32. data/spec/finders/data_mapper_spec.rb +103 -0
  33. data/spec/finders/data_mapper_test_connector.rb +54 -0
  34. data/spec/finders/sequel_spec.rb +67 -0
  35. data/spec/finders/sequel_test_connector.rb +9 -0
  36. data/spec/fixtures/admin.rb +3 -0
  37. data/spec/fixtures/developer.rb +13 -0
  38. data/spec/fixtures/developers_projects.yml +13 -0
  39. data/spec/fixtures/project.rb +15 -0
  40. data/spec/fixtures/projects.yml +6 -0
  41. data/spec/fixtures/replies.yml +29 -0
  42. data/spec/fixtures/reply.rb +9 -0
  43. data/spec/fixtures/schema.rb +38 -0
  44. data/spec/fixtures/topic.rb +7 -0
  45. data/spec/fixtures/topics.yml +30 -0
  46. data/spec/fixtures/user.rb +2 -0
  47. data/spec/fixtures/users.yml +35 -0
  48. data/spec/page_number_spec.rb +65 -0
  49. data/spec/per_page_spec.rb +41 -0
  50. data/spec/spec_helper.rb +71 -0
  51. data/spec/view_helpers/action_view_spec.rb +423 -0
  52. data/spec/view_helpers/base_spec.rb +130 -0
  53. data/spec/view_helpers/link_renderer_base_spec.rb +87 -0
  54. data/spec/view_helpers/view_example_group.rb +114 -0
  55. metadata +104 -0
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ ZjdmYzE1NDE1YzU4NzFkOTM2MzcyZjJkNzM5ODIyYzNiYmNmZTcxYQ==
5
+ data.tar.gz: !binary |-
6
+ Mzc3MTg1ZDhkNTE1YmM4ZmQ3YzIwMGMzYTM2MTRmNTBkMmQxZmY2Zg==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ YmJmMzQ1ZWZkMGZkY2Y5ZjMwODg3YjA3ODY1ZDE1YTQzMmVhMzFiZDljMGJi
10
+ ODA5YTBhNzdjZWEyYWZiYWU1MzcwZTBiNWIxM2E3Y2QwZWM2NTUxNTRlOWZl
11
+ ODBhMmIwOWViMjcyY2NmNTNjZDUwNTBmZDljYTk1NzQ2NWM5MDU=
12
+ data.tar.gz: !binary |-
13
+ N2ZmNWM4MDBiM2IxOWRlNGMxOTU5MTQxMjIyMThlOTdiYjhiNTExZTNhZWRl
14
+ NDExY2Y5YWMxOGQ5OWEwZjQyYmVmNGIwZWE5MjhhMmRiOTJlMTVlN2QyOWJl
15
+ NDA2ZDMxODhlOGYzYzkwYzg0YzBlZmJkMTU0YzQ0YzMyMzFlNTI=
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.
data/README.md ADDED
@@ -0,0 +1,61 @@
1
+ # will_paginate
2
+
3
+ will_paginate is a pagination library that integrates with Ruby on Rails, Sinatra, Merb, DataMapper and Sequel.
4
+
5
+ Installation:
6
+
7
+ ``` ruby
8
+ ## Gemfile for Rails 3, Sinatra, and Merb
9
+ gem 'will_paginate', '~> 3.0'
10
+ ```
11
+
12
+ See [installation instructions][install] on the wiki for more info.
13
+
14
+
15
+ ## Basic will_paginate use
16
+
17
+ ``` ruby
18
+ ## perform a paginated query:
19
+ @posts = Post.paginate(:page => params[:page])
20
+
21
+ # or, use an explicit "per page" limit:
22
+ Post.paginate(:page => params[:page], :per_page => 30)
23
+
24
+ ## render page links in the view:
25
+ <%= will_paginate @posts %>
26
+ ```
27
+
28
+ And that's it! You're done. You just need to add some CSS styles to [make those pagination links prettier][css].
29
+
30
+ You can customize the default "per_page" value:
31
+
32
+ ``` ruby
33
+ # for the Post model
34
+ class Post
35
+ self.per_page = 10
36
+ end
37
+
38
+ # set per_page globally
39
+ WillPaginate.per_page = 10
40
+ ```
41
+
42
+ New in Active Record 3:
43
+
44
+ ``` ruby
45
+ # paginate in Active Record now returns a Relation
46
+ Post.where(:published => true).paginate(:page => params[:page]).order('id DESC')
47
+
48
+ # the new, shorter page() method
49
+ Post.page(params[:page]).order('created_at DESC')
50
+ ```
51
+
52
+ See [the wiki][wiki] for more documentation. [Ask on the group][group] if you have usage questions. [Report bugs][issues] on GitHub.
53
+
54
+ Happy paginating.
55
+
56
+
57
+ [wiki]: https://github.com/mislav/will_paginate/wiki
58
+ [install]: https://github.com/mislav/will_paginate/wiki/Installation "will_paginate installation"
59
+ [group]: http://groups.google.com/group/will_paginate "will_paginate discussion and support group"
60
+ [issues]: https://github.com/mislav/will_paginate/issues
61
+ [css]: http://mislav.uniqpath.com/will_paginate/
data/Rakefile ADDED
@@ -0,0 +1,25 @@
1
+ begin
2
+ require 'rspec/core/rake_task'
3
+ rescue LoadError
4
+ # no spec tasks
5
+ else
6
+ task :default => :spec
7
+
8
+ desc 'Run ALL OF the specs'
9
+ RSpec::Core::RakeTask.new(:spec) do |t|
10
+ # t.ruby_opts = '-w'
11
+ t.pattern = 'spec/finders/active_record_spec.rb' if ENV['DB'] and ENV['DB'] != 'sqlite3'
12
+ end
13
+
14
+ namespace :spec do
15
+ desc "Run Rails specs"
16
+ RSpec::Core::RakeTask.new(:rails) do |t|
17
+ t.pattern = %w'spec/finders/active_record_spec.rb spec/view_helpers/action_view_spec.rb'
18
+ end
19
+ end
20
+ end
21
+
22
+ desc 'Run specs against both Rails 3.1 and Rails 3.0'
23
+ task :rails3 do |variable|
24
+ system 'bundle exec rake spec && BUNDLE_GEMFILE=Gemfile.rails3.0 bundle exec rake spec:rails'
25
+ end
@@ -0,0 +1,25 @@
1
+ # You will paginate!
2
+ module WillPaginate
3
+ end
4
+
5
+ if defined?(Rails::Railtie)
6
+ require 'will_paginate/railtie'
7
+ elsif defined?(Rails::Initializer)
8
+ raise "will_paginate 3.0 is not compatible with Rails 2.3 or older"
9
+ end
10
+
11
+ if defined?(Merb::AbstractController)
12
+ require 'will_paginate/view_helpers/merb'
13
+
14
+ Merb::BootLoader.before_app_loads do
15
+ adapters = { :datamapper => 'data_mapper', :activerecord => 'active_record', :sequel => 'sequel' }
16
+ # auto-load the right ORM adapter
17
+ if adapter = adapters[Merb.orm]
18
+ require "will_paginate/#{adapter}"
19
+ end
20
+ end
21
+ end
22
+
23
+ if defined?(Sinatra) and Sinatra.respond_to? :register
24
+ require 'will_paginate/view_helpers/sinatra'
25
+ end
@@ -0,0 +1,216 @@
1
+ require 'will_paginate/per_page'
2
+ require 'will_paginate/page_number'
3
+ require 'will_paginate/collection'
4
+ require 'active_record'
5
+
6
+ module WillPaginate
7
+ # = Paginating finders for ActiveRecord models
8
+ #
9
+ # WillPaginate adds +paginate+, +per_page+ and other methods to
10
+ # ActiveRecord::Base class methods and associations.
11
+ #
12
+ # In short, paginating finders are equivalent to ActiveRecord finders; the
13
+ # only difference is that we start with "paginate" instead of "find" and
14
+ # that <tt>:page</tt> is required parameter:
15
+ #
16
+ # @posts = Post.paginate :all, :page => params[:page], :order => 'created_at DESC'
17
+ #
18
+ module ActiveRecord
19
+ # makes a Relation look like WillPaginate::Collection
20
+ module RelationMethods
21
+ include WillPaginate::CollectionMethods
22
+
23
+ attr_accessor :current_page
24
+ attr_writer :total_entries, :wp_count_options
25
+
26
+ def per_page(value = nil)
27
+ if value.nil? then limit_value
28
+ else limit(value)
29
+ end
30
+ end
31
+
32
+ # TODO: solve with less relation clones and code dups
33
+ def limit(num)
34
+ rel = super
35
+ if rel.current_page
36
+ rel.offset rel.current_page.to_offset(rel.limit_value).to_i
37
+ else
38
+ rel
39
+ end
40
+ end
41
+
42
+ def offset(value = nil)
43
+ if value.nil? then offset_value
44
+ else super(value)
45
+ end
46
+ end
47
+
48
+ def total_entries
49
+ @total_entries ||= begin
50
+ if loaded? and size < limit_value and (current_page == 1 or size > 0)
51
+ offset_value + size
52
+ else
53
+ @total_entries_queried = true
54
+ result = count
55
+ result = result.size if result.respond_to?(:size) and !result.is_a?(Integer)
56
+ result
57
+ end
58
+ end
59
+ end
60
+
61
+ def count
62
+ if limit_value
63
+ excluded = [:order, :limit, :offset]
64
+ excluded << :includes unless eager_loading?
65
+ rel = self.except(*excluded)
66
+ # TODO: hack. decide whether to keep
67
+ rel = rel.apply_finder_options(@wp_count_options) if defined? @wp_count_options
68
+ rel.count
69
+ else
70
+ super
71
+ end
72
+ end
73
+
74
+ # workaround for Active Record 3.0
75
+ def size
76
+ if !loaded? and limit_value and group_values.empty?
77
+ [super, limit_value].min
78
+ else
79
+ super
80
+ end
81
+ end
82
+
83
+ # overloaded to be pagination-aware
84
+ def empty?
85
+ if !loaded? and offset_value
86
+ result = count
87
+ result = result.size if result.respond_to?(:size) and !result.is_a?(Integer)
88
+ result <= offset_value
89
+ else
90
+ super
91
+ end
92
+ end
93
+
94
+ def clone
95
+ copy_will_paginate_data super
96
+ end
97
+
98
+ # workaround for Active Record 3.0
99
+ def scoped(options = nil)
100
+ copy_will_paginate_data super
101
+ end
102
+
103
+ def to_a
104
+ if current_page.nil? then super # workaround for Active Record 3.0
105
+ else
106
+ ::WillPaginate::Collection.create(current_page, limit_value) do |col|
107
+ col.replace super
108
+ col.total_entries ||= total_entries
109
+ end
110
+ end
111
+ end
112
+
113
+ private
114
+
115
+ def copy_will_paginate_data(other)
116
+ other.current_page = current_page unless other.current_page
117
+ other.total_entries = nil if defined? @total_entries_queried
118
+ other.wp_count_options = @wp_count_options if defined? @wp_count_options
119
+ other
120
+ end
121
+ end
122
+
123
+ module Pagination
124
+ def paginate(options)
125
+ options = options.dup
126
+ pagenum = options.fetch(:page) { raise ArgumentError, ":page parameter required" }
127
+ per_page = options.delete(:per_page) || self.per_page
128
+ total = options.delete(:total_entries)
129
+
130
+ count_options = options.delete(:count)
131
+ options.delete(:page)
132
+
133
+ rel = limit(per_page.to_i).page(pagenum)
134
+ rel.wp_count_options = count_options if count_options
135
+ rel.total_entries = total.to_i unless total.blank?
136
+ rel
137
+ end
138
+
139
+ def page(num)
140
+ rel = self.extending(RelationMethods)
141
+ pagenum = ::WillPaginate::PageNumber(num.nil? ? 1 : num)
142
+ per_page = rel.limit_value || self.per_page
143
+ rel = rel.offset(pagenum.to_offset(per_page).to_i)
144
+ rel = rel.limit(per_page) unless rel.limit_value
145
+ rel.current_page = pagenum
146
+ rel
147
+ end
148
+ end
149
+
150
+ module BaseMethods
151
+ # Wraps +find_by_sql+ by simply adding LIMIT and OFFSET to your SQL string
152
+ # based on the params otherwise used by paginating finds: +page+ and
153
+ # +per_page+.
154
+ #
155
+ # Example:
156
+ #
157
+ # @developers = Developer.paginate_by_sql ['select * from developers where salary > ?', 80000],
158
+ # :page => params[:page], :per_page => 3
159
+ #
160
+ # A query for counting rows will automatically be generated if you don't
161
+ # supply <tt>:total_entries</tt>. If you experience problems with this
162
+ # generated SQL, you might want to perform the count manually in your
163
+ # application.
164
+ #
165
+ def paginate_by_sql(sql, options)
166
+ pagenum = options.fetch(:page) { raise ArgumentError, ":page parameter required" } || 1
167
+ per_page = options[:per_page] || self.per_page
168
+ total = options[:total_entries]
169
+
170
+ WillPaginate::Collection.create(pagenum, per_page, total) do |pager|
171
+ query = sanitize_sql(sql.dup)
172
+ original_query = query.dup
173
+ oracle = self.connection.adapter_name =~ /^(oracle|oci$)/i
174
+
175
+ # add limit, offset
176
+ if oracle
177
+ query = <<-SQL
178
+ SELECT * FROM (
179
+ SELECT rownum rnum, a.* FROM (#{query}) a
180
+ WHERE rownum <= #{pager.offset + pager.per_page}
181
+ ) WHERE rnum >= #{pager.offset}
182
+ SQL
183
+ else
184
+ query << " LIMIT #{pager.per_page} OFFSET #{pager.offset}"
185
+ end
186
+
187
+ # perfom the find
188
+ pager.replace find_by_sql(query)
189
+
190
+ unless pager.total_entries
191
+ count_query = original_query.sub /\bORDER\s+BY\s+[\w`,\s.]+$/mi, ''
192
+ count_query = "SELECT COUNT(*) FROM (#{count_query})"
193
+ count_query << ' AS count_table' unless oracle
194
+ # perform the count query
195
+ pager.total_entries = count_by_sql(count_query)
196
+ end
197
+ end
198
+ end
199
+ end
200
+
201
+ # mix everything into Active Record
202
+ ::ActiveRecord::Base.extend PerPage
203
+ ::ActiveRecord::Base.extend Pagination
204
+ ::ActiveRecord::Base.extend BaseMethods
205
+
206
+ klasses = [::ActiveRecord::Relation]
207
+ if defined? ::ActiveRecord::Associations::CollectionProxy
208
+ klasses << ::ActiveRecord::Associations::CollectionProxy
209
+ else
210
+ klasses << ::ActiveRecord::Associations::AssociationCollection
211
+ end
212
+
213
+ # support pagination on associations and scopes
214
+ klasses.each { |klass| klass.send(:include, Pagination) }
215
+ end
216
+ end
@@ -0,0 +1,57 @@
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
+ page = options[:page] || 1
26
+ per_page = options[:per_page] || WillPaginate.per_page
27
+ total = options[:total_entries] || self.length
28
+
29
+ WillPaginate::Collection.create(page, per_page, total) do |pager|
30
+ pager.replace self[pager.offset, pager.per_page].to_a
31
+ end
32
+ end
33
+
34
+ attr_accessor :member_class, :origin, :origin_attribute
35
+
36
+ # Hobo Extension
37
+ def to_url_path
38
+ base_path = origin.try.to_url_path
39
+ "#{base_path}/#{origin_attribute}" unless base_path.blank?
40
+ end
41
+
42
+ # Hobo Extension
43
+ def typed_id
44
+ origin and origin_id = origin.try.typed_id and "#{origin_id}:#{origin_attribute}"
45
+ end
46
+
47
+ # Hobo Extension
48
+ def paginate_with_hobo_metadata(*args, &block)
49
+ collection = paginate_without_hobo_metadata(*args, &block)
50
+ collection.member_class = member_class
51
+ collection.origin = try.proxy_owner
52
+ collection.origin_attribute = try.proxy_association._?.reflection._?.name
53
+ collection
54
+ end
55
+ alias_method_chain :paginate, :hobo_metadata
56
+
57
+ end
@@ -0,0 +1,149 @@
1
+ require 'will_paginate/per_page'
2
+ require 'will_paginate/page_number'
3
+
4
+ module WillPaginate
5
+ # Any will_paginate-compatible collection should have these methods:
6
+ #
7
+ # current_page, per_page, offset, total_entries, total_pages
8
+ #
9
+ # It can also define some of these optional methods:
10
+ #
11
+ # out_of_bounds?, previous_page, next_page
12
+ #
13
+ # This module provides few of these methods.
14
+ module CollectionMethods
15
+ def total_pages
16
+ total_entries.zero? ? 1 : (total_entries / per_page.to_f).ceil
17
+ end
18
+
19
+ # current_page - 1 or nil if there is no previous page
20
+ def previous_page
21
+ current_page > 1 ? (current_page - 1) : nil
22
+ end
23
+
24
+ # current_page + 1 or nil if there is no next page
25
+ def next_page
26
+ current_page < total_pages ? (current_page + 1) : nil
27
+ end
28
+
29
+ # Helper method that is true when someone tries to fetch a page with a
30
+ # larger number than the last page. Can be used in combination with flashes
31
+ # and redirecting.
32
+ def out_of_bounds?
33
+ current_page > total_pages
34
+ end
35
+ end
36
+
37
+ # = The key to pagination
38
+ # Arrays returned from paginating finds are, in fact, instances of this little
39
+ # class. You may think of WillPaginate::Collection as an ordinary array with
40
+ # some extra properties. Those properties are used by view helpers to generate
41
+ # correct page links.
42
+ #
43
+ # WillPaginate::Collection also assists in rolling out your own pagination
44
+ # solutions: see +create+.
45
+ #
46
+ # If you are writing a library that provides a collection which you would like
47
+ # to conform to this API, you don't have to copy these methods over; simply
48
+ # make your plugin/gem dependant on this library and do:
49
+ #
50
+ # require 'will_paginate/collection'
51
+ # # WillPaginate::Collection is now available for use
52
+ class Collection < Array
53
+ include CollectionMethods
54
+
55
+ attr_reader :current_page, :per_page, :total_entries
56
+
57
+ # Arguments to the constructor are the current page number, per-page limit
58
+ # and the total number of entries. The last argument is optional because it
59
+ # is best to do lazy counting; in other words, count *conditionally* after
60
+ # populating the collection using the +replace+ method.
61
+ def initialize(page, per_page = WillPaginate.per_page, total = nil)
62
+ @current_page = WillPaginate::PageNumber(page)
63
+ @per_page = per_page.to_i
64
+ self.total_entries = total if total
65
+ end
66
+
67
+ # Just like +new+, but yields the object after instantiation and returns it
68
+ # afterwards. This is very useful for manual pagination:
69
+ #
70
+ # @entries = WillPaginate::Collection.create(1, 10) do |pager|
71
+ # result = Post.find(:all, :limit => pager.per_page, :offset => pager.offset)
72
+ # # inject the result array into the paginated collection:
73
+ # pager.replace(result)
74
+ #
75
+ # unless pager.total_entries
76
+ # # the pager didn't manage to guess the total count, do it manually
77
+ # pager.total_entries = Post.count
78
+ # end
79
+ # end
80
+ #
81
+ # The possibilities with this are endless. For another example, here is how
82
+ # WillPaginate used to define pagination for Array instances:
83
+ #
84
+ # Array.class_eval do
85
+ # def paginate(page = 1, per_page = 15)
86
+ # WillPaginate::Collection.create(page, per_page, size) do |pager|
87
+ # pager.replace self[pager.offset, pager.per_page].to_a
88
+ # end
89
+ # end
90
+ # end
91
+ #
92
+ # The Array#paginate API has since then changed, but this still serves as a
93
+ # fine example of WillPaginate::Collection usage.
94
+ def self.create(page, per_page, total = nil)
95
+ pager = new(page, per_page, total)
96
+ yield pager
97
+ pager
98
+ end
99
+
100
+ # Current offset of the paginated collection. If we're on the first page,
101
+ # it is always 0. If we're on the 2nd page and there are 30 entries per page,
102
+ # the offset is 30. This property is useful if you want to render ordinals
103
+ # side by side with records in the view: simply start with offset + 1.
104
+ def offset
105
+ current_page.to_offset(per_page).to_i
106
+ end
107
+
108
+ def total_entries=(number)
109
+ @total_entries = number.to_i
110
+ end
111
+
112
+ # This is a magic wrapper for the original Array#replace method. It serves
113
+ # for populating the paginated collection after initialization.
114
+ #
115
+ # Why magic? Because it tries to guess the total number of entries judging
116
+ # by the size of given array. If it is shorter than +per_page+ limit, then we
117
+ # know we're on the last page. This trick is very useful for avoiding
118
+ # unnecessary hits to the database to do the counting after we fetched the
119
+ # data for the current page.
120
+ #
121
+ # However, after using +replace+ you should always test the value of
122
+ # +total_entries+ and set it to a proper value if it's +nil+. See the example
123
+ # in +create+.
124
+ def replace(array)
125
+ result = super
126
+
127
+ # The collection is shorter then page limit? Rejoice, because
128
+ # then we know that we are on the last page!
129
+ if total_entries.nil? and length < per_page and (current_page == 1 or length > 0)
130
+ self.total_entries = offset + length
131
+ end
132
+
133
+ result
134
+ end
135
+
136
+ attr_accessor :member_class, :origin, :origin_attribute
137
+
138
+ # Hobo extension: make paginate_by_sql, etc. carry metadata
139
+ def replace_with_hobo_metadata(array)
140
+ result = replace_without_hobo_metadata(array)
141
+ self.member_class = array.try.member_class
142
+ self.origin = array.try.origin
143
+ self.origin_attribute = array.try.origin_attribute
144
+ result
145
+ end
146
+ alias_method_chain :replace, :hobo_metadata
147
+
148
+ end
149
+ end