hobo_will_paginate 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/LICENSE +18 -0
- data/README.md +61 -0
- data/Rakefile +25 -0
- data/lib/will_paginate.rb +25 -0
- data/lib/will_paginate/active_record.rb +216 -0
- data/lib/will_paginate/array.rb +57 -0
- data/lib/will_paginate/collection.rb +149 -0
- data/lib/will_paginate/core_ext.rb +30 -0
- data/lib/will_paginate/data_mapper.rb +95 -0
- data/lib/will_paginate/deprecation.rb +55 -0
- data/lib/will_paginate/i18n.rb +22 -0
- data/lib/will_paginate/locale/en.yml +33 -0
- data/lib/will_paginate/page_number.rb +57 -0
- data/lib/will_paginate/per_page.rb +27 -0
- data/lib/will_paginate/railtie.rb +68 -0
- data/lib/will_paginate/sequel.rb +39 -0
- data/lib/will_paginate/version.rb +9 -0
- data/lib/will_paginate/view_helpers.rb +161 -0
- data/lib/will_paginate/view_helpers/action_view.rb +148 -0
- data/lib/will_paginate/view_helpers/link_renderer.rb +132 -0
- data/lib/will_paginate/view_helpers/link_renderer_base.rb +77 -0
- data/lib/will_paginate/view_helpers/merb.rb +26 -0
- data/lib/will_paginate/view_helpers/sinatra.rb +41 -0
- data/spec/ci.rb +29 -0
- data/spec/collection_spec.rb +139 -0
- data/spec/console +12 -0
- data/spec/console_fixtures.rb +28 -0
- data/spec/database.yml +22 -0
- data/spec/finders/active_record_spec.rb +543 -0
- data/spec/finders/activerecord_test_connector.rb +113 -0
- data/spec/finders/data_mapper_spec.rb +103 -0
- data/spec/finders/data_mapper_test_connector.rb +54 -0
- data/spec/finders/sequel_spec.rb +67 -0
- data/spec/finders/sequel_test_connector.rb +9 -0
- data/spec/fixtures/admin.rb +3 -0
- data/spec/fixtures/developer.rb +13 -0
- data/spec/fixtures/developers_projects.yml +13 -0
- data/spec/fixtures/project.rb +15 -0
- data/spec/fixtures/projects.yml +6 -0
- data/spec/fixtures/replies.yml +29 -0
- data/spec/fixtures/reply.rb +9 -0
- data/spec/fixtures/schema.rb +38 -0
- data/spec/fixtures/topic.rb +7 -0
- data/spec/fixtures/topics.yml +30 -0
- data/spec/fixtures/user.rb +2 -0
- data/spec/fixtures/users.yml +35 -0
- data/spec/page_number_spec.rb +65 -0
- data/spec/per_page_spec.rb +41 -0
- data/spec/spec_helper.rb +71 -0
- data/spec/view_helpers/action_view_spec.rb +423 -0
- data/spec/view_helpers/base_spec.rb +130 -0
- data/spec/view_helpers/link_renderer_base_spec.rb +87 -0
- data/spec/view_helpers/view_example_group.rb +114 -0
- 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
|