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
@@ -4,7 +4,7 @@ usage 'create-site [options] path'
4
4
  aliases :create_site, :cs
5
5
  summary 'create a site'
6
6
  description <<-EOS
7
- Create a new site at the given path. The site will use the filesystem_unified data source by default, but this can be changed using the --datasource commandline option.
7
+ Create a new site at the given path. The site will use the `filesystem_unified` data source by default, but this can be changed using the `--datasource` commandline option.
8
8
  EOS
9
9
 
10
10
  required :d, :datasource, 'specify the data source for the new site'
@@ -208,9 +208,9 @@ a:hover {
208
208
 
209
209
  #main p {
210
210
  margin: 20px 0;
211
-
211
+
212
212
  font-size: 15px;
213
-
213
+
214
214
  line-height: 20px;
215
215
  }
216
216
 
@@ -220,7 +220,7 @@ a:hover {
220
220
 
221
221
  #main li {
222
222
  font-size: 15px;
223
-
223
+
224
224
  line-height: 20px;
225
225
  }
226
226
 
@@ -273,7 +273,7 @@ EOS
273
273
  <head>
274
274
  <meta charset="utf-8">
275
275
  <title>A Brand New nanoc Site - <%= @item[:title] %></title>
276
- <link rel="stylesheet" type="text/css" href="/style.css" media="screen">
276
+ <link rel="stylesheet" href="/style.css">
277
277
 
278
278
  <!-- you don't need to keep this, but it's cool for stats! -->
279
279
  <meta name="generator" content="nanoc <%= Nanoc::VERSION %>">
@@ -9,15 +9,16 @@ deployer_names = deployers.keys.sort_by { |k| k.to_s }
9
9
  usage 'deploy [options]'
10
10
  summary 'deploy the compiled site'
11
11
  description <<-EOS
12
- Deploys the compiled site. The compiled site contents in the output directory will be uploaded to the destination, which is specified using the -t/--target option.
12
+ Deploys the compiled site. The compiled site contents in the output directory will be uploaded to the destination, which is specified using the `--target` option.
13
13
 
14
14
  Available deployers: #{deployer_names.join(', ')}
15
15
 
16
16
  EOS
17
17
 
18
- option :t, :target, 'specify the location to deploy to', :argument => :required
19
- flag :L, :list, 'list available locations to deploy to'
20
- option :n, :'dry-run', 'show what would be deployed'
18
+ option :t, :target, 'specify the location to deploy to (default: `default`)', :argument => :required
19
+ flag :C, :'no-check', 'do not run the issue checks marked for deployment'
20
+ flag :L, :list, 'list available locations to deploy to'
21
+ option :n, :'dry-run', 'show what would be deployed'
21
22
 
22
23
  module Nanoc::CLI::Commands
23
24
 
@@ -57,6 +58,17 @@ module Nanoc::CLI::Commands
57
58
  raise Nanoc::Errors::GenericTrivial, "The specified deploy target has an unrecognised kind “#{name}” (expected one of #{names.join(', ')})."
58
59
  end
59
60
 
61
+ # Check
62
+ unless options[:'no-check']
63
+ puts "Running issue checks…"
64
+ ok = Nanoc::Extra::Checking::Runner.new(site).run_for_deploy
65
+ if !ok
66
+ puts "Issues found, deploy aborted."
67
+ return
68
+ end
69
+ puts "No issues found. Deploying!"
70
+ end
71
+
60
72
  # Run
61
73
  deployer = deployer_class.new(
62
74
  site.config[:output_dir],
@@ -5,10 +5,10 @@ summary 'remove files not managed by nanoc from the output directory'
5
5
  description <<-EOS
6
6
  Find all files in the output directory that do not correspond to an item
7
7
  managed by nanoc and remove them. Since this is a hazardous operation, an
8
- additional --yes flag is needed as confirmation.
8
+ additional `--yes` flag is needed as confirmation.
9
9
 
10
- Also see the auto_prune site configuration option in config.yaml, which will
11
- automatically prune after compilation.
10
+ Also see the `auto_prune` site configuration option in `config.yamlz, which
11
+ will automatically prune after compilation.
12
12
  EOS
13
13
 
14
14
  flag :y, :yes, 'confirm deletion'
@@ -13,26 +13,59 @@ module Nanoc::CLI::Commands
13
13
  class ShowData < ::Nanoc::CLI::CommandRunner
14
14
 
15
15
  def run
16
- # Make sure we are in a nanoc site directory
17
- print "Loading site data... "
18
16
  self.require_site
19
- puts "done"
20
- puts
21
17
 
22
18
  # Get data
23
- items = self.site.items
24
- reps = items.map { |i| i.reps }.flatten
25
- layouts = self.site.layouts
19
+ items = self.site.items
20
+ item_reps = items.map { |i| i.reps }.flatten
21
+ layouts = self.site.layouts
26
22
 
27
23
  # Get dependency tracker
28
24
  compiler = self.site.compiler
29
25
  compiler.load
30
26
  dependency_tracker = compiler.dependency_tracker
31
27
 
32
- # Print item dependencies
33
- puts '=== Item dependencies ======================================================='
34
- puts
28
+ # Print data
29
+ self.print_item_dependencies(items, dependency_tracker)
30
+ self.print_item_rep_paths(items)
31
+ self.print_item_rep_outdatedness(items, compiler)
32
+ self.print_layouts(layouts, compiler)
33
+ end
34
+
35
+ protected
36
+
37
+ def sorted_with_prev(objects)
38
+ prev = nil
39
+ objects.sort_by { |o| o.identifier }.each do |object|
40
+ yield(object, prev)
41
+ prev = object
42
+ end
43
+ end
44
+
45
+ def sorted_reps_with_prev(items)
46
+ prev = nil
35
47
  items.sort_by { |i| i.identifier }.each do |item|
48
+ item.reps.sort_by { |r| r.name.to_s }.each do |rep|
49
+ yield(rep, prev)
50
+ prev = rep
51
+ end
52
+ end
53
+ end
54
+
55
+ def print_header(title)
56
+ header = '=' * 78
57
+ header[3..(title.length+5)] = " #{title} "
58
+
59
+ puts
60
+ puts header
61
+ puts
62
+ end
63
+
64
+ def print_item_dependencies(items, dependency_tracker)
65
+ self.print_header('Item dependencies')
66
+
67
+ self.sorted_with_prev(items) do |item, prev|
68
+ puts if prev
36
69
  puts "item #{item.identifier} depends on:"
37
70
  predecessors = dependency_tracker.objects_causing_outdatedness_of(item).sort_by { |i| i ? i.identifier : '' }
38
71
  predecessors.each do |pred|
@@ -43,63 +76,45 @@ module Nanoc::CLI::Commands
43
76
  end
44
77
  end
45
78
  puts " (nothing)" if predecessors.empty?
46
- puts
47
79
  end
80
+ end
48
81
 
49
- # Print representation paths
50
- puts '=== Representation paths ===================================================='
51
- puts
52
- items.sort_by { |i| i.identifier }.each do |item|
53
- item.reps.sort_by { |r| r.name.to_s }.each do |rep|
54
- puts "item #{item.identifier}, rep #{rep.name}:"
55
- if rep.raw_paths.empty?
56
- puts " (not written)"
57
- end
58
- length = rep.raw_paths.keys.map { |s| s.to_s.length }.max
59
- rep.raw_paths.each do |snapshot_name, raw_path|
60
- puts " [ %-#{length}s ] %s" % [ snapshot_name, raw_path ]
61
- end
62
- end
63
- puts
64
- end
82
+ def print_item_rep_paths(items)
83
+ self.print_header('Item representation paths')
65
84
 
66
- # Print representation outdatedness
67
- puts '=== Representation outdatedness ============================================='
68
- puts
69
- items.sort_by { |i| i.identifier }.each do |item|
70
- item.reps.sort_by { |r| r.name.to_s }.each do |rep|
71
- puts "item #{item.identifier}, rep #{rep.name}:"
72
- outdatedness_reason = compiler.outdatedness_checker.outdatedness_reason_for(rep)
73
- if outdatedness_reason
74
- puts " is outdated: #{outdatedness_reason.message}"
75
- else
76
- puts " is not outdated"
77
- end
85
+ self.sorted_reps_with_prev(items) do |rep, prev|
86
+ puts if prev
87
+ puts "item #{rep.item.identifier}, rep #{rep.name}:"
88
+ if rep.raw_paths.empty?
89
+ puts " (not written)"
90
+ end
91
+ length = rep.raw_paths.keys.map { |s| s.to_s.length }.max
92
+ rep.raw_paths.each do |snapshot_name, raw_path|
93
+ puts " [ %-#{length}s ] %s" % [ snapshot_name, raw_path ]
78
94
  end
79
- puts
80
95
  end
96
+ end
81
97
 
82
- # Print layout dependencies
83
- puts '=== Layout dependencies ====================================================='
84
- puts
85
- layouts.sort_by { |l| l.identifier }.each do |layout|
86
- puts "layout #{layout.identifier} depends on:"
87
- predecessors = dependency_tracker.objects_causing_outdatedness_of(layout).sort_by { |i| i ? i.identifier : '' }
88
- predecessors.each do |pred|
89
- if pred
90
- puts " [ #{format '%6s', pred.type} ] #{pred.identifier}"
91
- else
92
- puts " ( removed item )"
93
- end
98
+ def print_item_rep_outdatedness(items, compiler)
99
+ self.print_header('Item representation outdatedness')
100
+
101
+ self.sorted_reps_with_prev(items) do |rep, prev|
102
+ puts if prev
103
+ puts "item #{rep.item.identifier}, rep #{rep.name}:"
104
+ outdatedness_reason = compiler.outdatedness_checker.outdatedness_reason_for(rep)
105
+ if outdatedness_reason
106
+ puts " is outdated: #{outdatedness_reason.message}"
107
+ else
108
+ puts " is not outdated"
94
109
  end
95
- puts " (nothing)" if predecessors.empty?
96
- puts
97
110
  end
111
+ end
98
112
 
99
- # Print layouts
100
- puts '=== Layouts'
101
- puts
102
- layouts.sort_by { |l| l.identifier }.each do |layout|
113
+ def print_layouts(layouts, compiler)
114
+ self.print_header('Layouts')
115
+
116
+ self.sorted_with_prev(layouts) do |layout, prev|
117
+ puts if prev
103
118
  puts "layout #{layout.identifier}:"
104
119
  outdatedness_reason = compiler.outdatedness_checker.outdatedness_reason_for(layout)
105
120
  if outdatedness_reason
@@ -4,7 +4,7 @@ usage 'show-rules [thing]'
4
4
  aliases :explain
5
5
  summary 'describe the rules for each item'
6
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).
7
+ Prints the rules used for all items and layouts in the current site.
8
8
  EOS
9
9
 
10
10
  module Nanoc::CLI::Commands
@@ -3,6 +3,7 @@
3
3
  usage 'validate-css [options]'
4
4
  aliases :validate_css, :vcss
5
5
  summary 'validate the site’s CSS'
6
+ be_hidden
6
7
  description <<-EOS
7
8
  Validates the site’s CSS files.
8
9
  EOS
@@ -12,9 +13,7 @@ module Nanoc::CLI::Commands
12
13
  class ValidateCSS < ::Nanoc::CLI::CommandRunner
13
14
 
14
15
  def run
15
- require_site
16
- validator = ::Nanoc::Extra::Validators::W3C.new(site.config[:output_dir], [ :css ])
17
- validator.run
16
+ Nanoc::CLI.run %w( check css )
18
17
  end
19
18
 
20
19
  end
@@ -3,6 +3,7 @@
3
3
  usage 'validate-html [options]'
4
4
  aliases :validate_html, :vhtml
5
5
  summary 'validate the site’s HTML'
6
+ be_hidden
6
7
  description <<-EOS
7
8
  Validates the site’s HTML files.
8
9
  EOS
@@ -12,9 +13,7 @@ module Nanoc::CLI::Commands
12
13
  class ValidateHTML < ::Nanoc::CLI::CommandRunner
13
14
 
14
15
  def run
15
- require_site
16
- validator = ::Nanoc::Extra::Validators::W3C.new(site.config[:output_dir], [ :html ])
17
- validator.run
16
+ Nanoc::CLI.run %w( check html )
18
17
  end
19
18
 
20
19
  end
@@ -3,6 +3,7 @@
3
3
  usage 'validate-links [options]'
4
4
  aliases :validate_links, :vlink
5
5
  summary 'validate links in site'
6
+ be_hidden
6
7
  description <<-EOS
7
8
  Validates the site’s links. By default, both internal and external links will be checked.
8
9
  EOS
@@ -15,17 +16,10 @@ module Nanoc::CLI::Commands
15
16
  class ValidateLinks < ::Nanoc::CLI::CommandRunner
16
17
 
17
18
  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
19
+ checks = []
20
+ checks << 'ilinks' if options[:internal]
21
+ checks << 'elinks' if options[:external]
22
+ Nanoc::CLI.run [ 'check', checks ].flatten
29
23
  end
30
24
 
31
25
  end
@@ -3,7 +3,7 @@
3
3
  usage 'view [options]'
4
4
  summary 'start the web server that serves static files'
5
5
  description <<-EOS
6
- Start the static web server. Unless specified, the web server will run on port 3000 and listen on all IP addresses. Running this static web server requires 'adsf' (not 'asdf'!).
6
+ Start the static web server. Unless specified, the web server will run on port 3000 and listen on all IP addresses. Running this static web server requires `adsf` (not `asdf`!).
7
7
  EOS
8
8
 
9
9
  required :H, :handler, 'specify the handler to use (webrick/mongrel/...)'
@@ -11,7 +11,7 @@ module Nanoc::CLI::Commands
11
11
  class Watch < ::Nanoc::CLI::CommandRunner
12
12
 
13
13
  def run
14
- require 'fssm'
14
+ require 'listen'
15
15
  require 'pathname'
16
16
 
17
17
  require_site
@@ -20,12 +20,12 @@ module Nanoc::CLI::Commands
20
20
  @notifier = Notifier.new
21
21
 
22
22
  # Define rebuilder
23
- rebuilder = lambda do |base, relative|
23
+ rebuilder = lambda do |file_path|
24
24
  # Determine filename
25
- if base.nil? || relative.nil?
25
+ if file_path.nil?
26
26
  filename = nil
27
27
  else
28
- filename = ::Pathname.new(File.join([ base, relative ])).relative_path_from(::Pathname.new(Dir.getwd)).to_s
28
+ filename = ::Pathname.new(file_path).relative_path_from(::Pathname.new(Dir.getwd)).to_s
29
29
  end
30
30
 
31
31
  # Notify
@@ -63,24 +63,33 @@ module Nanoc::CLI::Commands
63
63
  end
64
64
 
65
65
  # Rebuild once
66
- rebuilder.call(nil, nil)
66
+ rebuilder.call(nil)
67
67
 
68
68
  # Get directories to watch
69
- dirs_to_watch = watcher_config[:dirs_to_watch] || %w( content layouts lib )
70
- files_to_watch = watcher_config[:files_to_watch] || %w( config.yaml Rules rules Rules.rb rules.rb' )
71
- files_to_watch.delete_if { |f| !File.file?(f) }
69
+ dirs_to_watch = watcher_config[:dirs_to_watch] || ['content', 'layouts', 'lib']
70
+ files_to_watch = watcher_config[:files_to_watch] || ['config.yaml', 'Rules', 'rules', 'Rules.rb', 'rules.rb']
71
+ files_to_watch = Regexp.new(files_to_watch.map { |name| "#{Regexp.quote(name)}$"}.join("|"))
72
+ ignore_dir = Regexp.new(Dir.glob("*").map{|dir| dir if File::ftype(dir) == "directory" }.compact.join("|"))
72
73
 
73
74
  # Watch
74
75
  puts "Watching for changes…"
75
- watcher = lambda do |*args|
76
- update(&rebuilder)
77
- delete(&rebuilder)
78
- create(&rebuilder)
79
- end
80
- monitor = FSSM::Monitor.new
81
- dirs_to_watch.each { |dir| monitor.path(dir, '**/*', &watcher) }
82
- files_to_watch.each { |filename| monitor.file(filename, &watcher) }
83
- monitor.run
76
+
77
+ callback = Proc.new do |modified, added, removed|
78
+ rebuilder.call(modified[0]) if modified[0]
79
+ rebuilder.call(added[0]) if added[0]
80
+ rebuilder.call(removed[0]) if removed[0]
81
+ end
82
+
83
+ listener = Listen::MultiListener.new(*dirs_to_watch).change(&callback)
84
+ listener_root = Listen::MultiListener.new('', :filter => files_to_watch, :ignore => ignore_dir).change(&callback)
85
+
86
+ begin
87
+ listener_root.start(false)
88
+ listener.start
89
+ rescue Interrupt
90
+ listener.stop
91
+ listener_root.stop
92
+ end
84
93
  end
85
94
 
86
95
  # Allows sending user notifications in a cross-platform way.
@@ -105,12 +114,21 @@ module Nanoc::CLI::Commands
105
114
 
106
115
  def tool
107
116
  @tool ||= begin
108
- TOOLS.find { |t| !`#{FIND_BINARY_COMMAND} #{t}`.empty? }
109
- rescue Errno::ENOENT
110
- nil
117
+ require 'terminal-notifier'
118
+ 'terminal-notify'
119
+ rescue LoadError
120
+ begin
121
+ TOOLS.find { |t| !`#{FIND_BINARY_COMMAND} #{t}`.empty? }
122
+ rescue Errno::ENOENT
123
+ nil
124
+ end
111
125
  end
112
126
  end
113
127
 
128
+ def terminal_notify(message)
129
+ TerminalNotifier.notify(message, :title => "nanoc")
130
+ end
131
+
114
132
  def growlnotify(message)
115
133
  system('growlnotify', '-m', message)
116
134
  end
@@ -58,6 +58,10 @@ module Nanoc::CLI
58
58
  exit!(0)
59
59
  end
60
60
  end
61
+ Signal.trap('USR1') do
62
+ puts "Caught USR1; dumping a stack trace"
63
+ puts caller.map { |i| " #{i}" }.join("\n")
64
+ end
61
65
 
62
66
  # Run
63
67
  yield
@@ -89,7 +93,12 @@ module Nanoc::CLI
89
93
  # @return [void]
90
94
  def print_error(error)
91
95
  write_compact_error(error, $stderr)
92
- File.open('crash.log', 'w') { |io| write_verbose_error(error, io) }
96
+
97
+ File.open('crash.log', 'w') do |io|
98
+ cio = Nanoc::CLI.wrap_in_cleaning_stream(io)
99
+ cio.add_stream_cleaner(::Nanoc::CLI::StreamCleaners::ANSIColors)
100
+ write_verbose_error(error, cio)
101
+ end
93
102
  end
94
103
 
95
104
  # Writes a compact representation of the error, suitable for a terminal, on
@@ -107,48 +116,13 @@ module Nanoc::CLI
107
116
  stream.puts
108
117
  stream.puts "Captain! We’ve been hit!"
109
118
 
110
- # Exception and resolution (if any)
111
- stream.puts
112
- stream.puts format_title('Message:')
113
- stream.puts
114
- stream.puts "#{error.class}: #{error.message}"
115
- resolution = self.resolution_for(error)
116
- stream.puts "#{resolution}" if resolution
117
-
118
- # Compilation stack
119
- stream.puts
120
- stream.puts format_title('Compilation stack:')
121
- stream.puts
122
- if self.stack.empty?
123
- stream.puts " (empty)"
124
- else
125
- self.stack.reverse.each do |obj|
126
- if obj.is_a?(Nanoc::ItemRep)
127
- stream.puts " - [item] #{obj.item.identifier} (rep #{obj.name})"
128
- else # layout
129
- stream.puts " - [layout] #{obj.identifier}"
130
- end
131
- end
132
- end
133
-
134
- # Backtrace
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
119
+ # Sections
120
+ self.write_error_message( stream, error)
121
+ self.write_compilation_stack(stream, error)
122
+ self.write_stack_trace( stream, error)
145
123
 
146
124
  # Issue link
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."
125
+ self.write_issue_link(stream)
152
126
  end
153
127
 
154
128
  # Writes a verbose representation of the error on the given stream.
@@ -161,86 +135,19 @@ module Nanoc::CLI
161
135
  #
162
136
  # @return [void]
163
137
  def write_verbose_error(error, stream)
164
- # Date and time
138
+ # Header
165
139
  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
140
 
198
- # Version information
199
- stream.puts '=== VERSION INFORMATION:'
200
- stream.puts
201
- stream.puts Nanoc.version_information
202
- stream.puts
203
-
204
- # System information
205
- begin
206
- uname = `uname -a`
207
- stream.puts '=== SYSTEM INFORMATION:'
208
- stream.puts
209
- stream.puts uname
210
- stream.puts
211
- rescue Errno::ENOENT
212
- end
213
-
214
- # Installed gems
215
- stream.puts '=== INSTALLED GEMS:'
216
- stream.puts
217
- self.gems_and_versions.each do |g|
218
- stream.puts " #{g.first} #{g.last.join(', ')}"
219
- end
220
- stream.puts
221
-
222
- # Environment
223
- stream.puts '=== ENVIRONMENT:'
224
- stream.puts
225
- ENV.sort.each do |e|
226
- stream.puts "#{e.first} => #{e.last.inspect}"
227
- end
228
- stream.puts
229
-
230
- # Gemfile
231
- if File.exist?('Gemfile.lock')
232
- stream.puts '=== GEMFILE.LOCK:'
233
- stream.puts
234
- stream.puts File.read('Gemfile.lock')
235
- stream.puts
236
- end
237
-
238
- # Load paths
239
- stream.puts '=== $LOAD_PATH:'
240
- stream.puts
241
- $LOAD_PATH.each_with_index do |i, index|
242
- stream.puts " #{index}. #{i}"
243
- end
141
+ # Sections
142
+ self.write_error_message( stream, error, :verbose => true)
143
+ self.write_compilation_stack( stream, error, :verbose => true)
144
+ self.write_stack_trace( stream, error, :verbose => true)
145
+ self.write_version_information(stream, :verbose => true)
146
+ self.write_system_information( stream, :verbose => true)
147
+ self.write_installed_gems( stream, :verbose => true)
148
+ self.write_environment( stream, :verbose => true)
149
+ self.write_gemfile_lock( stream, :verbose => true)
150
+ self.write_load_paths( stream, :verbose => true)
244
151
  end
245
152
 
246
153
  protected
@@ -267,6 +174,7 @@ module Nanoc::CLI
267
174
  (compiler && compiler.stack) || []
268
175
  end
269
176
 
177
+ # @return [Hash<String, Array>] A hash containing the gem names as keys and gem versions as value
270
178
  def gems_and_versions
271
179
  gems = {}
272
180
  Gem::Specification.find_all.sort_by { |s| [ s.name, s.version ] }.each do |spec|
@@ -287,12 +195,12 @@ module Nanoc::CLI
287
195
  'erubis' => 'erubis',
288
196
  'escape' => 'escape',
289
197
  'fog' => 'fog',
290
- 'fssm' => 'fssm',
291
198
  'haml' => 'haml',
292
199
  'handlebars' => 'hbs',
293
200
  'json' => 'json',
294
201
  'kramdown' => 'kramdown',
295
202
  'less' => 'less',
203
+ 'listen' => 'listen',
296
204
  'markaby' => 'markaby',
297
205
  'maruku' => 'maruku',
298
206
  'mime/types' => 'mime-types',
@@ -338,8 +246,100 @@ module Nanoc::CLI
338
246
  end
339
247
  end
340
248
 
341
- def format_title(s)
342
- "\e[1m\e[31m" + s + "\e[0m"
249
+ def write_section_header(stream, title, params={})
250
+ stream.puts
251
+ if params[:verbose]
252
+ stream.puts '===== ' + title.upcase + ':'
253
+ else
254
+ stream.puts "\e[1m\e[31m" + title + ':' + "\e[0m"
255
+ end
256
+ stream.puts
257
+ end
258
+
259
+ def write_error_message(stream, error, params={})
260
+ self.write_section_header(stream, 'Message', params)
261
+
262
+ stream.puts "#{error.class}: #{error.message}"
263
+ resolution = self.resolution_for(error)
264
+ stream.puts "#{resolution}" if resolution
265
+ end
266
+
267
+ def write_compilation_stack(stream, error, params={})
268
+ self.write_section_header(stream, 'Compilation stack', params)
269
+
270
+ if self.stack.empty?
271
+ stream.puts " (empty)"
272
+ else
273
+ self.stack.reverse.each do |obj|
274
+ if obj.is_a?(Nanoc::ItemRep)
275
+ stream.puts " - [item] #{obj.item.identifier} (rep #{obj.name})"
276
+ else # layout
277
+ stream.puts " - [layout] #{obj.identifier}"
278
+ end
279
+ end
280
+ end
281
+ end
282
+
283
+ def write_stack_trace(stream, error, params={})
284
+ self.write_section_header(stream, 'Stack trace', params)
285
+
286
+ count = params[:verbose] ? -1 : 10
287
+ error.backtrace[0...count].each_with_index do |item, index|
288
+ stream.puts " #{index}. #{item}"
289
+ end
290
+ if error.backtrace.size > count
291
+ stream.puts " ... #{error.backtrace.size - count} more lines omitted. See full crash log for details."
292
+ end
293
+ end
294
+
295
+ def write_issue_link(stream, params={})
296
+ stream.puts
297
+ stream.puts "If you believe this is a bug in nanoc, please do report it at"
298
+ stream.puts "-> https://github.com/ddfreyne/nanoc/issues/new <-"
299
+ stream.puts
300
+ stream.puts "A detailed crash log has been written to ./crash.log."
301
+ end
302
+
303
+ def write_version_information(stream, params={})
304
+ self.write_section_header(stream, 'Version information', params)
305
+ stream.puts Nanoc.version_information
306
+ end
307
+
308
+ def write_system_information(stream, params={})
309
+ begin
310
+ uname = `uname -a`
311
+ self.write_section_header(stream, 'System information', params)
312
+ stream.puts uname
313
+ rescue Errno::ENOENT
314
+ end
315
+ end
316
+
317
+ def write_installed_gems(stream, params={})
318
+ self.write_section_header(stream, 'Installed gems', params)
319
+ self.gems_and_versions.each do |g|
320
+ stream.puts " #{g.first} #{g.last.join(', ')}"
321
+ end
322
+ end
323
+
324
+ def write_environment(stream, params={})
325
+ self.write_section_header(stream, 'Environment', params)
326
+ ENV.sort.each do |e|
327
+ stream.puts "#{e.first} => #{e.last.inspect}"
328
+ end
329
+ end
330
+
331
+ def write_gemfile_lock(stream, params={})
332
+ if File.exist?('Gemfile.lock')
333
+ self.write_section_header(stream, 'Gemfile.lock', params)
334
+ stream.puts File.read('Gemfile.lock')
335
+ end
336
+ end
337
+
338
+ def write_load_paths(stream, params={})
339
+ self.write_section_header(stream, 'Load paths', params)
340
+ $LOAD_PATH.each_with_index do |i, index|
341
+ stream.puts " #{index}. #{i}"
342
+ end
343
343
  end
344
344
 
345
345
  end