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

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
@@ -0,0 +1,91 @@
1
+ require 'dm-core'
2
+ require 'dm-aggregates'
3
+ require 'will_paginate/per_page'
4
+ require 'will_paginate/page_number'
5
+ require 'will_paginate/collection'
6
+
7
+ module WillPaginate
8
+ module DataMapper
9
+ module Pagination
10
+ def page(num)
11
+ pagenum = ::WillPaginate::PageNumber(num.nil? ? 1 : num)
12
+ per_page = query.limit || self.per_page
13
+ options = {:offset => pagenum.to_offset(per_page).to_i}
14
+ options[:limit] = per_page unless query.limit
15
+ col = new_collection(query.merge(options))
16
+ col.current_page = pagenum
17
+ col
18
+ end
19
+
20
+ def paginate(options)
21
+ options = options.dup
22
+ pagenum = options.fetch(:page) { raise ArgumentError, ":page parameter required" }
23
+ per_page = options.delete(:per_page) || self.per_page
24
+
25
+ options.delete(:page)
26
+ options[:limit] = per_page.to_i
27
+
28
+ all(options).page(pagenum)
29
+ end
30
+ end
31
+
32
+ module CollectionMethods
33
+ attr_accessor :current_page
34
+
35
+ def paginated?
36
+ !current_page.nil?
37
+ end
38
+
39
+ def per_page
40
+ query.limit || model.per_page
41
+ end
42
+
43
+ def offset
44
+ query.offset
45
+ end
46
+
47
+ def total_entries
48
+ @total_entries ||= begin
49
+ if loaded? and @array.size < per_page and (current_page == 1 or @array.size > 0)
50
+ offset + @array.size
51
+ else
52
+ clean_query = query.merge(:order => [])
53
+ # seems like the only way
54
+ clean_query.instance_variable_set('@limit', nil)
55
+ clean_query.instance_variable_set('@offset', 0)
56
+ new_collection(clean_query).count
57
+ end
58
+ end
59
+ end
60
+
61
+ def total_pages
62
+ (total_entries / per_page.to_f).ceil
63
+ end
64
+
65
+ def to_a
66
+ ::WillPaginate::Collection.create(current_page, per_page) do |col|
67
+ col.replace super
68
+ col.total_entries ||= total_entries
69
+ end
70
+ end
71
+
72
+ private
73
+
74
+ def new_collection(query, resources = nil)
75
+ col = super
76
+ col.current_page = self.current_page
77
+ col
78
+ end
79
+
80
+ def initialize_copy(original)
81
+ super
82
+ @total_entries = nil
83
+ end
84
+ end
85
+
86
+ ::DataMapper::Model.append_extensions PerPage
87
+ ::DataMapper::Model.append_extensions Pagination
88
+ ::DataMapper::Collection.send(:include, Pagination)
89
+ ::DataMapper::Collection.send(:include, CollectionMethods)
90
+ end
91
+ end
@@ -0,0 +1,22 @@
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
+ defaults << Proc.new if block_given?
15
+ ::I18n.translate(defaults.shift, options.merge(:default => defaults, :scope => :will_paginate))
16
+ else
17
+ key = Array === keys ? keys.first : keys
18
+ yield key, options
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,33 @@
1
+ en:
2
+ will_paginate:
3
+ previous_label: "&#8592; Previous"
4
+ next_label: "Next &#8594;"
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,57 @@
1
+ require 'delegate'
2
+ require 'forwardable'
3
+
4
+ module WillPaginate
5
+ # a module that page number exceptions are tagged with
6
+ module InvalidPage; end
7
+
8
+ # integer representing a page number
9
+ class PageNumber < DelegateClass(Integer)
10
+ # a value larger than this is not supported in SQL queries
11
+ BIGINT = 9223372036854775807
12
+
13
+ extend Forwardable
14
+
15
+ def initialize(value, name)
16
+ value = Integer(value)
17
+ if 'offset' == name ? (value < 0 or value > BIGINT) : value < 1
18
+ raise RangeError, "invalid #{name}: #{value.inspect}"
19
+ end
20
+ @name = name
21
+ super(value)
22
+ rescue ArgumentError, TypeError, RangeError => error
23
+ error.extend InvalidPage
24
+ raise error
25
+ end
26
+
27
+ alias_method :to_i, :__getobj__
28
+
29
+ def inspect
30
+ "#{@name} #{to_i}"
31
+ end
32
+
33
+ def to_offset(per_page)
34
+ PageNumber.new((to_i - 1) * per_page.to_i, 'offset')
35
+ end
36
+
37
+ def kind_of?(klass)
38
+ super || to_i.kind_of?(klass)
39
+ end
40
+ alias is_a? kind_of?
41
+ end
42
+
43
+ # Ultrahax: makes `Fixnum === current_page` checks pass
44
+ Numeric.extend Module.new {
45
+ def ===(obj)
46
+ obj.instance_of? PageNumber or super
47
+ end
48
+ }
49
+
50
+ # An idemptotent coercion method
51
+ def self.PageNumber(value, name = 'page')
52
+ case value
53
+ when PageNumber then value
54
+ else PageNumber.new(value, name)
55
+ end
56
+ end
57
+ end
@@ -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
@@ -1,22 +1,31 @@
1
1
  require 'will_paginate'
2
2
  require 'will_paginate/collection'
3
+ require 'will_paginate/i18n'
3
4
 
4
5
  module WillPaginate
5
6
  class Railtie < Rails::Railtie
6
7
  initializer "will_paginate" do |app|
7
8
  ActiveSupport.on_load :active_record do
8
- require 'will_paginate/finders/active_record'
9
- WillPaginate::Finders::ActiveRecord.enable!
9
+ require 'will_paginate/active_record'
10
10
  end
11
11
 
12
12
  ActiveSupport.on_load :action_controller do
13
- ActionDispatch::ShowExceptions.rescue_responses['WillPaginate::InvalidPage'] = :not_found
13
+ WillPaginate::Railtie.setup_actioncontroller
14
14
  end
15
15
 
16
16
  ActiveSupport.on_load :action_view do
17
17
  require 'will_paginate/view_helpers/action_view'
18
- include WillPaginate::ViewHelpers::ActionView
19
18
  end
19
+
20
+ self.class.add_locale_path config
21
+ end
22
+
23
+ def self.setup_actioncontroller
24
+ ActionDispatch::ShowExceptions.rescue_responses['WillPaginate::InvalidPage'] = :not_found
25
+ end
26
+
27
+ def self.add_locale_path(config)
28
+ config.i18n.railties_load_path.unshift(*WillPaginate::I18n.load_path)
20
29
  end
21
30
  end
22
31
  end
@@ -0,0 +1,36 @@
1
+ require 'sequel'
2
+ require 'sequel/extensions/pagination'
3
+
4
+ module WillPaginate
5
+ # Sequel already supports pagination; we only need to make the
6
+ # resulting dataset look a bit more like WillPaginate::Collection
7
+ module SequelMethods
8
+ def total_pages
9
+ page_count
10
+ end
11
+
12
+ def per_page
13
+ page_size
14
+ end
15
+
16
+ def size
17
+ current_page_record_count
18
+ end
19
+ alias length size
20
+
21
+ def total_entries
22
+ pagination_record_count
23
+ end
24
+
25
+ def out_of_bounds?
26
+ current_page > total_pages
27
+ end
28
+
29
+ # Current offset of the paginated collection
30
+ def offset
31
+ (current_page - 1) * per_page
32
+ end
33
+ end
34
+
35
+ Sequel::Dataset::Pagination.send(:include, SequelMethods)
36
+ end
@@ -2,7 +2,7 @@ module WillPaginate #:nodoc:
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 3
4
4
  MINOR = 0
5
- TINY = 'pre4'
5
+ TINY = 0
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -1,4 +1,6 @@
1
- require 'will_paginate/deprecation'
1
+ # encoding: utf-8
2
+ require 'will_paginate/core_ext'
3
+ require 'will_paginate/i18n'
2
4
 
3
5
  module WillPaginate
4
6
  # = Will Paginate view helpers
@@ -7,36 +9,148 @@ module WillPaginate
7
9
  # for the given collection. The helper itself is lightweight and serves only
8
10
  # as a wrapper around LinkRenderer instantiation; the renderer then does
9
11
  # all the hard work of generating the HTML.
10
- #
11
- # Read more in WillPaginate::ViewHelpers::Base
12
12
  module ViewHelpers
13
- # ==== Global options for helpers
14
- #
15
- # Options for pagination helpers are optional and get their default values
16
- # from the WillPaginate::ViewHelpers.pagination_options hash. You can write
17
- # to this hash to override default options on the global level:
18
- #
19
- # WillPaginate::ViewHelpers.pagination_options[:previous_label] = 'Previous page'
20
- #
21
- # By putting this into your environment.rb you can easily translate link
22
- # texts to previous and next pages, as well as override some other defaults
23
- # to your liking.
24
- def self.pagination_options() @pagination_options; end
25
- # Overrides the default +pagination_options+
26
- def self.pagination_options=(value) @pagination_options = value; end
27
-
13
+ class << self
14
+ # Write to this hash to override default options on the global level:
15
+ #
16
+ # WillPaginate::ViewHelpers.pagination_options[:page_links] = false
17
+ #
18
+ attr_accessor :pagination_options
19
+ end
20
+
21
+ # default view options
28
22
  self.pagination_options = {
29
23
  :class => 'pagination',
30
- :previous_label => '&#8592; Previous',
31
- :next_label => 'Next &#8594;',
24
+ :previous_label => nil,
25
+ :next_label => nil,
32
26
  :inner_window => 4, # links around the current page
33
27
  :outer_window => 1, # links around beginning and end
34
- :separator => ' ', # single space is friendly to spiders and non-graphic browsers
28
+ :link_separator => ' ', # single space is friendly to spiders and non-graphic browsers
35
29
  :param_name => :page,
36
30
  :params => nil,
37
- :renderer => 'WillPaginate::ViewHelpers::LinkRenderer',
31
+ :renderer => nil,
38
32
  :page_links => true,
39
33
  :container => true
40
34
  }
35
+
36
+ include WillPaginate::I18n
37
+
38
+ # Returns HTML representing page links for a WillPaginate::Collection-like object.
39
+ # In case there is no more than one page in total, nil is returned.
40
+ #
41
+ # ==== Options
42
+ # * <tt>:class</tt> -- CSS class name for the generated DIV (default: "pagination")
43
+ # * <tt>:previous_label</tt> -- default: "« Previous"
44
+ # * <tt>:next_label</tt> -- default: "Next »"
45
+ # * <tt>:page_links</tt> -- when false, only previous/next links are rendered (default: true)
46
+ # * <tt>:inner_window</tt> -- how many links are shown around the current page (default: 4)
47
+ # * <tt>:outer_window</tt> -- how many links are around the first and the last page (default: 1)
48
+ # * <tt>:link_separator</tt> -- string separator for page HTML elements (default: single space)
49
+ # * <tt>:param_name</tt> -- parameter name for page number in URLs (default: <tt>:page</tt>)
50
+ # * <tt>:params</tt> -- additional parameters when generating pagination links
51
+ # (eg. <tt>:controller => "foo", :action => nil</tt>)
52
+ # * <tt>:renderer</tt> -- class name, class or instance of a link renderer (default:
53
+ # <tt>WillPaginate::LinkRenderer</tt>)
54
+ # * <tt>:page_links</tt> -- when false, only previous/next links are rendered (default: true)
55
+ # * <tt>:container</tt> -- toggles rendering of the DIV container for pagination links, set to
56
+ # false only when you are rendering your own pagination markup (default: true)
57
+ #
58
+ # All options not recognized by will_paginate will become HTML attributes on the container
59
+ # element for pagination links (the DIV). For example:
60
+ #
61
+ # <%= will_paginate @posts, :style => 'color:blue' %>
62
+ #
63
+ # will result in:
64
+ #
65
+ # <div class="pagination" style="color:blue"> ... </div>
66
+ #
67
+ def will_paginate(collection, options = {})
68
+ # early exit if there is nothing to render
69
+ return nil unless collection.total_pages > 1
70
+
71
+ options = WillPaginate::ViewHelpers.pagination_options.merge(options)
72
+
73
+ options[:previous_label] ||= will_paginate_translate(:previous_label) { '&#8592; Previous' }
74
+ options[:next_label] ||= will_paginate_translate(:next_label) { 'Next &#8594;' }
75
+
76
+ # get the renderer instance
77
+ renderer = case options[:renderer]
78
+ when nil
79
+ raise ArgumentError, ":renderer not specified"
80
+ when String
81
+ klass = if options[:renderer].respond_to? :constantize then options[:renderer].constantize
82
+ else Object.const_get(options[:renderer]) # poor man's constantize
83
+ end
84
+ klass.new
85
+ when Class then options[:renderer].new
86
+ else options[:renderer]
87
+ end
88
+ # render HTML for pagination
89
+ renderer.prepare collection, options, self
90
+ renderer.to_html
91
+ end
92
+
93
+ # Renders a message containing number of displayed vs. total entries.
94
+ #
95
+ # <%= page_entries_info @posts %>
96
+ # #-> Displaying posts 6 - 12 of 26 in total
97
+ #
98
+ # The default output contains HTML. Use ":html => false" for plain text.
99
+ def page_entries_info(collection, options = {})
100
+ model = options[:model]
101
+ model = collection.first.class unless model or collection.empty?
102
+ model ||= 'entry'
103
+ model_key = if model.respond_to? :model_name
104
+ model.model_name.i18n_key # ActiveModel::Naming
105
+ else
106
+ model.to_s.underscore
107
+ end
108
+
109
+ if options.fetch(:html, true)
110
+ b, eb = '<b>', '</b>'
111
+ sp = '&nbsp;'
112
+ html_key = '_html'
113
+ else
114
+ b = eb = html_key = ''
115
+ sp = ' '
116
+ end
117
+
118
+ model_count = collection.total_pages > 1 ? 5 : collection.size
119
+ defaults = ["models.#{model_key}"]
120
+ defaults << Proc.new { |_, opts|
121
+ if model.respond_to? :model_name
122
+ model.model_name.human(:count => opts[:count])
123
+ else
124
+ name = model_key.to_s.tr('_', ' ')
125
+ raise "can't pluralize model name: #{model.inspect}" unless name.respond_to? :pluralize
126
+ opts[:count] == 1 ? name : name.pluralize
127
+ end
128
+ }
129
+ model_name = will_paginate_translate defaults, :count => model_count
130
+
131
+ if collection.total_pages < 2
132
+ i18n_key = :"page_entries_info.single_page#{html_key}"
133
+ keys = [:"#{model_key}.#{i18n_key}", i18n_key]
134
+
135
+ will_paginate_translate keys, :count => collection.size, :model => model_name do |_, opts|
136
+ case opts[:count]
137
+ when 0; "No #{opts[:model]} found"
138
+ when 1; "Displaying #{b}1#{eb} #{opts[:model]}"
139
+ else "Displaying #{b}all#{sp}#{opts[:count]}#{eb} #{opts[:model]}"
140
+ end
141
+ end
142
+ else
143
+ i18n_key = :"page_entries_info.multi_page#{html_key}"
144
+ keys = [:"#{model_key}.#{i18n_key}", i18n_key]
145
+ params = {
146
+ :model => model_name, :count => collection.total_entries,
147
+ :from => collection.offset + 1, :to => collection.offset + collection.length
148
+ }
149
+ will_paginate_translate keys, params do |_, opts|
150
+ %{Displaying %s #{b}%d#{sp}-#{sp}%d#{eb} of #{b}%d#{eb} in total} %
151
+ [ opts[:model], opts[:from], opts[:to], opts[:count] ]
152
+ end
153
+ end
154
+ end
41
155
  end
42
156
  end