will_paginate 2.3.15 → 2.3.16

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/CHANGELOG.rdoc CHANGED
@@ -1,3 +1,25 @@
1
+ = 2.3.16, released 2011-08-09
2
+
3
+ * added global <tt>WillPaginate.per_page = 30</tt> setting
4
+ * added i18n capabilities to previous/next labels and page_entries_info
5
+ * page_entries_info ":entry_name" parameter is now renamed to ":model"
6
+ * new option: page_entries_info(:html => false)
7
+ * fix page_entries_info output with rails_xss
8
+ * raise an InvalidPage exception if the calculated offset is bigger than BIGINT
9
+ * fixed bugs with Rails 1.2.6
10
+
11
+ = 2.3.15, released 2010-09-09
12
+
13
+ * ensure respond_to? works as expected considering methods that begin with 'paginate'
14
+ * fix counting in pagination when Integer has length method (Ruby Facets)
15
+ * stop using deprecated Object#returning
16
+ * fixed has_many :through in combination with `:uniq` for Postgres and SQLite3
17
+
18
+ = 2.3.14, released 2010-05-26
19
+ = 2.3.13, released 2010-05-26
20
+
21
+ * better rails_xss compatibility
22
+
1
23
  = 2.3.12, released 2009-12-01
2
24
 
3
25
  * make view helpers "HTML safe" for Rails 2.3.5 with rails_xss plugin
data/README.md ADDED
@@ -0,0 +1,53 @@
1
+ # will_paginate
2
+
3
+ will_paginate v2.3 is a pagination plugin for Rails and Active Record.
4
+
5
+ Installation:
6
+
7
+ ~~~ ruby
8
+ ## environment.rb
9
+ Rails::Initializer.run do |config|
10
+ config.gem 'will_paginate', :version => '~> 2.3.16'
11
+ end
12
+ ~~~
13
+
14
+ See [installation instructions][install] on the wiki for more info.
15
+
16
+
17
+ ## Basic will_paginate use
18
+
19
+ ~~~ ruby
20
+ ## perform a paginated query:
21
+ @posts = Post.paginate(:page => params[:page])
22
+
23
+ # or, use an explicit "per page" limit:
24
+ Post.paginate(:page => params[:page], :per_page => 30)
25
+
26
+ ## render page links in the view:
27
+ <%= will_paginate @posts %>
28
+ ~~~
29
+
30
+ And that's it! You're done. You just need to add some CSS styles to [make those pagination links prettier][css].
31
+
32
+ You can customize the default "per_page" value:
33
+
34
+ ~~~ ruby
35
+ # for the Post model
36
+ class Post
37
+ self.per_page = 10
38
+ end
39
+
40
+ # set per_page globally
41
+ WillPaginate.per_page = 10
42
+ ~~~
43
+
44
+ See [the wiki][wiki] for more documentation. [Ask on the group][group] if you have usage questions. [Report bugs][issues] on GitHub.
45
+
46
+ Happy paginating.
47
+
48
+
49
+ [wiki]: https://github.com/mislav/will_paginate/wiki
50
+ [install]: https://github.com/mislav/will_paginate/wiki/Installation "will_paginate installation"
51
+ [group]: http://groups.google.com/group/will_paginate "will_paginate discussion and support group"
52
+ [issues]: https://github.com/mislav/will_paginate/issues
53
+ [css]: http://mislav.uniqpath.com/will_paginate/
data/Rakefile CHANGED
@@ -1,53 +1,28 @@
1
- require 'rubygems'
2
- begin
3
- hanna_dir = '/Users/mislav/Projects/Hanna/lib'
4
- $:.unshift hanna_dir if File.exists? hanna_dir
5
- require 'hanna/rdoctask'
6
- rescue LoadError
7
- require 'rake'
8
- require 'rake/rdoctask'
9
- end
10
- load 'test/tasks.rake'
1
+ require 'rake/testtask'
11
2
 
12
3
  desc 'Default: run unit tests.'
13
4
  task :default => :test
14
5
 
15
- desc 'Generate RDoc documentation for the will_paginate plugin.'
16
- Rake::RDocTask.new(:rdoc) do |rdoc|
17
- rdoc.rdoc_files.include('README.rdoc', 'LICENSE', 'CHANGELOG.rdoc').
18
- include('lib/**/*.rb').
19
- exclude('lib/will_paginate/named_scope*').
20
- exclude('lib/will_paginate/array.rb').
21
- exclude('lib/will_paginate/version.rb')
22
-
23
- rdoc.main = "README.rdoc" # page to start on
24
- rdoc.title = "will_paginate documentation"
25
-
26
- rdoc.rdoc_dir = 'doc' # rdoc output folder
27
- rdoc.options << '--inline-source' << '--charset=UTF-8'
28
- rdoc.options << '--webcvs=http://github.com/mislav/will_paginate/tree/master/'
6
+ desc 'Test the will_paginate plugin.'
7
+ Rake::TestTask.new(:test) do |t|
8
+ if ENV['DB'] and ENV['DB'] != 'sqlite3'
9
+ t.pattern = %w[test/finder_test.rb]
10
+ else
11
+ t.pattern = 'test/**/*_test.rb'
12
+ end
13
+ t.libs << 'test'
29
14
  end
30
15
 
31
- desc %{Update ".manifest" with the latest list of project filenames. Respect\
32
- .gitignore by excluding everything that git ignores. Update `files` and\
33
- `test_files` arrays in "*.gemspec" file if it's present.}
34
- task :manifest do
35
- list = `git ls-files --full-name --exclude=*.gemspec --exclude=.*`.chomp.split("\n")
36
-
37
- if spec_file = Dir['*.gemspec'].first
38
- spec = File.read spec_file
39
- spec.gsub! /^(\s* s.(test_)?files \s* = \s* )( \[ [^\]]* \] | %w\( [^)]* \) )/mx do
40
- assignment = $1
41
- bunch = $2 ? list.grep(/^test\//) : list
42
- '%s%%w(%s)' % [assignment, bunch.join(' ')]
43
- end
44
-
45
- File.open(spec_file, 'w') { |f| f << spec }
16
+ namespace :test do ||
17
+ desc 'Test only Rails integration'
18
+ Rake::TestTask.new(:rails) do |t|
19
+ t.pattern = %w[test/finder_test.rb test/view_test.rb]
20
+ t.libs << 'test'
46
21
  end
47
- File.open('.manifest', 'w') { |f| f << list.join("\n") }
48
- end
49
22
 
50
- task :examples do
51
- %x(haml examples/index.haml examples/index.html)
52
- %x(sass examples/pagination.sass examples/pagination.css)
23
+ desc 'Test only ActiveRecord integration'
24
+ Rake::TestTask.new(:db) do |t|
25
+ t.pattern = %w[test/finder_test.rb]
26
+ t.libs << 'test'
27
+ end
53
28
  end
data/lib/will_paginate.rb CHANGED
@@ -36,12 +36,13 @@ module WillPaginate
36
36
 
37
37
  # support pagination on associations
38
38
  a = ActiveRecord::Associations
39
- [ a::AssociationCollection ].tap { |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|
39
+ klasses = [ a::AssociationCollection ]
40
+ # detect http://dev.rubyonrails.org/changeset/9230
41
+ unless a::HasManyThroughAssociation.superclass == a::HasManyAssociation
42
+ klasses << a::HasManyThroughAssociation
43
+ end
44
+
45
+ klasses.each do |klass|
45
46
  klass.send :include, Finder::ClassMethods
46
47
  klass.class_eval { alias_method_chain :method_missing, :paginate }
47
48
  end
@@ -88,3 +89,9 @@ if defined? Rails
88
89
  WillPaginate.enable_activerecord if defined? ActiveRecord
89
90
  WillPaginate.enable_actionpack if defined? ActionController
90
91
  end
92
+
93
+ # load default translations only for newer versions of I18n
94
+ if defined? I18n and not defined? I18n::Backend::Simple::MATCH
95
+ require 'will_paginate/i18n'
96
+ I18n.load_path.unshift(*WillPaginate::I18n.load_path)
97
+ end
@@ -1,16 +1,16 @@
1
1
  require 'will_paginate/collection'
2
+ require 'will_paginate/per_page'
2
3
 
3
4
  # http://www.desimcadam.com/archives/8
4
5
  Array.class_eval do
5
6
  def paginate(options = {})
6
7
  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|
8
+ page = options[:page] || 1
9
+ per_page = options[:per_page] || WillPaginate.per_page
10
+ total = options[:total_entries] || self.length
11
+
12
+ WillPaginate::Collection.create(page, per_page, total) do |pager|
13
13
  pager.replace self[pager.offset, pager.per_page].to_a
14
- }
14
+ end
15
15
  end
16
16
  end
@@ -1,3 +1,5 @@
1
+ require 'will_paginate/per_page'
2
+
1
3
  module WillPaginate
2
4
  # = Invalid page number error
3
5
  # This is an ArgumentError raised in case a page was requested that is either
@@ -17,8 +19,24 @@ module WillPaginate
17
19
  # requested. Use <tt>WillPaginate::Collection#out_of_bounds?</tt> method to
18
20
  # check for those cases and manually deal with them as you see fit.
19
21
  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
+ # a value bigger than this would result in invalid SQL queries
23
+ BIGINT = 9223372036854775807
24
+
25
+ def self.validate(page_value, per_page_value)
26
+ page = page_value.to_i
27
+ raise self.new(page_value, page) if page < 1
28
+ per_page = per_page_value.to_i
29
+ offset = (page - 1) * per_page
30
+ raise self, "invalid offset: #{offset.inspect}" if offset < 0 or offset > BIGINT
31
+ [page, per_page]
32
+ end
33
+
34
+ def initialize(value, page_num = nil)
35
+ if page_num
36
+ super "#{value.inspect} given as value, which translates to '#{page_num}' as page number"
37
+ else
38
+ super value
39
+ end
22
40
  end
23
41
  end
24
42
 
@@ -44,12 +62,8 @@ module WillPaginate
44
62
  # and the total number of entries. The last argument is optional because it
45
63
  # is best to do lazy counting; in other words, count *conditionally* after
46
64
  # 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
-
65
+ def initialize(page, per_page = WillPaginate.per_page, total = nil)
66
+ @current_page, @per_page = InvalidPage.validate(page, per_page)
53
67
  self.total_entries = total if total
54
68
  end
55
69
 
@@ -1,4 +1,5 @@
1
1
  require 'will_paginate/core_ext'
2
+ require 'will_paginate/per_page'
2
3
 
3
4
  module WillPaginate
4
5
  # A mixin for ActiveRecord::Base. Provides +per_page+ class method
@@ -8,11 +9,10 @@ module WillPaginate
8
9
  #
9
10
  module Finder
10
11
  def self.included(base)
12
+ base.extend PerPage
11
13
  base.extend ClassMethods
12
14
  class << base
13
15
  alias_method_chain :method_missing, :paginate
14
- # alias_method_chain :find_every, :paginate
15
- define_method(:per_page) { 30 } unless respond_to?(:per_page)
16
16
  end
17
17
  end
18
18
 
@@ -0,0 +1,29 @@
1
+ module WillPaginate
2
+ module I18n
3
+ def self.locale_dir
4
+ File.expand_path('../locale', __FILE__)
5
+ end
6
+
7
+ def self.load_path
8
+ Dir["#{locale_dir}/*.{rb,yml}"]
9
+ end
10
+
11
+ def will_paginate_translate(keys, options = {})
12
+ if defined? ::I18n
13
+ defaults = Array(keys).dup
14
+ if block_given?
15
+ if defined? ::I18n::Backend::Simple::MATCH
16
+ # procs in defaults array were not supported back then
17
+ defaults << yield(defaults.first, options)
18
+ else
19
+ defaults << Proc.new
20
+ end
21
+ end
22
+ ::I18n.translate(defaults.shift, options.merge(:default => defaults, :scope => :will_paginate))
23
+ else
24
+ key = Array === keys ? keys.first : keys
25
+ yield key, options
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,33 @@
1
+ en:
2
+ will_paginate:
3
+ previous_label: "&laquo; Previous"
4
+ next_label: "Next &raquo;"
5
+ page_gap: "&hellip;"
6
+
7
+ page_entries_info:
8
+ single_page:
9
+ zero: "No %{model} found"
10
+ one: "Displaying 1 %{model}"
11
+ other: "Displaying all %{count} %{model}"
12
+ single_page_html:
13
+ zero: "No %{model} found"
14
+ one: "Displaying <b>1</b> %{model}"
15
+ other: "Displaying <b>all&nbsp;%{count}</b> %{model}"
16
+
17
+ multi_page: "Displaying %{model} %{from} - %{to} of %{count} in total"
18
+ multi_page_html: "Displaying %{model} <b>%{from}&nbsp;-&nbsp;%{to}</b> of <b>%{count}</b> in total"
19
+
20
+ # models:
21
+ # entry:
22
+ # zero: entries
23
+ # one: entry
24
+ # few: entries
25
+ # other: entries
26
+
27
+ # line_item:
28
+ # page_entries_info:
29
+ # single_page:
30
+ # zero: "Your shopping cart is empty"
31
+ # one: "Displaying one item in your cart"
32
+ # other: "Displaying all %{count} items"
33
+ # multi_page: "Displaying items %{from} - %{to} of %{count} in total"
@@ -0,0 +1,27 @@
1
+ module WillPaginate
2
+ module PerPage
3
+ def per_page
4
+ defined?(@per_page) ? @per_page : WillPaginate.per_page
5
+ end
6
+
7
+ def per_page=(limit)
8
+ @per_page = limit.to_i
9
+ end
10
+
11
+ def self.extended(base)
12
+ base.extend Inheritance if base.is_a? Class
13
+ end
14
+
15
+ module Inheritance
16
+ def inherited(subclass)
17
+ super
18
+ subclass.per_page = self.per_page
19
+ end
20
+ end
21
+ end
22
+
23
+ extend PerPage
24
+
25
+ # default number of items per page
26
+ self.per_page = 30
27
+ end
@@ -2,7 +2,7 @@ module WillPaginate
2
2
  module VERSION
3
3
  MAJOR = 2
4
4
  MINOR = 3
5
- TINY = 15
5
+ TINY = 16
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -1,4 +1,5 @@
1
1
  require 'will_paginate/core_ext'
2
+ require 'will_paginate/i18n'
2
3
 
3
4
  module WillPaginate
4
5
  # = Will Paginate view helpers
@@ -20,11 +21,13 @@ module WillPaginate
20
21
  # older versions of Rails) you can easily translate link texts to previous
21
22
  # and next pages, as well as override some other defaults to your liking.
22
23
  module ViewHelpers
24
+ include WillPaginate::I18n
25
+
23
26
  # default options that can be overridden on the global level
24
27
  @@pagination_options = {
25
28
  :class => 'pagination',
26
- :previous_label => '&laquo; Previous',
27
- :next_label => 'Next &raquo;',
29
+ :previous_label => nil,
30
+ :next_label => nil,
28
31
  :inner_window => 4, # links around the current page
29
32
  :outer_window => 1, # links around beginning and end
30
33
  :separator => ' ', # single space is friendly to spiders and non-graphic browsers
@@ -102,7 +105,10 @@ module WillPaginate
102
105
  WillPaginate::Deprecation::warn(":prev_label view parameter is now :previous_label; the old name has been deprecated", caller)
103
106
  options[:previous_label] = options.delete(:prev_label)
104
107
  end
105
-
108
+
109
+ options[:previous_label] ||= will_paginate_translate(:previous_label) { '&laquo; Previous' }
110
+ options[:next_label] ||= will_paginate_translate(:next_label) { 'Next &raquo;' }
111
+
106
112
  # get the renderer instance
107
113
  renderer = case options[:renderer]
108
114
  when String
@@ -142,7 +148,7 @@ module WillPaginate
142
148
  def paginated_section(*args, &block)
143
149
  pagination = will_paginate(*args).to_s
144
150
 
145
- unless ActionView::Base.respond_to? :erb_variable
151
+ if respond_to? :output_buffer
146
152
  concat pagination
147
153
  yield
148
154
  concat pagination
@@ -160,31 +166,64 @@ module WillPaginate
160
166
  #
161
167
  # By default, the message will use the humanized class name of objects
162
168
  # in collection: for instance, "project types" for ProjectType models.
163
- # Override this with the <tt>:entry_name</tt> parameter:
169
+ # Override this with the <tt>:model</tt> parameter:
164
170
  #
165
- # <%= page_entries_info @posts, :entry_name => 'item' %>
171
+ # <%= page_entries_info @posts, :model => 'item' %>
166
172
  # #-> Displaying items 6 - 10 of 26 in total
167
173
  def page_entries_info(collection, options = {})
168
- entry_name = options[:entry_name] ||
169
- (collection.empty?? 'entry' : collection.first.class.name.underscore.sub('_', ' '))
170
-
171
- if collection.total_pages < 2
172
- case collection.size
173
- when 0; "No #{entry_name.pluralize} found"
174
- when 1; "Displaying <b>1</b> #{entry_name}"
175
- else; "Displaying <b>all #{collection.size}</b> #{entry_name.pluralize}"
174
+ if options.key? :entry_name
175
+ WillPaginate::Deprecation::warn(":entry_name parameter is now called :model", caller)
176
+ end
177
+ model = options[:model] || options[:entry_name]
178
+ model = collection.first.class unless model or collection.empty?
179
+ model ||= 'entry'
180
+
181
+ if html = options.fetch(:html, true)
182
+ b, eb = '<b>', '</b>'
183
+ sp = '&nbsp;'
184
+ html_key = '_html'
185
+ else
186
+ b = eb = html_key = ''
187
+ sp = ' '
188
+ end
189
+
190
+ model_key = model.to_s.underscore
191
+ model_count = collection.total_pages > 1 ? 5 : collection.size
192
+ model_name = will_paginate_translate "models.#{model_key}", :count => model_count do |_, opts|
193
+ name = model_key.to_s.tr('_/', ' ')
194
+ raise "can't pluralize model name: #{model.inspect}" unless name.respond_to? :pluralize
195
+ opts[:count] == 1 ? name : name.pluralize
196
+ end
197
+
198
+ output = if collection.total_pages < 2
199
+ i18n_key = :"page_entries_info.single_page#{html_key}"
200
+ keys = [:"#{model_key}.#{i18n_key}", i18n_key]
201
+
202
+ will_paginate_translate keys, :count => collection.size, :model => model_name do |_, opts|
203
+ case opts[:count]
204
+ when 0; "No #{opts[:model]} found"
205
+ when 1; "Displaying #{b}1#{eb} #{opts[:model]}"
206
+ else "Displaying #{b}all#{sp}#{opts[:count]}#{eb} #{opts[:model]}"
207
+ end
176
208
  end
177
209
  else
178
- %{Displaying #{entry_name.pluralize} <b>%d&nbsp;-&nbsp;%d</b> of <b>%d</b> in total} % [
179
- collection.offset + 1,
180
- collection.offset + collection.length,
181
- collection.total_entries
182
- ]
210
+ i18n_key = :"page_entries_info.multi_page#{html_key}"
211
+ keys = [:"#{model_key}.#{i18n_key}", i18n_key]
212
+ params = {
213
+ :model => model_name, :count => collection.total_entries,
214
+ :from => collection.offset + 1, :to => collection.offset + collection.length
215
+ }
216
+ will_paginate_translate keys, params do |_, opts|
217
+ %{Displaying %s #{b}%d#{sp}-#{sp}%d#{eb} of #{b}%d#{eb} in total} %
218
+ [ opts[:model], opts[:from], opts[:to], opts[:count] ]
219
+ end
220
+ end
221
+
222
+ if html and output.respond_to?(:html_safe)
223
+ output.html_safe
224
+ else
225
+ output
183
226
  end
184
- end
185
-
186
- if respond_to? :safe_helper
187
- safe_helper :will_paginate, :paginated_section, :page_entries_info
188
227
  end
189
228
 
190
229
  def self.total_pages_for_collection(collection) #:nodoc:
@@ -205,15 +244,6 @@ module WillPaginate
205
244
  # This class does the heavy lifting of actually building the pagination
206
245
  # links. It is used by the <tt>will_paginate</tt> helper internally.
207
246
  class LinkRenderer
208
-
209
- # The gap in page links is represented by:
210
- #
211
- # <span class="gap">&hellip;</span>
212
- attr_accessor :gap_marker
213
-
214
- def initialize
215
- @gap_marker = '<span class="gap">&hellip;</span>'
216
- end
217
247
 
218
248
  # * +collection+ is a WillPaginate::Collection instance or any other object
219
249
  # that conforms to that API
@@ -253,6 +283,18 @@ module WillPaginate
253
283
  end
254
284
  @html_attributes
255
285
  end
286
+
287
+ attr_writer :gap_marker
288
+
289
+ # The gap in page links is represented by:
290
+ #
291
+ # <span class="gap">&hellip;</span>
292
+ def gap_marker
293
+ @gap_marker ||= begin
294
+ gap_text = @template.will_paginate_translate(:page_gap) { '&hellip;' }
295
+ %(<span class="gap">#{gap_text}</span>)
296
+ end
297
+ end
256
298
 
257
299
  protected
258
300
 
@@ -395,7 +437,8 @@ module WillPaginate
395
437
  if defined? Rack::Utils
396
438
  # For Rails > 2.3
397
439
  Rack::Utils.parse_nested_query(params)
398
- elsif defined?(ActionController::AbstractRequest)
440
+ elsif defined?(ActionController::AbstractRequest) and
441
+ ActionController::AbstractRequest.respond_to? :parse_query_parameters
399
442
  ActionController::AbstractRequest.parse_query_parameters(params)
400
443
  elsif defined?(ActionController::UrlEncodedPairParser)
401
444
  # For Rails > 2.2