nanoc 4.8.10 → 4.8.11

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