hobo-will_paginate 3.0.4.hobo

Sign up to get free protection for your applications and to get access to all the features.
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 +217 -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 +102 -0
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ ODlhY2JmNjYwMTQ5ZDJlYTBjNGRmNzdhYTQwNjVlOTdiNDY3Yzc3NQ==
5
+ data.tar.gz: !binary |-
6
+ NzQwYTkxYmQ1OTQ1MTk0OGE2MGEwNTFkODNlMjYxYjQyZDk5Yjk4Ng==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ MzlmMGQzNGYyYWJjNjU0MWZjZTU4ZDdmNGE4OTQ2MWFiYTkyNmY4ZGUzZTli
10
+ MWFiMjFkMGQzZTNiNmMxNzJhM2E3ODI1MThhMjJmOTk1YWFmZmNlYzA5MDc2
11
+ M2QwY2NkNTM3NmMwNDg2MjE5MDM5ZmUzYTQ5ZWY4YTI3ODM1ZGQ=
12
+ data.tar.gz: !binary |-
13
+ ZDljMGRlNDcyMmE1NjZjZjk2OTA0MDA4MDVjODY2YmM0NzIyZDgwMDk4YTBh
14
+ YTkzYmMwMTRlMDljYWNkNDNiZGUxZmFjMGIzNzgxMTVmNDAzZjExZDQ1OWNj
15
+ M2NiM2QyZDk1OTAwN2E4YmI2NjgxMmQ0MjU0Y2Y1Y2MzY2ZmZDg=
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,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/
@@ -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,217 @@
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 = rel.apply_finder_options(options) if options.any?
135
+ rel.wp_count_options = count_options if count_options
136
+ rel.total_entries = total.to_i unless total.blank?
137
+ rel
138
+ end
139
+
140
+ def page(num)
141
+ rel = scoped.extending(RelationMethods)
142
+ pagenum = ::WillPaginate::PageNumber(num.nil? ? 1 : num)
143
+ per_page = rel.limit_value || self.per_page
144
+ rel = rel.offset(pagenum.to_offset(per_page).to_i)
145
+ rel = rel.limit(per_page) unless rel.limit_value
146
+ rel.current_page = pagenum
147
+ rel
148
+ end
149
+ end
150
+
151
+ module BaseMethods
152
+ # Wraps +find_by_sql+ by simply adding LIMIT and OFFSET to your SQL string
153
+ # based on the params otherwise used by paginating finds: +page+ and
154
+ # +per_page+.
155
+ #
156
+ # Example:
157
+ #
158
+ # @developers = Developer.paginate_by_sql ['select * from developers where salary > ?', 80000],
159
+ # :page => params[:page], :per_page => 3
160
+ #
161
+ # A query for counting rows will automatically be generated if you don't
162
+ # supply <tt>:total_entries</tt>. If you experience problems with this
163
+ # generated SQL, you might want to perform the count manually in your
164
+ # application.
165
+ #
166
+ def paginate_by_sql(sql, options)
167
+ pagenum = options.fetch(:page) { raise ArgumentError, ":page parameter required" } || 1
168
+ per_page = options[:per_page] || self.per_page
169
+ total = options[:total_entries]
170
+
171
+ WillPaginate::Collection.create(pagenum, per_page, total) do |pager|
172
+ query = sanitize_sql(sql.dup)
173
+ original_query = query.dup
174
+ oracle = self.connection.adapter_name =~ /^(oracle|oci$)/i
175
+
176
+ # add limit, offset
177
+ if oracle
178
+ query = <<-SQL
179
+ SELECT * FROM (
180
+ SELECT rownum rnum, a.* FROM (#{query}) a
181
+ WHERE rownum <= #{pager.offset + pager.per_page}
182
+ ) WHERE rnum >= #{pager.offset}
183
+ SQL
184
+ else
185
+ query << " LIMIT #{pager.per_page} OFFSET #{pager.offset}"
186
+ end
187
+
188
+ # perfom the find
189
+ pager.replace find_by_sql(query)
190
+
191
+ unless pager.total_entries
192
+ count_query = original_query.sub /\bORDER\s+BY\s+[\w`,\s.]+$/mi, ''
193
+ count_query = "SELECT COUNT(*) FROM (#{count_query})"
194
+ count_query << ' AS count_table' unless oracle
195
+ # perform the count query
196
+ pager.total_entries = count_by_sql(count_query)
197
+ end
198
+ end
199
+ end
200
+ end
201
+
202
+ # mix everything into Active Record
203
+ ::ActiveRecord::Base.extend PerPage
204
+ ::ActiveRecord::Base.extend Pagination
205
+ ::ActiveRecord::Base.extend BaseMethods
206
+
207
+ klasses = [::ActiveRecord::Relation]
208
+ if defined? ::ActiveRecord::Associations::CollectionProxy
209
+ klasses << ::ActiveRecord::Associations::CollectionProxy
210
+ else
211
+ klasses << ::ActiveRecord::Associations::AssociationCollection
212
+ end
213
+
214
+ # support pagination on associations and scopes
215
+ klasses.each { |klass| klass.send(:include, Pagination) }
216
+ end
217
+ 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