pb-will_paginate 2.3.12
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.
- data/.gitignore +4 -0
- data/.manifest +43 -0
- data/CHANGELOG.rdoc +139 -0
- data/LICENSE +18 -0
- data/README.rdoc +107 -0
- data/Rakefile +71 -0
- data/VERSION +1 -0
- data/examples/apple-circle.gif +0 -0
- data/examples/index.haml +69 -0
- data/examples/index.html +92 -0
- data/examples/pagination.css +90 -0
- data/examples/pagination.sass +91 -0
- data/init.rb +2 -0
- data/lib/will_paginate.rb +90 -0
- data/lib/will_paginate/array.rb +16 -0
- data/lib/will_paginate/collection.rb +144 -0
- data/lib/will_paginate/core_ext.rb +43 -0
- data/lib/will_paginate/finder.rb +264 -0
- data/lib/will_paginate/i18n.rb +178 -0
- data/lib/will_paginate/named_scope.rb +170 -0
- data/lib/will_paginate/named_scope_patch.rb +37 -0
- data/lib/will_paginate/version.rb +9 -0
- data/lib/will_paginate/view_helpers.rb +397 -0
- data/locales/en.yml +11 -0
- data/pb-will_paginate.gemspec +106 -0
- data/test/boot.rb +21 -0
- data/test/collection_test.rb +143 -0
- data/test/console +8 -0
- data/test/database.yml +22 -0
- data/test/finder_test.rb +473 -0
- data/test/fixtures/admin.rb +3 -0
- data/test/fixtures/developer.rb +14 -0
- data/test/fixtures/developers_projects.yml +13 -0
- data/test/fixtures/project.rb +15 -0
- data/test/fixtures/projects.yml +6 -0
- data/test/fixtures/replies.yml +29 -0
- data/test/fixtures/reply.rb +7 -0
- data/test/fixtures/schema.rb +38 -0
- data/test/fixtures/topic.rb +10 -0
- data/test/fixtures/topics.yml +30 -0
- data/test/fixtures/user.rb +2 -0
- data/test/fixtures/users.yml +35 -0
- data/test/helper.rb +37 -0
- data/test/i18n_test.rb +194 -0
- data/test/lib/activerecord_test_case.rb +43 -0
- data/test/lib/activerecord_test_connector.rb +75 -0
- data/test/lib/load_fixtures.rb +11 -0
- data/test/lib/view_test_process.rb +179 -0
- data/test/tasks.rake +59 -0
- data/test/view_test.rb +289 -0
- metadata +122 -0
data/examples/index.html
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
2
|
+
<html>
|
3
|
+
</html>
|
4
|
+
<head>
|
5
|
+
<title>Samples of pagination styling for will_paginate</title>
|
6
|
+
<link href='pagination.css' rel='stylesheet' type='text/css' />
|
7
|
+
<style type='text/css'>
|
8
|
+
html {
|
9
|
+
margin: 0;
|
10
|
+
padding: 0;
|
11
|
+
background: #999;
|
12
|
+
font: normal 76% "Lucida Grande", Verdana, Helvetica, sans-serif; }
|
13
|
+
|
14
|
+
body {
|
15
|
+
margin: 2em;
|
16
|
+
padding: 2em;
|
17
|
+
border: 2px solid gray;
|
18
|
+
background: white;
|
19
|
+
color: #222; }
|
20
|
+
|
21
|
+
h1 {
|
22
|
+
font-size: 2em;
|
23
|
+
font-weight: normal;
|
24
|
+
margin: 0 0 1em 0; }
|
25
|
+
|
26
|
+
h2 {
|
27
|
+
font-size: 1.4em;
|
28
|
+
margin: 1em 0 .5em 0; }
|
29
|
+
|
30
|
+
pre {
|
31
|
+
font-size: 13px;
|
32
|
+
font-family: Monaco, "DejaVu Sans Mono", "Bitstream Vera Mono", "Courier New", monospace; }
|
33
|
+
</style>
|
34
|
+
</head>
|
35
|
+
<body>
|
36
|
+
<h1>Samples of pagination styling for will_paginate</h1>
|
37
|
+
<p>
|
38
|
+
Find these styles in <b>"examples/pagination.css"</b> of <i>will_paginate</i> library.
|
39
|
+
There is a Sass version of it for all you sassy people.
|
40
|
+
</p>
|
41
|
+
<p>
|
42
|
+
Read about good rules for pagination:
|
43
|
+
<a href='http://kurafire.net/log/archive/2007/06/22/pagination-101'>Pagination 101</a>
|
44
|
+
</p>
|
45
|
+
<p>
|
46
|
+
<em>Warning:</em>
|
47
|
+
page links below don't lead anywhere (so don't click on them).
|
48
|
+
</p>
|
49
|
+
<h2>
|
50
|
+
Unstyled pagination <span style="font-weight:normal">(<i>ewww!</i>)</span>
|
51
|
+
</h2>
|
52
|
+
<div>
|
53
|
+
<span class="disabled prev_page">« Previous</span> <span class="current">1</span> <a href="./?page=2" rel="next">2</a> <a href="./?page=3">3</a> <a href="./?page=4">4</a> <a href="./?page=5">5</a> <a href="./?page=6">6</a> <a href="./?page=7">7</a> <a href="./?page=8">8</a> <a href="./?page=9">9</a> <span class="gap">…</span> <a href="./?page=29">29</a> <a href="./?page=30">30</a> <a href="./?page=2" rel="next" class="next_page">Next »</a>
|
54
|
+
</div>
|
55
|
+
<h2>Digg.com</h2>
|
56
|
+
<div class='digg_pagination'>
|
57
|
+
<span class="disabled prev_page">« Previous</span> <span class="current">1</span> <a href="./?page=2" rel="next">2</a> <a href="./?page=3">3</a> <a href="./?page=4">4</a> <a href="./?page=5">5</a> <a href="./?page=6">6</a> <a href="./?page=7">7</a> <a href="./?page=8">8</a> <a href="./?page=9">9</a> <span class="gap">…</span> <a href="./?page=29">29</a> <a href="./?page=30">30</a> <a href="./?page=2" rel="next" class="next_page">Next »</a>
|
58
|
+
</div>
|
59
|
+
<h2>Digg-style, no page links</h2>
|
60
|
+
<div class='digg_pagination'>
|
61
|
+
<span class="disabled prev_page">« Previous</span> <a href="./?page=2" rel="next" class="next_page">Next »</a>
|
62
|
+
</div>
|
63
|
+
<p>Code that renders this:</p>
|
64
|
+
<pre>
|
65
|
+
<code><%= will_paginate @posts, :page_links => false %></code>
|
66
|
+
</pre>
|
67
|
+
<h2>Digg-style, extra content</h2>
|
68
|
+
<div class='digg_pagination'>
|
69
|
+
<div class='page_info'>
|
70
|
+
Displaying entries <b>1 - 6</b> of <b>180</b> in total
|
71
|
+
</div>
|
72
|
+
<span class="disabled prev_page">« Previous</span> <span class="current">1</span> <a href="./?page=2" rel="next">2</a> <a href="./?page=3">3</a> <a href="./?page=4">4</a> <a href="./?page=5">5</a> <a href="./?page=6">6</a> <a href="./?page=7">7</a> <a href="./?page=8">8</a> <a href="./?page=9">9</a> <span class="gap">…</span> <a href="./?page=29">29</a> <a href="./?page=30">30</a> <a href="./?page=2" rel="next" class="next_page">Next »</a>
|
73
|
+
</div>
|
74
|
+
<p>Code that renders this:</p>
|
75
|
+
<pre>
|
76
|
+
<code><div class="digg_pagination">
|
77
|
+
<div clas="page_info">
|
78
|
+
<%= page_entries_info @posts %>
|
79
|
+
</div>
|
80
|
+
<%= will_paginate @posts, :container => false %>
|
81
|
+
</div></code>
|
82
|
+
</pre>
|
83
|
+
<h2>Apple.com store</h2>
|
84
|
+
<div class='apple_pagination'>
|
85
|
+
<span class="disabled prev_page">« Previous</span> <span class="current">1</span> <a href="./?page=2" rel="next">2</a> <a href="./?page=3">3</a> <a href="./?page=4">4</a> <a href="./?page=5">5</a> <a href="./?page=6">6</a> <a href="./?page=7">7</a> <a href="./?page=8">8</a> <a href="./?page=9">9</a> <span class="gap">…</span> <a href="./?page=29">29</a> <a href="./?page=30">30</a> <a href="./?page=2" rel="next" class="next_page">Next »</a>
|
86
|
+
</div>
|
87
|
+
<h2>Flickr.com</h2>
|
88
|
+
<div class='flickr_pagination'>
|
89
|
+
<span class="disabled prev_page">« Previous</span> <span class="current">1</span> <a href="./?page=2" rel="next">2</a> <a href="./?page=3">3</a> <a href="./?page=4">4</a> <a href="./?page=5">5</a> <a href="./?page=6">6</a> <a href="./?page=7">7</a> <a href="./?page=8">8</a> <a href="./?page=9">9</a> <span class="gap">…</span> <a href="./?page=29">29</a> <a href="./?page=30">30</a> <a href="./?page=2" rel="next" class="next_page">Next »</a>
|
90
|
+
<div class='page_info'>(118 photos)</div>
|
91
|
+
</div>
|
92
|
+
</body>
|
@@ -0,0 +1,90 @@
|
|
1
|
+
.digg_pagination {
|
2
|
+
background: white;
|
3
|
+
/* self-clearing method: */ }
|
4
|
+
.digg_pagination a, .digg_pagination span {
|
5
|
+
padding: .2em .5em;
|
6
|
+
display: block;
|
7
|
+
float: left;
|
8
|
+
margin-right: 1px; }
|
9
|
+
.digg_pagination span.disabled {
|
10
|
+
color: #999;
|
11
|
+
border: 1px solid #DDD; }
|
12
|
+
.digg_pagination span.current {
|
13
|
+
font-weight: bold;
|
14
|
+
background: #2E6AB1;
|
15
|
+
color: white;
|
16
|
+
border: 1px solid #2E6AB1; }
|
17
|
+
.digg_pagination a {
|
18
|
+
text-decoration: none;
|
19
|
+
color: #105CB6;
|
20
|
+
border: 1px solid #9AAFE5; }
|
21
|
+
.digg_pagination a:hover, .digg_pagination a:focus {
|
22
|
+
color: #003;
|
23
|
+
border-color: #003; }
|
24
|
+
.digg_pagination .page_info {
|
25
|
+
background: #2E6AB1;
|
26
|
+
color: white;
|
27
|
+
padding: .4em .6em;
|
28
|
+
width: 22em;
|
29
|
+
margin-bottom: .3em;
|
30
|
+
text-align: center; }
|
31
|
+
.digg_pagination .page_info b {
|
32
|
+
color: #003;
|
33
|
+
background: #6aa6ed;
|
34
|
+
padding: .1em .25em; }
|
35
|
+
.digg_pagination:after {
|
36
|
+
content: ".";
|
37
|
+
display: block;
|
38
|
+
height: 0;
|
39
|
+
clear: both;
|
40
|
+
visibility: hidden; }
|
41
|
+
* html .digg_pagination {
|
42
|
+
height: 1%; }
|
43
|
+
*:first-child+html .digg_pagination {
|
44
|
+
overflow: hidden; }
|
45
|
+
|
46
|
+
.apple_pagination {
|
47
|
+
background: #F1F1F1;
|
48
|
+
border: 1px solid #E5E5E5;
|
49
|
+
text-align: center;
|
50
|
+
padding: 1em; }
|
51
|
+
.apple_pagination a, .apple_pagination span {
|
52
|
+
padding: .2em .3em; }
|
53
|
+
.apple_pagination span.disabled {
|
54
|
+
color: #AAA; }
|
55
|
+
.apple_pagination span.current {
|
56
|
+
font-weight: bold;
|
57
|
+
background: transparent url(apple-circle.gif) no-repeat 50% 50%; }
|
58
|
+
.apple_pagination a {
|
59
|
+
text-decoration: none;
|
60
|
+
color: black; }
|
61
|
+
.apple_pagination a:hover, .apple_pagination a:focus {
|
62
|
+
text-decoration: underline; }
|
63
|
+
|
64
|
+
.flickr_pagination {
|
65
|
+
text-align: center;
|
66
|
+
padding: .3em; }
|
67
|
+
.flickr_pagination a, .flickr_pagination span {
|
68
|
+
padding: .2em .5em; }
|
69
|
+
.flickr_pagination span.disabled {
|
70
|
+
color: #AAA; }
|
71
|
+
.flickr_pagination span.current {
|
72
|
+
font-weight: bold;
|
73
|
+
color: #FF0084; }
|
74
|
+
.flickr_pagination a {
|
75
|
+
border: 1px solid #DDDDDD;
|
76
|
+
color: #0063DC;
|
77
|
+
text-decoration: none; }
|
78
|
+
.flickr_pagination a:hover, .flickr_pagination a:focus {
|
79
|
+
border-color: #003366;
|
80
|
+
background: #0063DC;
|
81
|
+
color: white; }
|
82
|
+
.flickr_pagination .page_info {
|
83
|
+
color: #aaa;
|
84
|
+
padding-top: .8em; }
|
85
|
+
.flickr_pagination .prev_page, .flickr_pagination .next_page {
|
86
|
+
border-width: 2px; }
|
87
|
+
.flickr_pagination .prev_page {
|
88
|
+
margin-right: 1em; }
|
89
|
+
.flickr_pagination .next_page {
|
90
|
+
margin-left: 1em; }
|
@@ -0,0 +1,91 @@
|
|
1
|
+
.digg_pagination
|
2
|
+
:background white
|
3
|
+
a, span
|
4
|
+
:padding .2em .5em
|
5
|
+
:display block
|
6
|
+
:float left
|
7
|
+
:margin-right 1px
|
8
|
+
span.disabled
|
9
|
+
:color #999
|
10
|
+
:border 1px solid #DDD
|
11
|
+
span.current
|
12
|
+
:font-weight bold
|
13
|
+
:background #2E6AB1
|
14
|
+
:color white
|
15
|
+
:border 1px solid #2E6AB1
|
16
|
+
a
|
17
|
+
:text-decoration none
|
18
|
+
:color #105CB6
|
19
|
+
:border 1px solid #9AAFE5
|
20
|
+
&:hover, &:focus
|
21
|
+
:color #003
|
22
|
+
:border-color #003
|
23
|
+
.page_info
|
24
|
+
:background #2E6AB1
|
25
|
+
:color white
|
26
|
+
:padding .4em .6em
|
27
|
+
:width 22em
|
28
|
+
:margin-bottom .3em
|
29
|
+
:text-align center
|
30
|
+
b
|
31
|
+
:color #003
|
32
|
+
:background = #2E6AB1 + 60
|
33
|
+
:padding .1em .25em
|
34
|
+
|
35
|
+
/* self-clearing method:
|
36
|
+
&:after
|
37
|
+
:content "."
|
38
|
+
:display block
|
39
|
+
:height 0
|
40
|
+
:clear both
|
41
|
+
:visibility hidden
|
42
|
+
* html &
|
43
|
+
:height 1%
|
44
|
+
*:first-child+html &
|
45
|
+
:overflow hidden
|
46
|
+
|
47
|
+
.apple_pagination
|
48
|
+
:background #F1F1F1
|
49
|
+
:border 1px solid #E5E5E5
|
50
|
+
:text-align center
|
51
|
+
:padding 1em
|
52
|
+
a, span
|
53
|
+
:padding .2em .3em
|
54
|
+
span.disabled
|
55
|
+
:color #AAA
|
56
|
+
span.current
|
57
|
+
:font-weight bold
|
58
|
+
:background transparent url(apple-circle.gif) no-repeat 50% 50%
|
59
|
+
a
|
60
|
+
:text-decoration none
|
61
|
+
:color black
|
62
|
+
&:hover, &:focus
|
63
|
+
:text-decoration underline
|
64
|
+
|
65
|
+
.flickr_pagination
|
66
|
+
:text-align center
|
67
|
+
:padding .3em
|
68
|
+
a, span
|
69
|
+
:padding .2em .5em
|
70
|
+
span.disabled
|
71
|
+
:color #AAA
|
72
|
+
span.current
|
73
|
+
:font-weight bold
|
74
|
+
:color #FF0084
|
75
|
+
a
|
76
|
+
:border 1px solid #DDDDDD
|
77
|
+
:color #0063DC
|
78
|
+
:text-decoration none
|
79
|
+
&:hover, &:focus
|
80
|
+
:border-color #003366
|
81
|
+
:background #0063DC
|
82
|
+
:color white
|
83
|
+
.page_info
|
84
|
+
:color #aaa
|
85
|
+
:padding-top .8em
|
86
|
+
.prev_page, .next_page
|
87
|
+
:border-width 2px
|
88
|
+
.prev_page
|
89
|
+
:margin-right 1em
|
90
|
+
.next_page
|
91
|
+
:margin-left 1em
|
data/init.rb
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
require 'will_paginate/core_ext'
|
3
|
+
|
4
|
+
# = You *will* paginate!
|
5
|
+
#
|
6
|
+
# First read about WillPaginate::Finder::ClassMethods, then see
|
7
|
+
# WillPaginate::ViewHelpers. The magical array you're handling in-between is
|
8
|
+
# WillPaginate::Collection.
|
9
|
+
#
|
10
|
+
# Happy paginating!
|
11
|
+
module WillPaginate
|
12
|
+
class << self
|
13
|
+
# shortcut for <tt>enable_actionpack</tt> and <tt>enable_activerecord</tt> combined
|
14
|
+
def enable
|
15
|
+
enable_actionpack
|
16
|
+
enable_activerecord
|
17
|
+
end
|
18
|
+
|
19
|
+
# hooks WillPaginate::ViewHelpers into ActionView::Base
|
20
|
+
def enable_actionpack
|
21
|
+
return if ActionView::Base.instance_methods.include_method? :will_paginate
|
22
|
+
require 'will_paginate/view_helpers'
|
23
|
+
ActionView::Base.send :include, ViewHelpers
|
24
|
+
|
25
|
+
if defined?(ActionController::Base) and ActionController::Base.respond_to? :rescue_responses
|
26
|
+
ActionController::Base.rescue_responses['WillPaginate::InvalidPage'] = :not_found
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# hooks WillPaginate::Finder into ActiveRecord::Base and classes that deal
|
31
|
+
# with associations
|
32
|
+
def enable_activerecord
|
33
|
+
return if ActiveRecord::Base.respond_to? :paginate
|
34
|
+
require 'will_paginate/finder'
|
35
|
+
ActiveRecord::Base.send :include, Finder
|
36
|
+
|
37
|
+
# support pagination on associations
|
38
|
+
a = ActiveRecord::Associations
|
39
|
+
returning([ a::AssociationCollection ]) { |classes|
|
40
|
+
# detect http://dev.rubyonrails.org/changeset/9230
|
41
|
+
unless a::HasManyThroughAssociation.superclass == a::HasManyAssociation
|
42
|
+
classes << a::HasManyThroughAssociation
|
43
|
+
end
|
44
|
+
}.each do |klass|
|
45
|
+
klass.send :include, Finder::ClassMethods
|
46
|
+
klass.class_eval { alias_method_chain :method_missing, :paginate }
|
47
|
+
end
|
48
|
+
|
49
|
+
# monkeypatch Rails ticket #2189: "count breaks has_many :through"
|
50
|
+
ActiveRecord::Base.class_eval do
|
51
|
+
protected
|
52
|
+
def self.construct_count_options_from_args(*args)
|
53
|
+
result = super
|
54
|
+
result[0] = '*' if result[0].is_a?(String) and result[0] =~ /\.\*$/
|
55
|
+
result
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Enable named_scope, a feature of Rails 2.1, even if you have older Rails
|
61
|
+
# (tested on Rails 2.0.2 and 1.2.6).
|
62
|
+
#
|
63
|
+
# You can pass +false+ for +patch+ parameter to skip monkeypatching
|
64
|
+
# *associations*. Use this if you feel that <tt>named_scope</tt> broke
|
65
|
+
# has_many, has_many :through or has_and_belongs_to_many associations in
|
66
|
+
# your app. By passing +false+, you can still use <tt>named_scope</tt> in
|
67
|
+
# your models, but not through associations.
|
68
|
+
def enable_named_scope(patch = true)
|
69
|
+
return if defined? ActiveRecord::NamedScope
|
70
|
+
require 'will_paginate/named_scope'
|
71
|
+
require 'will_paginate/named_scope_patch' if patch
|
72
|
+
|
73
|
+
ActiveRecord::Base.send :include, WillPaginate::NamedScope
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
module Deprecation # :nodoc:
|
78
|
+
extend ActiveSupport::Deprecation
|
79
|
+
|
80
|
+
def self.warn(message, callstack = caller)
|
81
|
+
message = 'WillPaginate: ' + message.strip.gsub(/\s+/, ' ')
|
82
|
+
ActiveSupport::Deprecation.warn(message, callstack)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
if defined? Rails
|
88
|
+
WillPaginate.enable_activerecord if defined? ActiveRecord
|
89
|
+
WillPaginate.enable_actionpack if defined? ActionController
|
90
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'will_paginate/collection'
|
2
|
+
|
3
|
+
# http://www.desimcadam.com/archives/8
|
4
|
+
Array.class_eval do
|
5
|
+
def paginate(options = {})
|
6
|
+
raise ArgumentError, "parameter hash expected (got #{options.inspect})" unless Hash === options
|
7
|
+
|
8
|
+
WillPaginate::Collection.create(
|
9
|
+
options[:page] || 1,
|
10
|
+
options[:per_page] || 30,
|
11
|
+
options[:total_entries] || self.length
|
12
|
+
) { |pager|
|
13
|
+
pager.replace self[pager.offset, pager.per_page].to_a
|
14
|
+
}
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,144 @@
|
|
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)
|
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 this library and do:
|
37
|
+
#
|
38
|
+
# require 'will_paginate/collection'
|
39
|
+
# # WillPaginate::Collection is now available for use
|
40
|
+
class Collection < Array
|
41
|
+
attr_reader :current_page, :per_page, :total_entries, :total_pages
|
42
|
+
|
43
|
+
# Arguments to the constructor are the current page number, per-page limit
|
44
|
+
# and the total number of entries. The last argument is optional because it
|
45
|
+
# is best to do lazy counting; in other words, count *conditionally* after
|
46
|
+
# populating the collection using the +replace+ method.
|
47
|
+
def initialize(page, per_page, total = nil)
|
48
|
+
@current_page = page.to_i
|
49
|
+
raise InvalidPage.new(page, @current_page) if @current_page < 1
|
50
|
+
@per_page = per_page.to_i
|
51
|
+
raise ArgumentError, "`per_page` setting cannot be less than 1 (#{@per_page} given)" if @per_page < 1
|
52
|
+
|
53
|
+
self.total_entries = total if total
|
54
|
+
end
|
55
|
+
|
56
|
+
# Just like +new+, but yields the object after instantiation and returns it
|
57
|
+
# afterwards. This is very useful for manual pagination:
|
58
|
+
#
|
59
|
+
# @entries = WillPaginate::Collection.create(1, 10) do |pager|
|
60
|
+
# result = Post.find(:all, :limit => pager.per_page, :offset => pager.offset)
|
61
|
+
# # inject the result array into the paginated collection:
|
62
|
+
# pager.replace(result)
|
63
|
+
#
|
64
|
+
# unless pager.total_entries
|
65
|
+
# # the pager didn't manage to guess the total count, do it manually
|
66
|
+
# pager.total_entries = Post.count
|
67
|
+
# end
|
68
|
+
# end
|
69
|
+
#
|
70
|
+
# The possibilities with this are endless. For another example, here is how
|
71
|
+
# WillPaginate used to define pagination for Array instances:
|
72
|
+
#
|
73
|
+
# Array.class_eval do
|
74
|
+
# def paginate(page = 1, per_page = 15)
|
75
|
+
# WillPaginate::Collection.create(page, per_page, size) do |pager|
|
76
|
+
# pager.replace self[pager.offset, pager.per_page].to_a
|
77
|
+
# end
|
78
|
+
# end
|
79
|
+
# end
|
80
|
+
#
|
81
|
+
# The Array#paginate API has since then changed, but this still serves as a
|
82
|
+
# fine example of WillPaginate::Collection usage.
|
83
|
+
def self.create(page, per_page, total = nil)
|
84
|
+
pager = new(page, per_page, total)
|
85
|
+
yield pager
|
86
|
+
pager
|
87
|
+
end
|
88
|
+
|
89
|
+
# Helper method that is true when someone tries to fetch a page with a
|
90
|
+
# larger number than the last page. Can be used in combination with flashes
|
91
|
+
# and redirecting.
|
92
|
+
def out_of_bounds?
|
93
|
+
current_page > total_pages
|
94
|
+
end
|
95
|
+
|
96
|
+
# Current offset of the paginated collection. If we're on the first page,
|
97
|
+
# it is always 0. If we're on the 2nd page and there are 30 entries per page,
|
98
|
+
# the offset is 30. This property is useful if you want to render ordinals
|
99
|
+
# side by side with records in the view: simply start with offset + 1.
|
100
|
+
def offset
|
101
|
+
(current_page - 1) * per_page
|
102
|
+
end
|
103
|
+
|
104
|
+
# current_page - 1 or nil if there is no previous page
|
105
|
+
def previous_page
|
106
|
+
current_page > 1 ? (current_page - 1) : nil
|
107
|
+
end
|
108
|
+
|
109
|
+
# current_page + 1 or nil if there is no next page
|
110
|
+
def next_page
|
111
|
+
current_page < total_pages ? (current_page + 1) : nil
|
112
|
+
end
|
113
|
+
|
114
|
+
# sets the <tt>total_entries</tt> property and calculates <tt>total_pages</tt>
|
115
|
+
def total_entries=(number)
|
116
|
+
@total_entries = number.to_i
|
117
|
+
@total_pages = (@total_entries / per_page.to_f).ceil
|
118
|
+
end
|
119
|
+
|
120
|
+
# This is a magic wrapper for the original Array#replace method. It serves
|
121
|
+
# for populating the paginated collection after initialization.
|
122
|
+
#
|
123
|
+
# Why magic? Because it tries to guess the total number of entries judging
|
124
|
+
# by the size of given array. If it is shorter than +per_page+ limit, then we
|
125
|
+
# know we're on the last page. This trick is very useful for avoiding
|
126
|
+
# unnecessary hits to the database to do the counting after we fetched the
|
127
|
+
# data for the current page.
|
128
|
+
#
|
129
|
+
# However, after using +replace+ you should always test the value of
|
130
|
+
# +total_entries+ and set it to a proper value if it's +nil+. See the example
|
131
|
+
# in +create+.
|
132
|
+
def replace(array)
|
133
|
+
result = super
|
134
|
+
|
135
|
+
# The collection is shorter then page limit? Rejoice, because
|
136
|
+
# then we know that we are on the last page!
|
137
|
+
if total_entries.nil? and length < per_page and (current_page == 1 or length > 0)
|
138
|
+
self.total_entries = offset + length
|
139
|
+
end
|
140
|
+
|
141
|
+
result
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|