will_paginate 2.1.0

Sign up to get free protection for your applications and to get access to all the features.

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