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,441 @@
1
+ require 'spec_helper'
2
+ require 'active_support/rescuable' # needed for Ruby 1.9.1
3
+ require 'action_controller'
4
+ require 'action_view'
5
+ require 'will_paginate/view_helpers/action_view'
6
+ require 'will_paginate/collection'
7
+
8
+ Routes = ActionDispatch::Routing::RouteSet.new
9
+
10
+ Routes.draw do
11
+ get 'dummy/page/:page' => 'dummy#index'
12
+ get 'dummy/dots/page.:page' => 'dummy#dots'
13
+ get 'ibocorp(/:page)' => 'ibocorp#index',
14
+ :constraints => { :page => /\d+/ }, :defaults => { :page => 1 }
15
+
16
+ get ':controller(/:action(/:id(.:format)))'
17
+ end
18
+
19
+ describe WillPaginate::ActionView do
20
+
21
+ before(:all) do
22
+ I18n.load_path.concat WillPaginate::I18n.load_path
23
+ end
24
+
25
+ before(:each) do
26
+ I18n.reload!
27
+ end
28
+
29
+ before(:each) do
30
+ @assigns = {}
31
+ @controller = DummyController.new
32
+ @request = @controller.request
33
+ @template = '<%= will_paginate collection, options %>'
34
+ end
35
+
36
+ attr_reader :assigns, :controller, :request
37
+
38
+ def render(locals)
39
+ @view = ActionView::Base.new([], @assigns, @controller)
40
+ @view.request = @request
41
+ @view.singleton_class.send(:include, @controller._routes.url_helpers)
42
+ @view.render(:inline => @template, :locals => locals)
43
+ end
44
+
45
+ ## basic pagination ##
46
+
47
+ it "should render" do
48
+ paginate do |pagination|
49
+ assert_select 'a[href]', 3 do |elements|
50
+ validate_page_numbers [2,3,2], elements
51
+ assert_select elements.last, ':last-child', "Next &#8594;"
52
+ end
53
+ assert_select 'span', 1
54
+ assert_select 'span.disabled:first-child', '&#8592; Previous'
55
+ assert_select 'em.current', '1'
56
+ pagination.first.inner_text.should == '&#8592; Previous 1 2 3 Next &#8594;'
57
+ end
58
+ end
59
+
60
+ it "should override existing page param value" do
61
+ request.params :page => 1
62
+ paginate do |pagination|
63
+ assert_select 'a[href]', 3 do |elements|
64
+ validate_page_numbers [2,3,2], elements
65
+ end
66
+ end
67
+ end
68
+
69
+ it "should render nothing when there is only 1 page" do
70
+ paginate(:per_page => 30).should be_empty
71
+ end
72
+
73
+ it "should paginate with options" do
74
+ paginate({ :page => 2 }, :class => 'will_paginate', :previous_label => 'Prev', :next_label => 'Next') do
75
+ assert_select 'a[href]', 4 do |elements|
76
+ validate_page_numbers [1,1,3,3], elements
77
+ # test rel attribute values:
78
+ assert_select elements[1], 'a', '1' do |link|
79
+ link.first['rel'].should == 'prev start'
80
+ end
81
+ assert_select elements.first, 'a', "Prev" do |link|
82
+ link.first['rel'].should == 'prev start'
83
+ end
84
+ assert_select elements.last, 'a', "Next" do |link|
85
+ link.first['rel'].should == 'next'
86
+ end
87
+ end
88
+ assert_select '.current', '2'
89
+ end
90
+ end
91
+
92
+ it "should paginate using a custom renderer class" do
93
+ paginate({}, :renderer => AdditionalLinkAttributesRenderer) do
94
+ assert_select 'a[default=true]', 3
95
+ end
96
+ end
97
+
98
+ it "should paginate using a custom renderer instance" do
99
+ renderer = WillPaginate::ActionView::LinkRenderer.new
100
+ def renderer.gap() '<span class="my-gap">~~</span>' end
101
+
102
+ paginate({ :per_page => 2 }, :inner_window => 0, :outer_window => 0, :renderer => renderer) do
103
+ assert_select 'span.my-gap', '~~'
104
+ end
105
+
106
+ renderer = AdditionalLinkAttributesRenderer.new(:title => 'rendered')
107
+ paginate({}, :renderer => renderer) do
108
+ assert_select 'a[title=rendered]', 3
109
+ end
110
+ end
111
+
112
+ it "should have classnames on previous/next links" do
113
+ paginate do |pagination|
114
+ assert_select 'span.disabled.previous_page:first-child'
115
+ assert_select 'a.next_page[href]:last-child'
116
+ end
117
+ end
118
+
119
+ it "should match expected markup" do
120
+ paginate
121
+ expected = <<-HTML
122
+ <div class="pagination"><span class="previous_page disabled">&#8592; Previous</span>
123
+ <em class="current">1</em>
124
+ <a href="/foo/bar?page=2" rel="next">2</a>
125
+ <a href="/foo/bar?page=3">3</a>
126
+ <a href="/foo/bar?page=2" class="next_page" rel="next">Next &#8594;</a></div>
127
+ HTML
128
+ expected.strip!.gsub!(/\s{2,}/, ' ')
129
+ expected_dom = HTML::Document.new(expected).root
130
+
131
+ html_document.root.should == expected_dom
132
+ end
133
+
134
+ it "should output escaped URLs" do
135
+ paginate({:page => 1, :per_page => 1, :total_entries => 2},
136
+ :page_links => false, :params => { :tag => '<br>' })
137
+
138
+ assert_select 'a[href]', 1 do |links|
139
+ query = links.first['href'].split('?', 2)[1]
140
+ query.split('&amp;').sort.should == %w(page=2 tag=%3Cbr%3E)
141
+ end
142
+ end
143
+
144
+ ## advanced options for pagination ##
145
+
146
+ it "should be able to render without container" do
147
+ paginate({}, :container => false)
148
+ assert_select 'div.pagination', 0, 'main DIV present when it shouldn\'t'
149
+ assert_select 'a[href]', 3
150
+ end
151
+
152
+ it "should be able to render without page links" do
153
+ paginate({ :page => 2 }, :page_links => false) do
154
+ assert_select 'a[href]', 2 do |elements|
155
+ validate_page_numbers [1,3], elements
156
+ end
157
+ end
158
+ end
159
+
160
+ ## other helpers ##
161
+
162
+ it "should render a paginated section" do
163
+ @template = <<-ERB
164
+ <%= paginated_section collection, options do %>
165
+ <%= content_tag :div, '', :id => "developers" %>
166
+ <% end %>
167
+ ERB
168
+
169
+ paginate
170
+ assert_select 'div.pagination', 2
171
+ assert_select 'div.pagination + div#developers', 1
172
+ end
173
+
174
+ it "should not render a paginated section with a single page" do
175
+ @template = <<-ERB
176
+ <%= paginated_section collection, options do %>
177
+ <%= content_tag :div, '', :id => "developers" %>
178
+ <% end %>
179
+ ERB
180
+
181
+ paginate(:total_entries => 1)
182
+ assert_select 'div.pagination', 0
183
+ assert_select 'div#developers', 1
184
+ end
185
+
186
+ ## parameter handling in page links ##
187
+
188
+ it "should preserve parameters on GET" do
189
+ request.params :foo => { :bar => 'baz' }
190
+ paginate
191
+ assert_links_match /foo\[bar\]=baz/
192
+ end
193
+
194
+ it "doesn't allow tampering with host, port, protocol" do
195
+ request.params :host => 'disney.com', :port => '99', :protocol => 'ftp'
196
+ paginate
197
+ assert_links_match %r{^/foo/bar}
198
+ assert_no_links_match /disney/
199
+ assert_no_links_match /99/
200
+ assert_no_links_match /ftp/
201
+ end
202
+
203
+ it "should not preserve parameters on POST" do
204
+ request.post
205
+ request.params :foo => 'bar'
206
+ paginate
207
+ assert_no_links_match /foo=bar/
208
+ end
209
+
210
+ it "should add additional parameters to links" do
211
+ paginate({}, :params => { :foo => 'bar' })
212
+ assert_links_match /foo=bar/
213
+ end
214
+
215
+ it "should add anchor parameter" do
216
+ paginate({}, :params => { :anchor => 'anchor' })
217
+ assert_links_match /#anchor$/
218
+ end
219
+
220
+ it "should remove arbitrary parameters" do
221
+ request.params :foo => 'bar'
222
+ paginate({}, :params => { :foo => nil })
223
+ assert_no_links_match /foo=bar/
224
+ end
225
+
226
+ it "should override default route parameters" do
227
+ paginate({}, :params => { :controller => 'baz', :action => 'list' })
228
+ assert_links_match %r{\Wbaz/list\W}
229
+ end
230
+
231
+ it "should paginate with custom page parameter" do
232
+ paginate({ :page => 2 }, :param_name => :developers_page) do
233
+ assert_select 'a[href]', 4 do |elements|
234
+ validate_page_numbers [1,1,3,3], elements, :developers_page
235
+ end
236
+ end
237
+ end
238
+
239
+ it "should paginate with complex custom page parameter" do
240
+ request.params :developers => { :page => 2 }
241
+
242
+ paginate({ :page => 2 }, :param_name => 'developers[page]') do
243
+ assert_select 'a[href]', 4 do |links|
244
+ assert_links_match /\?developers\[page\]=\d+$/, links
245
+ validate_page_numbers [1,1,3,3], links, 'developers[page]'
246
+ end
247
+ end
248
+ end
249
+
250
+ it "should paginate with custom route page parameter" do
251
+ request.symbolized_path_parameters.update :controller => 'dummy', :action => nil
252
+ paginate :per_page => 2 do
253
+ assert_select 'a[href]', 6 do |links|
254
+ assert_links_match %r{/page/(\d+)$}, links, [2, 3, 4, 5, 6, 2]
255
+ end
256
+ end
257
+ end
258
+
259
+ it "should paginate with custom route with dot separator page parameter" do
260
+ request.symbolized_path_parameters.update :controller => 'dummy', :action => 'dots'
261
+ paginate :per_page => 2 do
262
+ assert_select 'a[href]', 6 do |links|
263
+ assert_links_match %r{/page\.(\d+)$}, links, [2, 3, 4, 5, 6, 2]
264
+ end
265
+ end
266
+ end
267
+
268
+ it "should paginate with custom route and first page number implicit" do
269
+ request.symbolized_path_parameters.update :controller => 'ibocorp', :action => nil
270
+ paginate :page => 2, :per_page => 2 do
271
+ assert_select 'a[href]', 7 do |links|
272
+ assert_links_match %r{/ibocorp(?:/(\d+))?$}, links, [nil, nil, 3, 4, 5, 6, 3]
273
+ end
274
+ end
275
+ # Routes.recognize_path('/ibocorp/2').should == {:page=>'2', :action=>'index', :controller=>'ibocorp'}
276
+ # Routes.recognize_path('/ibocorp/foo').should == {:action=>'foo', :controller=>'ibocorp'}
277
+ end
278
+
279
+ ## internal hardcore stuff ##
280
+
281
+ it "should be able to guess the collection name" do
282
+ collection = mock
283
+ collection.expects(:total_pages).returns(1)
284
+
285
+ @template = '<%= will_paginate options %>'
286
+ controller.controller_name = 'developers'
287
+ assigns['developers'] = collection
288
+
289
+ paginate(nil)
290
+ end
291
+
292
+ it "should fail if the inferred collection is nil" do
293
+ @template = '<%= will_paginate options %>'
294
+ controller.controller_name = 'developers'
295
+
296
+ lambda {
297
+ paginate(nil)
298
+ }.should raise_error(ActionView::TemplateError, /@developers/)
299
+ end
300
+
301
+ ## i18n
302
+
303
+ it "is able to translate previous/next labels" do
304
+ translation :will_paginate => {
305
+ :previous_label => 'Go back',
306
+ :next_label => 'Load more'
307
+ }
308
+
309
+ paginate do |pagination|
310
+ assert_select 'span.disabled:first-child', 'Go back'
311
+ assert_select 'a[rel=next]', 'Load more'
312
+ end
313
+ end
314
+
315
+ it "renders using ActionView helpers on a custom object" do
316
+ helper = Class.new {
317
+ attr_reader :controller
318
+ include ActionView::Helpers::UrlHelper
319
+ include Routes.url_helpers
320
+ include WillPaginate::ActionView
321
+ }.new
322
+ helper.default_url_options[:controller] = 'dummy'
323
+
324
+ collection = WillPaginate::Collection.new(2, 1, 3)
325
+ @render_output = helper.will_paginate(collection)
326
+
327
+ assert_select 'a[href]', 4 do |links|
328
+ urls = links.map {|l| l['href'] }.uniq
329
+ urls.should == ['/dummy/page/1', '/dummy/page/3']
330
+ end
331
+ end
332
+
333
+ it "renders using ActionDispatch helper on a custom object" do
334
+ helper = Class.new {
335
+ include ActionDispatch::Routing::UrlFor
336
+ include Routes.url_helpers
337
+ include WillPaginate::ActionView
338
+ }.new
339
+ helper.default_url_options.update \
340
+ :only_path => true,
341
+ :controller => 'dummy'
342
+
343
+ collection = WillPaginate::Collection.new(2, 1, 3)
344
+ @render_output = helper.will_paginate(collection)
345
+
346
+ assert_select 'a[href]', 4 do |links|
347
+ urls = links.map {|l| l['href'] }.uniq
348
+ urls.should == ['/dummy/page/1', '/dummy/page/3']
349
+ end
350
+ end
351
+
352
+ private
353
+
354
+ def translation(data)
355
+ I18n.available_locales # triggers loading existing translations
356
+ I18n.backend.store_translations(:en, data)
357
+ end
358
+ end
359
+
360
+ class AdditionalLinkAttributesRenderer < WillPaginate::ActionView::LinkRenderer
361
+ def initialize(link_attributes = nil)
362
+ super()
363
+ @additional_link_attributes = link_attributes || { :default => 'true' }
364
+ end
365
+
366
+ def link(text, target, attributes = {})
367
+ super(text, target, attributes.merge(@additional_link_attributes))
368
+ end
369
+ end
370
+
371
+ class DummyController
372
+ attr_reader :request
373
+ attr_accessor :controller_name
374
+
375
+ include ActionController::UrlFor
376
+ include Routes.url_helpers
377
+
378
+ def initialize
379
+ @request = DummyRequest.new
380
+ end
381
+
382
+ def params
383
+ @request.params
384
+ end
385
+
386
+ def env
387
+ {}
388
+ end
389
+
390
+ def _prefixes
391
+ []
392
+ end
393
+ end
394
+
395
+ class IbocorpController < DummyController
396
+ end
397
+
398
+ class DummyRequest
399
+ attr_accessor :symbolized_path_parameters
400
+ alias :path_parameters :symbolized_path_parameters
401
+
402
+ def initialize
403
+ @get = true
404
+ @params = {}
405
+ @symbolized_path_parameters = { :controller => 'foo', :action => 'bar' }
406
+ end
407
+
408
+ def get?
409
+ @get
410
+ end
411
+
412
+ def post
413
+ @get = false
414
+ end
415
+
416
+ def relative_url_root
417
+ ''
418
+ end
419
+
420
+ def script_name
421
+ ''
422
+ end
423
+
424
+ def params(more = nil)
425
+ @params.update(more) if more
426
+ @params
427
+ end
428
+
429
+ def host_with_port
430
+ 'example.com'
431
+ end
432
+ alias host host_with_port
433
+
434
+ def optional_port
435
+ ''
436
+ end
437
+
438
+ def protocol
439
+ 'http:'
440
+ end
441
+ end
@@ -0,0 +1,142 @@
1
+ require 'spec_helper'
2
+ require 'will_paginate/view_helpers'
3
+ require 'will_paginate/array'
4
+ require 'active_support'
5
+ require 'active_support/core_ext/string/inflections'
6
+ require 'active_support/inflections'
7
+
8
+ describe WillPaginate::ViewHelpers do
9
+
10
+ before(:all) do
11
+ # make sure default translations aren't loaded
12
+ I18n.load_path.clear
13
+ end
14
+
15
+ before(:each) do
16
+ I18n.reload!
17
+ end
18
+
19
+ include WillPaginate::ViewHelpers
20
+
21
+ describe "will_paginate" do
22
+ it "should render" do
23
+ collection = WillPaginate::Collection.new(1, 2, 4)
24
+ renderer = mock 'Renderer'
25
+ renderer.expects(:prepare).with(collection, instance_of(Hash), self)
26
+ renderer.expects(:to_html).returns('<PAGES>')
27
+
28
+ will_paginate(collection, :renderer => renderer).should == '<PAGES>'
29
+ end
30
+
31
+ it "should return nil for single-page collections" do
32
+ collection = mock 'Collection', :total_pages => 1
33
+ will_paginate(collection).should be_nil
34
+ end
35
+
36
+ it "should call html_safe on result" do
37
+ collection = WillPaginate::Collection.new(1, 2, 4)
38
+
39
+ html = mock 'HTML'
40
+ html.expects(:html_safe).returns(html)
41
+ renderer = mock 'Renderer'
42
+ renderer.stubs(:prepare)
43
+ renderer.expects(:to_html).returns(html)
44
+
45
+ will_paginate(collection, :renderer => renderer).should eql(html)
46
+ end
47
+ end
48
+
49
+ describe "pagination_options" do
50
+ let(:pagination_options) { WillPaginate::ViewHelpers.pagination_options }
51
+
52
+ it "deprecates setting :renderer" do
53
+ begin
54
+ lambda {
55
+ pagination_options[:renderer] = 'test'
56
+ }.should have_deprecation("pagination_options[:renderer] shouldn't be set")
57
+ ensure
58
+ pagination_options.delete :renderer
59
+ end
60
+ end
61
+ end
62
+
63
+ describe "page_entries_info" do
64
+ before :all do
65
+ @array = ('a'..'z').to_a
66
+ end
67
+
68
+ def info(params, options = {})
69
+ collection = Hash === params ? @array.paginate(params) : params
70
+ page_entries_info collection, {:html => false}.merge(options)
71
+ end
72
+
73
+ it "should display middle results and total count" do
74
+ info(:page => 2, :per_page => 5).should == "Displaying strings 6 - 10 of 26 in total"
75
+ end
76
+
77
+ it "uses translation if available" do
78
+ translation :will_paginate => {
79
+ :page_entries_info => {:multi_page => 'Showing %{from} - %{to}'}
80
+ }
81
+ info(:page => 2, :per_page => 5).should == "Showing 6 - 10"
82
+ end
83
+
84
+ it "uses specific translation if available" do
85
+ translation :will_paginate => {
86
+ :page_entries_info => {:multi_page => 'Showing %{from} - %{to}'},
87
+ :string => { :page_entries_info => {:multi_page => 'Strings %{from} to %{to}'} }
88
+ }
89
+ info(:page => 2, :per_page => 5).should == "Strings 6 to 10"
90
+ end
91
+
92
+ it "should output HTML by default" do
93
+ info({ :page => 2, :per_page => 5 }, :html => true).should ==
94
+ "Displaying strings <b>6&nbsp;-&nbsp;10</b> of <b>26</b> in total"
95
+ end
96
+
97
+ it "should display shortened end results" do
98
+ info(:page => 7, :per_page => 4).should include_phrase('strings 25 - 26')
99
+ end
100
+
101
+ it "should handle longer class names" do
102
+ collection = @array.paginate(:page => 2, :per_page => 5)
103
+ model = stub('Class', :name => 'ProjectType', :to_s => 'ProjectType')
104
+ collection.first.stubs(:class).returns(model)
105
+ info(collection).should include_phrase('project types')
106
+ end
107
+
108
+ it "should adjust output for single-page collections" do
109
+ info(('a'..'d').to_a.paginate(:page => 1, :per_page => 5)).should == "Displaying all 4 strings"
110
+ info(['a'].paginate(:page => 1, :per_page => 5)).should == "Displaying 1 string"
111
+ end
112
+
113
+ it "should display 'no entries found' for empty collections" do
114
+ info([].paginate(:page => 1, :per_page => 5)).should == "No entries found"
115
+ end
116
+
117
+ it "uses model_name.human when available" do
118
+ name = stub('model name', :i18n_key => :flower_key)
119
+ name.expects(:human).with(:count => 1).returns('flower')
120
+ model = stub('Class', :model_name => name)
121
+ collection = [1].paginate(:page => 1)
122
+
123
+ info(collection, :model => model).should == "Displaying 1 flower"
124
+ end
125
+
126
+ it "uses custom translation instead of model_name.human" do
127
+ name = stub('model name', :i18n_key => :flower_key)
128
+ name.expects(:human).never
129
+ model = stub('Class', :model_name => name)
130
+ translation :will_paginate => {:models => {:flower_key => 'tulip'}}
131
+ collection = [1].paginate(:page => 1)
132
+
133
+ info(collection, :model => model).should == "Displaying 1 tulip"
134
+ end
135
+
136
+ private
137
+
138
+ def translation(data)
139
+ I18n.backend.store_translations(:en, data)
140
+ end
141
+ end
142
+ end