nanoc 3.2.4 → 3.3.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 (230) hide show
  1. data/.gemtest +0 -0
  2. data/ChangeLog +3 -0
  3. data/Gemfile +32 -0
  4. data/LICENSE +19 -0
  5. data/NEWS.md +470 -0
  6. data/README.md +114 -0
  7. data/Rakefile +14 -0
  8. data/bin/nanoc +7 -27
  9. data/bin/nanoc3 +3 -0
  10. data/doc/yardoc_templates/default/layout/html/footer.erb +10 -0
  11. data/lib/nanoc.rb +41 -0
  12. data/lib/nanoc/base.rb +49 -0
  13. data/lib/nanoc/base/compilation/checksum_store.rb +57 -0
  14. data/lib/nanoc/base/compilation/compiled_content_cache.rb +62 -0
  15. data/lib/nanoc/base/compilation/compiler.rb +458 -0
  16. data/lib/nanoc/base/compilation/compiler_dsl.rb +214 -0
  17. data/lib/nanoc/base/compilation/dependency_tracker.rb +200 -0
  18. data/lib/nanoc/base/compilation/filter.rb +165 -0
  19. data/lib/nanoc/base/compilation/item_rep_proxy.rb +103 -0
  20. data/lib/nanoc/base/compilation/item_rep_recorder_proxy.rb +102 -0
  21. data/lib/nanoc/base/compilation/outdatedness_checker.rb +223 -0
  22. data/lib/nanoc/base/compilation/outdatedness_reasons.rb +46 -0
  23. data/lib/nanoc/base/compilation/rule.rb +73 -0
  24. data/lib/nanoc/base/compilation/rule_context.rb +84 -0
  25. data/lib/nanoc/base/compilation/rule_memory_calculator.rb +40 -0
  26. data/lib/nanoc/base/compilation/rule_memory_store.rb +53 -0
  27. data/lib/nanoc/base/compilation/rules_collection.rb +243 -0
  28. data/lib/nanoc/base/context.rb +47 -0
  29. data/lib/nanoc/base/core_ext.rb +6 -0
  30. data/lib/nanoc/base/core_ext/array.rb +62 -0
  31. data/lib/nanoc/base/core_ext/hash.rb +63 -0
  32. data/lib/nanoc/base/core_ext/pathname.rb +26 -0
  33. data/lib/nanoc/base/core_ext/string.rb +46 -0
  34. data/lib/nanoc/base/directed_graph.rb +275 -0
  35. data/lib/nanoc/base/errors.rb +211 -0
  36. data/lib/nanoc/base/memoization.rb +67 -0
  37. data/lib/nanoc/base/notification_center.rb +84 -0
  38. data/lib/nanoc/base/ordered_hash.rb +200 -0
  39. data/lib/nanoc/base/plugin_registry.rb +181 -0
  40. data/lib/nanoc/base/result_data/item_rep.rb +492 -0
  41. data/lib/nanoc/base/source_data/code_snippet.rb +58 -0
  42. data/lib/nanoc/base/source_data/configuration.rb +24 -0
  43. data/lib/nanoc/base/source_data/data_source.rb +234 -0
  44. data/lib/nanoc/base/source_data/item.rb +301 -0
  45. data/lib/nanoc/base/source_data/layout.rb +130 -0
  46. data/lib/nanoc/base/source_data/site.rb +361 -0
  47. data/lib/nanoc/base/store.rb +135 -0
  48. data/lib/nanoc/cli.rb +137 -0
  49. data/lib/nanoc/cli/command_runner.rb +104 -0
  50. data/lib/nanoc/cli/commands/autocompile.rb +58 -0
  51. data/lib/nanoc/cli/commands/compile.rb +297 -0
  52. data/lib/nanoc/cli/commands/create_item.rb +60 -0
  53. data/lib/nanoc/cli/commands/create_layout.rb +73 -0
  54. data/lib/nanoc/cli/commands/create_site.rb +411 -0
  55. data/lib/nanoc/cli/commands/debug.rb +117 -0
  56. data/lib/nanoc/cli/commands/deploy.rb +79 -0
  57. data/lib/nanoc/cli/commands/info.rb +98 -0
  58. data/lib/nanoc/cli/commands/nanoc.rb +38 -0
  59. data/lib/nanoc/cli/commands/prune.rb +50 -0
  60. data/lib/nanoc/cli/commands/update.rb +70 -0
  61. data/lib/nanoc/cli/commands/view.rb +82 -0
  62. data/lib/nanoc/cli/commands/watch.rb +124 -0
  63. data/lib/nanoc/cli/error_handler.rb +199 -0
  64. data/lib/nanoc/cli/logger.rb +92 -0
  65. data/lib/nanoc/data_sources.rb +29 -0
  66. data/lib/nanoc/data_sources/deprecated/delicious.rb +42 -0
  67. data/lib/nanoc/data_sources/deprecated/last_fm.rb +87 -0
  68. data/lib/nanoc/data_sources/deprecated/twitter.rb +38 -0
  69. data/lib/nanoc/data_sources/filesystem.rb +299 -0
  70. data/lib/nanoc/data_sources/filesystem_unified.rb +121 -0
  71. data/lib/nanoc/data_sources/filesystem_verbose.rb +91 -0
  72. data/lib/nanoc/extra.rb +24 -0
  73. data/lib/nanoc/extra/auto_compiler.rb +103 -0
  74. data/lib/nanoc/extra/chick.rb +125 -0
  75. data/lib/nanoc/extra/core_ext.rb +6 -0
  76. data/lib/nanoc/extra/core_ext/enumerable.rb +33 -0
  77. data/lib/nanoc/extra/core_ext/pathname.rb +30 -0
  78. data/lib/nanoc/extra/core_ext/time.rb +19 -0
  79. data/lib/nanoc/extra/deployer.rb +47 -0
  80. data/lib/nanoc/extra/deployers.rb +15 -0
  81. data/lib/nanoc/extra/deployers/fog.rb +98 -0
  82. data/lib/nanoc/extra/deployers/rsync.rb +70 -0
  83. data/lib/nanoc/extra/file_proxy.rb +40 -0
  84. data/lib/nanoc/extra/pruner.rb +86 -0
  85. data/lib/nanoc/extra/validators.rb +12 -0
  86. data/lib/nanoc/extra/validators/links.rb +268 -0
  87. data/lib/nanoc/extra/validators/w3c.rb +95 -0
  88. data/lib/nanoc/extra/vcs.rb +66 -0
  89. data/lib/nanoc/extra/vcses.rb +17 -0
  90. data/lib/nanoc/extra/vcses/bazaar.rb +25 -0
  91. data/lib/nanoc/extra/vcses/dummy.rb +24 -0
  92. data/lib/nanoc/extra/vcses/git.rb +25 -0
  93. data/lib/nanoc/extra/vcses/mercurial.rb +25 -0
  94. data/lib/nanoc/extra/vcses/subversion.rb +25 -0
  95. data/lib/nanoc/filters.rb +59 -0
  96. data/lib/nanoc/filters/asciidoc.rb +38 -0
  97. data/lib/nanoc/filters/bluecloth.rb +19 -0
  98. data/lib/nanoc/filters/coderay.rb +21 -0
  99. data/lib/nanoc/filters/coffeescript.rb +20 -0
  100. data/lib/nanoc/filters/colorize_syntax.rb +298 -0
  101. data/lib/nanoc/filters/erb.rb +38 -0
  102. data/lib/nanoc/filters/erubis.rb +34 -0
  103. data/lib/nanoc/filters/haml.rb +27 -0
  104. data/lib/nanoc/filters/kramdown.rb +20 -0
  105. data/lib/nanoc/filters/less.rb +53 -0
  106. data/lib/nanoc/filters/markaby.rb +20 -0
  107. data/lib/nanoc/filters/maruku.rb +20 -0
  108. data/lib/nanoc/filters/mustache.rb +24 -0
  109. data/lib/nanoc/filters/rainpress.rb +19 -0
  110. data/lib/nanoc/filters/rdiscount.rb +22 -0
  111. data/lib/nanoc/filters/rdoc.rb +33 -0
  112. data/lib/nanoc/filters/redcarpet.rb +62 -0
  113. data/lib/nanoc/filters/redcloth.rb +47 -0
  114. data/lib/nanoc/filters/relativize_paths.rb +94 -0
  115. data/lib/nanoc/filters/rubypants.rb +20 -0
  116. data/lib/nanoc/filters/sass.rb +74 -0
  117. data/lib/nanoc/filters/slim.rb +25 -0
  118. data/lib/nanoc/filters/typogruby.rb +23 -0
  119. data/lib/nanoc/filters/uglify_js.rb +42 -0
  120. data/lib/nanoc/filters/xsl.rb +46 -0
  121. data/lib/nanoc/filters/yui_compressor.rb +23 -0
  122. data/lib/nanoc/helpers.rb +16 -0
  123. data/lib/nanoc/helpers/blogging.rb +319 -0
  124. data/lib/nanoc/helpers/breadcrumbs.rb +40 -0
  125. data/lib/nanoc/helpers/capturing.rb +138 -0
  126. data/lib/nanoc/helpers/filtering.rb +50 -0
  127. data/lib/nanoc/helpers/html_escape.rb +55 -0
  128. data/lib/nanoc/helpers/link_to.rb +151 -0
  129. data/lib/nanoc/helpers/rendering.rb +140 -0
  130. data/lib/nanoc/helpers/tagging.rb +71 -0
  131. data/lib/nanoc/helpers/text.rb +44 -0
  132. data/lib/nanoc/helpers/xml_sitemap.rb +76 -0
  133. data/lib/nanoc/tasks.rb +10 -0
  134. data/lib/nanoc/tasks/clean.rake +16 -0
  135. data/lib/nanoc/tasks/clean.rb +29 -0
  136. data/lib/nanoc/tasks/deploy/rsync.rake +16 -0
  137. data/lib/nanoc/tasks/validate.rake +92 -0
  138. data/nanoc.gemspec +49 -0
  139. data/tasks/doc.rake +16 -0
  140. data/tasks/test.rake +46 -0
  141. data/test/base/core_ext/array_spec.rb +73 -0
  142. data/test/base/core_ext/hash_spec.rb +98 -0
  143. data/test/base/core_ext/pathname_spec.rb +27 -0
  144. data/test/base/core_ext/string_spec.rb +37 -0
  145. data/test/base/test_checksum_store.rb +35 -0
  146. data/test/base/test_code_snippet.rb +31 -0
  147. data/test/base/test_compiler.rb +403 -0
  148. data/test/base/test_compiler_dsl.rb +161 -0
  149. data/test/base/test_context.rb +31 -0
  150. data/test/base/test_data_source.rb +46 -0
  151. data/test/base/test_dependency_tracker.rb +262 -0
  152. data/test/base/test_directed_graph.rb +288 -0
  153. data/test/base/test_filter.rb +83 -0
  154. data/test/base/test_item.rb +179 -0
  155. data/test/base/test_item_rep.rb +579 -0
  156. data/test/base/test_layout.rb +59 -0
  157. data/test/base/test_memoization.rb +90 -0
  158. data/test/base/test_notification_center.rb +34 -0
  159. data/test/base/test_outdatedness_checker.rb +394 -0
  160. data/test/base/test_plugin.rb +30 -0
  161. data/test/base/test_rule.rb +19 -0
  162. data/test/base/test_rule_context.rb +65 -0
  163. data/test/base/test_site.rb +190 -0
  164. data/test/cli/commands/test_compile.rb +33 -0
  165. data/test/cli/commands/test_create_item.rb +14 -0
  166. data/test/cli/commands/test_create_layout.rb +28 -0
  167. data/test/cli/commands/test_create_site.rb +24 -0
  168. data/test/cli/commands/test_deploy.rb +74 -0
  169. data/test/cli/commands/test_help.rb +12 -0
  170. data/test/cli/commands/test_info.rb +11 -0
  171. data/test/cli/commands/test_prune.rb +98 -0
  172. data/test/cli/commands/test_update.rb +10 -0
  173. data/test/cli/test_cli.rb +102 -0
  174. data/test/cli/test_error_handler.rb +29 -0
  175. data/test/cli/test_logger.rb +10 -0
  176. data/test/data_sources/test_filesystem.rb +433 -0
  177. data/test/data_sources/test_filesystem_unified.rb +536 -0
  178. data/test/data_sources/test_filesystem_verbose.rb +357 -0
  179. data/test/extra/core_ext/test_enumerable.rb +30 -0
  180. data/test/extra/core_ext/test_pathname.rb +17 -0
  181. data/test/extra/core_ext/test_time.rb +15 -0
  182. data/test/extra/deployers/test_fog.rb +67 -0
  183. data/test/extra/deployers/test_rsync.rb +100 -0
  184. data/test/extra/test_auto_compiler.rb +417 -0
  185. data/test/extra/test_file_proxy.rb +19 -0
  186. data/test/extra/test_vcs.rb +22 -0
  187. data/test/extra/validators/test_links.rb +62 -0
  188. data/test/extra/validators/test_w3c.rb +47 -0
  189. data/test/filters/test_asciidoc.rb +22 -0
  190. data/test/filters/test_bluecloth.rb +18 -0
  191. data/test/filters/test_coderay.rb +44 -0
  192. data/test/filters/test_coffeescript.rb +18 -0
  193. data/test/filters/test_colorize_syntax.rb +379 -0
  194. data/test/filters/test_erb.rb +105 -0
  195. data/test/filters/test_erubis.rb +78 -0
  196. data/test/filters/test_haml.rb +96 -0
  197. data/test/filters/test_kramdown.rb +18 -0
  198. data/test/filters/test_less.rb +113 -0
  199. data/test/filters/test_markaby.rb +24 -0
  200. data/test/filters/test_maruku.rb +18 -0
  201. data/test/filters/test_mustache.rb +25 -0
  202. data/test/filters/test_rainpress.rb +29 -0
  203. data/test/filters/test_rdiscount.rb +31 -0
  204. data/test/filters/test_rdoc.rb +18 -0
  205. data/test/filters/test_redcarpet.rb +73 -0
  206. data/test/filters/test_redcloth.rb +33 -0
  207. data/test/filters/test_relativize_paths.rb +533 -0
  208. data/test/filters/test_rubypants.rb +18 -0
  209. data/test/filters/test_sass.rb +229 -0
  210. data/test/filters/test_slim.rb +35 -0
  211. data/test/filters/test_typogruby.rb +21 -0
  212. data/test/filters/test_uglify_js.rb +30 -0
  213. data/test/filters/test_xsl.rb +105 -0
  214. data/test/filters/test_yui_compressor.rb +44 -0
  215. data/test/gem_loader.rb +11 -0
  216. data/test/helper.rb +207 -0
  217. data/test/helpers/test_blogging.rb +754 -0
  218. data/test/helpers/test_breadcrumbs.rb +81 -0
  219. data/test/helpers/test_capturing.rb +41 -0
  220. data/test/helpers/test_filtering.rb +106 -0
  221. data/test/helpers/test_html_escape.rb +32 -0
  222. data/test/helpers/test_link_to.rb +249 -0
  223. data/test/helpers/test_rendering.rb +89 -0
  224. data/test/helpers/test_tagging.rb +87 -0
  225. data/test/helpers/test_text.rb +24 -0
  226. data/test/helpers/test_xml_sitemap.rb +103 -0
  227. data/test/tasks/test_clean.rb +67 -0
  228. metadata +327 -15
  229. data/bin/nanoc-select +0 -86
  230. data/lib/nanoc-select.rb +0 -11
@@ -0,0 +1,82 @@
1
+ # encoding: utf-8
2
+
3
+ usage 'view [options]'
4
+ summary 'start the web server that serves static files'
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'!).
7
+ EOS
8
+
9
+ required :H, :handler, 'specify the handler to use (webrick/mongrel/...)'
10
+ required :o, :host, 'specify the host to listen on (default: 0.0.0.0)'
11
+ required :p, :port, 'specify the port to listen on (default: 3000)'
12
+
13
+ module Nanoc::CLI::Commands
14
+
15
+ class View < ::Nanoc::CLI::CommandRunner
16
+
17
+ def run
18
+ load_adsf
19
+ require 'rack'
20
+
21
+ # Make sure we are in a nanoc site directory
22
+ self.require_site
23
+
24
+ # Set options
25
+ options_for_rack = {
26
+ :Port => (options[:port] || 3000).to_i,
27
+ :Host => (options[:host] || '0.0.0.0')
28
+ }
29
+
30
+ # Guess which handler we should use
31
+ unless handler = Rack::Handler.get(options[:handler])
32
+ begin
33
+ handler = Rack::Handler::Mongrel
34
+ rescue LoadError => e
35
+ handler = Rack::Handler::WEBrick
36
+ end
37
+ end
38
+
39
+ # Build app
40
+ site = self.site
41
+ app = Rack::Builder.new do
42
+ use Rack::CommonLogger
43
+ use Rack::ShowExceptions
44
+ use Rack::Lint
45
+ use Rack::Head
46
+ use Adsf::Rack::IndexFileFinder, :root => site.config[:output_dir]
47
+ run Rack::File.new(site.config[:output_dir])
48
+ end.to_app
49
+
50
+ # Run autocompiler
51
+ handler.run(app, options_for_rack)
52
+ end
53
+
54
+ protected
55
+
56
+ def load_adsf
57
+ # Load adsf
58
+ begin
59
+ require 'adsf'
60
+ return
61
+ rescue LoadError
62
+ $stderr.puts "Could not find the required 'adsf' gem, " \
63
+ "which is necessary for the view command."
64
+ end
65
+
66
+ # Check asdf
67
+ begin
68
+ require 'asdf'
69
+ $stderr.puts "You appear to have 'asdf' installed, " \
70
+ "but not 'adsf'. Please install 'adsf' (check the spelling)!"
71
+ rescue LoadError
72
+ end
73
+
74
+ # Done
75
+ exit 1
76
+ end
77
+
78
+ end
79
+
80
+ end
81
+
82
+ runner Nanoc::CLI::Commands::View
@@ -0,0 +1,124 @@
1
+ # encoding: utf-8
2
+
3
+ usage 'watch [options]'
4
+ summary 'start the watcher'
5
+ description <<-EOS
6
+ Start the watcher. When a change is detected, the site will be recompiled.
7
+ EOS
8
+
9
+ module Nanoc::CLI::Commands
10
+
11
+ class Watch < ::Nanoc::CLI::CommandRunner
12
+
13
+ def run
14
+ require 'fssm'
15
+ require 'pathname'
16
+
17
+ require_site
18
+ watcher_config = self.site.config[:watcher] || {}
19
+
20
+ @notifier = Notifier.new
21
+
22
+ # Define rebuilder
23
+ rebuilder = lambda do |base, relative|
24
+ # Determine filename
25
+ if base.nil? || relative.nil?
26
+ filename = nil
27
+ else
28
+ filename = ::Pathname.new(File.join([ base, relative ])).relative_path_from(::Pathname.new(Dir.getwd)).to_s
29
+ end
30
+
31
+ # Notify
32
+ if filename
33
+ print "Change detected to #{filename}; recompiling… ".make_compatible_with_env
34
+ else
35
+ print "Watcher started; compiling the entire site… ".make_compatible_with_env
36
+ end
37
+
38
+ # Recompile
39
+ start = Time.now
40
+ site = Nanoc::Site.new('.')
41
+ begin
42
+ site.compile
43
+
44
+ # TODO include icon (--image misc/success-icon.png)
45
+ notify_on_compilation_success = watcher_config.fetch(:notify_on_compilation_success) { true }
46
+ if notify_on_compilation_success
47
+ @notifier.notify('Compilation complete')
48
+ end
49
+
50
+ time_spent = ((Time.now - start)*1000.0).round
51
+ puts "done in #{format '%is %ims', *(time_spent.divmod(1000))}"
52
+ rescue Exception => e
53
+ # TODO include icon (--image misc/error-icon.png)
54
+ notify_on_compilation_failure = watcher_config.fetch(:notify_on_compilation_failure) { true }
55
+ if notify_on_compilation_failure
56
+ @notifier.notify('Compilation failed')
57
+ end
58
+
59
+ puts
60
+ Nanoc::CLI::ErrorHandler.print_error(e)
61
+ puts
62
+ end
63
+ end
64
+
65
+ # Rebuild once
66
+ rebuilder.call(nil, nil)
67
+
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) }
72
+
73
+ # Watch
74
+ puts "Watching for changes…".make_compatible_with_env
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
84
+ end
85
+
86
+ # Allows sending user notifications in a cross-platform way.
87
+ class Notifier
88
+
89
+ # A list of commandline tool names that can be used to send notifications
90
+ TOOLS = %w( growlnotify notify-send )
91
+
92
+ # The tool to use for discovering binaries' locations
93
+ FIND_BINARY_COMMAND = RUBY_PLATFORM =~ /mingw|mswin/ ? "where" : "which"
94
+
95
+ # Send a notification. If no notifier is found, no notification will be
96
+ # created.
97
+ #
98
+ # @param [String] message The message to include in the notification
99
+ def notify(message)
100
+ return if tool.nil?
101
+ send(tool.tr('-', '_'), message)
102
+ end
103
+
104
+ private
105
+
106
+ def tool
107
+ @tool ||= TOOLS.find { |t| !`#{FIND_BINARY_COMMAND} #{t}`.empty? }
108
+ end
109
+
110
+ def growlnotify(message)
111
+ system('growlnotify', '-m', message)
112
+ end
113
+
114
+ def notify_send(message)
115
+ system('notify-send', message)
116
+ end
117
+
118
+ end
119
+
120
+ end
121
+
122
+ end
123
+
124
+ runner Nanoc::CLI::Commands::Watch
@@ -0,0 +1,199 @@
1
+ # encoding: utf-8
2
+
3
+ module Nanoc::CLI
4
+
5
+ # Catches errors and prints nice diagnostic messages, then exits.
6
+ #
7
+ # @api private
8
+ class ErrorHandler
9
+
10
+ # @option params [Nanoc::CLI::Command, nil] command The command that is
11
+ # currently being executed, or nil if there is none
12
+ def initialize(params={})
13
+ @command = params[:command]
14
+ end
15
+
16
+ # Enables error handling in the given block.
17
+ #
18
+ # @option params [Nanoc::CLI::Command, nil] command The command that is
19
+ # currently being executed, or nil if there is none
20
+ #
21
+ # @return [void]
22
+ def self.handle_while(params={}, &block)
23
+ self.new(params).handle_while(&block)
24
+ end
25
+
26
+ # Enables error handling in the given block. This method should not be
27
+ # called directly; use {Nanoc::CLI::ErrorHandler.handle_while} instead.
28
+ #
29
+ # @return [void]
30
+ #
31
+ # @api private
32
+ def handle_while(&block)
33
+ # Set exit handler
34
+ [ 'INT', 'TERM' ].each do |signal|
35
+ Signal.trap(signal) do
36
+ puts
37
+ exit!(0)
38
+ end
39
+ end
40
+
41
+ # Run
42
+ yield
43
+ rescue Interrupt => e
44
+ exit(1)
45
+ rescue StandardError, ScriptError => e
46
+ self.print_error(e)
47
+ exit(1)
48
+ end
49
+
50
+ # Prints the given error to stderr. Includes message, possible resolution
51
+ # (see {#resolution_for}), compilation stack, backtrace, etc.
52
+ #
53
+ # @param [Error] error The error that should be described
54
+ #
55
+ # @return [void]
56
+ def self.print_error(error)
57
+ self.new.print_error(error)
58
+ end
59
+
60
+ # Prints the given error to stderr. Includes message, possible resolution
61
+ # (see {#resolution_for}), compilation stack, backtrace, etc.
62
+ #
63
+ # @param [Error] error The error that should be described
64
+ #
65
+ # @return [void]
66
+ def print_error(error)
67
+ # Header
68
+ $stderr.puts
69
+ $stderr.puts "Captain! We’ve been hit!"
70
+
71
+ # Exception and resolution (if any)
72
+ $stderr.puts
73
+ $stderr.puts '=== MESSAGE:'
74
+ $stderr.puts
75
+ $stderr.puts "#{error.class}: #{error.message}"
76
+ resolution = self.resolution_for(error)
77
+ $stderr.puts "#{resolution}" if resolution
78
+
79
+ # Compilation stack
80
+ $stderr.puts
81
+ $stderr.puts '=== COMPILATION STACK:'
82
+ $stderr.puts
83
+ if self.stack.empty?
84
+ $stderr.puts " (empty)"
85
+ else
86
+ self.stack.reverse.each do |obj|
87
+ if obj.is_a?(Nanoc::ItemRep)
88
+ $stderr.puts " - [item] #{obj.item.identifier} (rep #{obj.name})"
89
+ else # layout
90
+ $stderr.puts " - [layout] #{obj.identifier}"
91
+ end
92
+ end
93
+ end
94
+
95
+ # Backtrace
96
+ $stderr.puts
97
+ $stderr.puts '=== BACKTRACE:'
98
+ $stderr.puts
99
+ $stderr.puts error.backtrace.to_enum(:each_with_index).map { |item, index| " #{index}. #{item}" }.join("\n")
100
+
101
+ # Extra information
102
+ $stderr.puts
103
+ $stderr.puts '=== VERSION INFORMATION:'
104
+ $stderr.puts
105
+ $stderr.puts Nanoc.version_information
106
+
107
+ # Issue link
108
+ $stderr.puts
109
+ $stderr.puts "If you believe this is a bug in nanoc, please do report it at"
110
+ $stderr.puts "<https://github.com/ddfreyne/nanoc/issues/new>--thanks!"
111
+ end
112
+
113
+ protected
114
+
115
+ # @return [Boolean] true if debug output is enabled, false if not
116
+ #
117
+ # @see Nanoc::CLI.debug?
118
+ def debug?
119
+ Nanoc::CLI.debug?
120
+ end
121
+
122
+ # @return [Nanoc::Site] The site that is currently being processed
123
+ def site
124
+ @command && @command.site
125
+ end
126
+
127
+ # @return [Nanoc::Compiler] The compiler for the current site
128
+ def compiler
129
+ site && site.compiler
130
+ end
131
+
132
+ # @return [Array] The current compilation stack
133
+ def stack
134
+ (compiler && compiler.stack) || []
135
+ end
136
+
137
+ # A hash that contains the name of the gem for a given required file. If a
138
+ # `#require` fails, the gem name is looked up in this hash.
139
+ GEM_NAMES = {
140
+ 'adsf' => 'adsf',
141
+ 'bluecloth' => 'bluecloth',
142
+ 'builder' => 'builder',
143
+ 'coderay' => 'coderay',
144
+ 'cri' => 'cri',
145
+ 'erubis' => 'erubis',
146
+ 'escape' => 'escape',
147
+ 'fssm' => 'fssm',
148
+ 'haml' => 'haml',
149
+ 'json' => 'json',
150
+ 'kramdown' => 'kramdown',
151
+ 'less' => 'less',
152
+ 'markaby' => 'markaby',
153
+ 'maruku' => 'maruku',
154
+ 'mime/types' => 'mime-types',
155
+ 'nokogiri' => 'nokogiri',
156
+ 'rack' => 'rack',
157
+ 'rack/cache' => 'rack-cache',
158
+ 'rainpress' => 'rainpress',
159
+ 'rdiscount' => 'rdiscount',
160
+ 'redcarpet' => 'redcarpet',
161
+ 'redcloth' => 'redcloth',
162
+ 'rubypants' => 'rubypants',
163
+ 'sass' => 'sass',
164
+ 'systemu' => 'systemu',
165
+ 'w3c_validators' => 'w3c_validators'
166
+ }
167
+
168
+ # Attempts to find a resolution for the given error, or nil if no
169
+ # resolution can be automatically obtained.
170
+ #
171
+ # @param [Error] error The error to find a resolution for
172
+ #
173
+ # @return [String] The resolution for the given error
174
+ def resolution_for(error)
175
+ case error
176
+ when LoadError
177
+ # Get gem name
178
+ matches = error.message.match(/(no such file to load|cannot load such file) -- ([^\s]+)/)
179
+ return nil if matches.nil?
180
+ gem_name = GEM_NAMES[matches[2]]
181
+
182
+ # Build message
183
+ if gem_name
184
+ "Try installing the '#{gem_name}' gem (`gem install #{gem_name}`) and then re-running the command."
185
+ end
186
+ when RuntimeError
187
+ if error.message =~ /^can't modify frozen/
188
+ "You attempted to modify immutable data. Some data, such as " \
189
+ "item/layout attributes and raw item/layout content, can not " \
190
+ "be modified once compilation has started. (This was " \
191
+ "unintentionally possible in 3.1.x and before, but has been " \
192
+ "disabled in 3.2.x in order to allow compiler optimisations.)"
193
+ end
194
+ end
195
+ end
196
+
197
+ end
198
+
199
+ end
@@ -0,0 +1,92 @@
1
+ # encoding: utf-8
2
+
3
+ require 'singleton'
4
+
5
+ module Nanoc::CLI
6
+
7
+ # Nanoc::CLI::Logger is a singleton class responsible for generating
8
+ # feedback in the terminal.
9
+ class Logger
10
+
11
+ # Maps actions (`:create`, `:update`, `:identical`, `:skip` and `:delete`)
12
+ # onto their ANSI color codes.
13
+ ACTION_COLORS = {
14
+ :create => "\e[1m" + "\e[32m", # bold + green
15
+ :update => "\e[1m" + "\e[33m", # bold + yellow
16
+ :identical => "\e[1m", # bold
17
+ :skip => "\e[1m", # bold
18
+ :delete => "\e[1m" + "\e[31m" # bold + red
19
+ }
20
+
21
+ include Singleton
22
+
23
+ # Returns the log level, which can be :high, :low or :off (which will log
24
+ # all messages, only high-priority messages, or no messages at all,
25
+ # respectively).
26
+ #
27
+ # @return [Symbol] The log level
28
+ attr_accessor :level
29
+
30
+ # @return [Boolean] True if color should be used, false otherwise
31
+ attr_writer :color
32
+
33
+ def initialize
34
+ @level = :high
35
+ end
36
+
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
+ # Logs a file-related action.
52
+ #
53
+ # @param [:high, :low] level The importance of this action
54
+ #
55
+ # @param [:create, :update, :identical, :skip, :delete] action The kind of file action
56
+ #
57
+ # @param [String] name The name of the file the action was performed on
58
+ #
59
+ # @return [void]
60
+ def file(level, action, identifier, duration=nil)
61
+ log(
62
+ level,
63
+ '%s%12s%s %s%s' % [
64
+ color? ? ACTION_COLORS[action.to_sym] : '',
65
+ action,
66
+ color? ? "\e[0m" : '',
67
+ duration.nil? ? '' : "[%2.2fs] " % [ duration ],
68
+ identifier
69
+ ]
70
+ )
71
+ end
72
+
73
+ # Logs a message.
74
+ #
75
+ # @param [:high, :low] level The importance of this message
76
+ #
77
+ # @param [String] message The message to be logged
78
+ #
79
+ # @param [#puts] io The stream to which the message should be written
80
+ #
81
+ # @return [void]
82
+ def log(level, message, io=$stdout)
83
+ # Don't log when logging is disabled
84
+ return if @level == :off
85
+
86
+ # Log when level permits it
87
+ io.puts(message) if (@level == :low or @level == level)
88
+ end
89
+
90
+ end
91
+
92
+ end