nanoc 3.4.3 → 3.5.0b1

Sign up to get free protection for your applications and to get access to all the features.
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