will_paginate 3.0.pre4 → 3.0.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.

Files changed (51) hide show
  1. data/README.md +61 -0
  2. data/Rakefile +19 -36
  3. data/lib/will_paginate.rb +16 -14
  4. data/lib/will_paginate/{finders/active_record.rb → active_record.rb} +66 -35
  5. data/lib/will_paginate/array.rb +5 -5
  6. data/lib/will_paginate/collection.rb +12 -36
  7. data/lib/will_paginate/core_ext.rb +0 -28
  8. data/lib/will_paginate/data_mapper.rb +91 -0
  9. data/lib/will_paginate/i18n.rb +22 -0
  10. data/lib/will_paginate/locale/en.yml +33 -0
  11. data/lib/will_paginate/page_number.rb +57 -0
  12. data/lib/will_paginate/per_page.rb +27 -0
  13. data/lib/will_paginate/railtie.rb +13 -4
  14. data/lib/will_paginate/sequel.rb +36 -0
  15. data/lib/will_paginate/version.rb +1 -1
  16. data/lib/will_paginate/view_helpers.rb +136 -22
  17. data/lib/will_paginate/view_helpers/action_view.rb +129 -117
  18. data/lib/will_paginate/view_helpers/link_renderer.rb +10 -11
  19. data/lib/will_paginate/view_helpers/link_renderer_base.rb +2 -8
  20. data/lib/will_paginate/view_helpers/merb.rb +20 -7
  21. data/lib/will_paginate/view_helpers/sinatra.rb +41 -0
  22. data/spec/ci.rb +29 -0
  23. data/spec/collection_spec.rb +7 -23
  24. data/spec/console +9 -1
  25. data/spec/console_fixtures.rb +1 -3
  26. data/spec/database.yml +10 -10
  27. data/spec/finders/active_record_spec.rb +82 -28
  28. data/spec/finders/activerecord_test_connector.rb +9 -1
  29. data/spec/finders/data_mapper_spec.rb +59 -48
  30. data/spec/finders/data_mapper_test_connector.rb +8 -1
  31. data/spec/finders/sequel_spec.rb +9 -3
  32. data/spec/fixtures/project.rb +2 -0
  33. data/spec/page_number_spec.rb +65 -0
  34. data/spec/per_page_spec.rb +41 -0
  35. data/spec/spec_helper.rb +5 -29
  36. data/spec/view_helpers/action_view_spec.rb +48 -32
  37. data/spec/view_helpers/base_spec.rb +59 -7
  38. data/spec/view_helpers/link_renderer_base_spec.rb +7 -12
  39. data/spec/view_helpers/view_example_group.rb +1 -0
  40. metadata +22 -23
  41. data/CHANGELOG.rdoc +0 -105
  42. data/README.rdoc +0 -111
  43. data/lib/will_paginate/deprecation.rb +0 -50
  44. data/lib/will_paginate/finders.rb +0 -9
  45. data/lib/will_paginate/finders/active_resource.rb +0 -51
  46. data/lib/will_paginate/finders/base.rb +0 -112
  47. data/lib/will_paginate/finders/data_mapper.rb +0 -30
  48. data/lib/will_paginate/finders/sequel.rb +0 -23
  49. data/lib/will_paginate/view_helpers/base.rb +0 -126
  50. data/spec/finders/active_resource_spec.rb +0 -52
  51. data/spec/finders_spec.rb +0 -76
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.pre4'
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 CHANGED
@@ -1,42 +1,25 @@
1
- require 'rake/rdoctask'
2
- require 'rspec/core/rake_task'
1
+ begin
2
+ require 'rspec/core/rake_task'
3
+ rescue LoadError
4
+ # no spec tasks
5
+ else
6
+ task :default => :spec
3
7
 
4
- task :default => :spec
5
-
6
- desc 'Run ALL OF the specs'
7
- RSpec::Core::RakeTask.new(:spec) do |t|
8
- # t.ruby_opts = '-w'
9
- end
10
-
11
- namespace :spec do
12
- desc "Run Rails specs"
13
- RSpec::Core::RakeTask.new(:rails) do |t|
14
- t.pattern = %w'spec/finders/active_record_spec.rb spec/view_helpers/action_view_spec.rb'
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'
15
12
  end
16
- end
17
13
 
18
- desc 'Generate RDoc documentation for the will_paginate plugin.'
19
- Rake::RDocTask.new(:rdoc) do |rdoc|
20
- rdoc.rdoc_files.include('README.rdoc', 'LICENSE', 'CHANGELOG.rdoc').
21
- include('lib/**/*.rb').
22
- exclude('lib/will_paginate/finders/active_record/named_scope*').
23
- exclude('lib/will_paginate/finders/sequel.rb').
24
- exclude('lib/will_paginate/view_helpers/merb.rb').
25
- exclude('lib/will_paginate/deprecation.rb').
26
- exclude('lib/will_paginate/core_ext.rb').
27
- exclude('lib/will_paginate/version.rb')
28
-
29
- rdoc.main = "README.rdoc" # page to start on
30
- rdoc.title = "will_paginate documentation"
31
-
32
- rdoc.rdoc_dir = 'doc' # rdoc output folder
33
- rdoc.options << '--inline-source' << '--charset=UTF-8'
34
- rdoc.options << '--webcvs=http://github.com/mislav/will_paginate/tree/master/'
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
35
20
  end
36
21
 
37
- task :website do
38
- Dir.chdir('website') do
39
- %x(haml index.haml index.html)
40
- %x(sass pagination.sass pagination.css)
41
- end
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'
42
25
  end
data/lib/will_paginate.rb CHANGED
@@ -1,23 +1,25 @@
1
- require 'will_paginate/deprecation'
2
-
3
- # = You *will* paginate!
4
- #
5
- # First read about WillPaginate::Finders::Base, then see
6
- # WillPaginate::ViewHelpers. The magical array you're handling in-between is
7
- # WillPaginate::Collection.
8
- #
9
- # Happy paginating!
1
+ # You will paginate!
10
2
  module WillPaginate
11
3
  end
12
4
 
13
- if defined?(::Rails::Railtie)
5
+ if defined?(Rails::Railtie)
14
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"
15
9
  end
16
10
 
17
- if defined?(::Merb::Plugins)
11
+ if defined?(Merb::AbstractController)
18
12
  require 'will_paginate/view_helpers/merb'
19
- # auto-load the right ORM adapter
20
- if adapter = { :datamapper => 'data_mapper', :activerecord => 'active_record', :sequel => 'sequel' }[Merb.orm]
21
- require "will_paginate/finders/#{adapter}"
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
22
20
  end
23
21
  end
22
+
23
+ if defined?(Sinatra) and Sinatra.respond_to? :register
24
+ require 'will_paginate/view_helpers/sinatra'
25
+ end
@@ -1,7 +1,9 @@
1
- require 'will_paginate/finders/base'
1
+ require 'will_paginate/per_page'
2
+ require 'will_paginate/page_number'
3
+ require 'will_paginate/collection'
2
4
  require 'active_record'
3
5
 
4
- module WillPaginate::Finders
6
+ module WillPaginate
5
7
  # = Paginating finders for ActiveRecord models
6
8
  #
7
9
  # WillPaginate adds +paginate+, +per_page+ and other methods to
@@ -14,23 +16,6 @@ module WillPaginate::Finders
14
16
  # @posts = Post.paginate :all, :page => params[:page], :order => 'created_at DESC'
15
17
  #
16
18
  module ActiveRecord
17
- # In Rails, this is automatically called to mix-in pagination functionality to ActiveRecord.
18
- def self.enable!
19
- ::ActiveRecord::Base.extend Base
20
- ::ActiveRecord::Base.extend ActiveRecord::Pagination
21
- ::ActiveRecord::Base.extend ActiveRecord::BaseMethods
22
-
23
- klasses = [::ActiveRecord::Relation]
24
- if defined? ::ActiveRecord::Associations::CollectionProxy
25
- klasses << ::ActiveRecord::Associations::CollectionProxy
26
- else
27
- klasses << ::ActiveRecord::Associations::AssociationCollection
28
- end
29
-
30
- # support pagination on associations and scopes
31
- klasses.each { |klass| klass.send(:include, ActiveRecord::Pagination) }
32
- end
33
-
34
19
  # makes a Relation look like WillPaginate::Collection
35
20
  module RelationMethods
36
21
  attr_accessor :current_page
@@ -46,7 +31,7 @@ module WillPaginate::Finders
46
31
  def limit(num)
47
32
  rel = super
48
33
  if rel.current_page
49
- rel.offset((rel.current_page-1) * rel.limit_value)
34
+ rel.offset rel.current_page.to_offset(rel.limit_value).to_i
50
35
  else
51
36
  rel
52
37
  end
@@ -63,16 +48,27 @@ module WillPaginate::Finders
63
48
  if loaded? and size < limit_value and (current_page == 1 or size > 0)
64
49
  offset_value + size
65
50
  else
66
- excluded = [:order, :limit, :offset]
67
- excluded << :includes unless eager_loading?
68
- rel = self.except(*excluded)
69
- # TODO: hack. decide whether to keep
70
- rel = rel.apply_finder_options(@wp_count_options) if defined? @wp_count_options
71
- rel.count
51
+ @total_entries_queried = true
52
+ result = count
53
+ result = result.size if result.respond_to?(:size) and !result.is_a?(Integer)
54
+ result
72
55
  end
73
56
  end
74
57
  end
75
58
 
59
+ def count
60
+ if limit_value
61
+ excluded = [:order, :limit, :offset]
62
+ excluded << :includes unless eager_loading?
63
+ rel = self.except(*excluded)
64
+ # TODO: hack. decide whether to keep
65
+ rel = rel.apply_finder_options(@wp_count_options) if defined? @wp_count_options
66
+ rel.count
67
+ else
68
+ super
69
+ end
70
+ end
71
+
76
72
  # workaround for Active Record 3.0
77
73
  def size
78
74
  if !loaded? and limit_value
@@ -82,6 +78,15 @@ module WillPaginate::Finders
82
78
  end
83
79
  end
84
80
 
81
+ # overloaded to be pagination-aware
82
+ def empty?
83
+ if !loaded? and offset_value
84
+ count <= offset_value
85
+ else
86
+ super
87
+ end
88
+ end
89
+
85
90
  def total_pages
86
91
  (total_entries / limit_value.to_f).ceil
87
92
  end
@@ -89,7 +94,8 @@ module WillPaginate::Finders
89
94
  def clone
90
95
  other = super
91
96
  other.current_page = current_page unless other.current_page
92
- other.total_entries = nil
97
+ other.total_entries = nil if defined? @total_entries_queried
98
+ other.wp_count_options = @wp_count_options if defined? @wp_count_options
93
99
  other
94
100
  end
95
101
 
@@ -106,20 +112,26 @@ module WillPaginate::Finders
106
112
 
107
113
  module Pagination
108
114
  def paginate(options)
109
- pagenum, per_page, total = wp_parse_options(options)
110
- count_options = options[:count]
111
- options = options.except(:page, :per_page, :total_entries, :count)
112
- rel = limit(per_page).page(pagenum)
115
+ options = options.dup
116
+ pagenum = options.fetch(:page) { raise ArgumentError, ":page parameter required" }
117
+ per_page = options.delete(:per_page) || self.per_page
118
+ total = options.delete(:total_entries)
119
+
120
+ count_options = options.delete(:count)
121
+ options.delete(:page)
122
+
123
+ rel = limit(per_page.to_i).page(pagenum)
113
124
  rel = rel.apply_finder_options(options) if options.any?
114
125
  rel.wp_count_options = count_options if count_options
126
+ rel.total_entries = total.to_i unless total.blank?
115
127
  rel
116
128
  end
117
129
 
118
130
  def page(num)
119
- pagenum = num.nil? ? 1 : num.to_i
120
- raise ::WillPaginate::InvalidPage, num, pagenum if pagenum < 1
121
131
  rel = scoped.extending(RelationMethods)
122
- rel = rel.offset((pagenum-1) * (rel.limit_value || per_page))
132
+ pagenum = ::WillPaginate::PageNumber(num.nil? ? 1 : num)
133
+ per_page = rel.limit_value || self.per_page
134
+ rel = rel.offset(pagenum.to_offset(per_page).to_i)
123
135
  rel = rel.limit(per_page) unless rel.limit_value
124
136
  rel.current_page = pagenum
125
137
  rel
@@ -142,7 +154,11 @@ module WillPaginate::Finders
142
154
  # application.
143
155
  #
144
156
  def paginate_by_sql(sql, options)
145
- WillPaginate::Collection.create(*wp_parse_options(options)) do |pager|
157
+ pagenum = options.fetch(:page) { raise ArgumentError, ":page parameter required" }
158
+ per_page = options[:per_page] || self.per_page
159
+ total = options[:total_entries]
160
+
161
+ WillPaginate::Collection.create(pagenum, per_page, total) do |pager|
146
162
  query = sanitize_sql(sql.dup)
147
163
  original_query = query.dup
148
164
  # add limit, offset
@@ -163,5 +179,20 @@ module WillPaginate::Finders
163
179
  end
164
180
  end
165
181
  end
182
+
183
+ # mix everything into Active Record
184
+ ::ActiveRecord::Base.extend PerPage
185
+ ::ActiveRecord::Base.extend Pagination
186
+ ::ActiveRecord::Base.extend BaseMethods
187
+
188
+ klasses = [::ActiveRecord::Relation]
189
+ if defined? ::ActiveRecord::Associations::CollectionProxy
190
+ klasses << ::ActiveRecord::Associations::CollectionProxy
191
+ else
192
+ klasses << ::ActiveRecord::Associations::AssociationCollection
193
+ end
194
+
195
+ # support pagination on associations and scopes
196
+ klasses.each { |klass| klass.send(:include, Pagination) }
166
197
  end
167
198
  end
@@ -22,11 +22,11 @@ class Array
22
22
  # McAdam}[http://www.desimcadam.com/archives/8] and later proved to be the
23
23
  # most useful method of will_paginate library.
24
24
  def paginate(options = {})
25
- raise ArgumentError, "parameter hash expected (got #{options.inspect})" unless Hash === options
26
-
27
- WillPaginate::Collection.create options[:page] || 1,
28
- options[:per_page] || 30,
29
- options[:total_entries] || self.length do |pager|
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
30
  pager.replace self[pager.offset, pager.per_page].to_a
31
31
  end
32
32
  end
@@ -1,27 +1,7 @@
1
+ require 'will_paginate/per_page'
2
+ require 'will_paginate/page_number'
3
+
1
4
  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) #:nodoc:
21
- super "#{page.inspect} given as value, which translates to '#{page_num}' as page number"
22
- end
23
- end
24
-
25
5
  # = The key to pagination
26
6
  # Arrays returned from paginating finds are, in fact, instances of this little
27
7
  # class. You may think of WillPaginate::Collection as an ordinary array with
@@ -33,12 +13,10 @@ module WillPaginate
33
13
  #
34
14
  # If you are writing a library that provides a collection which you would like
35
15
  # to conform to this API, you don't have to copy these methods over; simply
36
- # make your plugin/gem dependant on the "will_paginate" gem:
16
+ # make your plugin/gem dependant on this library and do:
37
17
  #
38
- # gem 'will_paginate'
39
18
  # require 'will_paginate/collection'
40
- #
41
- # # now use WillPaginate::Collection directly or subclass it
19
+ # # WillPaginate::Collection is now available for use
42
20
  class Collection < Array
43
21
  attr_reader :current_page, :per_page, :total_entries, :total_pages
44
22
 
@@ -46,12 +24,9 @@ module WillPaginate
46
24
  # and the total number of entries. The last argument is optional because it
47
25
  # is best to do lazy counting; in other words, count *conditionally* after
48
26
  # populating the collection using the +replace+ method.
49
- def initialize(page, per_page, total = nil)
50
- @current_page = page.to_i
51
- raise InvalidPage.new(page, @current_page) if @current_page < 1
27
+ def initialize(page, per_page = WillPaginate.per_page, total = nil)
28
+ @current_page = WillPaginate::PageNumber(page)
52
29
  @per_page = per_page.to_i
53
- raise ArgumentError, "`per_page` setting cannot be less than 1 (#{@per_page} given)" if @per_page < 1
54
-
55
30
  self.total_entries = total if total
56
31
  end
57
32
 
@@ -82,7 +57,7 @@ module WillPaginate
82
57
  #
83
58
  # The Array#paginate API has since then changed, but this still serves as a
84
59
  # fine example of WillPaginate::Collection usage.
85
- def self.create(page, per_page, total = nil, &block)
60
+ def self.create(page, per_page, total = nil)
86
61
  pager = new(page, per_page, total)
87
62
  yield pager
88
63
  pager
@@ -98,9 +73,9 @@ module WillPaginate
98
73
  # Current offset of the paginated collection. If we're on the first page,
99
74
  # it is always 0. If we're on the 2nd page and there are 30 entries per page,
100
75
  # the offset is 30. This property is useful if you want to render ordinals
101
- # besides your records: simply start with offset + 1.
76
+ # side by side with records in the view: simply start with offset + 1.
102
77
  def offset
103
- (current_page - 1) * per_page
78
+ @current_page.to_offset(per_page).to_i
104
79
  end
105
80
 
106
81
  # current_page - 1 or nil if there is no previous page
@@ -112,7 +87,8 @@ module WillPaginate
112
87
  def next_page
113
88
  current_page < total_pages ? (current_page + 1) : nil
114
89
  end
115
-
90
+
91
+ # sets the <tt>total_entries</tt> property and calculates <tt>total_pages</tt>
116
92
  def total_entries=(number)
117
93
  @total_entries = number.to_i
118
94
  @total_pages = (@total_entries / per_page.to_f).ceil
@@ -1,5 +1,4 @@
1
1
  require 'set'
2
- require 'will_paginate/array'
3
2
 
4
3
  # copied from ActiveSupport so we don't depend on it
5
4
 
@@ -18,33 +17,6 @@ unless Hash.method_defined? :except
18
17
  end
19
18
  end
20
19
 
21
- unless Hash.method_defined? :slice
22
- Hash.class_eval do
23
- # Returns a new hash with only the given keys.
24
- def slice(*keys)
25
- allowed = Set.new(respond_to?(:convert_key) ? keys.map { |key| convert_key(key) } : keys)
26
- reject { |key,| !allowed.include?(key) }
27
- end
28
-
29
- # Replaces the hash with only the given keys.
30
- def slice!(*keys)
31
- replace(slice(*keys))
32
- end
33
- end
34
- end
35
-
36
- unless String.method_defined? :constantize
37
- String.class_eval do
38
- def constantize
39
- unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ self
40
- raise NameError, "#{self.inspect} is not a valid constant name!"
41
- end
42
-
43
- Object.module_eval("::#{$1}", __FILE__, __LINE__)
44
- end
45
- end
46
- end
47
-
48
20
  unless String.method_defined? :underscore
49
21
  String.class_eval do
50
22
  def underscore