nanoc 3.2.4 → 3.3.0

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