nanoc 4.8.10 → 4.8.11

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 (79) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +6 -0
  3. data/NEWS.md +7 -0
  4. data/lib/nanoc.rb +11 -6
  5. data/lib/nanoc/base.rb +1 -0
  6. data/lib/nanoc/base/changes_stream.rb +53 -0
  7. data/lib/nanoc/base/contracts_support.rb +0 -2
  8. data/lib/nanoc/base/feature.rb +3 -0
  9. data/lib/nanoc/base/memoization.rb +0 -2
  10. data/lib/nanoc/base/repos/aggregate_data_source.rb +8 -0
  11. data/lib/nanoc/base/repos/data_source.rb +12 -0
  12. data/lib/nanoc/base/repos/in_mem_data_source.rb +10 -1
  13. data/lib/nanoc/base/repos/prefixed_data_source.rb +8 -0
  14. data/lib/nanoc/base/repos/site_loader.rb +11 -7
  15. data/lib/nanoc/base/services/compiler.rb +2 -3
  16. data/lib/nanoc/base/services/compiler/stages/postprocess.rb +2 -3
  17. data/lib/nanoc/base/services/compiler/stages/preprocess.rb +1 -1
  18. data/lib/nanoc/base/services/pruner.rb +0 -2
  19. data/lib/nanoc/base/services/temp_filename_factory.rb +0 -2
  20. data/lib/nanoc/checking/checks/external_links.rb +0 -5
  21. data/lib/nanoc/checking/checks/internal_links.rb +0 -2
  22. data/lib/nanoc/checking/checks/stale.rb +0 -2
  23. data/lib/nanoc/cli.rb +7 -1
  24. data/lib/nanoc/cli/commands/compile_listeners/diff_generator.rb +0 -3
  25. data/lib/nanoc/cli/commands/live.rb +30 -0
  26. data/lib/nanoc/cli/commands/view.rb +4 -5
  27. data/lib/nanoc/cli/error_handler.rb +52 -36
  28. data/lib/nanoc/cli/logger.rb +0 -2
  29. data/lib/nanoc/cli/stack_trace_writer.rb +50 -0
  30. data/lib/nanoc/data_sources/filesystem.rb +25 -0
  31. data/lib/nanoc/extra.rb +1 -0
  32. data/lib/nanoc/extra/jruby_nokogiri_warner.rb +0 -2
  33. data/lib/nanoc/extra/link_collector.rb +0 -2
  34. data/lib/nanoc/extra/live_recompiler.rb +131 -0
  35. data/lib/nanoc/extra/parallel_collection.rb +0 -2
  36. data/lib/nanoc/extra/piper.rb +0 -2
  37. data/lib/nanoc/filters/relativize_paths.rb +8 -7
  38. data/lib/nanoc/helpers/link_to.rb +0 -2
  39. data/lib/nanoc/rule_dsl/action_provider.rb +2 -2
  40. data/lib/nanoc/version.rb +1 -1
  41. data/nanoc.gemspec +15 -4
  42. data/nanoc.manifest +545 -0
  43. data/spec/manifest_spec.rb +22 -0
  44. data/spec/nanoc/base/changes_stream_spec.rb +45 -0
  45. data/spec/nanoc/base/checksummer_spec.rb +0 -2
  46. data/spec/nanoc/base/directed_graph_spec.rb +66 -0
  47. data/spec/nanoc/base/entities/code_snippet_spec.rb +9 -0
  48. data/spec/nanoc/base/entities/context_spec.rb +26 -0
  49. data/spec/nanoc/base/entities/identifiable_collection_spec.rb +53 -0
  50. data/spec/nanoc/base/repos/aggregate_data_source_spec.rb +87 -0
  51. data/spec/nanoc/base/repos/data_source_spec.rb +95 -0
  52. data/spec/nanoc/base/repos/in_mem_data_source_spec.rb +39 -0
  53. data/spec/nanoc/base/repos/prefixed_data_source_spec.rb +39 -0
  54. data/spec/nanoc/cli/error_handler_spec.rb +43 -0
  55. data/spec/nanoc/cli/stack_trace_writer_spec.rb +156 -0
  56. data/spec/nanoc/data_sources/filesystem_spec.rb +46 -0
  57. data/spec/nanoc/extra/live_recompiler_spec.rb +129 -0
  58. data/spec/nanoc/helpers/blogging_spec.rb +1 -1
  59. data/spec/spec_helper.rb +60 -0
  60. data/test/base/test_compiler.rb +11 -11
  61. data/test/cli/test_cli.rb +0 -1
  62. data/test/cli/test_error_handler.rb +4 -5
  63. data/test/filters/test_relativize_paths.rb +30 -0
  64. data/test/filters/test_sass.rb +3 -3
  65. data/test/rule_dsl/test_compiler_dsl.rb +2 -2
  66. metadata +39 -43
  67. data/.github/CONTRIBUTING.md +0 -17
  68. data/.github/ISSUE_TEMPLATE.md +0 -23
  69. data/.github/PULL_REQUEST_TEMPLATE.md +0 -18
  70. data/.gitignore +0 -10
  71. data/.travis.yml +0 -27
  72. data/Gemfile +0 -73
  73. data/Guardfile +0 -5
  74. data/scripts/release +0 -95
  75. data/test/base/test_code_snippet.rb +0 -17
  76. data/test/base/test_context.rb +0 -35
  77. data/test/base/test_data_source.rb +0 -60
  78. data/test/base/test_directed_graph.rb +0 -56
  79. data/test/base/test_item_array.rb +0 -37
@@ -20,7 +20,7 @@ module Nanoc::CLI::Commands
20
20
  load_adsf
21
21
  require 'rack'
22
22
 
23
- @site = load_site
23
+ config = Nanoc::Int::ConfigLoader.new.new_from_cwd
24
24
 
25
25
  # Set options
26
26
  options_for_rack = {
@@ -40,16 +40,15 @@ module Nanoc::CLI::Commands
40
40
  end
41
41
 
42
42
  # Build app
43
- site = @site
44
43
  app = Rack::Builder.new do
45
44
  use Rack::CommonLogger
46
45
  use Rack::ShowExceptions
47
46
  use Rack::Lint
48
47
  use Rack::Head
49
48
  use Adsf::Rack::IndexFileFinder,
50
- root: site.config[:output_dir],
51
- index_filenames: site.config[:index_filenames]
52
- run Rack::File.new(site.config[:output_dir])
49
+ root: config[:output_dir],
50
+ index_filenames: config[:index_filenames]
51
+ run Rack::File.new(config[:output_dir])
53
52
  end.to_app
54
53
 
55
54
  # Print a link
@@ -64,13 +64,16 @@ module Nanoc::CLI
64
64
 
65
65
  # Run
66
66
  yield
67
- rescue Nanoc::Int::Errors::GenericTrivial => e
68
- $stderr.puts "Error: #{e.message}"
69
- exit(1)
70
67
  rescue Interrupt
71
68
  exit(1)
72
69
  rescue StandardError, ScriptError => e
73
- print_error(e)
70
+ if trivial?(e)
71
+ $stderr.puts "Error: #{e.message}"
72
+ resolution = resolution_for(e)
73
+ $stderr.puts resolution if resolution
74
+ else
75
+ print_error(e)
76
+ end
74
77
  exit(1)
75
78
  end
76
79
 
@@ -109,17 +112,21 @@ module Nanoc::CLI
109
112
  #
110
113
  # @return [void]
111
114
  def write_compact_error(error, stream)
112
- # Header
113
115
  stream.puts
114
116
  stream.puts 'Captain! We’ve been hit!'
115
117
 
116
- # Sections
117
- write_error_message(stream, error)
118
- write_item_rep(stream, error)
119
- write_stack_trace(stream, error)
118
+ if forwards_stack_trace?
119
+ write_stack_trace(stream, error)
120
+ write_error_message(stream, error)
121
+ write_item_rep(stream, error)
122
+ else
123
+ write_error_message(stream, error)
124
+ write_item_rep(stream, error)
125
+ write_stack_trace(stream, error)
126
+ end
120
127
 
121
- # Issue link
122
- write_issue_link(stream)
128
+ stream.puts
129
+ stream.puts 'A detailed crash log has been written to ./crash.log.'
123
130
  end
124
131
 
125
132
  # Writes a verbose representation of the error on the given stream.
@@ -130,10 +137,8 @@ module Nanoc::CLI
130
137
  #
131
138
  # @return [void]
132
139
  def write_verbose_error(error, stream)
133
- # Header
134
140
  stream.puts "Crashlog created at #{Time.now}"
135
141
 
136
- # Sections
137
142
  write_error_message(stream, error, verbose: true)
138
143
  write_item_rep(stream, error, verbose: true)
139
144
  write_stack_trace(stream, error, verbose: true)
@@ -144,6 +149,14 @@ module Nanoc::CLI
144
149
  write_load_paths(stream, verbose: true)
145
150
  end
146
151
 
152
+ # @api private
153
+ def forwards_stack_trace?
154
+ feature_enabled = Nanoc::Feature.enabled?(Nanoc::Feature::SENSIBLE_STACK_TRACES)
155
+ ruby_2_5_used = ruby_version.start_with?('2.5')
156
+
157
+ feature_enabled || ruby_2_5_used
158
+ end
159
+
147
160
  protected
148
161
 
149
162
  # @return [Hash<String, Array>] A hash containing the gem names as keys and gem versions as value
@@ -191,6 +204,15 @@ module Nanoc::CLI
191
204
  'w3c_validators' => 'w3c_validators',
192
205
  }.freeze
193
206
 
207
+ def trivial?(error)
208
+ case error
209
+ when Nanoc::Int::Errors::GenericTrivial, Errno::EADDRINUSE
210
+ true
211
+ else
212
+ false
213
+ end
214
+ end
215
+
194
216
  # Attempts to find a resolution for the given error, or nil if no
195
217
  # resolution can be automatically obtained.
196
218
  #
@@ -221,6 +243,9 @@ module Nanoc::CLI
221
243
  'be modified once compilation has started. Such data includes ' \
222
244
  'content and attributes of items and layouts, and filter arguments.'
223
245
  end
246
+ when Errno::EADDRINUSE
247
+ 'There already is a server running. Either shut down that one, or ' \
248
+ 'specify a different port to run this server on.'
224
249
  end
225
250
  end
226
251
 
@@ -228,14 +253,17 @@ module Nanoc::CLI
228
253
  defined?(Bundler) && Bundler::SharedHelpers.in_bundle?
229
254
  end
230
255
 
256
+ def ruby_version
257
+ RUBY_VERSION
258
+ end
259
+
231
260
  def write_section_header(stream, title, verbose: false)
232
261
  stream.puts
262
+
233
263
  if verbose
234
264
  stream.puts '===== ' + title.upcase + ':'
235
- else
236
- stream.puts "\e[1m\e[31m" + title + ':' + "\e[0m"
265
+ stream.puts
237
266
  end
238
- stream.puts
239
267
  end
240
268
 
241
269
  def write_error_message(stream, error, verbose: false)
@@ -243,7 +271,11 @@ module Nanoc::CLI
243
271
 
244
272
  error = unwrap_error(error)
245
273
 
246
- stream.puts "#{error.class}: #{error.message}"
274
+ message = "#{error.class}: #{error.message}"
275
+ unless verbose
276
+ message = "\e[1m\e[31m" + message + "\e[0m"
277
+ end
278
+ stream.puts message
247
279
  resolution = resolution_for(error)
248
280
  stream.puts resolution.to_s if resolution
249
281
  end
@@ -254,30 +286,14 @@ module Nanoc::CLI
254
286
  write_section_header(stream, 'Item being compiled', verbose: verbose)
255
287
 
256
288
  item_rep = error.item_rep
257
- stream.puts "Item identifier: #{item_rep.item.identifier}"
258
- stream.puts "Item rep name: #{item_rep.name.inspect}"
289
+ stream.puts "Current item: #{item_rep.item.identifier} (#{item_rep.name.inspect} representation)"
259
290
  end
260
291
 
261
292
  def write_stack_trace(stream, error, verbose: false)
262
293
  write_section_header(stream, 'Stack trace', verbose: verbose)
263
294
 
264
- error = unwrap_error(error)
265
-
266
- count = verbose ? -1 : 10
267
- error.backtrace[0...count].each_with_index do |item, index|
268
- stream.puts " #{index}. #{item}"
269
- end
270
- if !verbose && error.backtrace.size > count
271
- stream.puts " ... #{error.backtrace.size - count} more lines omitted. See full crash log for details."
272
- end
273
- end
274
-
275
- def write_issue_link(stream, _params = {})
276
- stream.puts
277
- stream.puts 'If you believe this is a bug in Nanoc, please do report it at'
278
- stream.puts '-> https://github.com/nanoc/nanoc/issues/new <-'
279
- stream.puts
280
- stream.puts 'A detailed crash log has been written to ./crash.log.'
295
+ writer = StackTraceWriter.new(stream, forwards: forwards_stack_trace?)
296
+ writer.write(unwrap_error(error), verbose: verbose)
281
297
  end
282
298
 
283
299
  def write_version_information(stream, verbose: false)
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'singleton'
4
-
5
3
  module Nanoc::CLI
6
4
  # Nanoc::CLI::Logger is a singleton class responsible for generating
7
5
  # feedback in the terminal.
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc::CLI
4
+ # @api private
5
+ class StackTraceWriter
6
+ def initialize(stream, forwards:)
7
+ @stream = stream
8
+ @forwards = forwards
9
+ end
10
+
11
+ def write(error, verbose:)
12
+ if @forwards
13
+ write_forwards(error, verbose: verbose)
14
+ else
15
+ write_backwards(error, verbose: verbose)
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def write_backwards(error, verbose:)
22
+ count = verbose ? -1 : 10
23
+
24
+ error.backtrace[0...count].each_with_index do |item, index|
25
+ @stream.puts " #{index}. #{item}"
26
+ end
27
+
28
+ if !verbose && error.backtrace.size > count
29
+ @stream.puts " ... #{error.backtrace.size - count} lines omitted (see crash.log for details)"
30
+ end
31
+ end
32
+
33
+ def write_forwards(error, verbose:)
34
+ count = 10
35
+ backtrace = verbose ? error.backtrace : error.backtrace.take(count)
36
+
37
+ if !verbose && error.backtrace.size > count
38
+ @stream.puts " ... #{error.backtrace.size - count} lines omitted (see crash.log for details)"
39
+ end
40
+
41
+ backtrace.each_with_index.to_a.reverse.each do |(item, index)|
42
+ if index.zero?
43
+ @stream.puts " #{item}"
44
+ else
45
+ @stream.puts " #{index}. from #{item}"
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -74,6 +74,31 @@ module Nanoc::DataSources
74
74
  load_objects(layouts_dir_name, Nanoc::Int::Layout)
75
75
  end
76
76
 
77
+ def item_changes
78
+ changes_for_dir(content_dir_name)
79
+ end
80
+
81
+ def layout_changes
82
+ changes_for_dir(layouts_dir_name)
83
+ end
84
+
85
+ def changes_for_dir(dir)
86
+ require 'listen'
87
+
88
+ Nanoc::ChangesStream.new do |cl|
89
+ listener =
90
+ Listen.to(dir, latency: 0.0, wait_for_delay: 0.0) do |_modifieds, _addeds, _deleteds|
91
+ cl.unknown
92
+ end
93
+
94
+ listener.start
95
+
96
+ cl.to_stop { listener.stop }
97
+
98
+ sleep
99
+ end
100
+ end
101
+
77
102
  protected
78
103
 
79
104
  class ProtoDocument
data/lib/nanoc/extra.rb CHANGED
@@ -16,6 +16,7 @@ module Nanoc::Extra
16
16
  end
17
17
 
18
18
  require_relative 'extra/link_collector.rb'
19
+ require_relative 'extra/live_recompiler'
19
20
  require_relative 'extra/piper'
20
21
  require_relative 'extra/jruby_nokogiri_warner'
21
22
  require_relative 'extra/core_ext'
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'singleton'
4
-
5
3
  module Nanoc::Extra
6
4
  # @api private
7
5
  class JRubyNokogiriWarner
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'set'
4
-
5
3
  module ::Nanoc::Extra
6
4
  # @api private
7
5
  class LinkCollector
@@ -0,0 +1,131 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc::Extra
4
+ class LiveRecompiler
5
+ def initialize(command_runner:)
6
+ @command_runner = command_runner
7
+ end
8
+
9
+ def run
10
+ run_parent do |site|
11
+ handle_changes(site, @command_runner)
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def gen_changes_for_child(site)
18
+ changes = [
19
+ site.data_source.item_changes,
20
+ site.data_source.layout_changes,
21
+ gen_config_and_rules_changes,
22
+ ]
23
+
24
+ SlowEnumeratorTools.batch(SlowEnumeratorTools.merge(changes))
25
+ end
26
+
27
+ def run_child(pipe_write, pipe_read)
28
+ pipe_write.close
29
+
30
+ site = Nanoc::Int::SiteLoader.new.new_from_cwd
31
+ changes_enum = gen_changes_for_child(site)
32
+ yield(site)
33
+
34
+ quit = Object.new
35
+ parent_enum = Enumerator.new do |y|
36
+ pipe_read.read
37
+ y << quit
38
+ end
39
+
40
+ SlowEnumeratorTools.merge([parent_enum, changes_enum]).each do |e|
41
+ break if quit.equal?(e)
42
+
43
+ $stderr.print 'Reloading site… '
44
+ $stderr.flush
45
+ site_loader = Nanoc::Int::SiteLoader.new
46
+ site = Nanoc::Int::Site.new(
47
+ config: Nanoc::Int::ConfigLoader.new.new_from_cwd,
48
+ data_source: site_loader.gen_data_source_for_config(site.config),
49
+ code_snippets: site.code_snippets,
50
+ )
51
+ $stderr.puts 'done'
52
+
53
+ yield(site)
54
+ end
55
+
56
+ exit 0
57
+ rescue Interrupt
58
+ exit 0
59
+ end
60
+
61
+ def run_parent
62
+ # create initial child
63
+ pipe_read, pipe_write = IO.pipe
64
+ fork { run_child(pipe_write, pipe_read) { |s| yield(s) } }
65
+ pipe_read.close
66
+
67
+ gen_lib_changes.each do |_e|
68
+ # stop child
69
+ pipe_write.write('q')
70
+ pipe_write.close
71
+ Process.wait
72
+
73
+ # create new child
74
+ pipe_read, pipe_write = IO.pipe
75
+ fork { run_child(pipe_write, pipe_read) { |s| yield(s) } }
76
+ pipe_read.close
77
+ end
78
+ rescue Interrupt
79
+ end
80
+
81
+ def handle_changes(site, command_runner)
82
+ time_before = Time.now
83
+
84
+ puts 'Compiling site…'
85
+ compiler = Nanoc::Int::Compiler.new_for(site)
86
+ listener = Nanoc::CLI::Commands::CompileListeners::Aggregate.new(
87
+ command_runner: command_runner,
88
+ site: site,
89
+ compiler: compiler,
90
+ )
91
+ listener.run_while do
92
+ compiler.run_until_end
93
+ end
94
+
95
+ time_after = Time.now
96
+ puts "Site compiled in #{format('%.2f', time_after - time_before)}s."
97
+ puts
98
+ end
99
+
100
+ def gen_lib_changes
101
+ require 'listen'
102
+
103
+ Nanoc::ChangesStream.new do |cl|
104
+ opts = {
105
+ latency: 0.0,
106
+ wait_for_delay: 0.0,
107
+ }
108
+
109
+ listener = Listen.to('lib', opts) { |*| cl.lib }
110
+ listener.start
111
+ sleep
112
+ end
113
+ end
114
+
115
+ def gen_config_and_rules_changes
116
+ require 'listen'
117
+
118
+ Nanoc::ChangesStream.new do |cl|
119
+ opts = {
120
+ only: /(\/|\A)(nanoc\.yaml|config\.yaml|rules|Rules|rules\.rb|Rules\.rb)\z/,
121
+ latency: 0.0,
122
+ wait_for_delay: 0.0,
123
+ }
124
+
125
+ listener = Listen.to('.', opts) { |*| cl.unknown }
126
+ listener.start
127
+ sleep
128
+ end
129
+ end
130
+ end
131
+ end