pb-will_paginate 2.3.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/.gitignore +4 -0
  2. data/.manifest +43 -0
  3. data/CHANGELOG.rdoc +139 -0
  4. data/LICENSE +18 -0
  5. data/README.rdoc +107 -0
  6. data/Rakefile +71 -0
  7. data/VERSION +1 -0
  8. data/examples/apple-circle.gif +0 -0
  9. data/examples/index.haml +69 -0
  10. data/examples/index.html +92 -0
  11. data/examples/pagination.css +90 -0
  12. data/examples/pagination.sass +91 -0
  13. data/init.rb +2 -0
  14. data/lib/will_paginate.rb +90 -0
  15. data/lib/will_paginate/array.rb +16 -0
  16. data/lib/will_paginate/collection.rb +144 -0
  17. data/lib/will_paginate/core_ext.rb +43 -0
  18. data/lib/will_paginate/finder.rb +264 -0
  19. data/lib/will_paginate/i18n.rb +178 -0
  20. data/lib/will_paginate/named_scope.rb +170 -0
  21. data/lib/will_paginate/named_scope_patch.rb +37 -0
  22. data/lib/will_paginate/version.rb +9 -0
  23. data/lib/will_paginate/view_helpers.rb +397 -0
  24. data/locales/en.yml +11 -0
  25. data/pb-will_paginate.gemspec +106 -0
  26. data/test/boot.rb +21 -0
  27. data/test/collection_test.rb +143 -0
  28. data/test/console +8 -0
  29. data/test/database.yml +22 -0
  30. data/test/finder_test.rb +473 -0
  31. data/test/fixtures/admin.rb +3 -0
  32. data/test/fixtures/developer.rb +14 -0
  33. data/test/fixtures/developers_projects.yml +13 -0
  34. data/test/fixtures/project.rb +15 -0
  35. data/test/fixtures/projects.yml +6 -0
  36. data/test/fixtures/replies.yml +29 -0
  37. data/test/fixtures/reply.rb +7 -0
  38. data/test/fixtures/schema.rb +38 -0
  39. data/test/fixtures/topic.rb +10 -0
  40. data/test/fixtures/topics.yml +30 -0
  41. data/test/fixtures/user.rb +2 -0
  42. data/test/fixtures/users.yml +35 -0
  43. data/test/helper.rb +37 -0
  44. data/test/i18n_test.rb +194 -0
  45. data/test/lib/activerecord_test_case.rb +43 -0
  46. data/test/lib/activerecord_test_connector.rb +75 -0
  47. data/test/lib/load_fixtures.rb +11 -0
  48. data/test/lib/view_test_process.rb +179 -0
  49. data/test/tasks.rake +59 -0
  50. data/test/view_test.rb +289 -0
  51. metadata +122 -0
@@ -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">&laquo; 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">&hellip;</span> <a href="./?page=29">29</a> <a href="./?page=30">30</a> <a href="./?page=2" rel="next" class="next_page">Next &raquo;</a>
54
+ </div>
55
+ <h2>Digg.com</h2>
56
+ <div class='digg_pagination'>
57
+ <span class="disabled prev_page">&laquo; 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">&hellip;</span> <a href="./?page=29">29</a> <a href="./?page=30">30</a> <a href="./?page=2" rel="next" class="next_page">Next &raquo;</a>
58
+ </div>
59
+ <h2>Digg-style, no page links</h2>
60
+ <div class='digg_pagination'>
61
+ <span class="disabled prev_page">&laquo; Previous</span> <a href="./?page=2" rel="next" class="next_page">Next &raquo;</a>
62
+ </div>
63
+ <p>Code that renders this:</p>
64
+ <pre>
65
+ <code>&lt;%= will_paginate @posts, :page_links =&gt; false %&gt;</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&nbsp;-&nbsp;6</b> of <b>180</b> in total
71
+ </div>
72
+ <span class="disabled prev_page">&laquo; 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">&hellip;</span> <a href="./?page=29">29</a> <a href="./?page=30">30</a> <a href="./?page=2" rel="next" class="next_page">Next &raquo;</a>
73
+ </div>
74
+ <p>Code that renders this:</p>
75
+ <pre>
76
+ <code>&lt;div class="digg_pagination"&gt;
77
+ &lt;div clas="page_info"&gt;
78
+ &lt;%= page_entries_info @posts %&gt;
79
+ &lt;/div&gt;
80
+ &lt;%= will_paginate @posts, :container =&gt; false %&gt;
81
+ &lt;/div&gt;</code>
82
+ </pre>
83
+ <h2>Apple.com store</h2>
84
+ <div class='apple_pagination'>
85
+ <span class="disabled prev_page">&laquo; 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">&hellip;</span> <a href="./?page=29">29</a> <a href="./?page=30">30</a> <a href="./?page=2" rel="next" class="next_page">Next &raquo;</a>
86
+ </div>
87
+ <h2>Flickr.com</h2>
88
+ <div class='flickr_pagination'>
89
+ <span class="disabled prev_page">&laquo; 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">&hellip;</span> <a href="./?page=29">29</a> <a href="./?page=30">30</a> <a href="./?page=2" rel="next" class="next_page">Next &raquo;</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,2 @@
1
+ require 'will_paginate'
2
+ WillPaginate::I18n.append_translations_load_path
@@ -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