middleman-blog 3.0.0 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. data/CHANGELOG.md +18 -0
  2. data/Gemfile +1 -0
  3. data/features/article_dirs.feature +7 -0
  4. data/features/blog_sources.feature +5 -0
  5. data/features/calendar-and-tag.feature +56 -0
  6. data/features/calendar.feature +71 -12
  7. data/features/future_date.feature +36 -0
  8. data/features/layouts.feature +21 -0
  9. data/features/no_date.feature +9 -0
  10. data/features/paginate.feature +157 -0
  11. data/features/published.feature +33 -0
  12. data/features/support/time_steps.rb +24 -0
  13. data/features/tags.feature +15 -2
  14. data/fixtures/article-dirs-app/config.rb +3 -0
  15. data/fixtures/article-dirs-app/source/blog/2011-01-01-new-article.html.markdown +6 -0
  16. data/fixtures/article-dirs-app/source/blog/2011-01-01-new-article/example.txt +1 -0
  17. data/fixtures/article-dirs-app/source/index.html.erb +9 -0
  18. data/fixtures/article-dirs-app/source/layout.erb +13 -0
  19. data/fixtures/calendar-and-tag-app/config-directory-indexes.rb +10 -0
  20. data/fixtures/calendar-and-tag-app/config.rb +7 -0
  21. data/fixtures/calendar-and-tag-app/source/archive.html.erb +17 -0
  22. data/fixtures/calendar-and-tag-app/source/blog/2011-01-01-new-article.html.markdown +7 -0
  23. data/fixtures/calendar-and-tag-app/source/blog/2011-01-02-another-article.html.markdown +8 -0
  24. data/fixtures/calendar-and-tag-app/source/index.html.erb +7 -0
  25. data/fixtures/calendar-and-tag-app/source/layout.erb +15 -0
  26. data/fixtures/calendar-app/config-directory-indexes.rb +10 -0
  27. data/fixtures/calendar-app/config.rb +3 -1
  28. data/fixtures/calendar-app/source/calendar.html.erb +4 -4
  29. data/fixtures/calendar-app/source/index.html.erb +4 -0
  30. data/fixtures/filename-date-app/config.rb +3 -1
  31. data/fixtures/filename-date-app/source/layout.erb +1 -1
  32. data/fixtures/future-date-app/config.rb +5 -0
  33. data/fixtures/future-date-app/source/_article_template.erb +1 -0
  34. data/fixtures/future-date-app/source/blog/2011-01-01-new-article.html.markdown +6 -0
  35. data/fixtures/future-date-app/source/blog/2012-06-19-future-article.html.markdown +6 -0
  36. data/fixtures/future-date-app/source/blog/2012-06-19-future-article/example.txt +1 -0
  37. data/fixtures/future-date-app/source/index.html.erb +9 -0
  38. data/fixtures/future-date-app/source/layout.erb +13 -0
  39. data/fixtures/layouts-app/config.rb +5 -0
  40. data/fixtures/layouts-app/source/2011/01/01/first-article.html.markdown +7 -0
  41. data/fixtures/layouts-app/source/2011/01/01/second-article.html.markdown +7 -0
  42. data/fixtures/layouts-app/source/2011/01/01/third-article.html.markdown +7 -0
  43. data/fixtures/layouts-app/source/2011/01/02/article-in-normal-layout.markdown +6 -0
  44. data/fixtures/layouts-app/source/2011/01/03/article-without-layout.markdown +7 -0
  45. data/fixtures/layouts-app/source/layouts/first.html.erb +2 -0
  46. data/fixtures/layouts-app/source/layouts/layout.erb +9 -0
  47. data/fixtures/layouts-app/source/layouts/second.html.erb +2 -0
  48. data/fixtures/layouts-app/source/layouts/third.html.erb +2 -0
  49. data/fixtures/no-date-app/config.rb +3 -0
  50. data/fixtures/no-date-app/source/_article_template.erb +1 -0
  51. data/fixtures/no-date-app/source/blog/new-article.html.markdown +6 -0
  52. data/fixtures/no-date-app/source/index.html.erb +9 -0
  53. data/fixtures/no-date-app/source/layout.erb +13 -0
  54. data/fixtures/no-day-app/config.rb +3 -0
  55. data/fixtures/no-day-app/source/2012-08-testing.html.markdown +6 -0
  56. data/fixtures/no-day-app/source/layout.erb +13 -0
  57. data/fixtures/paginate-app/config-directory-indexes.rb +11 -0
  58. data/fixtures/paginate-app/config-paginate-off.rb +8 -0
  59. data/fixtures/paginate-app/config.rb +8 -0
  60. data/fixtures/paginate-app/source/blog/2011-01-01-test-article.html.markdown +6 -0
  61. data/fixtures/paginate-app/source/blog/2011-01-02-test-article.html.markdown +6 -0
  62. data/fixtures/paginate-app/source/blog/2011-01-03-test-article.html.markdown +6 -0
  63. data/fixtures/paginate-app/source/blog/2011-01-04-test-article.html.markdown +6 -0
  64. data/fixtures/paginate-app/source/blog/2011-01-05-test-article.html.markdown +6 -0
  65. data/fixtures/paginate-app/source/blog/2011-02-01-test-article.html.markdown +6 -0
  66. data/fixtures/paginate-app/source/blog/2011-02-02-test-article.html.markdown +6 -0
  67. data/fixtures/paginate-app/source/calendar.html.erb +28 -0
  68. data/fixtures/paginate-app/source/index.html.erb +19 -0
  69. data/fixtures/paginate-app/source/layout.erb +15 -0
  70. data/fixtures/paginate-app/source/tag.html.erb +23 -0
  71. data/fixtures/published-app/config.rb +3 -0
  72. data/fixtures/published-app/source/_article_template.erb +1 -0
  73. data/fixtures/published-app/source/blog/2011-01-01-new-article.html.markdown +6 -0
  74. data/fixtures/published-app/source/blog/2012-06-19-draft-article.html.markdown +7 -0
  75. data/fixtures/published-app/source/blog/2012-06-19-draft-article/example.txt +1 -0
  76. data/fixtures/published-app/source/index.html.erb +9 -0
  77. data/fixtures/published-app/source/layout.erb +13 -0
  78. data/fixtures/tags-app/config-directory-indexes.rb +9 -0
  79. data/fixtures/tags-app/source/index.html.erb +2 -0
  80. data/fixtures/tags-app/source/tag.html.erb +3 -3
  81. data/lib/middleman-blog/blog_article.rb +59 -21
  82. data/lib/middleman-blog/blog_data.rb +59 -7
  83. data/lib/middleman-blog/calendar_pages.rb +50 -3
  84. data/lib/middleman-blog/extension.rb +37 -8
  85. data/lib/middleman-blog/paginator.rb +127 -0
  86. data/lib/middleman-blog/tag_pages.rb +20 -1
  87. data/lib/middleman-blog/template/config.tt +8 -1
  88. data/lib/middleman-blog/template/source/calendar.html.erb +27 -9
  89. data/lib/middleman-blog/template/source/index.html.erb +21 -7
  90. data/lib/middleman-blog/template/source/tag.html.erb +22 -4
  91. data/lib/middleman-blog/version.rb +1 -1
  92. data/middleman-blog.gemspec +3 -2
  93. metadata +211 -55
@@ -4,6 +4,31 @@ module Middleman
4
4
  # A sitemap plugin that adds month/day/year pages to the sitemap
5
5
  # based on the dates of blog articles.
6
6
  class CalendarPages
7
+ class << self
8
+ # Get a path to the given calendar page, based on the :year_link, :month_link or :day_link setting.
9
+ # @param [Middleman::Application] app
10
+ # @param [Number] year
11
+ # @param [Number] month
12
+ # @param [Number] day
13
+ # @return [String]
14
+ def link(app, year, month=nil, day=nil)
15
+ path = if day
16
+ app.blog.options.day_link.
17
+ sub(':year', year.to_s).
18
+ sub(':month', month.to_s.rjust(2,'0')).
19
+ sub(':day', day.to_s.rjust(2,'0'))
20
+ elsif month
21
+ app.blog.options.month_link.
22
+ sub(':year', year.to_s).
23
+ sub(':month', month.to_s.rjust(2,'0'))
24
+ else
25
+ app.blog.options.year_link.
26
+ sub(':year', year.to_s)
27
+ end
28
+ ::Middleman::Util.normalize_path(path)
29
+ end
30
+ end
31
+
7
32
  def initialize(app)
8
33
  @app = app
9
34
  end
@@ -17,7 +42,7 @@ module Middleman
17
42
  if @app.blog.options.year_template
18
43
  @app.ignore @app.blog.options.year_template
19
44
 
20
- path = Middleman::Util.normalize_path(@app.blog_year_path(year))
45
+ path = CalendarPages.link(@app, year)
21
46
 
22
47
  p = ::Middleman::Sitemap::Resource.new(
23
48
  @app.sitemap,
@@ -25,6 +50,14 @@ module Middleman
25
50
  )
26
51
  p.proxy_to(@app.blog.options.year_template)
27
52
 
53
+ # Add metadata in local variables so it's accessible to
54
+ # later extensions
55
+ p.add_metadata :locals => {
56
+ 'page_type' => 'year',
57
+ 'year' => year,
58
+ 'articles' => year_articles
59
+ }
60
+ # Add metadata in instance variables for backwards compatibility
28
61
  p.add_metadata do
29
62
  @year = year
30
63
  @articles = year_articles
@@ -37,7 +70,7 @@ module Middleman
37
70
  if @app.blog.options.month_template
38
71
  @app.ignore @app.blog.options.month_template
39
72
 
40
- path = Middleman::Util.normalize_path(@app.blog_month_path(year, month))
73
+ path = CalendarPages.link(@app, year, month)
41
74
 
42
75
  p = ::Middleman::Sitemap::Resource.new(
43
76
  @app.sitemap,
@@ -45,6 +78,12 @@ module Middleman
45
78
  )
46
79
  p.proxy_to(@app.blog.options.month_template)
47
80
 
81
+ p.add_metadata :locals => {
82
+ 'page_type' => 'month',
83
+ 'year' => year,
84
+ 'month' => month,
85
+ 'articles' => month_articles
86
+ }
48
87
  p.add_metadata do
49
88
  @year = year
50
89
  @month = month
@@ -58,13 +97,21 @@ module Middleman
58
97
  if @app.blog.options.day_template
59
98
  @app.ignore @app.blog.options.day_template
60
99
 
61
- path = Middleman::Util.normalize_path(@app.blog_day_path(year, month, day))
100
+ path = CalendarPages.link(@app, year, month, day)
101
+
62
102
  p = ::Middleman::Sitemap::Resource.new(
63
103
  @app.sitemap,
64
104
  path
65
105
  )
66
106
  p.proxy_to(@app.blog.options.day_template)
67
107
 
108
+ p.add_metadata :locals => {
109
+ 'page_type' => 'day',
110
+ 'year' => year,
111
+ 'month' => month,
112
+ 'day' => day,
113
+ 'articles' => day_articles
114
+ }
68
115
  p.add_metadata do
69
116
  @year = year
70
117
  @month = month
@@ -9,6 +9,7 @@ module Middleman
9
9
  :layout,
10
10
  :summary_separator,
11
11
  :summary_length,
12
+ :summary_generator,
12
13
  :year_link,
13
14
  :month_link,
14
15
  :day_link,
@@ -17,7 +18,10 @@ module Middleman
17
18
  :year_template,
18
19
  :month_template,
19
20
  :day_template,
20
- :tag_template
21
+ :tag_template,
22
+ :paginate,
23
+ :per_page,
24
+ :page_link
21
25
  ]
22
26
 
23
27
  KEYS.each do |name|
@@ -51,6 +55,9 @@ module Middleman
51
55
  options.month_link ||= "/:year/:month.html"
52
56
  options.day_link ||= "/:year/:month/:day.html"
53
57
  options.default_extension ||= ".markdown"
58
+ options.paginate ||= false
59
+ options.per_page ||= 10
60
+ options.page_link ||= "page/:num"
54
61
 
55
62
  # optional: :tag_template
56
63
  # optional: :year_template
@@ -106,6 +113,15 @@ module Middleman
106
113
  false
107
114
  )
108
115
  end
116
+
117
+ if options.paginate
118
+ require 'middleman-blog/paginator'
119
+ sitemap.register_resource_list_manipulator(
120
+ :blog_paginate,
121
+ Paginator.new(self),
122
+ false
123
+ )
124
+ end
109
125
  end
110
126
  end
111
127
  alias :included :registered
@@ -136,14 +152,14 @@ module Middleman
136
152
  # @param [String] tag
137
153
  # @return [String]
138
154
  def tag_path(tag)
139
- blog.options.taglink.sub(':tag', tag.parameterize)
155
+ sitemap.find_resource_by_path(TagPages.link(self, tag)).try(:url)
140
156
  end
141
157
 
142
158
  # Get a path to the given year-based calendar page, based on the :year_link setting.
143
159
  # @param [Number] year
144
160
  # @return [String]
145
161
  def blog_year_path(year)
146
- blog.options.year_link.sub(':year', year.to_s)
162
+ sitemap.find_resource_by_path(CalendarPages.link(self, year)).try(:url)
147
163
  end
148
164
 
149
165
  # Get a path to the given month-based calendar page, based on the :month_link setting.
@@ -151,8 +167,7 @@ module Middleman
151
167
  # @param [Number] month
152
168
  # @return [String]
153
169
  def blog_month_path(year, month)
154
- blog.options.month_link.sub(':year', year.to_s).
155
- sub(':month', month.to_s.rjust(2,'0'))
170
+ sitemap.find_resource_by_path(CalendarPages.link(self, year, month)).try(:url)
156
171
  end
157
172
 
158
173
  # Get a path to the given day-based calendar page, based on the :day_link setting.
@@ -161,9 +176,23 @@ module Middleman
161
176
  # @param [Number] day
162
177
  # @return [String]
163
178
  def blog_day_path(year, month, day)
164
- blog.options.day_link.sub(':year', year.to_s).
165
- sub(':month', month.to_s.rjust(2,'0')).
166
- sub(':day', day.to_s.rjust(2,'0'))
179
+ sitemap.find_resource_by_path(CalendarPages.link(self, year, month, day)).try(:url)
180
+ end
181
+
182
+
183
+ # Pagination Helpers
184
+ # These are used by the template if pagination is off, to allow a single template to work
185
+ # in both modes. They get overridden by the local variables if the paginator is active.
186
+
187
+ # Returns true if pagination is turned on for this template; false otherwise.
188
+ # @return [Boolean]
189
+ def paginate; false; end
190
+
191
+ # Returns the list of articles to display on this page.
192
+ # @return [Array<Middleman::Sitemap::Resource>]
193
+ def page_articles
194
+ limit = (current_resource.metadata[:page]["per_page"] || 0) - 1
195
+ blog.articles[0..limit]
167
196
  end
168
197
  end
169
198
  end
@@ -0,0 +1,127 @@
1
+ module Middleman
2
+ module Blog
3
+
4
+ # A sitemap plugin that splits indexes (including tag
5
+ # and calendar indexes) over multiple pages
6
+ class Paginator
7
+ def initialize(app)
8
+ @app = app
9
+ end
10
+
11
+ # Substitute the page number into the resource URL.
12
+ # @return [String]
13
+ def page_sub(res, num, page_link)
14
+ if num == 1
15
+ # First page has an unmodified URL.
16
+ res.path
17
+ else
18
+ page_url = page_link.sub(":num", num.to_s)
19
+ index_re = %r{(^|/)#{Regexp.escape(@app.index_file)}$}
20
+ if res.path =~ index_re
21
+ res.path.sub(index_re, "\\1#{page_url}/#{@app.index_file}")
22
+ else
23
+ res.path.sub(%r{(^|/)([^/]*)\.([^/]*)$}, "\\1\\2/#{page_url}.\\3")
24
+ end
25
+ end
26
+ end
27
+
28
+ # Update the main sitemap resource list
29
+ # @return [void]
30
+ def manipulate_resource_list(resources)
31
+ new_resources = []
32
+
33
+ resources.each do |res|
34
+ next if res.ignored?
35
+
36
+ md = res.metadata
37
+ if md[:page]["pageable"]
38
+ # "articles" local variable is populated by Calendar and Tag page generators
39
+ # If it's not set then use the complete list of articles
40
+ # TODO: Some way to allow the frontmatter to specify the article filter?
41
+ articles = md[:locals]["articles"] || @app.blog.articles
42
+
43
+ # Allow blog.per_page and blog.page_link to be overridden in the frontmatter
44
+ per_page = md[:page]["per_page"] || @app.blog.options.per_page
45
+ page_link = md[:page]["page_link"] || @app.blog.options.page_link
46
+
47
+ num_pages = (articles.length / per_page.to_f).ceil
48
+
49
+ # Add the pagination metadata to the base page (page 1)
50
+ res.add_metadata :locals => {
51
+ # Set a flag to allow templates to be used with and without pagination
52
+ 'paginate' => true,
53
+
54
+ # Include the numbers, useful for displaying "Page X of Y"
55
+ 'page_number' => 1,
56
+ 'num_pages' => num_pages,
57
+ 'per_page' => per_page,
58
+
59
+ # The range of article numbers on this page
60
+ # (1-based, for showing "Items X to Y of Z")
61
+ 'page_start' => 1,
62
+ 'page_end' => [per_page, articles.length].min,
63
+
64
+ # These contain the next and previous page.
65
+ # They are set to nil if there are no more pages.
66
+ # The nils are overwritten when the later pages are generated, below.
67
+ 'next_page' => nil,
68
+ 'prev_page' => nil,
69
+
70
+ # The list of articles for this page.
71
+ 'page_articles' => articles[0..per_page-1],
72
+
73
+ # Include the articles so that non-proxied pages can use "articles" instead
74
+ # of "blog.articles" for consistency with the calendar and tag templates.
75
+ 'articles' => articles
76
+ }
77
+
78
+ prev_page_res = res
79
+
80
+ # Create additional resources for the 2nd and subsequent pages.
81
+ (2..num_pages).each do |num|
82
+ p = ::Middleman::Sitemap::Resource.new(
83
+ @app.sitemap,
84
+ page_sub(res, num, page_link),
85
+ res.source_file
86
+ )
87
+
88
+ # Copy the metadata from the base page.
89
+ p.add_metadata md
90
+
91
+ # Copy the proxy state from the base page.
92
+ p.proxy_to(res.proxied_to) if res.proxy?
93
+
94
+ page_start = (num-1)*per_page
95
+ page_end = (num*per_page)-1
96
+
97
+ # Add pagination metadata, meanings as above.
98
+ p.add_metadata :locals => {
99
+ 'paginate' => true,
100
+ 'page_number' => num,
101
+ 'num_pages' => num_pages,
102
+ 'per_page' => per_page,
103
+ 'page_start' => page_start+1,
104
+ 'page_end' => [page_end+1, articles.length].min,
105
+ 'next_page' => nil,
106
+ 'prev_page' => prev_page_res,
107
+ 'page_articles' => articles[page_start..page_end],
108
+ 'articles' => articles
109
+ }
110
+
111
+ # Add a reference in the previous page to this page
112
+ prev_page_res.add_metadata :locals => {
113
+ 'next_page' => p
114
+ }
115
+
116
+ prev_page_res = p
117
+
118
+ new_resources << p
119
+ end
120
+ end
121
+ end
122
+
123
+ resources + new_resources
124
+ end
125
+ end
126
+ end
127
+ end
@@ -4,6 +4,17 @@ module Middleman
4
4
  # A sitemap plugin that adds tag pages to the sitemap
5
5
  # based on the tags of blog articles.
6
6
  class TagPages
7
+ class << self
8
+ # Get a path to the given tag, based on the :taglink setting.
9
+ # @param [Middleman::Application] app
10
+ # @param [String] tag
11
+ # @return [String]
12
+ def link(app, tag)
13
+ ::Middleman::Util.normalize_path(
14
+ app.blog.options.taglink.sub(':tag', tag.parameterize))
15
+ end
16
+ end
17
+
7
18
  def initialize(app)
8
19
  @app = app
9
20
  end
@@ -12,7 +23,7 @@ module Middleman
12
23
  # @return [void]
13
24
  def manipulate_resource_list(resources)
14
25
  resources + @app.blog.tags.map do |tag, articles|
15
- path = Middleman::Util.normalize_path(@app.tag_path(tag))
26
+ path = TagPages.link(@app, tag)
16
27
 
17
28
  p = ::Middleman::Sitemap::Resource.new(
18
29
  @app.sitemap,
@@ -20,6 +31,14 @@ module Middleman
20
31
  )
21
32
  p.proxy_to(@app.blog.options.tag_template)
22
33
 
34
+ # Add metadata in local variables so it's accessible to
35
+ # later extensions
36
+ p.add_metadata :locals => {
37
+ 'page_type' => 'tag',
38
+ 'tagname' => tag,
39
+ 'articles' => articles
40
+ }
41
+ # Add metadata in instance variables for backwards compatibility
23
42
  p.add_metadata do
24
43
  @tag = tag
25
44
  @articles = articles
@@ -2,7 +2,10 @@
2
2
  # Blog settings
3
3
  ###
4
4
 
5
+ # Time.zone = "UTC"
6
+
5
7
  activate :blog do |blog|
8
+ # blog.prefix = "blog"
6
9
  # blog.permalink = ":year/:month/:day/:title.html"
7
10
  # blog.sources = ":year-:month-:day-:title.html"
8
11
  # blog.taglink = "tags/:tag.html"
@@ -16,6 +19,10 @@ activate :blog do |blog|
16
19
 
17
20
  blog.tag_template = "tag.html"
18
21
  blog.calendar_template = "calendar.html"
22
+
23
+ # blog.paginate = true
24
+ # blog.per_page = 10
25
+ # blog.page_link = "page/:num"
19
26
  end
20
27
 
21
28
  page "/feed.xml", :layout => false
@@ -25,7 +32,7 @@ page "/feed.xml", :layout => false
25
32
  ###
26
33
 
27
34
  # Susy grids in Compass
28
- # First: gem install compass-susy-plugin
35
+ # First: gem install susy
29
36
  # require 'susy'
30
37
 
31
38
  # Change Compass configuration
@@ -1,15 +1,33 @@
1
+ ---
2
+ pageable: true
3
+ ---
1
4
  <h1>Archive for
2
- <% if @day %>
3
- <%= Date.new(@year, @month, @day).strftime('%b %e %Y') %>
4
- <% elsif @month %>
5
- <%= Date.new(@year, @month, 1).strftime('%b %Y') %>
6
- <% else %>
7
- <%= @year %>
5
+ <% case page_type
6
+ when 'day' %>
7
+ <%= Date.new(year, month, day).strftime('%b %e %Y') %>
8
+ <% when 'month' %>
9
+ <%= Date.new(year, month, 1).strftime('%b %Y') %>
10
+ <% when 'year' %>
11
+ <%= year %>
8
12
  <% end %>
9
13
  </h1>
10
14
 
15
+ <% if paginate %>
16
+ <p>Page <%= page_number %> of <%= num_pages %></p>
17
+
18
+ <% if prev_page %>
19
+ <p><%= link_to 'Previous page', prev_page %></p>
20
+ <% end %>
21
+ <% end %>
22
+
11
23
  <ul>
12
- <% @articles.each_with_index do |article, i| %>
13
- <li><a href="<%= article.url %>"><%= article.title %></a> <span><%= article.date.strftime('%b %e') %></span></li>
14
- <% end %>
24
+ <% page_articles.each_with_index do |article, i| %>
25
+ <li><%= link_to article.title, article %> <span><%= article.date.strftime('%b %e') %></span></li>
26
+ <% end %>
15
27
  </ul>
28
+
29
+ <% if paginate %>
30
+ <% if next_page %>
31
+ <p><%= link_to 'Next page', next_page %></p>
32
+ <% end %>
33
+ <% end %>
@@ -1,8 +1,22 @@
1
- <ul>
2
- <% blog.articles[0...10].each do |article| %>
3
- <li>
4
- <h2><a href="<%= article.url %>"><%= article.title %></a> <span><%= article.date.strftime('%b %e') %></span></h2>
5
- <p><%= article.summary %></p>
6
- </li>
1
+ ---
2
+ pageable: true
3
+ per_page: 10
4
+ ---
5
+ <% if paginate %>
6
+ <p>Page <%= page_number %> of <%= num_pages %></p>
7
+
8
+ <% if prev_page %>
9
+ <p><%= link_to 'Previous page', prev_page %></p>
7
10
  <% end %>
8
- </ul>
11
+ <% end %>
12
+
13
+ <% page_articles.each_with_index do |article, i| %>
14
+ <h2><%= link_to article.title, article %> <span><%= article.date.strftime('%b %e') %></span></h2>
15
+ <p><%= article.summary %></p>
16
+ <% end %>
17
+
18
+ <% if paginate %>
19
+ <% if next_page %>
20
+ <p><%= link_to 'Next page', next_page %></p>
21
+ <% end %>
22
+ <% end %>