nanoc 3.3.7 → 3.4.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 (53) hide show
  1. data/Gemfile.lock +133 -0
  2. data/NEWS.md +15 -0
  3. data/lib/nanoc.rb +2 -2
  4. data/lib/nanoc/base/compilation/compiler.rb +1 -1
  5. data/lib/nanoc/base/compilation/item_rep_proxy.rb +3 -3
  6. data/lib/nanoc/base/core_ext/string.rb +0 -18
  7. data/lib/nanoc/base/errors.rb +24 -19
  8. data/lib/nanoc/base/plugin_registry.rb +1 -1
  9. data/lib/nanoc/base/result_data/item_rep.rb +1 -2
  10. data/lib/nanoc/base/source_data/site.rb +1 -1
  11. data/lib/nanoc/cli.rb +46 -4
  12. data/lib/nanoc/cli/ansi_string_colorizer.rb +30 -0
  13. data/lib/nanoc/cli/cleaning_stream.rb +91 -0
  14. data/lib/nanoc/cli/command_runner.rb +3 -11
  15. data/lib/nanoc/cli/commands/compile.rb +2 -2
  16. data/lib/nanoc/cli/commands/{create_item.rb → create-item.rb} +6 -7
  17. data/lib/nanoc/cli/commands/{create_layout.rb → create-layout.rb} +9 -10
  18. data/lib/nanoc/cli/commands/{create_site.rb → create-site.rb} +13 -14
  19. data/lib/nanoc/cli/commands/deploy.rb +4 -12
  20. data/lib/nanoc/cli/commands/nanoc.rb +4 -2
  21. data/lib/nanoc/cli/commands/prune.rb +1 -1
  22. data/lib/nanoc/cli/commands/{debug.rb → show-data.rb} +6 -5
  23. data/lib/nanoc/cli/commands/{info.rb → show-plugins.rb} +6 -6
  24. data/lib/nanoc/cli/commands/show-rules.rb +69 -0
  25. data/lib/nanoc/cli/commands/update.rb +1 -2
  26. data/lib/nanoc/cli/commands/validate-css.rb +24 -0
  27. data/lib/nanoc/cli/commands/validate-html.rb +24 -0
  28. data/lib/nanoc/cli/commands/validate-links.rb +35 -0
  29. data/lib/nanoc/cli/commands/watch.rb +3 -3
  30. data/lib/nanoc/cli/error_handler.rb +142 -26
  31. data/lib/nanoc/cli/logger.rb +4 -21
  32. data/lib/nanoc/cli/stream_cleaners.rb +14 -0
  33. data/lib/nanoc/cli/stream_cleaners/abstract.rb +23 -0
  34. data/lib/nanoc/cli/stream_cleaners/ansi_colors.rb +15 -0
  35. data/lib/nanoc/cli/stream_cleaners/utf8.rb +16 -0
  36. data/lib/nanoc/extra/deployers/rsync.rb +2 -7
  37. data/lib/nanoc/extra/pruner.rb +3 -3
  38. data/lib/nanoc/extra/validators/links.rb +14 -3
  39. data/lib/nanoc/filters.rb +4 -0
  40. data/lib/nanoc/filters/handlebars.rb +30 -0
  41. data/lib/nanoc/filters/mustache.rb +2 -2
  42. data/lib/nanoc/filters/pandoc.rb +20 -0
  43. data/lib/nanoc/helpers/filtering.rb +1 -1
  44. data/lib/nanoc/tasks/validate.rake +10 -68
  45. data/nanoc.gemspec +0 -16
  46. data/test/cli/commands/test_create_site.rb +1 -1
  47. data/test/cli/commands/test_deploy.rb +45 -0
  48. data/test/cli/test_cli.rb +1 -1
  49. data/test/extra/validators/test_links.rb +15 -5
  50. data/test/filters/test_handlebars.rb +42 -0
  51. data/test/filters/test_mustache.rb +19 -0
  52. data/test/filters/test_pandoc.rb +18 -0
  53. metadata +23 -33
@@ -0,0 +1,69 @@
1
+ # encoding: utf-8
2
+
3
+ usage 'show-rules [thing]'
4
+ aliases :explain
5
+ summary 'describe the rules for each item'
6
+ description <<-EOS
7
+ Prints the rules used for all items and layouts in the current site. An argument can be given, which can be either an item identifier (e.g. /foo/), the path to the source file (e.g. content/foo.html) or the path to the output file (e.g. output/foo.html).
8
+ EOS
9
+
10
+ module Nanoc::CLI::Commands
11
+
12
+ class ShowRules < ::Nanoc::CLI::CommandRunner
13
+
14
+ def run
15
+ self.require_site
16
+
17
+ @c = Nanoc::CLI::ANSIStringColorizer
18
+ @calc = self.site.compiler.rule_memory_calculator
19
+
20
+ # TODO explain /foo/
21
+ # TODO explain content/foo.html
22
+ # TODO explain output/foo/index.html
23
+
24
+ self.site.items.each { |i| self.explain_item(i) }
25
+ self.site.layouts.each { |l| self.explain_layout(l) }
26
+ end
27
+
28
+ protected
29
+
30
+ def explain_item(item)
31
+ puts "#{@c.c('Item ' + item.identifier, :bold, :yellow)}:"
32
+ puts " (from #{item[:filename]})" if item[:filename]
33
+ item.reps.each do |rep|
34
+ puts " Rep #{rep.name}:"
35
+ if @calc[rep].empty? && rep.raw_path.nil?
36
+ puts " (nothing)"
37
+ else
38
+ @calc[rep].each do |mem|
39
+ puts ' %s %s' % [
40
+ @c.c(format('%-10s', mem[0].to_s), :blue),
41
+ mem[1..-1].map { |m| m.inspect }.join(', ')
42
+ ]
43
+ end
44
+ if rep.raw_path
45
+ puts ' %s %s' % [
46
+ @c.c(format('%-10s', 'write to'), :blue),
47
+ rep.raw_path
48
+ ]
49
+ end
50
+ end
51
+ end
52
+ puts
53
+ end
54
+
55
+ def explain_layout(layout)
56
+ puts "#{@c.c('Layout ' + layout.identifier, :bold, :yellow)}:"
57
+ puts " (from #{layout[:filename]})" if layout[:filename]
58
+ puts " %s %s" % [
59
+ @c.c(format('%-10s', 'filter'), :blue),
60
+ @calc[layout].map { |m| m.inspect }.join(', ')
61
+ ]
62
+ puts
63
+ end
64
+
65
+ end
66
+
67
+ end
68
+
69
+ runner Nanoc::CLI::Commands::ShowRules
@@ -22,8 +22,7 @@ module Nanoc::CLI::Commands
22
22
  def run
23
23
  # Check arguments
24
24
  if arguments.size != 0
25
- $stderr.puts "usage: #{command.usage}"
26
- exit 1
25
+ raise Nanoc::Errors::GenericTrivial, "usage: #{command.usage}"
27
26
  end
28
27
 
29
28
  # Make sure we are in a nanoc site directory
@@ -0,0 +1,24 @@
1
+ # encoding: utf-8
2
+
3
+ usage 'validate-css [options]'
4
+ aliases :validate_css, :vcss
5
+ summary 'validate the site’s CSS'
6
+ description <<-EOS
7
+ Validates the site’s CSS files.
8
+ EOS
9
+
10
+ module Nanoc::CLI::Commands
11
+
12
+ class ValidateCSS < ::Nanoc::CLI::CommandRunner
13
+
14
+ def run
15
+ require_site
16
+ validator = ::Nanoc::Extra::Validators::W3C.new(site.config[:output_dir], [ :css ])
17
+ validator.run
18
+ end
19
+
20
+ end
21
+
22
+ end
23
+
24
+ runner Nanoc::CLI::Commands::ValidateCSS
@@ -0,0 +1,24 @@
1
+ # encoding: utf-8
2
+
3
+ usage 'validate-html [options]'
4
+ aliases :validate_html, :vhtml
5
+ summary 'validate the site’s HTML'
6
+ description <<-EOS
7
+ Validates the site’s HTML files.
8
+ EOS
9
+
10
+ module Nanoc::CLI::Commands
11
+
12
+ class ValidateHTML < ::Nanoc::CLI::CommandRunner
13
+
14
+ def run
15
+ require_site
16
+ validator = ::Nanoc::Extra::Validators::W3C.new(site.config[:output_dir], [ :html ])
17
+ validator.run
18
+ end
19
+
20
+ end
21
+
22
+ end
23
+
24
+ runner Nanoc::CLI::Commands::ValidateHTML
@@ -0,0 +1,35 @@
1
+ # encoding: utf-8
2
+
3
+ usage 'validate-links [options]'
4
+ aliases :validate_links, :vlink
5
+ summary 'validate links in site'
6
+ description <<-EOS
7
+ Validates the site’s links. By default, both internal and external links will be checked.
8
+ EOS
9
+
10
+ flag :i, :internal, 'validate internal links only'
11
+ flag :e, :external, 'validate external links only'
12
+
13
+ module Nanoc::CLI::Commands
14
+
15
+ class ValidateLinks < ::Nanoc::CLI::CommandRunner
16
+
17
+ def run
18
+ require_site
19
+
20
+ dir = site.config[:output_dir]
21
+ index_filenames = site.config[:index_filenames]
22
+
23
+ validator = ::Nanoc::Extra::Validators::Links.new(
24
+ dir,
25
+ index_filenames,
26
+ :internal => (options[:external] ? false : true),
27
+ :external => (options[:internal] ? false : true))
28
+ validator.run
29
+ end
30
+
31
+ end
32
+
33
+ end
34
+
35
+ runner Nanoc::CLI::Commands::ValidateLinks
@@ -30,9 +30,9 @@ module Nanoc::CLI::Commands
30
30
 
31
31
  # Notify
32
32
  if filename
33
- print "Change detected to #{filename}; recompiling… ".make_compatible_with_env
33
+ print "Change detected to #{filename}; recompiling… "
34
34
  else
35
- print "Watcher started; compiling the entire site… ".make_compatible_with_env
35
+ print "Watcher started; compiling the entire site… "
36
36
  end
37
37
 
38
38
  # Recompile
@@ -71,7 +71,7 @@ module Nanoc::CLI::Commands
71
71
  files_to_watch.delete_if { |f| !File.file?(f) }
72
72
 
73
73
  # Watch
74
- puts "Watching for changes…".make_compatible_with_env
74
+ puts "Watching for changes…"
75
75
  watcher = lambda do |*args|
76
76
  update(&rebuilder)
77
77
  delete(&rebuilder)
@@ -61,6 +61,9 @@ module Nanoc::CLI
61
61
 
62
62
  # Run
63
63
  yield
64
+ rescue Nanoc::Errors::GenericTrivial => e
65
+ $stderr.puts "Error: #{e.message}"
66
+ exit(1)
64
67
  rescue Interrupt => e
65
68
  exit(1)
66
69
  rescue StandardError, ScriptError => e
@@ -85,50 +88,149 @@ module Nanoc::CLI
85
88
  #
86
89
  # @return [void]
87
90
  def print_error(error)
91
+ write_compact_error(error, $stderr)
92
+ File.open('crash.log', 'w') { |io| write_verbose_error(error, io) }
93
+ end
94
+
95
+ # Writes a compact representation of the error, suitable for a terminal, on
96
+ # the given stream (probably stderr).
97
+ #
98
+ # @param [Error] error The error that should be described
99
+ #
100
+ # @param [IO] stream The stream to write the description too
101
+ #
102
+ # @api private
103
+ #
104
+ # @return [void]
105
+ def write_compact_error(error, stream)
88
106
  # Header
89
- $stderr.puts
90
- $stderr.puts "Captain! We’ve been hit!".make_compatible_with_env
107
+ stream.puts
108
+ stream.puts "Captain! We’ve been hit!"
91
109
 
92
110
  # Exception and resolution (if any)
93
- $stderr.puts
94
- $stderr.puts '=== MESSAGE:'
95
- $stderr.puts
96
- $stderr.puts "#{error.class}: #{error.message}"
111
+ stream.puts
112
+ stream.puts format_title('Message:')
113
+ stream.puts
114
+ stream.puts "#{error.class}: #{error.message}"
97
115
  resolution = self.resolution_for(error)
98
- $stderr.puts "#{resolution}" if resolution
116
+ stream.puts "#{resolution}" if resolution
99
117
 
100
118
  # Compilation stack
101
- $stderr.puts
102
- $stderr.puts '=== COMPILATION STACK:'
103
- $stderr.puts
119
+ stream.puts
120
+ stream.puts format_title('Compilation stack:')
121
+ stream.puts
104
122
  if self.stack.empty?
105
- $stderr.puts " (empty)"
123
+ stream.puts " (empty)"
106
124
  else
107
125
  self.stack.reverse.each do |obj|
108
126
  if obj.is_a?(Nanoc::ItemRep)
109
- $stderr.puts " - [item] #{obj.item.identifier} (rep #{obj.name})"
127
+ stream.puts " - [item] #{obj.item.identifier} (rep #{obj.name})"
110
128
  else # layout
111
- $stderr.puts " - [layout] #{obj.identifier}"
129
+ stream.puts " - [layout] #{obj.identifier}"
112
130
  end
113
131
  end
114
132
  end
115
133
 
116
134
  # Backtrace
117
- $stderr.puts
118
- $stderr.puts '=== BACKTRACE:'
119
- $stderr.puts
120
- $stderr.puts error.backtrace.to_enum(:each_with_index).map { |item, index| " #{index}. #{item}" }.join("\n")
121
-
122
- # Extra information
123
- $stderr.puts
124
- $stderr.puts '=== VERSION INFORMATION:'
125
- $stderr.puts
126
- $stderr.puts Nanoc.version_information
135
+ stream.puts
136
+ stream.puts format_title('Stack trace:')
137
+ stream.puts
138
+ count = 10
139
+ error.backtrace[0...count].each_with_index do |item, index|
140
+ stream.puts " #{index}. #{item}"
141
+ end
142
+ if error.backtrace.size > count
143
+ puts " ... #{error.backtrace.size - count} more lines omitted. See full crash log for details."
144
+ end
127
145
 
128
146
  # Issue link
129
- $stderr.puts
130
- $stderr.puts "If you believe this is a bug in nanoc, please do report it at"
131
- $stderr.puts "<https://github.com/ddfreyne/nanoc/issues/new>--thanks!"
147
+ stream.puts
148
+ stream.puts "If you believe this is a bug in nanoc, please do report it at"
149
+ stream.puts "-> https://github.com/ddfreyne/nanoc/issues/new <-"
150
+ stream.puts
151
+ stream.puts "A detailed crash log has been written to ./crash.log."
152
+ end
153
+
154
+ # Writes a verbose representation of the error on the given stream.
155
+ #
156
+ # @param [Error] error The error that should be described
157
+ #
158
+ # @param [IO] stream The stream to write the description too
159
+ #
160
+ # @api private
161
+ #
162
+ # @return [void]
163
+ def write_verbose_error(error, stream)
164
+ # Date and time
165
+ stream.puts "Crashlog created at #{Time.now}"
166
+ stream.puts
167
+
168
+ # Exception and resolution (if any)
169
+ stream.puts '=== MESSAGE:'
170
+ stream.puts
171
+ stream.puts "#{error.class}: #{error.message}"
172
+ resolution = self.resolution_for(error)
173
+ stream.puts "#{resolution}" if resolution
174
+ stream.puts
175
+
176
+ # Compilation stack
177
+ stream.puts '=== COMPILATION STACK:'
178
+ stream.puts
179
+ if self.stack.empty?
180
+ stream.puts " (empty)"
181
+ else
182
+ self.stack.reverse.each do |obj|
183
+ if obj.is_a?(Nanoc::ItemRep)
184
+ stream.puts " - [item] #{obj.item.identifier} (rep #{obj.name})"
185
+ else # layout
186
+ stream.puts " - [layout] #{obj.identifier}"
187
+ end
188
+ end
189
+ end
190
+ stream.puts
191
+
192
+ # Backtrace
193
+ stream.puts '=== BACKTRACE:'
194
+ stream.puts
195
+ stream.puts error.backtrace.to_enum(:each_with_index).map { |item, index| " #{index}. #{item}" }.join("\n")
196
+ stream.puts
197
+
198
+ # Version information
199
+ stream.puts '=== VERSION INFORMATION:'
200
+ stream.puts
201
+ stream.puts Nanoc.version_information
202
+ stream.puts
203
+
204
+ # Installed gems
205
+ stream.puts '=== INSTALLED GEMS:'
206
+ stream.puts
207
+ self.gems_and_versions.each do |g|
208
+ stream.puts " #{g.first} #{g.last.join(', ')}"
209
+ end
210
+ stream.puts
211
+
212
+ # Environment
213
+ stream.puts '=== ENVIRONMENT:'
214
+ stream.puts
215
+ ENV.sort.each do |e|
216
+ stream.puts "#{e.first} => #{e.last.inspect}"
217
+ end
218
+ stream.puts
219
+
220
+ # Gemfile
221
+ if File.exist?('Gemfile.lock')
222
+ stream.puts '=== GEMFILE.LOCK:'
223
+ stream.puts
224
+ stream.puts File.read('Gemfile.lock')
225
+ stream.puts
226
+ end
227
+
228
+ # Load paths
229
+ stream.puts '=== $LOAD_PATH:'
230
+ stream.puts
231
+ $LOAD_PATH.each_with_index do |i, index|
232
+ stream.puts " #{index}. #{i}"
233
+ end
132
234
  end
133
235
 
134
236
  protected
@@ -155,6 +257,15 @@ module Nanoc::CLI
155
257
  (compiler && compiler.stack) || []
156
258
  end
157
259
 
260
+ def gems_and_versions
261
+ gems = {}
262
+ Gem::Specification.find_all.sort_by { |s| [ s.name, s.version ] }.each do |spec|
263
+ gems[spec.name] ||= []
264
+ gems[spec.name] << spec.version.to_s
265
+ end
266
+ gems
267
+ end
268
+
158
269
  # A hash that contains the name of the gem for a given required file. If a
159
270
  # `#require` fails, the gem name is looked up in this hash.
160
271
  GEM_NAMES = {
@@ -168,6 +279,7 @@ module Nanoc::CLI
168
279
  'fog' => 'fog',
169
280
  'fssm' => 'fssm',
170
281
  'haml' => 'haml',
282
+ 'handlebars' => 'hbs',
171
283
  'json' => 'json',
172
284
  'kramdown' => 'kramdown',
173
285
  'less' => 'less',
@@ -216,6 +328,10 @@ module Nanoc::CLI
216
328
  end
217
329
  end
218
330
 
331
+ def format_title(s)
332
+ "\e[1m\e[31m" + s + "\e[0m"
333
+ end
334
+
219
335
  end
220
336
 
221
337
  end
@@ -27,27 +27,10 @@ module Nanoc::CLI
27
27
  # @return [Symbol] The log level
28
28
  attr_accessor :level
29
29
 
30
- # @return [Boolean] True if color should be used, false otherwise
31
- attr_writer :color
32
-
33
30
  def initialize
34
31
  @level = :high
35
32
  end
36
33
 
37
- # @return [Boolean] true if colors are enabled, false otherwise
38
- def color?
39
- if @color.nil?
40
- @color = true
41
- begin
42
- require 'Win32/Console/ANSI' if RUBY_PLATFORM =~ /mswin|mingw/
43
- rescue LoadError
44
- @color = false
45
- end
46
- end
47
-
48
- @color
49
- end
50
-
51
34
  # Logs a file-related action.
52
35
  #
53
36
  # @param [:high, :low] level The importance of this action
@@ -57,15 +40,15 @@ module Nanoc::CLI
57
40
  # @param [String] name The name of the file the action was performed on
58
41
  #
59
42
  # @return [void]
60
- def file(level, action, identifier, duration=nil)
43
+ def file(level, action, name, duration=nil)
61
44
  log(
62
45
  level,
63
46
  '%s%12s%s %s%s' % [
64
- color? ? ACTION_COLORS[action.to_sym] : '',
47
+ ACTION_COLORS[action.to_sym],
65
48
  action,
66
- color? ? "\e[0m" : '',
49
+ "\e[0m",
67
50
  duration.nil? ? '' : "[%2.2fs] " % [ duration ],
68
- identifier
51
+ name
69
52
  ]
70
53
  )
71
54
  end