will_paginate_seo 3.0.4

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.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +18 -0
  3. data/README.md +61 -0
  4. data/lib/will_paginate.rb +25 -0
  5. data/lib/will_paginate/active_record.rb +261 -0
  6. data/lib/will_paginate/array.rb +33 -0
  7. data/lib/will_paginate/collection.rb +136 -0
  8. data/lib/will_paginate/core_ext.rb +30 -0
  9. data/lib/will_paginate/data_mapper.rb +100 -0
  10. data/lib/will_paginate/deprecation.rb +55 -0
  11. data/lib/will_paginate/i18n.rb +22 -0
  12. data/lib/will_paginate/locale/en.yml +33 -0
  13. data/lib/will_paginate/mongoid.rb +46 -0
  14. data/lib/will_paginate/page_number.rb +57 -0
  15. data/lib/will_paginate/per_page.rb +27 -0
  16. data/lib/will_paginate/railtie.rb +68 -0
  17. data/lib/will_paginate/sequel.rb +39 -0
  18. data/lib/will_paginate/version.rb +9 -0
  19. data/lib/will_paginate/view_helpers.rb +162 -0
  20. data/lib/will_paginate/view_helpers/action_view.rb +152 -0
  21. data/lib/will_paginate/view_helpers/link_renderer.rb +131 -0
  22. data/lib/will_paginate/view_helpers/link_renderer_base.rb +77 -0
  23. data/lib/will_paginate/view_helpers/merb.rb +26 -0
  24. data/lib/will_paginate/view_helpers/sinatra.rb +41 -0
  25. data/spec/collection_spec.rb +139 -0
  26. data/spec/console +12 -0
  27. data/spec/console_fixtures.rb +28 -0
  28. data/spec/database.yml +22 -0
  29. data/spec/fake_rubygems.rb +18 -0
  30. data/spec/finders/active_record_spec.rb +517 -0
  31. data/spec/finders/activerecord_test_connector.rb +119 -0
  32. data/spec/finders/data_mapper_spec.rb +116 -0
  33. data/spec/finders/data_mapper_test_connector.rb +54 -0
  34. data/spec/finders/mongoid_spec.rb +140 -0
  35. data/spec/finders/sequel_spec.rb +67 -0
  36. data/spec/finders/sequel_test_connector.rb +15 -0
  37. data/spec/fixtures/admin.rb +3 -0
  38. data/spec/fixtures/developer.rb +16 -0
  39. data/spec/fixtures/developers_projects.yml +13 -0
  40. data/spec/fixtures/project.rb +13 -0
  41. data/spec/fixtures/projects.yml +6 -0
  42. data/spec/fixtures/replies.yml +29 -0
  43. data/spec/fixtures/reply.rb +8 -0
  44. data/spec/fixtures/schema.rb +38 -0
  45. data/spec/fixtures/topic.rb +8 -0
  46. data/spec/fixtures/topics.yml +30 -0
  47. data/spec/fixtures/user.rb +2 -0
  48. data/spec/fixtures/users.yml +35 -0
  49. data/spec/matchers/deprecation_matcher.rb +27 -0
  50. data/spec/matchers/phrase_matcher.rb +19 -0
  51. data/spec/matchers/query_count_matcher.rb +36 -0
  52. data/spec/page_number_spec.rb +65 -0
  53. data/spec/per_page_spec.rb +41 -0
  54. data/spec/spec_helper.rb +46 -0
  55. data/spec/view_helpers/action_view_spec.rb +441 -0
  56. data/spec/view_helpers/base_spec.rb +142 -0
  57. data/spec/view_helpers/link_renderer_base_spec.rb +87 -0
  58. data/spec/view_helpers/view_example_group.rb +125 -0
  59. metadata +106 -0
@@ -0,0 +1,39 @@
1
+ require 'sequel'
2
+ require 'sequel/extensions/pagination'
3
+ require 'will_paginate/collection'
4
+
5
+ module WillPaginate
6
+ # Sequel already supports pagination; we only need to make the
7
+ # resulting dataset look a bit more like WillPaginate::Collection
8
+ module SequelMethods
9
+ include WillPaginate::CollectionMethods
10
+
11
+ def total_pages
12
+ page_count
13
+ end
14
+
15
+ def per_page
16
+ page_size
17
+ end
18
+
19
+ def size
20
+ current_page_record_count
21
+ end
22
+ alias length size
23
+
24
+ def total_entries
25
+ pagination_record_count
26
+ end
27
+
28
+ def out_of_bounds?
29
+ current_page > total_pages
30
+ end
31
+
32
+ # Current offset of the paginated collection
33
+ def offset
34
+ (current_page - 1) * per_page
35
+ end
36
+ end
37
+
38
+ Sequel::Dataset::Pagination.send(:include, SequelMethods)
39
+ end
@@ -0,0 +1,9 @@
1
+ module WillPaginate #:nodoc:
2
+ module VERSION #:nodoc:
3
+ MAJOR = 3
4
+ MINOR = 0
5
+ TINY = 4
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
@@ -0,0 +1,162 @@
1
+ # encoding: utf-8
2
+ require 'will_paginate/core_ext'
3
+ require 'will_paginate/i18n'
4
+ require 'will_paginate/deprecation'
5
+
6
+ module WillPaginate
7
+ # = Will Paginate view helpers
8
+ #
9
+ # The main view helper is +will_paginate+. It renders the pagination links
10
+ # for the given collection. The helper itself is lightweight and serves only
11
+ # as a wrapper around LinkRenderer instantiation; the renderer then does
12
+ # all the hard work of generating the HTML.
13
+ module ViewHelpers
14
+ class << self
15
+ # Write to this hash to override default options on the global level:
16
+ #
17
+ # WillPaginate::ViewHelpers.pagination_options[:page_links] = false
18
+ #
19
+ attr_accessor :pagination_options
20
+ end
21
+
22
+ # default view options
23
+ self.pagination_options = Deprecation::Hash.new \
24
+ :class => 'pagination',
25
+ :previous_label => nil,
26
+ :next_label => nil,
27
+ :inner_window => 4, # links around the current page
28
+ :outer_window => 1, # links around beginning and end
29
+ :link_separator => ' ', # single space is friendly to spiders and non-graphic browsers
30
+ :param_name => :page,
31
+ :params => nil,
32
+ :page_links => true,
33
+ :container => true
34
+
35
+ label_deprecation = Proc.new { |key, value|
36
+ "set the 'will_paginate.#{key}' key in your i18n locale instead of editing pagination_options" if defined? Rails
37
+ }
38
+ pagination_options.deprecate_key(:previous_label, :next_label, &label_deprecation)
39
+ pagination_options.deprecate_key(:renderer) { |key, _| "pagination_options[#{key.inspect}] shouldn't be set globally" }
40
+
41
+ include WillPaginate::I18n
42
+
43
+ # Returns HTML representing page links for a WillPaginate::Collection-like object.
44
+ # In case there is no more than one page in total, nil is returned.
45
+ #
46
+ # ==== Options
47
+ # * <tt>:class</tt> -- CSS class name for the generated DIV (default: "pagination")
48
+ # * <tt>:previous_label</tt> -- default: "« Previous"
49
+ # * <tt>:next_label</tt> -- default: "Next »"
50
+ # * <tt>:inner_window</tt> -- how many links are shown around the current page (default: 4)
51
+ # * <tt>:outer_window</tt> -- how many links are around the first and the last page (default: 1)
52
+ # * <tt>:link_separator</tt> -- string separator for page HTML elements (default: single space)
53
+ # * <tt>:param_name</tt> -- parameter name for page number in URLs (default: <tt>:page</tt>)
54
+ # * <tt>:params</tt> -- additional parameters when generating pagination links
55
+ # (eg. <tt>:controller => "foo", :action => nil</tt>)
56
+ # * <tt>:renderer</tt> -- class name, class or instance of a link renderer (default in Rails:
57
+ # <tt>WillPaginate::ActionView::LinkRenderer</tt>)
58
+ # * <tt>:page_links</tt> -- when false, only previous/next links are rendered (default: true)
59
+ # * <tt>:container</tt> -- toggles rendering of the DIV container for pagination links, set to
60
+ # false only when you are rendering your own pagination markup (default: true)
61
+ #
62
+ # All options not recognized by will_paginate will become HTML attributes on the container
63
+ # element for pagination links (the DIV). For example:
64
+ #
65
+ # <%= will_paginate @posts, :style => 'color:blue' %>
66
+ #
67
+ # will result in:
68
+ #
69
+ # <div class="pagination" style="color:blue"> ... </div>
70
+ #
71
+ def will_paginate(collection, options = {})
72
+ # early exit if there is nothing to render
73
+ return nil unless collection.total_pages > 1
74
+
75
+ options = WillPaginate::ViewHelpers.pagination_options.merge(options)
76
+
77
+ options[:previous_label] ||= will_paginate_translate(:previous_label) { '&#8592; Previous' }
78
+ options[:next_label] ||= will_paginate_translate(:next_label) { 'Next &#8594;' }
79
+
80
+ # get the renderer instance
81
+ renderer = case options[:renderer]
82
+ when nil
83
+ raise ArgumentError, ":renderer not specified"
84
+ when String
85
+ klass = if options[:renderer].respond_to? :constantize then options[:renderer].constantize
86
+ else Object.const_get(options[:renderer]) # poor man's constantize
87
+ end
88
+ klass.new
89
+ when Class then options[:renderer].new
90
+ else options[:renderer]
91
+ end
92
+ # render HTML for pagination
93
+ renderer.prepare collection, options, self
94
+ output = renderer.to_html
95
+ output = output.html_safe if output.respond_to?(:html_safe)
96
+ output
97
+ end
98
+
99
+ # Renders a message containing number of displayed vs. total entries.
100
+ #
101
+ # <%= page_entries_info @posts %>
102
+ # #-> Displaying posts 6 - 12 of 26 in total
103
+ #
104
+ # The default output contains HTML. Use ":html => false" for plain text.
105
+ def page_entries_info(collection, options = {})
106
+ model = options[:model]
107
+ model = collection.first.class unless model or collection.empty?
108
+ model ||= 'entry'
109
+ model_key = if model.respond_to? :model_name
110
+ model.model_name.i18n_key # ActiveModel::Naming
111
+ else
112
+ model.to_s.underscore
113
+ end
114
+
115
+ if options.fetch(:html, true)
116
+ b, eb = '<b>', '</b>'
117
+ sp = '&nbsp;'
118
+ html_key = '_html'
119
+ else
120
+ b = eb = html_key = ''
121
+ sp = ' '
122
+ end
123
+
124
+ model_count = collection.total_pages > 1 ? 5 : collection.size
125
+ defaults = ["models.#{model_key}"]
126
+ defaults << Proc.new { |_, opts|
127
+ if model.respond_to? :model_name
128
+ model.model_name.human(:count => opts[:count])
129
+ else
130
+ name = model_key.to_s.tr('_', ' ')
131
+ raise "can't pluralize model name: #{model.inspect}" unless name.respond_to? :pluralize
132
+ opts[:count] == 1 ? name : name.pluralize
133
+ end
134
+ }
135
+ model_name = will_paginate_translate defaults, :count => model_count
136
+
137
+ if collection.total_pages < 2
138
+ i18n_key = :"page_entries_info.single_page#{html_key}"
139
+ keys = [:"#{model_key}.#{i18n_key}", i18n_key]
140
+
141
+ will_paginate_translate keys, :count => collection.total_entries, :model => model_name do |_, opts|
142
+ case opts[:count]
143
+ when 0; "No #{opts[:model]} found"
144
+ when 1; "Displaying #{b}1#{eb} #{opts[:model]}"
145
+ else "Displaying #{b}all#{sp}#{opts[:count]}#{eb} #{opts[:model]}"
146
+ end
147
+ end
148
+ else
149
+ i18n_key = :"page_entries_info.multi_page#{html_key}"
150
+ keys = [:"#{model_key}.#{i18n_key}", i18n_key]
151
+ params = {
152
+ :model => model_name, :count => collection.total_entries,
153
+ :from => collection.offset + 1, :to => collection.offset + collection.length
154
+ }
155
+ will_paginate_translate keys, params do |_, opts|
156
+ %{Displaying %s #{b}%d#{sp}-#{sp}%d#{eb} of #{b}%d#{eb} in total} %
157
+ [ opts[:model], opts[:from], opts[:to], opts[:count] ]
158
+ end
159
+ end
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,152 @@
1
+ require 'will_paginate/view_helpers'
2
+ require 'will_paginate/view_helpers/link_renderer'
3
+
4
+ module WillPaginate
5
+ # = ActionView helpers
6
+ #
7
+ # This module serves for availability in ActionView templates. It also adds a new
8
+ # view helper: +paginated_section+.
9
+ #
10
+ # == Using the helper without arguments
11
+ # If the helper is called without passing in the collection object, it will
12
+ # try to read from the instance variable inferred by the controller name.
13
+ # For example, calling +will_paginate+ while the current controller is
14
+ # PostsController will result in trying to read from the <tt>@posts</tt>
15
+ # variable. Example:
16
+ #
17
+ # <%= will_paginate :id => true %>
18
+ #
19
+ # ... will result in <tt>@post</tt> collection getting paginated:
20
+ #
21
+ # <div class="pagination" id="posts_pagination"> ... </div>
22
+ #
23
+ module ActionView
24
+ include ViewHelpers
25
+
26
+ def will_paginate(collection = nil, options = {}) #:nodoc:
27
+ options, collection = collection, nil if collection.is_a? Hash
28
+ collection ||= infer_collection_from_controller
29
+
30
+ options = options.symbolize_keys
31
+ options[:renderer] ||= LinkRenderer
32
+
33
+ super(collection, options)
34
+ end
35
+
36
+ def page_entries_info(collection = nil, options = {}) #:nodoc:
37
+ options, collection = collection, nil if collection.is_a? Hash
38
+ collection ||= infer_collection_from_controller
39
+
40
+ super(collection, options.symbolize_keys)
41
+ end
42
+
43
+ # Wrapper for rendering pagination links at both top and bottom of a block
44
+ # of content.
45
+ #
46
+ # <% paginated_section @posts do %>
47
+ # <ol id="posts">
48
+ # <% for post in @posts %>
49
+ # <li> ... </li>
50
+ # <% end %>
51
+ # </ol>
52
+ # <% end %>
53
+ #
54
+ # will result in:
55
+ #
56
+ # <div class="pagination"> ... </div>
57
+ # <ol id="posts">
58
+ # ...
59
+ # </ol>
60
+ # <div class="pagination"> ... </div>
61
+ #
62
+ # Arguments are passed to a <tt>will_paginate</tt> call, so the same options
63
+ # apply. Don't use the <tt>:id</tt> option; otherwise you'll finish with two
64
+ # blocks of pagination links sharing the same ID (which is invalid HTML).
65
+ def paginated_section(*args, &block)
66
+ pagination = will_paginate(*args)
67
+ if pagination
68
+ pagination + capture(&block) + pagination
69
+ else
70
+ capture(&block)
71
+ end
72
+ end
73
+
74
+ def will_paginate_translate(keys, options = {})
75
+ if respond_to? :translate
76
+ if Array === keys
77
+ defaults = keys.dup
78
+ key = defaults.shift
79
+ else
80
+ defaults = nil
81
+ key = keys
82
+ end
83
+ translate(key, options.merge(:default => defaults, :scope => :will_paginate))
84
+ else
85
+ super
86
+ end
87
+ end
88
+
89
+ protected
90
+
91
+ def infer_collection_from_controller
92
+ collection_name = "@#{controller.controller_name}"
93
+ collection = instance_variable_get(collection_name)
94
+ raise ArgumentError, "The #{collection_name} variable appears to be empty. Did you " +
95
+ "forget to pass the collection object for will_paginate?" if collection.nil?
96
+ collection
97
+ end
98
+
99
+ class LinkRenderer < ViewHelpers::LinkRenderer
100
+ protected
101
+
102
+ def default_url_params
103
+ {}
104
+ end
105
+
106
+ def url(page)
107
+ @base_url_params ||= begin
108
+ url_params = merge_get_params(default_url_params)
109
+ url_params[:only_path] = true
110
+ merge_optional_params(url_params)
111
+ end
112
+
113
+ url_params = @base_url_params.dup
114
+ add_current_page_param(url_params, page)
115
+
116
+ if page == 1
117
+ url_params.delete(:page)
118
+ end
119
+ @template.url_for(url_params)
120
+ end
121
+
122
+ def merge_get_params(url_params)
123
+ if @template.respond_to? :request and @template.request and @template.request.get?
124
+ symbolized_update(url_params, @template.params)
125
+ end
126
+ url_params
127
+ end
128
+
129
+ def merge_optional_params(url_params)
130
+ symbolized_update(url_params, @options[:params]) if @options[:params]
131
+ url_params
132
+ end
133
+
134
+ def add_current_page_param(url_params, page)
135
+ unless param_name.index(/[^\w-]/)
136
+ url_params[param_name.to_sym] = page
137
+ else
138
+ page_param = parse_query_parameters("#{param_name}=#{page}")
139
+ symbolized_update(url_params, page_param)
140
+ end
141
+ end
142
+
143
+ private
144
+
145
+ def parse_query_parameters(params)
146
+ Rack::Utils.parse_nested_query(params)
147
+ end
148
+ end
149
+
150
+ ::ActionView::Base.send :include, self
151
+ end
152
+ end
@@ -0,0 +1,131 @@
1
+ require 'cgi'
2
+ require 'will_paginate/core_ext'
3
+ require 'will_paginate/view_helpers'
4
+ require 'will_paginate/view_helpers/link_renderer_base'
5
+
6
+ module WillPaginate
7
+ module ViewHelpers
8
+ # This class does the heavy lifting of actually building the pagination
9
+ # links. It is used by +will_paginate+ helper internally.
10
+ class LinkRenderer < LinkRendererBase
11
+
12
+ # * +collection+ is a WillPaginate::Collection instance or any other object
13
+ # that conforms to that API
14
+ # * +options+ are forwarded from +will_paginate+ view helper
15
+ # * +template+ is the reference to the template being rendered
16
+ def prepare(collection, options, template)
17
+ super(collection, options)
18
+ @template = template
19
+ @container_attributes = @base_url_params = nil
20
+ end
21
+
22
+ # Process it! This method returns the complete HTML string which contains
23
+ # pagination links. Feel free to subclass LinkRenderer and change this
24
+ # method as you see fit.
25
+ def to_html
26
+ html = pagination.map do |item|
27
+ item.is_a?(Fixnum) ?
28
+ page_number(item) :
29
+ send(item)
30
+ end.join(@options[:link_separator])
31
+
32
+ @options[:container] ? html_container(html) : html
33
+ end
34
+
35
+ # Returns the subset of +options+ this instance was initialized with that
36
+ # represent HTML attributes for the container element of pagination links.
37
+ def container_attributes
38
+ @container_attributes ||= @options.except(*(ViewHelpers.pagination_options.keys + [:renderer] - [:class]))
39
+ end
40
+
41
+ protected
42
+
43
+ def page_number(page)
44
+ unless page == current_page
45
+ link(page, page, :rel => rel_value(page))
46
+ else
47
+ tag(:em, page, :class => 'current')
48
+ end
49
+ end
50
+
51
+ def gap
52
+ text = @template.will_paginate_translate(:page_gap) { '&hellip;' }
53
+ %(<span class="gap">#{text}</span>)
54
+ end
55
+
56
+ def previous_page
57
+ num = @collection.current_page > 1 && @collection.current_page - 1
58
+ previous_or_next_page(num, @options[:previous_label], 'previous_page')
59
+ end
60
+
61
+ def next_page
62
+ num = @collection.current_page < total_pages && @collection.current_page + 1
63
+ previous_or_next_page(num, @options[:next_label], 'next_page')
64
+ end
65
+
66
+ def previous_or_next_page(page, text, classname)
67
+ if page
68
+ link(text, page, :class => classname)
69
+ else
70
+ tag(:span, text, :class => classname + ' disabled')
71
+ end
72
+ end
73
+
74
+ def html_container(html)
75
+ tag(:div, html, container_attributes)
76
+ end
77
+
78
+ # Returns URL params for +page_link_or_span+, taking the current GET params
79
+ # and <tt>:params</tt> option into account.
80
+ def url(page)
81
+ raise NotImplementedError
82
+ end
83
+
84
+ private
85
+
86
+ def param_name
87
+ @options[:param_name].to_s
88
+ end
89
+
90
+ def link(text, target, attributes = {})
91
+ if target.is_a? Fixnum
92
+ attributes[:rel] = rel_value(target)
93
+ target = url(target)
94
+ end
95
+ attributes[:href] = target
96
+ tag(:a, text, attributes)
97
+ end
98
+
99
+ def tag(name, value, attributes = {})
100
+ string_attributes = attributes.inject('') do |attrs, pair|
101
+ unless pair.last.nil?
102
+ attrs << %( #{pair.first}="#{CGI::escapeHTML(pair.last.to_s)}")
103
+ end
104
+ attrs
105
+ end
106
+ "<#{name}#{string_attributes}>#{value}</#{name}>"
107
+ end
108
+
109
+ def rel_value(page)
110
+ case page
111
+ when @collection.current_page - 1; 'prev' + (page == 1 ? ' start' : '')
112
+ when @collection.current_page + 1; 'next'
113
+ when 1; 'start'
114
+ end
115
+ end
116
+
117
+ def symbolized_update(target, other)
118
+ other.each do |key, value|
119
+ key = key.to_sym
120
+ existing = target[key]
121
+
122
+ if value.is_a?(Hash) and (existing.is_a?(Hash) or existing.nil?)
123
+ symbolized_update(existing || (target[key] = {}), value)
124
+ else
125
+ target[key] = value
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end