middleman-blog 3.4.1 → 3.5.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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +0 -6
  3. data/CHANGELOG.md +32 -1
  4. data/Gemfile +5 -9
  5. data/README.md +3 -2
  6. data/features/article_cli.feature +10 -1
  7. data/features/custom_collections.feature +11 -0
  8. data/features/language.feature +82 -0
  9. data/features/multiblog.feature +4 -2
  10. data/features/permalink-data.feature +12 -0
  11. data/features/summary.feature +13 -0
  12. data/features/support/env.rb +3 -0
  13. data/features/tags.feature +18 -1
  14. data/features/time_zone.feature +1 -1
  15. data/fixtures/custom-article-template-app/config.rb +3 -0
  16. data/fixtures/custom-article-template-app/my_custom_article.tt +7 -0
  17. data/fixtures/custom-article-template-app/source/index.html.erb +9 -0
  18. data/fixtures/custom-article-template-app/source/layout.erb +30 -0
  19. data/fixtures/custom-collections-sources-app/config.rb +11 -0
  20. data/fixtures/custom-collections-sources-app/source/articles/2011-01-02-another-article.html.markdown +8 -0
  21. data/fixtures/custom-collections-sources-app/source/category.html.erb +7 -0
  22. data/fixtures/custom-collections-sources-app/source/index.html.erb +5 -0
  23. data/fixtures/custom-collections-sources-app/source/layout.erb +13 -0
  24. data/fixtures/custom-collections-sources-app/source/news/2011-01-01-new-article.html.markdown +7 -0
  25. data/fixtures/language-app/config.rb +2 -0
  26. data/fixtures/language-app/locales/en.yml +4 -0
  27. data/fixtures/language-app/locales/ru.yml +4 -0
  28. data/fixtures/language-app/source/2013-09-07-english-article-with-lang-in-frontmatter.html.erb +6 -0
  29. data/fixtures/language-app/source/2013-09-07-russian-article-with-lang-in-frontmatter.html.erb +6 -0
  30. data/fixtures/language-app/source/en/2013-09-07-english-article-with-lang-in-path.html.erb +5 -0
  31. data/fixtures/language-app/source/layouts/layout.erb +8 -0
  32. data/fixtures/language-app/source/localizable/index.html.erb +5 -0
  33. data/fixtures/language-app/source/ru/2013-09-07-russian-article-with-lang-in-path.html.erb +5 -0
  34. data/fixtures/multiblog-app/source/blog1/index.html.erb +7 -0
  35. data/fixtures/permalink-data-app/config.rb +5 -0
  36. data/fixtures/permalink-data-app/source/index.html.erb +3 -0
  37. data/fixtures/permalink-data-app/source/layout.erb +14 -0
  38. data/fixtures/permalink-data-app/source/news/2011-01-01-new-article.html.markdown +7 -0
  39. data/fixtures/time-zone-app/source/blog/2013-06-24-hello.html.erb +1 -0
  40. data/lib/middleman-blog.rb +3 -8
  41. data/lib/middleman-blog/blog_article.rb +96 -60
  42. data/lib/middleman-blog/blog_data.rb +78 -76
  43. data/lib/middleman-blog/calendar_pages.rb +87 -119
  44. data/lib/middleman-blog/commands/article.rb +20 -14
  45. data/lib/middleman-blog/custom_pages.rb +30 -64
  46. data/lib/middleman-blog/extension.rb +175 -0
  47. data/lib/middleman-blog/helpers.rb +152 -0
  48. data/lib/middleman-blog/paginator.rb +127 -123
  49. data/lib/middleman-blog/tag_pages.rb +27 -45
  50. data/lib/middleman-blog/template.rb +17 -15
  51. data/lib/middleman-blog/template/config.tt +30 -33
  52. data/lib/middleman-blog/template/source/layout.erb +1 -0
  53. data/lib/middleman-blog/uri_templates.rb +58 -0
  54. data/lib/middleman-blog/version.rb +1 -1
  55. data/middleman-blog.gemspec +4 -1
  56. metadata +75 -9
  57. data/Gemfile-3.0 +0 -27
  58. data/lib/middleman-blog/extension_3_0.rb +0 -248
  59. data/lib/middleman-blog/extension_3_1.rb +0 -278
data/Gemfile-3.0 DELETED
@@ -1,27 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- # Specify your gem's dependencies in middleman-blog.gemspec
4
- gemspec
5
-
6
- gem "middleman", "~> 3.0.0"
7
-
8
- gem "rake", "~> 10.0.3", :require => false
9
- gem "yard", "~> 0.8.0", :require => false
10
-
11
- # Test tools
12
- gem "cucumber", "~> 1.3.1"
13
- gem "fivemat"
14
- gem "aruba", "~> 0.5.1"
15
- gem "rspec", "~> 2.12"
16
- gem "simplecov"
17
-
18
- gem "timecop", "~> 0.4.0"
19
- gem "nokogiri", "~> 1.5.0" # 1.6.0 requires Ruby 1.9+ but we still test on 1.8
20
- gem "kramdown"
21
-
22
- # Code Quality
23
- gem "cane", :platforms => [:mri_19, :mri_20], :require => false
24
-
25
- platforms :ruby do
26
- gem "redcarpet", "~> 2.3.0"
27
- end
@@ -1,248 +0,0 @@
1
- module Middleman
2
- module Blog
3
- class Options
4
- KEYS = [
5
- :prefix,
6
- :permalink,
7
- :sources,
8
- :taglink,
9
- :layout,
10
- :summary_separator,
11
- :summary_length,
12
- :summary_generator,
13
- :year_link,
14
- :month_link,
15
- :day_link,
16
- :default_extension,
17
- :calendar_template,
18
- :year_template,
19
- :month_template,
20
- :day_template,
21
- :tag_template,
22
- :paginate,
23
- :per_page,
24
- :page_link,
25
- :publish_future_dated,
26
- :custom_collections
27
- ]
28
-
29
- KEYS.each do |name|
30
- attr_accessor name
31
- end
32
-
33
- def initialize(options={})
34
- options.each do |k,v|
35
- self.send(:"#{k}=", v)
36
- end
37
- end
38
- end
39
-
40
- class << self
41
- def registered(app, options_hash={}, &block)
42
- require 'middleman-blog/blog_data'
43
- require 'middleman-blog/blog_article'
44
- require 'active_support/core_ext/time/zones'
45
-
46
- app.send :include, Helpers
47
-
48
- options = Options.new(options_hash)
49
- yield options if block_given?
50
-
51
- options.permalink ||= "/:year/:month/:day/:title.html"
52
- options.sources ||= ":year-:month-:day-:title.html"
53
- options.taglink ||= "tags/:tag.html"
54
- options.layout ||= "layout"
55
- options.summary_separator ||= /(READMORE)/
56
- options.summary_length ||= 250
57
- options.year_link ||= "/:year.html"
58
- options.month_link ||= "/:year/:month.html"
59
- options.day_link ||= "/:year/:month/:day.html"
60
- options.default_extension ||= ".markdown"
61
- options.paginate ||= false
62
- options.per_page ||= 10
63
- options.page_link ||= "page/:num"
64
- options.publish_future_dated ||= false
65
- options.custom_collections ||= {}
66
-
67
- # optional: :tag_template
68
- # optional: :year_template
69
- # optional: :month_template
70
- # optional: :day_template
71
- # Allow one setting to set all the calendar templates
72
- if options.calendar_template
73
- options.year_template ||= options.calendar_template
74
- options.month_template ||= options.calendar_template
75
- options.day_template ||= options.calendar_template
76
- end
77
-
78
- # If "prefix" option is specified, all other paths are relative to it.
79
- if options.prefix
80
- options.prefix = "/#{options.prefix}" unless options.prefix.start_with? '/'
81
- options.permalink = File.join(options.prefix, options.permalink)
82
- options.sources = File.join(options.prefix, options.sources)
83
- options.taglink = File.join(options.prefix, options.taglink)
84
- options.year_link = File.join(options.prefix, options.year_link)
85
- options.month_link = File.join(options.prefix, options.month_link)
86
- options.day_link = File.join(options.prefix, options.day_link)
87
-
88
- options.custom_collections.each do |key, opts|
89
- opts[:link] = File.join(options.prefix, opts[:link])
90
- end
91
- end
92
-
93
- app.after_configuration do
94
- # Make sure ActiveSupport's TimeZone stuff has something to work with,
95
- # allowing people to set their desired time zone via Time.zone or
96
- # set :time_zone
97
- Time.zone = self.time_zone if self.respond_to?(:time_zone)
98
- time_zone = Time.zone if Time.zone
99
- zone_default = Time.find_zone!(time_zone || 'UTC')
100
- unless zone_default
101
- raise 'Value assigned to time_zone not recognized.'
102
- end
103
- Time.zone_default = zone_default
104
-
105
- ignore(options.calendar_template) if options.calendar_template
106
- ignore(options.year_template) if options.year_template
107
- ignore(options.month_template) if options.month_template
108
- ignore(options.day_template) if options.day_template
109
-
110
- # Initialize blog with options
111
- blog(options)
112
-
113
- sitemap.register_resource_list_manipulator(
114
- :blog_articles,
115
- blog,
116
- false
117
- )
118
-
119
- if options.tag_template
120
- ignore options.tag_template
121
-
122
- require 'middleman-blog/tag_pages'
123
- sitemap.register_resource_list_manipulator(
124
- :blog_tags,
125
- TagPages.new(self),
126
- false
127
- )
128
- end
129
-
130
- if options.year_template ||
131
- options.month_template ||
132
- options.day_template
133
-
134
- require 'middleman-blog/calendar_pages'
135
- sitemap.register_resource_list_manipulator(
136
- :blog_calendar,
137
- CalendarPages.new(self),
138
- false
139
- )
140
- end
141
-
142
- if options.paginate
143
- require 'middleman-blog/paginator'
144
- sitemap.register_resource_list_manipulator(
145
- :blog_paginate,
146
- Paginator.new(self),
147
- false
148
- )
149
- end
150
-
151
- if options.custom_collections
152
- require 'middleman-blog/custom_pages'
153
- options.custom_collections.each do |property, options|
154
- ignore options[:template]
155
- sitemap.register_resource_list_manipulator(
156
- :"blog_#{property}",
157
- CustomPages.new(property, self),
158
- false
159
- )
160
-
161
- m = Module.new
162
- m.module_eval(%Q{
163
- def #{property}_path(value, key = nil)
164
- sitemap.find_resource_by_path(CustomPages.link(self.blog.options, :#{property}, value)).try(:url)
165
- end
166
- })
167
-
168
- self.class.send(:include, m)
169
- end
170
- end
171
- end
172
- end
173
- alias :included :registered
174
- end
175
-
176
- # Helpers for use within templates and layouts.
177
- module Helpers
178
- # Get the {BlogData} for this site.
179
- # @return [BlogData]
180
- def blog(options=nil)
181
- @_blog ||= BlogData.new(self, options)
182
- end
183
-
184
- # Determine whether the currently rendering template is a blog article.
185
- # This can be useful in layouts.
186
- # @return [Boolean]
187
- def is_blog_article?
188
- !current_article.nil?
189
- end
190
-
191
- # Get a {Resource} with mixed in {BlogArticle} methods representing the current article.
192
- # @return [Middleman::Sitemap::Resource]
193
- def current_article
194
- blog.article(current_resource.path)
195
- end
196
-
197
- # Get a path to the given tag, based on the :taglink setting.
198
- # @param [String] tag
199
- # @return [String]
200
- def tag_path(tag)
201
- sitemap.find_resource_by_path(TagPages.link(self.blog.options, tag)).try(:url)
202
- end
203
-
204
- # Get a path to the given year-based calendar page, based on the :year_link setting.
205
- # @param [Number] year
206
- # @return [String]
207
- def blog_year_path(year)
208
- sitemap.find_resource_by_path(CalendarPages.link(self.blog.options, year)).try(:url)
209
- end
210
-
211
- # Get a path to the given month-based calendar page, based on the :month_link setting.
212
- # @param [Number] year
213
- # @param [Number] month
214
- # @return [String]
215
- def blog_month_path(year, month)
216
- sitemap.find_resource_by_path(CalendarPages.link(self.blog.options, year, month)).try(:url)
217
- end
218
-
219
- # Get a path to the given day-based calendar page, based on the :day_link setting.
220
- # @param [Number] year
221
- # @param [Number] month
222
- # @param [Number] day
223
- # @return [String]
224
- def blog_day_path(year, month, day)
225
- sitemap.find_resource_by_path(CalendarPages.link(self.blog.options, year, month, day)).try(:url)
226
- end
227
-
228
-
229
- # Pagination Helpers
230
- # These are used by the template if pagination is off, to allow a single template to work
231
- # in both modes. They get overridden by the local variables if the paginator is active.
232
-
233
- # Returns true if pagination is turned on for this template; false otherwise.
234
- # @return [Boolean]
235
- def paginate; false; end
236
-
237
- # Returns the list of articles to display on this page.
238
- # @return [Array<Middleman::Sitemap::Resource>]
239
- def page_articles
240
- limit = (current_resource.metadata[:page]["per_page"] || 0) - 1
241
-
242
- # "articles" local variable is populated by Calendar and Tag page generators
243
- # If it's not set then use the complete list of articles
244
- (current_resource.metadata[:locals]["articles"] || blog.articles)[0..limit]
245
- end
246
- end
247
- end
248
- end
@@ -1,278 +0,0 @@
1
- module Middleman
2
- class BlogExtension < Extension
3
- self.supports_multiple_instances = true
4
-
5
- option :name, nil, 'Unique ID for telling multiple blogs apart'
6
- option :prefix, nil, 'Prefix to mount the blog at'
7
- option :permalink, "/:year/:month/:day/:title.html", 'HTTP path to host articles at'
8
- option :sources, ":year-:month-:day-:title.html", 'How to extract metadata from on-disk files'
9
- option :taglink, "tags/:tag.html", 'HTTP path to host tag pages at'
10
- option :layout, "layout", 'Article-specific layout'
11
- option :summary_separator, /(READMORE)/, 'How to split article summaries around a delimeter'
12
- option :summary_length, 250, 'Length of words in automatic summaries'
13
- option :summary_generator, nil, 'Block to definte how summaries are extracted'
14
- option :year_link, "/:year.html", 'HTTP path for yearly archives'
15
- option :month_link, "/:year/:month.html", 'HTTP path for monthly archives'
16
- option :day_link, "/:year/:month/:day.html", 'HTTP path for daily archives'
17
- option :default_extension, ".markdown", 'Default article extension'
18
- option :calendar_template, nil, 'Template for calendar pages'
19
- option :year_template, nil, 'Template for yearly archive pages'
20
- option :month_template, nil, 'Template for monthyl archive pages'
21
- option :day_template, nil, 'Template for daily archive pages'
22
- option :tag_template, nil, 'Template for tag archive pages'
23
- option :paginate, false, 'Whether to paginate pages'
24
- option :per_page, 10, 'Articles per page when paginating'
25
- option :page_link, "page/:num", 'HTTP path for paging'
26
- option :publish_future_dated, false, 'Whether to pubish articles dated in the future'
27
- option :custom_collections, {}, 'Hash of custom frontmatter properties to collect articles on and their options'
28
-
29
- attr_accessor :data, :uid
30
-
31
- def initialize(app, options_hash={}, &block)
32
- super
33
-
34
- @uid = options.name
35
-
36
- require 'middleman-blog/blog_data'
37
- require 'middleman-blog/blog_article'
38
- require 'active_support/core_ext/time/zones'
39
-
40
- # app.set :time_zone, 'UTC'
41
-
42
- # optional: :tag_template
43
- # optional: :year_template
44
- # optional: :month_template
45
- # optional: :day_template
46
- # Allow one setting to set all the calendar templates
47
- if options.calendar_template
48
- options.year_template ||= options.calendar_template
49
- options.month_template ||= options.calendar_template
50
- options.day_template ||= options.calendar_template
51
- end
52
-
53
- # If "prefix" option is specified, all other paths are relative to it.
54
- if options.prefix
55
- options.prefix = "/#{options.prefix}" unless options.prefix.start_with? '/'
56
- options.permalink = File.join(options.prefix, options.permalink)
57
- options.sources = File.join(options.prefix, options.sources)
58
- options.taglink = File.join(options.prefix, options.taglink)
59
- options.year_link = File.join(options.prefix, options.year_link)
60
- options.month_link = File.join(options.prefix, options.month_link)
61
- options.day_link = File.join(options.prefix, options.day_link)
62
-
63
- options.custom_collections.each do |key, opts|
64
- opts[:link] = File.join(options.prefix, opts[:link])
65
- end
66
- end
67
- end
68
-
69
- def after_configuration
70
- @uid ||= "blog#{@app.blog_instances.keys.length}"
71
-
72
- @app.ignore(options.calendar_template) if options.calendar_template
73
- @app.ignore(options.year_template) if options.year_template
74
- @app.ignore(options.month_template) if options.month_template
75
- @app.ignore(options.day_template) if options.day_template
76
-
77
- @app.blog_instances[@uid.to_sym] = self
78
-
79
- # Make sure ActiveSupport's TimeZone stuff has something to work with,
80
- # allowing people to set their desired time zone via Time.zone or
81
- # set :time_zone
82
- Time.zone = app.config[:time_zone] if app.config[:time_zone]
83
- time_zone = Time.zone if Time.zone
84
- zone_default = Time.find_zone!(time_zone || 'UTC')
85
- unless zone_default
86
- raise 'Value assigned to time_zone not recognized.'
87
- end
88
- Time.zone_default = zone_default
89
-
90
- # Initialize blog with options
91
-
92
- @data = ::Middleman::Blog::BlogData.new(@app, options, self)
93
-
94
- @app.sitemap.register_resource_list_manipulator(
95
- :"blog_#{uid}_articles",
96
- @data,
97
- false
98
- )
99
-
100
- if options.tag_template
101
- @app.ignore options.tag_template
102
-
103
- require 'middleman-blog/tag_pages'
104
- @app.sitemap.register_resource_list_manipulator(
105
- :"blog_#{uid}_tags",
106
- ::Middleman::Blog::TagPages.new(@app, self),
107
- false
108
- )
109
- end
110
-
111
- if options.year_template || options.month_template || options.day_template
112
- require 'middleman-blog/calendar_pages'
113
- @app.sitemap.register_resource_list_manipulator(
114
- :"blog_#{uid}_calendar",
115
- ::Middleman::Blog::CalendarPages.new(@app, self),
116
- false
117
- )
118
- end
119
-
120
- if options.paginate
121
- require 'middleman-blog/paginator'
122
- @app.sitemap.register_resource_list_manipulator(
123
- :"blog_#{uid}_paginate",
124
- ::Middleman::Blog::Paginator.new(@app, self),
125
- false
126
- )
127
- end
128
-
129
- if options.custom_collections
130
- require 'middleman-blog/custom_pages'
131
- register_custom_pages
132
- end
133
- end
134
-
135
- # Register any custom page collections that may be set in the config
136
- #
137
- # A custom resource list manipulator will be generated for each key in the
138
- # custom collections hash.
139
- #
140
- # The following will collect posts on the "category" frontmatter property:
141
- # ```
142
- # activate :blog do |blog|
143
- # blog.custom_collections = {
144
- # link: "/categories/:category.html",
145
- # template: "/category.html"
146
- # }
147
- # end
148
- # ```
149
- #
150
- # Category pages in the example above will use the category.html as a template file
151
- # and it will be ignored when building.
152
- def register_custom_pages
153
- options.custom_collections.each do |property, options|
154
- @app.ignore options[:template]
155
- @app.sitemap.register_resource_list_manipulator(
156
- :"blog_#{uid}_#{property}",
157
- ::Middleman::Blog::CustomPages.new(property, @app, self),
158
- false
159
- )
160
-
161
- generate_custom_helper(property)
162
- end
163
- end
164
-
165
- # Generate helpers to access the path to a custom collection.
166
- #
167
- # For example, when using a custom property called "category" to collect articles on
168
- # the method **category_path** will be generated.
169
- #
170
- # @param [Symbol] custom_property Custom property which is being used to collect articles on
171
- def generate_custom_helper(custom_property)
172
- m = Module.new
173
- m.module_eval(%Q{
174
- def #{custom_property}_path(value, key = nil)
175
- sitemap.find_resource_by_path(::Middleman::Blog::CustomPages.link(blog_controller(key).options, :#{custom_property}, value)).try(:url)
176
- end
177
- })
178
-
179
- app.class.send(:include, m)
180
- end
181
-
182
- # Helpers for use within templates and layouts.
183
- helpers do
184
- def blog_instances
185
- @blog_instances ||= {}
186
- end
187
-
188
- def blog_controller(key=nil)
189
- if !key && current_resource
190
- key = current_resource.metadata[:page]["blog"]
191
-
192
- if !key && current_resource.respond_to?(:blog_controller) && current_resource.blog_controller
193
- return current_resource.blog_controller
194
- end
195
- end
196
-
197
- # In multiblog situations, force people to specify the blog
198
- if !key && blog_instances.size > 1
199
- raise "You must either specify the blog name in calling this method or in your page frontmatter (using the 'blog' key)"
200
- end
201
-
202
- key ||= blog_instances.keys.first
203
- blog_instances[key.to_sym]
204
- end
205
-
206
- def blog(key=nil)
207
- blog_controller(key).data
208
- end
209
-
210
- # Determine whether the currently rendering template is a blog article.
211
- # This can be useful in layouts.
212
- # @return [Boolean]
213
- def is_blog_article?
214
- !current_article.nil?
215
- end
216
-
217
- # Get a {Resource} with mixed in {BlogArticle} methods representing the current article.
218
- # @return [Middleman::Sitemap::Resource]
219
- def current_article
220
- blog_instances.each do |key, blog|
221
- found = blog.data.article(current_resource.path)
222
- return found if found
223
- end
224
-
225
- nil
226
- end
227
-
228
- # Get a path to the given tag, based on the :taglink setting.
229
- # @param [String] tag
230
- # @return [String]
231
- def tag_path(tag, key=nil)
232
- sitemap.find_resource_by_path(::Middleman::Blog::TagPages.link(blog_controller(key).options, tag)).try(:url)
233
- end
234
-
235
- # Get a path to the given year-based calendar page, based on the :year_link setting.
236
- # @param [Number] year
237
- # @return [String]
238
- def blog_year_path(year, key=nil)
239
- sitemap.find_resource_by_path(::Middleman::Blog::CalendarPages.link(blog_controller(key).options, year)).try(:url)
240
- end
241
-
242
- # Get a path to the given month-based calendar page, based on the :month_link setting.
243
- # @param [Number] year
244
- # @param [Number] month
245
- # @return [String]
246
- def blog_month_path(year, month, key=nil)
247
- sitemap.find_resource_by_path(::Middleman::Blog::CalendarPages.link(blog_controller(key).options, year, month)).try(:url)
248
- end
249
-
250
- # Get a path to the given day-based calendar page, based on the :day_link setting.
251
- # @param [Number] year
252
- # @param [Number] month
253
- # @param [Number] day
254
- # @return [String]
255
- def blog_day_path(year, month, day, key=nil)
256
- sitemap.find_resource_by_path(::Middleman::Blog::CalendarPages.link(blog_controller(key).options, year, month, day)).try(:url)
257
- end
258
-
259
- # Pagination Helpers
260
- # These are used by the template if pagination is off, to allow a single template to work
261
- # in both modes. They get overridden by the local variables if the paginator is active.
262
-
263
- # Returns true if pagination is turned on for this template; false otherwise.
264
- # @return [Boolean]
265
- def paginate; false; end
266
-
267
- # Returns the list of articles to display on this page.
268
- # @return [Array<Middleman::Sitemap::Resource>]
269
- def page_articles(key=nil)
270
- limit = (current_resource.metadata[:page]["per_page"] || 0) - 1
271
-
272
- # "articles" local variable is populated by Calendar and Tag page generators
273
- # If it's not set then use the complete list of articles
274
- d = (current_resource.metadata[:locals]["articles"] || blog(key).articles)[0..limit]
275
- end
276
- end
277
- end
278
- end