nanoc 3.4.3 → 3.5.0b1

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 (85) hide show
  1. data/CONTRIBUTING.md +25 -0
  2. data/Gemfile +3 -1
  3. data/Gemfile.lock +22 -13
  4. data/NEWS.md +27 -0
  5. data/README.md +3 -1
  6. data/lib/nanoc.rb +2 -2
  7. data/lib/nanoc/base/compilation/compiler_dsl.rb +19 -0
  8. data/lib/nanoc/base/core_ext/array.rb +18 -8
  9. data/lib/nanoc/base/core_ext/hash.rb +18 -8
  10. data/lib/nanoc/base/core_ext/pathname.rb +2 -8
  11. data/lib/nanoc/base/plugin_registry.rb +57 -19
  12. data/lib/nanoc/base/result_data/item_rep.rb +3 -15
  13. data/lib/nanoc/base/source_data/item.rb +1 -1
  14. data/lib/nanoc/base/source_data/layout.rb +1 -1
  15. data/lib/nanoc/base/source_data/site.rb +15 -19
  16. data/lib/nanoc/cli.rb +28 -23
  17. data/lib/nanoc/cli/command_runner.rb +4 -0
  18. data/lib/nanoc/cli/commands/autocompile.rb +15 -6
  19. data/lib/nanoc/cli/commands/check.rb +47 -0
  20. data/lib/nanoc/cli/commands/compile.rb +271 -195
  21. data/lib/nanoc/cli/commands/create-site.rb +5 -5
  22. data/lib/nanoc/cli/commands/deploy.rb +16 -4
  23. data/lib/nanoc/cli/commands/prune.rb +3 -3
  24. data/lib/nanoc/cli/commands/show-data.rb +73 -58
  25. data/lib/nanoc/cli/commands/show-rules.rb +1 -1
  26. data/lib/nanoc/cli/commands/validate-css.rb +2 -3
  27. data/lib/nanoc/cli/commands/validate-html.rb +2 -3
  28. data/lib/nanoc/cli/commands/validate-links.rb +5 -11
  29. data/lib/nanoc/cli/commands/view.rb +1 -1
  30. data/lib/nanoc/cli/commands/watch.rb +38 -20
  31. data/lib/nanoc/cli/error_handler.rb +122 -122
  32. data/lib/nanoc/data_sources.rb +2 -0
  33. data/lib/nanoc/data_sources/filesystem_unified.rb +1 -1
  34. data/lib/nanoc/data_sources/filesystem_verbose.rb +1 -1
  35. data/lib/nanoc/data_sources/static.rb +60 -0
  36. data/lib/nanoc/extra.rb +2 -0
  37. data/lib/nanoc/extra/checking.rb +16 -0
  38. data/lib/nanoc/extra/checking/check.rb +33 -0
  39. data/lib/nanoc/extra/checking/checks.rb +19 -0
  40. data/lib/nanoc/extra/checking/checks/css.rb +23 -0
  41. data/lib/nanoc/extra/checking/checks/external_links.rb +149 -0
  42. data/lib/nanoc/extra/checking/checks/html.rb +24 -0
  43. data/lib/nanoc/extra/checking/checks/internal_links.rb +57 -0
  44. data/lib/nanoc/extra/checking/checks/stale.rb +23 -0
  45. data/lib/nanoc/extra/checking/dsl.rb +31 -0
  46. data/lib/nanoc/extra/checking/issue.rb +19 -0
  47. data/lib/nanoc/extra/checking/runner.rb +130 -0
  48. data/lib/nanoc/extra/link_collector.rb +57 -0
  49. data/lib/nanoc/extra/pruner.rb +1 -1
  50. data/lib/nanoc/extra/validators/links.rb +5 -262
  51. data/lib/nanoc/extra/validators/w3c.rb +8 -76
  52. data/lib/nanoc/filters/colorize_syntax.rb +1 -1
  53. data/lib/nanoc/filters/handlebars.rb +2 -2
  54. data/lib/nanoc/filters/less.rb +1 -1
  55. data/lib/nanoc/filters/redcarpet.rb +1 -1
  56. data/lib/nanoc/filters/rubypants.rb +1 -1
  57. data/lib/nanoc/filters/slim.rb +1 -1
  58. data/lib/nanoc/helpers/blogging.rb +163 -104
  59. data/lib/nanoc/helpers/xml_sitemap.rb +9 -3
  60. data/tasks/doc.rake +2 -1
  61. data/test/base/core_ext/array_spec.rb +4 -4
  62. data/test/base/core_ext/hash_spec.rb +7 -7
  63. data/test/base/core_ext/pathname_spec.rb +12 -2
  64. data/test/base/test_compiler_dsl.rb +24 -0
  65. data/test/base/test_item_rep.rb +58 -26
  66. data/test/cli/commands/test_watch.rb +0 -8
  67. data/test/data_sources/test_static.rb +66 -0
  68. data/test/extra/checking/checks/test_css.rb +40 -0
  69. data/test/extra/checking/checks/test_external_links.rb +76 -0
  70. data/test/extra/checking/checks/test_html.rb +40 -0
  71. data/test/extra/checking/checks/test_internal_links.rb +46 -0
  72. data/test/extra/checking/checks/test_stale.rb +49 -0
  73. data/test/extra/checking/test_check.rb +16 -0
  74. data/test/extra/checking/test_dsl.rb +20 -0
  75. data/test/extra/checking/test_runner.rb +15 -0
  76. data/test/extra/test_link_collector.rb +93 -0
  77. data/test/extra/validators/test_links.rb +0 -64
  78. data/test/extra/validators/test_w3c.rb +20 -26
  79. data/test/filters/test_colorize_syntax.rb +15 -0
  80. data/test/filters/test_less.rb +14 -0
  81. data/test/filters/test_pandoc.rb +5 -1
  82. data/test/helpers/test_blogging.rb +52 -8
  83. data/test/helpers/test_xml_sitemap.rb +68 -0
  84. data/test/test_gem.rb +1 -1
  85. metadata +31 -6
@@ -2,92 +2,24 @@
2
2
 
3
3
  module Nanoc::Extra::Validators
4
4
 
5
- # A validator that uses the W3C web service to validate HTML and CSS files.
5
+ # @deprecated Use the Checking API or the `check` command instead
6
6
  class W3C
7
7
 
8
- # @param [String] dir The directory that will be searched for HTML and/or
9
- # CSS files to validate
10
- #
11
- # @param [Array<Symbol>] types A list of types to check. Allowed types are
12
- # `:html` and `:css`.
13
8
  def initialize(dir, types)
14
9
  @dir = dir
15
10
  @types = types
16
11
  end
17
12
 
18
- # Starts the validator. The results will be printed to stdout.
19
- #
20
- # @return [void]
21
13
  def run
22
- # Load validator
23
- require 'w3c_validators'
24
-
25
- # Find all files
26
- filenames = []
27
- extensions = types_to_extensions(@types)
28
- extensions.each { |extension| filenames.concat(Dir[@dir + '/**/*.' + extension]) }
29
-
30
- # Validate each file
31
- filenames.each do |filename|
32
- validation_started(filename)
33
-
34
- extension = File.extname(filename)[1..-1]
35
- results = validator_for(extension).validate_file(filename)
36
-
37
- validation_ended(filename, results.errors)
38
- end
39
- end
40
-
41
- private
42
-
43
- # Returns all extensions for the given types
44
- def types_to_extensions(types)
45
- extensions = []
46
- types.each { |type| extensions.concat(type_to_extensions(type)) }
47
- extensions
48
- end
49
-
50
- # Returns all extensions for the given type
51
- def type_to_extensions(type)
52
- case type
53
- when :html
54
- [ 'html', 'htm' ]
55
- when :css
56
- [ 'css' ]
57
- else
58
- raise RuntimeError, "unknown type: #{type}"
14
+ args = []
15
+ types = @types.dup
16
+ args << 'html' if types.delete(:html)
17
+ args << 'css' if types.delete(:css)
18
+ unless types.empty?
19
+ raise Nanoc::Errors::GenericTrivial, "unknown type(s) specified: #{types.join(', ')}"
59
20
  end
60
- end
61
-
62
- # Returns the validator class for the given extension
63
- def validator_class_for(extension)
64
- case extension
65
- when 'html', 'htm'
66
- ::W3CValidators::MarkupValidator
67
- when 'css'
68
- ::W3CValidators::CSSValidator
69
- else
70
- raise RuntimeError, "unknown extension: #{extension}"
71
- end
72
- end
73
-
74
- # Returns the validator for the given extension
75
- def validator_for(extension)
76
- @validators ||= {}
77
- @validators[extension] ||= validator_class_for(extension).new
78
- end
79
-
80
- def validation_started(file)
81
- $stdout.print "Validating #{file}... "
82
- $stdout.flush
83
- end
84
21
 
85
- def validation_ended(file, errors)
86
- $stdout.puts(errors.empty? ? "valid" : "INVALID")
87
-
88
- errors.each do |err|
89
- puts " #{err}"
90
- end
22
+ Nanoc::CLI.run([ 'check', args ].flatten)
91
23
  end
92
24
 
93
25
  end
@@ -117,7 +117,7 @@ module Nanoc::Filters
117
117
  has_class = true if language
118
118
  else
119
119
  # Get language from comment line
120
- match = element.inner_text.match(/^#!([^\/][^\n]*)$/)
120
+ match = element.inner_text.strip.split[0].match(/^#!([^\/][^\n]*)$/)
121
121
  language = match[1] if match
122
122
  element.content = element.content.sub(/^#!([^\/][^\n]*)$\n/, '') if language
123
123
  end
@@ -9,8 +9,8 @@ module Nanoc::Filters
9
9
 
10
10
  # Runs the content through
11
11
  # [Handlebars](http://handlebarsjs.com/) using
12
- # [Handlebars.rb](https://github.com/cowboyd/handlebars.rb).
13
- # This method takes no options.
12
+ # [Handlebars.rb](https://github.com/cowboyd/handlebars.rb).
13
+ # This method takes no options.
14
14
  #
15
15
  # @param [String] content The content to filter
16
16
  #
@@ -46,7 +46,7 @@ module Nanoc::Filters
46
46
  # Add filename to load path
47
47
  paths = [ File.dirname(@item[:content_filename]) ]
48
48
  parser = ::Less::Parser.new(:paths => paths)
49
- parser.parse(content).to_css
49
+ parser.parse(content).to_css params
50
50
  end
51
51
 
52
52
  end
@@ -7,7 +7,7 @@ module Nanoc::Filters
7
7
  # @since 3.2.0
8
8
  class Redcarpet < Nanoc::Filter
9
9
 
10
- # Runs the content through [Redcarpet](https://github.com/tanoku/redcarpet/).
10
+ # Runs the content through [Redcarpet](https://github.com/vmg/redcarpet).
11
11
  # This method optionally takes processing options to pass on to Redcarpet.
12
12
  #
13
13
  # @overload run(content, params={})
@@ -5,7 +5,7 @@ require 'rubypants'
5
5
  module Nanoc::Filters
6
6
  class RubyPants < Nanoc::Filter
7
7
 
8
- # Runs the content through [RubyPants](http://chneukirchen.org/blog/static/projects/rubypants.html).
8
+ # Runs the content through [RubyPants](http://rubydoc.info/gems/rubypants/).
9
9
  # This method takes no options.
10
10
  #
11
11
  # @param [String] content The content to filter
@@ -7,7 +7,7 @@ module Nanoc::Filters
7
7
  # @since 3.2.0
8
8
  class Slim < Nanoc::Filter
9
9
 
10
- # Runs the content through [Slim](http://slim-lang.com/)
10
+ # Runs the content through [Slim](http://slim-lang.com/).
11
11
  # This method takes no options.
12
12
  #
13
13
  # @param [String] content The content to filter
@@ -42,6 +42,146 @@ module Nanoc::Helpers
42
42
  end.reverse
43
43
  end
44
44
 
45
+ class AtomFeedBuilder
46
+
47
+ include Nanoc::Helpers::Blogging
48
+
49
+ attr_accessor :site
50
+
51
+ attr_accessor :limit
52
+ attr_accessor :relevant_articles
53
+ attr_accessor :content_proc
54
+ attr_accessor :excerpt_proc
55
+ attr_accessor :title
56
+ attr_accessor :author_name
57
+ attr_accessor :author_uri
58
+ attr_accessor :icon
59
+ attr_accessor :logo
60
+
61
+ def initialize(site, item)
62
+ @site = site
63
+ @item = item
64
+ end
65
+
66
+ def validate
67
+ self.validate_config
68
+ self.validate_feed_item
69
+ self.validate_articles
70
+ end
71
+
72
+ def build
73
+ buffer = ''
74
+ xml = Builder::XmlMarkup.new(:target => buffer, :indent => 2)
75
+ self.build_for_feed(xml)
76
+ buffer
77
+ end
78
+
79
+ protected
80
+
81
+ def sorted_relevant_articles
82
+ relevant_articles.sort_by do |a|
83
+ attribute_to_time(a[:created_at])
84
+ end.reverse.first(limit)
85
+ end
86
+
87
+ def last_article
88
+ sorted_relevant_articles.first
89
+ end
90
+
91
+ def validate_config
92
+ if @site.config[:base_url].nil?
93
+ raise Nanoc::Errors::GenericTrivial.new('Cannot build Atom feed: site configuration has no base_url')
94
+ end
95
+ end
96
+
97
+ def validate_feed_item
98
+ if title.nil?
99
+ raise Nanoc::Errors::GenericTrivial.new('Cannot build Atom feed: no title in params, item or site config')
100
+ end
101
+ if author_name.nil?
102
+ raise Nanoc::Errors::GenericTrivial.new('Cannot build Atom feed: no author_name in params, item or site config')
103
+ end
104
+ if author_uri.nil?
105
+ raise Nanoc::Errors::GenericTrivial.new('Cannot build Atom feed: no author_uri in params, item or site config')
106
+ end
107
+ end
108
+
109
+ def validate_articles
110
+ if relevant_articles.empty?
111
+ raise Nanoc::Errors::GenericTrivial.new('Cannot build Atom feed: no articles')
112
+ end
113
+ if relevant_articles.any? { |a| a[:created_at].nil? }
114
+ raise Nanoc::Errors::GenericTrivial.new('Cannot build Atom feed: one or more articles lack created_at')
115
+ end
116
+ end
117
+
118
+ def build_for_feed(xml)
119
+ xml.instruct!
120
+ xml.feed(:xmlns => 'http://www.w3.org/2005/Atom') do
121
+ root_url = @site.config[:base_url] + '/'
122
+
123
+ # Add primary attributes
124
+ xml.id root_url
125
+ xml.title title
126
+
127
+ # Add date
128
+ xml.updated(attribute_to_time(last_article[:created_at]).to_iso8601_time)
129
+
130
+ # Add links
131
+ xml.link(:rel => 'alternate', :href => root_url)
132
+ xml.link(:rel => 'self', :href => feed_url)
133
+
134
+ # Add author information
135
+ xml.author do
136
+ xml.name author_name
137
+ xml.uri author_uri
138
+ end
139
+
140
+ # Add icon and logo
141
+ xml.icon icon if icon
142
+ xml.logo logo if logo
143
+
144
+ # Add articles
145
+ sorted_relevant_articles.each do |a|
146
+ self.build_for_article(a, xml)
147
+ end
148
+ end
149
+ end
150
+
151
+ def build_for_article(a, xml)
152
+ # Get URL
153
+ url = url_for(a)
154
+ return if url.nil?
155
+
156
+ xml.entry do
157
+ # Add primary attributes
158
+ xml.id atom_tag_for(a)
159
+ xml.title a[:title], :type => 'html'
160
+
161
+ # Add dates
162
+ xml.published attribute_to_time(a[:created_at]).to_iso8601_time
163
+ xml.updated attribute_to_time(a[:updated_at] || a[:created_at]).to_iso8601_time
164
+
165
+ # Add specific author information
166
+ if a[:author_name] || a[:author_uri]
167
+ xml.author do
168
+ xml.name a[:author_name] || author_name
169
+ xml.uri a[:author_uri] || author_uri
170
+ end
171
+ end
172
+
173
+ # Add link
174
+ xml.link(:rel => 'alternate', :href => url)
175
+
176
+ # Add content
177
+ summary = excerpt_proc.call(a)
178
+ xml.content content_proc.call(a), :type => 'html'
179
+ xml.summary summary, :type => 'html' unless summary.nil?
180
+ end
181
+ end
182
+
183
+ end
184
+
45
185
  # Returns a string representing the atom feed containing recent articles,
46
186
  # sorted by descending creation date.
47
187
  #
@@ -83,7 +223,7 @@ module Nanoc::Helpers
83
223
  # The feed item will need to know about the feed title, the feed author
84
224
  # name, and the URI corresponding to the author. These can be specified
85
225
  # using parameters, as attributes in the feed item, or in the site
86
- # configuration.
226
+ # configuration.
87
227
  #
88
228
  # * `title` - The title of the feed, which is usually also the title of
89
229
  # the blog.
@@ -144,112 +284,31 @@ module Nanoc::Helpers
144
284
  # @option params [String] :author_uri The URI of the feed's author, if it
145
285
  # is not given in the item attributes.
146
286
  #
287
+ # @option params [String] :icon The URI of the feed's icon.
288
+ #
289
+ # @option params [String] :logo The URI of the feed's logo.
290
+ #
147
291
  # @return [String] The generated feed content
148
292
  def atom_feed(params={})
149
293
  require 'builder'
150
294
 
151
- # Extract parameters
152
- limit = params[:limit] || 5
153
- relevant_articles = params[:articles] || articles || []
154
- content_proc = params[:content_proc] || lambda { |a| a.compiled_content(:snapshot => :pre) }
155
- excerpt_proc = params[:excerpt_proc] || lambda { |a| a[:excerpt] }
156
-
157
- # Check config attributes
158
- if @site.config[:base_url].nil?
159
- raise RuntimeError.new('Cannot build Atom feed: site configuration has no base_url')
160
- end
161
-
162
- # Check feed item attributes
163
- title = params[:title] || @item[:title] || @site.config[:title]
164
- if title.nil?
165
- raise RuntimeError.new('Cannot build Atom feed: no title in params, item or site config')
166
- end
167
- author_name = params[:author_name] || @item[:author_name] || @site.config[:author_name]
168
- if author_name.nil?
169
- raise RuntimeError.new('Cannot build Atom feed: no author_name in params, item or site config')
170
- end
171
- author_uri = params[:author_uri] || @item[:author_uri] || @site.config[:author_uri]
172
- if author_uri.nil?
173
- raise RuntimeError.new('Cannot build Atom feed: no author_uri in params, item or site config')
174
- end
175
-
176
- # Check article attributes
177
- if relevant_articles.empty?
178
- raise RuntimeError.new('Cannot build Atom feed: no articles')
179
- end
180
- if relevant_articles.any? { |a| a[:created_at].nil? }
181
- raise RuntimeError.new('Cannot build Atom feed: one or more articles lack created_at')
182
- end
183
-
184
- # Get sorted relevant articles
185
- sorted_relevant_articles = relevant_articles.sort_by do |a|
186
- attribute_to_time(a[:created_at])
187
- end.reverse.first(limit)
188
-
189
- # Get most recent article
190
- last_article = sorted_relevant_articles.first
191
-
192
295
  # Create builder
193
- buffer = ''
194
- xml = Builder::XmlMarkup.new(:target => buffer, :indent => 2)
195
-
196
- # Build feed
197
- xml.instruct!
198
- xml.feed(:xmlns => 'http://www.w3.org/2005/Atom') do
199
- root_url = @site.config[:base_url] + '/'
200
-
201
- # Add primary attributes
202
- xml.id root_url
203
- xml.title title
204
-
205
- # Add date
206
- xml.updated(attribute_to_time(last_article[:created_at]).to_iso8601_time)
207
-
208
- # Add links
209
- xml.link(:rel => 'alternate', :href => root_url)
210
- xml.link(:rel => 'self', :href => feed_url)
211
-
212
- # Add author information
213
- xml.author do
214
- xml.name author_name
215
- xml.uri author_uri
216
- end
217
-
218
- # Add articles
219
- sorted_relevant_articles.each do |a|
220
- # Get URL
221
- url = url_for(a)
222
- next if url.nil?
223
-
224
- xml.entry do
225
- # Add primary attributes
226
- xml.id atom_tag_for(a)
227
- xml.title a[:title], :type => 'html'
228
-
229
- # Add dates
230
- xml.published attribute_to_time(a[:created_at]).to_iso8601_time
231
- xml.updated attribute_to_time(a[:updated_at] || a[:created_at]).to_iso8601_time
232
-
233
- # Add specific author information
234
- if a[:author_name] || a[:author_uri]
235
- xml.author do
236
- xml.name a[:author_name] || author_name
237
- xml.uri a[:author_uri] || author_uri
238
- end
239
- end
240
-
241
- # Add link
242
- xml.link(:rel => 'alternate', :href => url)
243
-
244
- # Add content
245
- summary = excerpt_proc.call(a)
246
- xml.content content_proc.call(a), :type => 'html'
247
- xml.summary summary, :type => 'html' unless summary.nil?
248
- end
249
- end
250
- end
251
-
252
- buffer
296
+ builder = AtomFeedBuilder.new(@site, @item)
297
+
298
+ # Fill builder
299
+ builder.limit = params[:limit] || 5
300
+ builder.relevant_articles = params[:articles] || articles || []
301
+ builder.content_proc = params[:content_proc] || lambda { |a| a.compiled_content(:snapshot => :pre) }
302
+ builder.excerpt_proc = params[:excerpt_proc] || lambda { |a| a[:excerpt] }
303
+ builder.title = params[:title] || @item[:title] || @site.config[:title]
304
+ builder.author_name = params[:author_name] || @item[:author_name] || @site.config[:author_name]
305
+ builder.author_uri = params[:author_uri] || @item[:author_uri] || @site.config[:author_uri]
306
+ builder.icon = params[:icon]
307
+ builder.logo = params[:logo]
308
+
309
+ # Run
310
+ builder.validate
311
+ builder.build
253
312
  end
254
313
 
255
314
  # Returns the URL for the given item. It will return the URL containing
@@ -261,7 +320,7 @@ module Nanoc::Helpers
261
320
  def url_for(item)
262
321
  # Check attributes
263
322
  if @site.config[:base_url].nil?
264
- raise RuntimeError.new('Cannot build Atom feed: site configuration has no base_url')
323
+ raise Nanoc::Errors::GenericTrivial.new('Cannot build Atom feed: site configuration has no base_url')
265
324
  end
266
325
 
267
326
  # Build URL
@@ -281,7 +340,7 @@ module Nanoc::Helpers
281
340
  def feed_url
282
341
  # Check attributes
283
342
  if @site.config[:base_url].nil?
284
- raise RuntimeError.new('Cannot build Atom feed: site configuration has no base_url')
343
+ raise Nanoc::Errors::GenericTrivial.new('Cannot build Atom feed: site configuration has no base_url')
285
344
  end
286
345
 
287
346
  @item[:feed_url] || @site.config[:base_url] + @item.path