will_paginate 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of will_paginate might be problematic. Click here for more details.

data/CHANGELOG ADDED
@@ -0,0 +1,3 @@
1
+ == 2.1.0 2008-01-20
2
+
3
+ * created a gem version from Rails plugin
data/LICENSE ADDED
@@ -0,0 +1,18 @@
1
+ Copyright (c) 2008 PJ Hyett and 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/Manifest.txt ADDED
@@ -0,0 +1,34 @@
1
+ CHANGELOG
2
+ LICENSE
3
+ Manifest.txt
4
+ README
5
+ Rakefile
6
+ config/release.rb
7
+ lib/will_paginate.rb
8
+ lib/will_paginate/collection.rb
9
+ lib/will_paginate/core_ext.rb
10
+ lib/will_paginate/finder.rb
11
+ lib/will_paginate/version.rb
12
+ lib/will_paginate/view_helpers.rb
13
+ setup.rb
14
+ test/array_pagination_test.rb
15
+ test/boot.rb
16
+ test/console
17
+ test/finder_test.rb
18
+ test/fixtures/admin.rb
19
+ test/fixtures/developer.rb
20
+ test/fixtures/developers_projects.yml
21
+ test/fixtures/project.rb
22
+ test/fixtures/projects.yml
23
+ test/fixtures/replies.yml
24
+ test/fixtures/reply.rb
25
+ test/fixtures/schema.rb
26
+ test/fixtures/topic.rb
27
+ test/fixtures/topics.yml
28
+ test/fixtures/user.rb
29
+ test/fixtures/users.yml
30
+ test/helper.rb
31
+ test/lib/activerecord_test_case.rb
32
+ test/lib/activerecord_test_connector.rb
33
+ test/lib/load_fixtures.rb
34
+ test/pagination_test.rb
data/README ADDED
@@ -0,0 +1,152 @@
1
+ = WillPaginate
2
+
3
+ Pagination is just limiting the number of records displayed. Why should you let
4
+ it get in your way while developing, then? This plugin makes magic happen. Did
5
+ you ever want to be able to do just this on a model:
6
+
7
+ Post.paginate :page => 1, :order => 'created_at DESC'
8
+
9
+ ... and then render the page links with a single view helper? Well, now you
10
+ can.
11
+
12
+ Ryan Bates made an awesome screencast[http://railscasts.com/episodes/51],
13
+ check it out.
14
+
15
+ Your mind reels with questions? Join our Google
16
+ group[http://groups.google.com/group/will_paginate].
17
+
18
+ You can find more documentation on the wiki[http://github.com/mislav/will_paginate/wikis].
19
+
20
+ == Example usage
21
+
22
+ Use a paginate finder in the controller:
23
+
24
+ @posts = Post.paginate_by_board_id @board.id, :page => params[:page], :order => 'updated_at DESC'
25
+
26
+ Yeah, +paginate+ works just like +find+ -- it just doesn't fetch all the
27
+ records. Don't forget to tell it which page you want, or it will complain!
28
+ Read more on WillPaginate::Finder::ClassMethods.
29
+
30
+ Render the posts in your view like you would normally do. When you need to render
31
+ pagination, just stick this in:
32
+
33
+ <%= will_paginate @posts %>
34
+
35
+ You're done. (Copy and paste the example fancy CSS styles from the bottom.) You
36
+ can find the option list at WillPaginate::ViewHelpers.
37
+
38
+ How does it know how much items to fetch per page? It asks your model by calling
39
+ its <tt>per_page</tt> class method. You can define it like this:
40
+
41
+ class Post < ActiveRecord::Base
42
+ cattr_reader :per_page
43
+ @@per_page = 50
44
+ end
45
+
46
+ ... or like this:
47
+
48
+ class Post < ActiveRecord::Base
49
+ def self.per_page
50
+ 50
51
+ end
52
+ end
53
+
54
+ ... or don't worry about it at all. WillPaginate defines it to be <b>30</b> by default.
55
+ But you can always specify the count explicitly when calling +paginate+:
56
+
57
+ @posts = Post.paginate :page => params[:page], :per_page => 50
58
+
59
+ The +paginate+ finder wraps the original finder and returns your resultset that now has
60
+ some new properties. You can use the collection as you would with any ActiveRecord
61
+ resultset. WillPaginate view helpers also need that object to be able to render pagination:
62
+
63
+ <ol>
64
+ <% for post in @posts -%>
65
+ <li>Render `post` in some nice way.</li>
66
+ <% end -%>
67
+ </ol>
68
+
69
+ <p>Now let's render us some pagination!</p>
70
+ <%= will_paginate @posts %>
71
+
72
+ More detailed documentation:
73
+
74
+ * WillPaginate::Finder::ClassMethods for pagination on your models;
75
+ * WillPaginate::ViewHelpers for your views.
76
+
77
+ == Oh noes, a bug!
78
+
79
+ Tell us what happened so we can fix it, quick! Issues are filed on the Lighthouse project:
80
+ http://err.lighthouseapp.com/projects/466-plugins/tickets?q=tagged:will_paginate
81
+
82
+ Steps to make an awesome bug report:
83
+
84
+ 1. Run <tt>rake test</tt> in the <i>will_paginate</i> directory. (You will need SQLite3.)
85
+ Copy the output if there are failing tests.
86
+ 2. Register on Lighthouse to create a new ticket.
87
+ 3. Write a descriptive, short title. Provide as much info as you can in the body.
88
+ Assign the ticket to Mislav and tag it with meaningful tags, <tt>"will_paginate"</tt>
89
+ being among them.
90
+ 4. Yay! You will be notified on updates automatically.
91
+
92
+ Here is an example of a great bug report and patch:
93
+ http://err.lighthouseapp.com/projects/466/tickets/172-total_entries-ignored-in-paginate_by_sql
94
+
95
+ == Authors, credits, contact
96
+
97
+ Want to discuss, request features, ask questions? Join the Google group:
98
+ http://groups.google.com/group/will_paginate
99
+
100
+ Authors:: Mislav Marohnić, PJ Hyett
101
+ Original announcement:: http://errtheblog.com/post/929
102
+ Original PHP source:: http://www.strangerstudios.com/sandbox/pagination/diggstyle.php
103
+
104
+ All these people helped making will_paginate what it is now with their code
105
+ contributions or simply awesome ideas:
106
+
107
+ Chris Wanstrath, Dr. Nic Williams, K. Adam Christensen, Mike Garey, Bence
108
+ Golda, Matt Aimonetti, Charles Brian Quinn, Desi McAdam, James Coglan, Matijs
109
+ van Zuijlen, Maria, Brendan Ribera, Todd Willey, Bryan Helmkamp, Jan Berkel.
110
+
111
+ == Usable pagination in the UI
112
+
113
+ Copy the following CSS into your stylesheet for a good start:
114
+
115
+ .pagination {
116
+ padding: 3px;
117
+ margin: 3px;
118
+ }
119
+ .pagination a {
120
+ padding: 2px 5px 2px 5px;
121
+ margin: 2px;
122
+ border: 1px solid #aaaadd;
123
+ text-decoration: none;
124
+ color: #000099;
125
+ }
126
+ .pagination a:hover, .pagination a:active {
127
+ border: 1px solid #000099;
128
+ color: #000;
129
+ }
130
+ .pagination span.current {
131
+ padding: 2px 5px 2px 5px;
132
+ margin: 2px;
133
+ border: 1px solid #000099;
134
+ font-weight: bold;
135
+ background-color: #000099;
136
+ color: #FFF;
137
+ }
138
+ .pagination span.disabled {
139
+ padding: 2px 5px 2px 5px;
140
+ margin: 2px;
141
+ border: 1px solid #eee;
142
+ color: #ddd;
143
+ }
144
+
145
+ More reading about pagination as design pattern:
146
+
147
+ * Pagination 101:
148
+ http://kurafire.net/log/archive/2007/06/22/pagination-101
149
+ * Pagination gallery:
150
+ http://www.smashingmagazine.com/2007/11/16/pagination-gallery-examples-and-good-practices/
151
+ * Pagination on Yahoo Design Pattern Library:
152
+ http://developer.yahoo.com/ypatterns/parent.php?pattern=pagination
data/Rakefile ADDED
@@ -0,0 +1,65 @@
1
+ require 'rake/testtask'
2
+ require 'rake/rdoctask'
3
+ require 'config/release'
4
+
5
+ desc 'Default: run unit tests.'
6
+ task :default => :test
7
+
8
+ desc 'Test the will_paginate plugin.'
9
+ Rake::TestTask.new(:test) do |t|
10
+ t.pattern = 'test/**/*_test.rb'
11
+ t.verbose = true
12
+ end
13
+
14
+ # I want to specify environment variables at call time
15
+ class EnvTestTask < Rake::TestTask
16
+ attr_accessor :env
17
+
18
+ def ruby(*args)
19
+ env.each { |key, value| ENV[key] = value } if env
20
+ super
21
+ env.keys.each { |key| ENV.delete key } if env
22
+ end
23
+ end
24
+
25
+ for configuration in %w( sqlite3 mysql postgres )
26
+ EnvTestTask.new("test_#{configuration}") do |t|
27
+ t.pattern = 'test/finder_test.rb'
28
+ t.verbose = true
29
+ t.env = { 'DB' => configuration }
30
+ end
31
+ end
32
+
33
+ task :test_databases => %w(test_mysql test_sqlite3 test_postgres)
34
+
35
+ desc %{Test everything on SQLite3, MySQL and PostgreSQL}
36
+ task :test_full => %w(test test_mysql test_postgres)
37
+
38
+ desc %{Test everything with Rails 1.2.x and 2.0.x gems}
39
+ task :test_all do
40
+ all = Rake::Task['test_full']
41
+ ENV['RAILS_VERSION'] = '~>1.2.6'
42
+ all.invoke
43
+ # reset the invoked flag
44
+ %w( test_full test test_mysql test_postgres ).each do |name|
45
+ Rake::Task[name].instance_variable_set '@already_invoked', false
46
+ end
47
+ # do it again
48
+ ENV['RAILS_VERSION'] = '~>2.0.2'
49
+ all.invoke
50
+ end
51
+
52
+ desc 'Generate RDoc documentation for the will_paginate plugin.'
53
+ Rake::RDocTask.new(:rdoc) do |rdoc|
54
+ files = ['README', 'LICENSE', 'lib/**/*.rb']
55
+ rdoc.rdoc_files.add(files)
56
+ rdoc.main = "README" # page to start on
57
+ rdoc.title = "will_paginate"
58
+
59
+ templates = %w[/Users/chris/ruby/projects/err/rock/template.rb /var/www/rock/template.rb]
60
+ rdoc.template = templates.find { |t| File.exists? t }
61
+
62
+ rdoc.rdoc_dir = 'doc' # rdoc output folder
63
+ rdoc.options << '--inline-source'
64
+ rdoc.options << '--charset=UTF-8'
65
+ end
data/config/release.rb ADDED
@@ -0,0 +1,82 @@
1
+ require 'rubyforge'
2
+ require 'rake/gempackagetask'
3
+ require 'lib/will_paginate/version.rb'
4
+
5
+ RUBYFORGE_NAME = 'will-paginate'
6
+ NAME = 'will_paginate'
7
+ version = WillPaginate::VERSION::STRING
8
+
9
+ DESCRIPTION = <<-DESC
10
+ A Rails plugin that provides pagination solutions
11
+ for querying models and rendering pagination links in views.
12
+ DESC
13
+ DESCRIPTION.strip!.gsub! /\s+/, ' '
14
+
15
+ changes = nil
16
+
17
+ spec = Gem::Specification.new do |s|
18
+ s.name = NAME
19
+ s.version = version
20
+ s.summary = s.description = DESCRIPTION
21
+ s.authors = ['Mislav Marohnić', 'PJ Hyett']
22
+ s.email = 'mislav.marohnic@gmail.com'
23
+ s.homepage = 'http://github.com/mislav/will_paginate/wikis'
24
+ s.rubyforge_project = RUBYFORGE_NAME
25
+
26
+ s.add_dependency 'activesupport', '>=1.4.4'
27
+
28
+ s.files = File.read("Manifest.txt").split("\n")
29
+ s.executables = s.files.grep(/^bin/) { |f| File.basename(f) }
30
+
31
+ s.bindir = "bin"
32
+ dirs = Dir['{lib,ext}']
33
+ s.require_paths = dirs unless dirs.empty?
34
+
35
+ s.rdoc_options = ['--main', 'README', '--inline-source', '--charset=UTF-8']
36
+ s.extra_rdoc_files = %w(README LICENSE) # + s.files.grep(/\.txt$/) - %w(Manifest.txt)
37
+ s.has_rdoc = true
38
+ end
39
+
40
+ Rake::GemPackageTask.new spec do |pkg|
41
+ pkg.need_tar = false
42
+ pkg.need_zip = false
43
+ end
44
+
45
+ desc 'Package and upload the release to rubyforge.'
46
+ task :release => [:clean, :package] do |t|
47
+ v = ENV["VERSION"] or abort "Must supply VERSION=x.y.z"
48
+ abort "Version doesn't match #{version}" if v != version
49
+ files = Dir["pkg/#{NAME}-#{version}.*"]
50
+
51
+ rf = RubyForge.new
52
+ puts "Logging in to RubyForge"
53
+ rf.login
54
+
55
+ c = rf.userconfig
56
+ c["release_notes"] = DESCRIPTION
57
+ c["release_changes"] = changes if changes
58
+ c["preformatted"] = true
59
+
60
+ puts "Releasing #{NAME} v. #{version}"
61
+ p files
62
+ rf.add_release RUBYFORGE_NAME, RUBYFORGE_NAME, version, *files
63
+ end
64
+
65
+ task :clean => [ :clobber_rdoc, :clobber_package ] do
66
+ removed = []
67
+ %w(diff diff.txt email.txt ri *.gem **/*~ **/.DS_Store).each do |pattern|
68
+ files = Dir[pattern]
69
+ next if files.empty?
70
+ FileUtils.rm_rf files
71
+ removed.concat files
72
+ end
73
+ puts "Removed files: #{removed.inspect}" unless removed.empty?
74
+ end
75
+
76
+ # desc 'Upload website files to rubyforge'
77
+ # task :website_upload do
78
+ # host = "#{rubyforge_username}@rubyforge.org"
79
+ # remote_dir = "/var/www/gforge-projects/#{PATH}/"
80
+ # local_dir = 'website'
81
+ # sh %{rsync -aCv #{local_dir}/ #{host}:#{remote_dir}}
82
+ # end
@@ -0,0 +1,132 @@
1
+ require 'will_paginate'
2
+
3
+ module WillPaginate
4
+ # = OMG, invalid page number!
5
+ # This is an ArgumentError raised in case a page was requested that is either
6
+ # zero or negative number. You should decide how do deal with such errors in
7
+ # the controller.
8
+ #
9
+ # This error is *not* raised when a page further than the last page is
10
+ # requested. Use <tt>WillPaginate::Collection#out_of_bounds?</tt> method to
11
+ # check for those cases and manually deal with them as you see fit.
12
+ class InvalidPage < ArgumentError
13
+ def initialize(page, page_num)
14
+ super "#{page.inspect} given as value, which translates to '#{page_num}' as page number"
15
+ end
16
+ end
17
+
18
+ # Arrays returned from paginating finds are, in fact, instances of this.
19
+ # You may think of WillPaginate::Collection as an ordinary array with some
20
+ # extra properties. Those properties are used by view helpers to generate
21
+ # correct page links.
22
+ #
23
+ # WillPaginate::Collection also assists in rolling out your own pagination
24
+ # solutions: see +create+.
25
+ #
26
+ class Collection < Array
27
+ attr_reader :current_page, :per_page, :total_entries
28
+
29
+ # Arguments to this constructor are the current page number, per-page limit
30
+ # and the total number of entries. The last argument is optional because it
31
+ # is best to do lazy counting; in other words, count *conditionally* after
32
+ # populating the collection using the +replace+ method.
33
+ #
34
+ def initialize(page, per_page, total = nil)
35
+ @current_page = page.to_i
36
+ raise InvalidPage.new(page, @current_page) if @current_page < 1
37
+ @per_page = per_page.to_i
38
+ raise ArgumentError, "`per_page` setting cannot be less than 1 (#{@per_page} given)" if @per_page < 1
39
+
40
+ self.total_entries = total if total
41
+ end
42
+
43
+ # Just like +new+, but yields the object after instantiation and returns it
44
+ # afterwards. This is very useful for manual pagination:
45
+ #
46
+ # @entries = WillPaginate::Collection.create(1, 10) do |pager|
47
+ # result = Post.find(:all, :limit => pager.per_page, :offset => pager.offset)
48
+ # # inject the result array into the paginated collection:
49
+ # pager.replace(result)
50
+ #
51
+ # unless pager.total_entries
52
+ # # the pager didn't manage to guess the total count, do it manually
53
+ # pager.total_entries = Post.count
54
+ # end
55
+ # end
56
+ #
57
+ # The possibilities with this are endless. For another example, here is how
58
+ # WillPaginate used to define pagination for Array instances:
59
+ #
60
+ # Array.class_eval do
61
+ # def paginate(page = 1, per_page = 15)
62
+ # WillPaginate::Collection.create(page, per_page, size) do |pager|
63
+ # pager.replace self[pager.offset, pager.per_page].to_a
64
+ # end
65
+ # end
66
+ # end
67
+ #
68
+ def self.create(page, per_page, total = nil, &block)
69
+ pager = new(page, per_page, total)
70
+ yield pager
71
+ pager
72
+ end
73
+
74
+ # The total number of pages.
75
+ def page_count
76
+ @total_pages
77
+ end
78
+
79
+ # Helper method that is true when someone tries to fetch a page with a
80
+ # larger number than the last page. Can be used in combination with flashes
81
+ # and redirecting.
82
+ def out_of_bounds?
83
+ current_page > page_count
84
+ end
85
+
86
+ # Current offset of the paginated collection. If we're on the first page,
87
+ # it is always 0. If we're on the 2nd page and there are 30 entries per page,
88
+ # the offset is 30. This property is useful if you want to render ordinals
89
+ # besides your records: simply start with offset + 1.
90
+ #
91
+ def offset
92
+ (current_page - 1) * per_page
93
+ end
94
+
95
+ # current_page - 1 or nil if there is no previous page
96
+ def previous_page
97
+ current_page > 1 ? (current_page - 1) : nil
98
+ end
99
+
100
+ # current_page + 1 or nil if there is no next page
101
+ def next_page
102
+ current_page < page_count ? (current_page + 1) : nil
103
+ end
104
+
105
+ def total_entries=(number)
106
+ @total_entries = number.to_i
107
+ @total_pages = (@total_entries / per_page.to_f).ceil
108
+ end
109
+
110
+ # This is a magic wrapper for the original Array#replace method. It serves
111
+ # for populating the paginated collection after initialization.
112
+ #
113
+ # Why magic? Because it tries to guess the total number of entries judging
114
+ # by the size of given array. If it is shorter than +per_page+ limit, then we
115
+ # know we're on the last page. This trick is very useful for avoiding
116
+ # unnecessary hits to the database to do the counting after we fetched the
117
+ # data for the current page.
118
+ #
119
+ # However, after using +replace+ you should always test the value of
120
+ # +total_entries+ and set it to a proper value if it's +nil+. See the example
121
+ # in +create+.
122
+ def replace(array)
123
+ returning super do
124
+ # The collection is shorter then page limit? Rejoice, because
125
+ # then we know that we are on the last page!
126
+ if total_entries.nil? and length > 0 and length < per_page
127
+ self.total_entries = offset + length
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,80 @@
1
+ require 'will_paginate'
2
+ require 'set'
3
+
4
+ unless Hash.instance_methods.include? 'except'
5
+ Hash.class_eval do
6
+ # Returns a new hash without the given keys.
7
+ def except(*keys)
8
+ rejected = Set.new(respond_to?(:convert_key) ? keys.map { |key| convert_key(key) } : keys)
9
+ reject { |key,| rejected.include?(key) }
10
+ end
11
+
12
+ # Replaces the hash without only the given keys.
13
+ def except!(*keys)
14
+ replace(except(*keys))
15
+ end
16
+ end
17
+ end
18
+
19
+ unless Hash.instance_methods.include? 'slice'
20
+ Hash.class_eval do
21
+ # Returns a new hash with only the given keys.
22
+ def slice(*keys)
23
+ allowed = Set.new(respond_to?(:convert_key) ? keys.map { |key| convert_key(key) } : keys)
24
+ reject { |key,| !allowed.include?(key) }
25
+ end
26
+
27
+ # Replaces the hash with only the given keys.
28
+ def slice!(*keys)
29
+ replace(slice(*keys))
30
+ end
31
+ end
32
+ end
33
+
34
+ unless Hash.instance_methods.include? 'rec_merge!'
35
+ Hash.class_eval do
36
+ # Same as Hash#merge!, but recursively merges sub-hashes
37
+ # (stolen from Haml)
38
+ def rec_merge!(other)
39
+ other.each do |key, other_value|
40
+ value = self[key]
41
+ if value.is_a?(Hash) and other_value.is_a?(Hash)
42
+ value.rec_merge! other_value
43
+ else
44
+ self[key] = other_value
45
+ end
46
+ end
47
+ self
48
+ end
49
+ end
50
+ end
51
+
52
+ require 'will_paginate/collection'
53
+
54
+ unless Array.instance_methods.include? 'paginate'
55
+ # http://www.desimcadam.com/archives/8
56
+ Array.class_eval do
57
+ def paginate(options_or_page = {}, per_page = nil)
58
+ if options_or_page.nil? or Fixnum === options_or_page
59
+ if defined? WillPaginate::Deprecation
60
+ WillPaginate::Deprecation.warn <<-DEPR
61
+ Array#paginate now conforms to the main, ActiveRecord::Base#paginate API. You should \
62
+ call it with a parameters hash (:page, :per_page). The old API (numbers as arguments) \
63
+ has been deprecated and is going to be unsupported in future versions of will_paginate.
64
+ DEPR
65
+ end
66
+ page = options_or_page
67
+ options = {}
68
+ else
69
+ options = options_or_page
70
+ page = options[:page]
71
+ raise ArgumentError, "wrong number of arguments (1 hash or 2 Fixnums expected)" if per_page
72
+ per_page = options[:per_page]
73
+ end
74
+
75
+ WillPaginate::Collection.create(page || 1, per_page || 30, options[:total_entries] || size) do |pager|
76
+ pager.replace self[pager.offset, pager.per_page].to_a
77
+ end
78
+ end
79
+ end
80
+ end