will_paginate 2.3.17 → 3.0.pre

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 (83) hide show
  1. data/CHANGELOG.rdoc +24 -80
  2. data/LICENSE +1 -1
  3. data/README.rdoc +125 -0
  4. data/Rakefile +26 -22
  5. data/lib/will_paginate.rb +10 -84
  6. data/lib/will_paginate/array.rb +25 -8
  7. data/lib/will_paginate/collection.rb +15 -28
  8. data/lib/will_paginate/core_ext.rb +26 -0
  9. data/lib/will_paginate/deprecation.rb +50 -0
  10. data/lib/will_paginate/finders.rb +9 -0
  11. data/lib/will_paginate/finders/active_record.rb +158 -0
  12. data/lib/will_paginate/finders/active_resource.rb +51 -0
  13. data/lib/will_paginate/finders/base.rb +112 -0
  14. data/lib/will_paginate/finders/data_mapper.rb +30 -0
  15. data/lib/will_paginate/finders/sequel.rb +23 -0
  16. data/lib/will_paginate/railtie.rb +26 -0
  17. data/lib/will_paginate/version.rb +5 -5
  18. data/lib/will_paginate/view_helpers.rb +25 -436
  19. data/lib/will_paginate/view_helpers/action_view.rb +142 -0
  20. data/lib/will_paginate/view_helpers/base.rb +126 -0
  21. data/lib/will_paginate/view_helpers/link_renderer.rb +130 -0
  22. data/lib/will_paginate/view_helpers/link_renderer_base.rb +83 -0
  23. data/lib/will_paginate/view_helpers/merb.rb +13 -0
  24. data/spec/collection_spec.rb +147 -0
  25. data/spec/console +8 -0
  26. data/spec/console_fixtures.rb +8 -0
  27. data/spec/database.yml +22 -0
  28. data/spec/finders/active_record_spec.rb +377 -0
  29. data/spec/finders/active_resource_spec.rb +52 -0
  30. data/spec/finders/activerecord_test_connector.rb +114 -0
  31. data/spec/finders/data_mapper_spec.rb +62 -0
  32. data/spec/finders/data_mapper_test_connector.rb +20 -0
  33. data/spec/finders/sequel_spec.rb +53 -0
  34. data/spec/finders/sequel_test_connector.rb +9 -0
  35. data/spec/finders_spec.rb +76 -0
  36. data/{test → spec}/fixtures/admin.rb +0 -0
  37. data/{test → spec}/fixtures/developer.rb +2 -3
  38. data/{test → spec}/fixtures/developers_projects.yml +0 -0
  39. data/{test → spec}/fixtures/project.rb +2 -6
  40. data/{test → spec}/fixtures/projects.yml +1 -1
  41. data/{test → spec}/fixtures/replies.yml +0 -0
  42. data/{test → spec}/fixtures/reply.rb +1 -1
  43. data/{test → spec}/fixtures/schema.rb +0 -0
  44. data/spec/fixtures/topic.rb +7 -0
  45. data/{test → spec}/fixtures/topics.yml +0 -0
  46. data/{test → spec}/fixtures/user.rb +0 -0
  47. data/{test → spec}/fixtures/users.yml +0 -0
  48. data/spec/rcov.opts +2 -0
  49. data/spec/spec.opts +2 -0
  50. data/spec/spec_helper.rb +74 -0
  51. data/spec/tasks.rake +60 -0
  52. data/spec/view_helpers/action_view_spec.rb +345 -0
  53. data/spec/view_helpers/base_spec.rb +64 -0
  54. data/spec/view_helpers/link_renderer_base_spec.rb +84 -0
  55. data/spec/view_helpers/view_example_group.rb +103 -0
  56. metadata +60 -65
  57. data/README.md +0 -53
  58. data/lib/will_paginate/finder.rb +0 -269
  59. data/lib/will_paginate/i18n.rb +0 -29
  60. data/lib/will_paginate/locale/en.yml +0 -33
  61. data/lib/will_paginate/named_scope.rb +0 -170
  62. data/lib/will_paginate/named_scope_patch.rb +0 -37
  63. data/lib/will_paginate/per_page.rb +0 -27
  64. data/test/ci.rb +0 -60
  65. data/test/collection_test.rb +0 -160
  66. data/test/console +0 -8
  67. data/test/database.yml +0 -16
  68. data/test/finder_test.rb +0 -527
  69. data/test/fixtures/topic.rb +0 -12
  70. data/test/gemfiles/Gemfile.1.2 +0 -13
  71. data/test/gemfiles/Gemfile.1.2.lock +0 -39
  72. data/test/gemfiles/Gemfile.2.0 +0 -16
  73. data/test/gemfiles/Gemfile.2.0.lock +0 -28
  74. data/test/gemfiles/Gemfile.2.1 +0 -16
  75. data/test/gemfiles/Gemfile.2.1.lock +0 -28
  76. data/test/gemfiles/Gemfile.2.2 +0 -16
  77. data/test/gemfiles/Gemfile.2.2.lock +0 -28
  78. data/test/helper.rb +0 -34
  79. data/test/lib/activerecord_test_case.rb +0 -38
  80. data/test/lib/activerecord_test_connector.rb +0 -86
  81. data/test/lib/load_fixtures.rb +0 -12
  82. data/test/lib/view_test_process.rb +0 -186
  83. data/test/view_test.rb +0 -380
@@ -0,0 +1,30 @@
1
+ require 'will_paginate/finders/base'
2
+ require 'dm-core'
3
+
4
+ module WillPaginate::Finders
5
+ module DataMapper
6
+ include WillPaginate::Finders::Base
7
+
8
+ protected
9
+
10
+ def wp_query(options, pager, args, &block) #:nodoc
11
+ find_options = options.except(:count).update(:offset => pager.offset, :limit => pager.per_page)
12
+
13
+ pager.replace all(find_options, &block)
14
+
15
+ unless pager.total_entries
16
+ pager.total_entries = wp_count(options)
17
+ end
18
+ end
19
+
20
+ def wp_count(options) #:nodoc
21
+ count_options = options.except(:count, :order)
22
+ # merge the hash found in :count
23
+ count_options.update options[:count] if options[:count]
24
+
25
+ count_options.empty?? count() : count(count_options)
26
+ end
27
+ end
28
+ end
29
+
30
+ DataMapper::Model.send(:include, WillPaginate::Finders::DataMapper)
@@ -0,0 +1,23 @@
1
+ require 'will_paginate/core_ext'
2
+ require 'sequel'
3
+ require 'sequel/extensions/pagination'
4
+
5
+ existing_methods = Sequel::Dataset::Pagination.instance_methods
6
+
7
+ Sequel::Dataset::Pagination.module_eval do
8
+ # it should quack like a WillPaginate::Collection
9
+
10
+ alias :total_pages :page_count unless existing_methods.include_method? :total_pages
11
+ alias :per_page :page_size unless existing_methods.include_method? :per_page
12
+ alias :previous_page :prev_page unless existing_methods.include_method? :previous_page
13
+ alias :total_entries :pagination_record_count unless existing_methods.include_method? :total_entries
14
+
15
+ def out_of_bounds?
16
+ current_page > total_pages
17
+ end
18
+
19
+ # Current offset of the paginated collection
20
+ def offset
21
+ (current_page - 1) * per_page
22
+ end
23
+ end
@@ -0,0 +1,26 @@
1
+ require 'will_paginate'
2
+ require 'will_paginate/collection'
3
+
4
+ module WillPaginate
5
+ class Railtie < Rails::Railtie
6
+ railtie_name :will_paginate
7
+
8
+ initializer "will_paginate.active_record" do |app|
9
+ if defined? ::ActiveRecord
10
+ require 'will_paginate/finders/active_record'
11
+ WillPaginate::Finders::ActiveRecord.enable!
12
+ end
13
+ end
14
+
15
+ initializer "will_paginate.action_dispatch" do |app|
16
+ if defined? ::ActionDispatch::ShowExceptions
17
+ ActionDispatch::ShowExceptions.rescue_responses['WillPaginate::InvalidPage'] = :not_found
18
+ end
19
+ end
20
+
21
+ initializer "will_paginate.action_view" do |app|
22
+ require 'will_paginate/view_helpers/action_view'
23
+ ActionView::Base.send(:include, WillPaginate::ViewHelpers::ActionView)
24
+ end
25
+ end
26
+ end
@@ -1,8 +1,8 @@
1
- module WillPaginate
2
- module VERSION
3
- MAJOR = 2
4
- MINOR = 3
5
- TINY = 17
1
+ module WillPaginate #:nodoc:
2
+ module VERSION #:nodoc:
3
+ MAJOR = 3
4
+ MINOR = 0
5
+ TINY = 'pre'
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -1,453 +1,42 @@
1
- require 'will_paginate/core_ext'
2
- require 'will_paginate/i18n'
1
+ require 'will_paginate/deprecation'
3
2
 
4
3
  module WillPaginate
5
4
  # = Will Paginate view helpers
6
5
  #
7
- # The main view helper, #will_paginate, renders
8
- # pagination links for the given collection. The helper itself is lightweight
9
- # and serves only as a wrapper around LinkRenderer instantiation; the
10
- # renderer then does all the hard work of generating the HTML.
6
+ # The main view helper is +will_paginate+. It renders the pagination links
7
+ # for the given collection. The helper itself is lightweight and serves only
8
+ # as a wrapper around LinkRenderer instantiation; the renderer then does
9
+ # all the hard work of generating the HTML.
11
10
  #
12
- # == Global options for helpers
13
- #
14
- # Options for pagination helpers are optional and get their default values from the
15
- # <tt>WillPaginate::ViewHelpers.pagination_options</tt> hash. You can write to this hash to
16
- # override default options on the global level:
17
- #
18
- # WillPaginate::ViewHelpers.pagination_options[:previous_label] = 'Previous page'
19
- #
20
- # By putting this into "config/initializers/will_paginate.rb" (or simply environment.rb in
21
- # older versions of Rails) you can easily translate link texts to previous
22
- # and next pages, as well as override some other defaults to your liking.
11
+ # Read more in WillPaginate::ViewHelpers::Base
23
12
  module ViewHelpers
24
- include WillPaginate::I18n
25
-
26
- # default options that can be overridden on the global level
27
- @@pagination_options = {
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
+
28
+ self.pagination_options = {
28
29
  :class => 'pagination',
29
- :previous_label => nil,
30
- :next_label => nil,
30
+ :previous_label => '&#8592; Previous',
31
+ :next_label => 'Next &#8594;',
31
32
  :inner_window => 4, # links around the current page
32
33
  :outer_window => 1, # links around beginning and end
33
34
  :separator => ' ', # single space is friendly to spiders and non-graphic browsers
34
35
  :param_name => :page,
35
36
  :params => nil,
36
- :renderer => 'WillPaginate::LinkRenderer',
37
+ :renderer => 'WillPaginate::ViewHelpers::LinkRenderer',
37
38
  :page_links => true,
38
39
  :container => true
39
40
  }
40
- mattr_reader :pagination_options
41
-
42
- # Renders Digg/Flickr-style pagination for a WillPaginate::Collection
43
- # object. Nil is returned if there is only one page in total; no point in
44
- # rendering the pagination in that case...
45
- #
46
- # ==== Options
47
- # Display options:
48
- # * <tt>:previous_label</tt> -- default: "« Previous" (this parameter is called <tt>:prev_label</tt> in versions <b>2.3.2</b> and older!)
49
- # * <tt>:next_label</tt> -- default: "Next »"
50
- # * <tt>:page_links</tt> -- when false, only previous/next links are rendered (default: true)
51
- # * <tt>:inner_window</tt> -- how many links are shown around the current page (default: 4)
52
- # * <tt>:outer_window</tt> -- how many links are around the first and the last page (default: 1)
53
- # * <tt>:separator</tt> -- string separator for page HTML elements (default: single space)
54
- #
55
- # HTML options:
56
- # * <tt>:class</tt> -- CSS class name for the generated DIV (default: "pagination")
57
- # * <tt>:container</tt> -- toggles rendering of the DIV container for pagination links, set to
58
- # false only when you are rendering your own pagination markup (default: true)
59
- # * <tt>:id</tt> -- HTML ID for the container (default: nil). Pass +true+ to have the ID
60
- # automatically generated from the class name of objects in collection: for example, paginating
61
- # ArticleComment models would yield an ID of "article_comments_pagination".
62
- #
63
- # Advanced options:
64
- # * <tt>:param_name</tt> -- parameter name for page number in URLs (default: <tt>:page</tt>)
65
- # * <tt>:params</tt> -- additional parameters when generating pagination links
66
- # (eg. <tt>:controller => "foo", :action => nil</tt>)
67
- # * <tt>:renderer</tt> -- class name, class or instance of a link renderer (default:
68
- # <tt>WillPaginate::LinkRenderer</tt>)
69
- #
70
- # All options not recognized by will_paginate will become HTML attributes on the container
71
- # element for pagination links (the DIV). For example:
72
- #
73
- # <%= will_paginate @posts, :style => 'font-size: small' %>
74
- #
75
- # ... will result in:
76
- #
77
- # <div class="pagination" style="font-size: small"> ... </div>
78
- #
79
- # ==== Using the helper without arguments
80
- # If the helper is called without passing in the collection object, it will
81
- # try to read from the instance variable inferred by the controller name.
82
- # For example, calling +will_paginate+ while the current controller is
83
- # PostsController will result in trying to read from the <tt>@posts</tt>
84
- # variable. Example:
85
- #
86
- # <%= will_paginate :id => true %>
87
- #
88
- # ... will result in <tt>@post</tt> collection getting paginated:
89
- #
90
- # <div class="pagination" id="posts_pagination"> ... </div>
91
- #
92
- def will_paginate(collection = nil, options = {})
93
- options, collection = collection, nil if collection.is_a? Hash
94
- unless collection or !controller
95
- collection_name = "@#{controller.controller_name}"
96
- collection = instance_variable_get(collection_name)
97
- raise ArgumentError, "The #{collection_name} variable appears to be empty. Did you " +
98
- "forget to pass the collection object for will_paginate?" unless collection
99
- end
100
- # early exit if there is nothing to render
101
- return nil unless WillPaginate::ViewHelpers.total_pages_for_collection(collection) > 1
102
-
103
- options = options.symbolize_keys.reverse_merge WillPaginate::ViewHelpers.pagination_options
104
- if options[:prev_label]
105
- WillPaginate::Deprecation::warn(":prev_label view parameter is now :previous_label; the old name has been deprecated", caller)
106
- options[:previous_label] = options.delete(:prev_label)
107
- end
108
-
109
- options[:previous_label] ||= will_paginate_translate(:previous_label) { '&laquo; Previous' }
110
- options[:next_label] ||= will_paginate_translate(:next_label) { 'Next &raquo;' }
111
-
112
- # get the renderer instance
113
- renderer = case options[:renderer]
114
- when String
115
- options[:renderer].to_s.constantize.new
116
- when Class
117
- options[:renderer].new
118
- else
119
- options[:renderer]
120
- end
121
- # render HTML for pagination
122
- renderer.prepare collection, options, self
123
- renderer.to_html
124
- end
125
-
126
- # Wrapper for rendering pagination links at both top and bottom of a block
127
- # of content.
128
- #
129
- # <% paginated_section @posts do %>
130
- # <ol id="posts">
131
- # <% for post in @posts %>
132
- # <li> ... </li>
133
- # <% end %>
134
- # </ol>
135
- # <% end %>
136
- #
137
- # will result in:
138
- #
139
- # <div class="pagination"> ... </div>
140
- # <ol id="posts">
141
- # ...
142
- # </ol>
143
- # <div class="pagination"> ... </div>
144
- #
145
- # Arguments are passed to a <tt>will_paginate</tt> call, so the same options
146
- # apply. Don't use the <tt>:id</tt> option; otherwise you'll finish with two
147
- # blocks of pagination links sharing the same ID (which is invalid HTML).
148
- def paginated_section(*args, &block)
149
- pagination = will_paginate(*args).to_s
150
-
151
- if respond_to? :output_buffer
152
- concat pagination
153
- yield
154
- concat pagination
155
- else
156
- content = pagination + capture(&block) + pagination
157
- concat(content, block.binding)
158
- end
159
- end
160
-
161
- # Renders a helpful message with numbers of displayed vs. total entries.
162
- # You can use this as a blueprint for your own, similar helpers.
163
- #
164
- # <%= page_entries_info @posts %>
165
- # #-> Displaying posts 6 - 10 of 26 in total
166
- #
167
- # By default, the message will use the humanized class name of objects
168
- # in collection: for instance, "project types" for ProjectType models.
169
- # Override this with the <tt>:model</tt> parameter:
170
- #
171
- # <%= page_entries_info @posts, :model => 'item' %>
172
- # #-> Displaying items 6 - 10 of 26 in total
173
- def page_entries_info(collection, options = {})
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
208
- end
209
- else
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
226
- end
227
- end
228
-
229
- def self.total_pages_for_collection(collection) #:nodoc:
230
- if collection.respond_to?('page_count') and !collection.respond_to?('total_pages')
231
- WillPaginate::Deprecation.warn %{
232
- You are using a paginated collection of class #{collection.class.name}
233
- which conforms to the old API of WillPaginate::Collection by using
234
- `page_count`, while the current method name is `total_pages`. Please
235
- upgrade yours or 3rd-party code that provides the paginated collection}, caller
236
- class << collection
237
- def total_pages; page_count; end
238
- end
239
- end
240
- collection.total_pages
241
- end
242
- end
243
-
244
- # This class does the heavy lifting of actually building the pagination
245
- # links. It is used by the <tt>will_paginate</tt> helper internally.
246
- class LinkRenderer
247
-
248
- # * +collection+ is a WillPaginate::Collection instance or any other object
249
- # that conforms to that API
250
- # * +options+ are forwarded from +will_paginate+ view helper
251
- # * +template+ is the reference to the template being rendered
252
- def prepare(collection, options, template)
253
- @collection = collection
254
- @options = options
255
- @template = template
256
-
257
- # reset values in case we're re-using this instance
258
- @total_pages = @param_name = @url_string = nil
259
- end
260
-
261
- # Process it! This method returns the complete HTML string which contains
262
- # pagination links. Feel free to subclass LinkRenderer and change this
263
- # method as you see fit.
264
- def to_html
265
- links = @options[:page_links] ? windowed_links : []
266
- # previous/next buttons
267
- links.unshift page_link_or_span(@collection.previous_page, 'disabled prev_page', @options[:previous_label])
268
- links.push page_link_or_span(@collection.next_page, 'disabled next_page', @options[:next_label])
269
-
270
- html = links.join(@options[:separator])
271
- html = html.html_safe if html.respond_to? :html_safe
272
- @options[:container] ? @template.content_tag(:div, html, html_attributes) : html
273
- end
274
-
275
- # Returns the subset of +options+ this instance was initialized with that
276
- # represent HTML attributes for the container element of pagination links.
277
- def html_attributes
278
- return @html_attributes if @html_attributes
279
- @html_attributes = @options.except *(WillPaginate::ViewHelpers.pagination_options.keys - [:class])
280
- # pagination of Post models will have the ID of "posts_pagination"
281
- if @options[:container] and @options[:id] === true
282
- @html_attributes[:id] = @collection.first.class.name.underscore.pluralize + '_pagination'
283
- end
284
- @html_attributes
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
298
-
299
- protected
300
-
301
- # Collects link items for visible page numbers.
302
- def windowed_links
303
- prev = nil
304
-
305
- visible_page_numbers.inject [] do |links, n|
306
- # detect gaps:
307
- links << gap_marker if prev and n > prev + 1
308
- links << page_link_or_span(n, 'current')
309
- prev = n
310
- links
311
- end
312
- end
313
-
314
- # Calculates visible page numbers using the <tt>:inner_window</tt> and
315
- # <tt>:outer_window</tt> options.
316
- def visible_page_numbers
317
- inner_window, outer_window = @options[:inner_window].to_i, @options[:outer_window].to_i
318
- window_from = current_page - inner_window
319
- window_to = current_page + inner_window
320
-
321
- # adjust lower or upper limit if other is out of bounds
322
- if window_to > total_pages
323
- window_from -= window_to - total_pages
324
- window_to = total_pages
325
- end
326
- if window_from < 1
327
- window_to += 1 - window_from
328
- window_from = 1
329
- window_to = total_pages if window_to > total_pages
330
- end
331
-
332
- visible = (1..total_pages).to_a
333
- left_gap = (2 + outer_window)...window_from
334
- right_gap = (window_to + 1)...(total_pages - outer_window)
335
- visible -= left_gap.to_a if left_gap.last - left_gap.first > 1
336
- visible -= right_gap.to_a if right_gap.last - right_gap.first > 1
337
-
338
- visible
339
- end
340
-
341
- def page_link_or_span(page, span_class, text = nil)
342
- text ||= page.to_s
343
- text = text.html_safe if text.respond_to? :html_safe
344
-
345
- if page and page != current_page
346
- classnames = span_class && span_class.index(' ') && span_class.split(' ', 2).last
347
- page_link page, text, :rel => rel_value(page), :class => classnames
348
- else
349
- page_span page, text, :class => span_class
350
- end
351
- end
352
-
353
- def page_link(page, text, attributes = {})
354
- @template.link_to text, url_for(page), attributes
355
- end
356
-
357
- def page_span(page, text, attributes = {})
358
- @template.content_tag :span, text, attributes
359
- end
360
-
361
- # Returns URL params for +page_link_or_span+, taking the current GET params
362
- # and <tt>:params</tt> option into account.
363
- def url_for(page)
364
- page_one = page == 1
365
- unless @url_string and !page_one
366
- @url_params = {}
367
- # page links should preserve GET parameters
368
- stringified_merge @url_params, @template.params if @template.request.get?
369
- stringified_merge @url_params, @options[:params] if @options[:params]
370
-
371
- if complex = param_name.index(/[^\w-]/)
372
- page_param = parse_query_parameters("#{param_name}=#{page}")
373
-
374
- stringified_merge @url_params, page_param
375
- else
376
- @url_params[param_name] = page_one ? 1 : 2
377
- end
378
-
379
- url = @template.url_for(@url_params)
380
- return url if page_one
381
-
382
- if complex
383
- @url_string = url.sub(%r!((?:\?|&amp;)#{CGI.escape param_name}=)#{page}!, "\\1\0")
384
- return url
385
- else
386
- @url_string = url
387
- @url_params[param_name] = 3
388
- @template.url_for(@url_params).split(//).each_with_index do |char, i|
389
- if char == '3' and url[i, 1] == '2'
390
- @url_string[i] = "\0"
391
- break
392
- end
393
- end
394
- end
395
- end
396
- # finally!
397
- @url_string.sub "\0", page.to_s
398
- end
399
-
400
- private
401
-
402
- def rel_value(page)
403
- case page
404
- when @collection.previous_page; 'prev' + (page == 1 ? ' start' : '')
405
- when @collection.next_page; 'next'
406
- when 1; 'start'
407
- end
408
- end
409
-
410
- def current_page
411
- @collection.current_page
412
- end
413
-
414
- def total_pages
415
- @total_pages ||= WillPaginate::ViewHelpers.total_pages_for_collection(@collection)
416
- end
417
-
418
- def param_name
419
- @param_name ||= @options[:param_name].to_s
420
- end
421
-
422
- # Recursively merge into target hash by using stringified keys from the other one
423
- def stringified_merge(target, other)
424
- other.each do |key, value|
425
- key = key.to_s # this line is what it's all about!
426
- existing = target[key]
427
-
428
- if value.is_a?(Hash) and (existing.is_a?(Hash) or existing.nil?)
429
- stringified_merge(existing || (target[key] = {}), value)
430
- else
431
- target[key] = value
432
- end
433
- end
434
- end
435
-
436
- def parse_query_parameters(params)
437
- if defined? Rack::Utils
438
- # For Rails > 2.3
439
- Rack::Utils.parse_nested_query(params)
440
- elsif defined?(ActionController::AbstractRequest) and
441
- ActionController::AbstractRequest.respond_to? :parse_query_parameters
442
- ActionController::AbstractRequest.parse_query_parameters(params)
443
- elsif defined?(ActionController::UrlEncodedPairParser)
444
- # For Rails > 2.2
445
- ActionController::UrlEncodedPairParser.parse_query_parameters(params)
446
- elsif defined?(CGIMethods)
447
- CGIMethods.parse_query_parameters(params)
448
- else
449
- raise "unsupported ActionPack version"
450
- end
451
- end
452
41
  end
453
42
  end