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
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Nanoc::Int::InMemDataSource, stdio: true do
4
+ let(:klass) do
5
+ Class.new(Nanoc::DataSource) do
6
+ def item_changes
7
+ %i[one_foo one_bar]
8
+ end
9
+
10
+ def layout_changes
11
+ %i[one_foo one_bar]
12
+ end
13
+ end
14
+ end
15
+
16
+ let(:original_data_source) do
17
+ klass.new({}, nil, nil, {})
18
+ end
19
+
20
+ subject(:data_source) do
21
+ described_class.new([], [], original_data_source)
22
+ end
23
+
24
+ describe '#item_changes' do
25
+ subject { data_source.item_changes }
26
+
27
+ it 'yields changes from the original' do
28
+ expect(subject).to eq(original_data_source.item_changes)
29
+ end
30
+ end
31
+
32
+ describe '#layout_changes' do
33
+ subject { data_source.layout_changes }
34
+
35
+ it 'yields changes from the original' do
36
+ expect(subject).to eq(original_data_source.layout_changes)
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Nanoc::Int::PrefixedDataSource, stdio: true do
4
+ let(:klass) do
5
+ Class.new(Nanoc::DataSource) do
6
+ def item_changes
7
+ %i[one_foo one_bar]
8
+ end
9
+
10
+ def layout_changes
11
+ %i[one_foo one_bar]
12
+ end
13
+ end
14
+ end
15
+
16
+ let(:original_data_source) do
17
+ klass.new({}, nil, nil, {})
18
+ end
19
+
20
+ subject(:data_source) do
21
+ described_class.new(original_data_source, '/itemz', '/layoutz')
22
+ end
23
+
24
+ describe '#item_changes' do
25
+ subject { data_source.item_changes }
26
+
27
+ it 'yields changes from the original' do
28
+ expect(subject).to eq(original_data_source.item_changes)
29
+ end
30
+ end
31
+
32
+ describe '#layout_changes' do
33
+ subject { data_source.layout_changes }
34
+
35
+ it 'yields changes from the original' do
36
+ expect(subject).to eq(original_data_source.layout_changes)
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Nanoc::CLI::ErrorHandler do
4
+ subject(:error_handler) { described_class.new }
5
+
6
+ describe '#forwards_stack_trace?' do
7
+ subject { error_handler.forwards_stack_trace? }
8
+
9
+ context 'feature enabled' do
10
+ around do |ex|
11
+ Nanoc::Feature.enable(Nanoc::Feature::SENSIBLE_STACK_TRACES) do
12
+ ex.run
13
+ end
14
+ end
15
+
16
+ context 'Ruby 2.4' do
17
+ it { is_expected.to be(true) }
18
+ end
19
+
20
+ context 'Ruby 2.5' do
21
+ it { is_expected.to be(true) }
22
+ end
23
+ end
24
+
25
+ context 'feature not enabled' do
26
+ context 'Ruby 2.4' do
27
+ before do
28
+ expect(error_handler).to receive(:ruby_version).and_return('2.4.2')
29
+ end
30
+
31
+ it { is_expected.to be(false) }
32
+ end
33
+
34
+ context 'Ruby 2.5' do
35
+ before do
36
+ expect(error_handler).to receive(:ruby_version).and_return('2.5.0')
37
+ end
38
+
39
+ it { is_expected.to be(true) }
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,156 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Nanoc::CLI::StackTraceWriter do
4
+ subject(:writer) do
5
+ described_class.new(io, forwards: forwards)
6
+ end
7
+
8
+ let(:io) { StringIO.new }
9
+ let(:forwards) { true }
10
+
11
+ describe '#write' do
12
+ let(:exception) do
13
+ backtrace_generator = lambda do |af|
14
+ if af.zero?
15
+ raise 'finally!'
16
+ else
17
+ backtrace_generator.call(af - 1)
18
+ end
19
+ end
20
+
21
+ begin
22
+ backtrace_generator.call(3)
23
+ rescue => e
24
+ return e
25
+ end
26
+ end
27
+
28
+ subject { writer.write(exception, verbose: verbose) }
29
+
30
+ let(:verbose) { false }
31
+
32
+ context 'backwards' do
33
+ let(:forwards) { false }
34
+
35
+ context 'verbose' do
36
+ let(:verbose) { true }
37
+
38
+ it 'starts with zero' do
39
+ expect { subject }
40
+ .to change { io.string }
41
+ .from('')
42
+ .to(start_with(' 0. '))
43
+ end
44
+
45
+ it 'has more recent stack frames at the top' do
46
+ expect { subject }
47
+ .to change { io.string }
48
+ .from('')
49
+ .to(match(%r{^ 0\. /.+/spec/nanoc/cli/stack_trace_writer_spec\.rb:\d+.*$\n 1\. /.+/spec/nanoc/cli/stack_trace_writer_spec\.rb:\d}m))
50
+ end
51
+
52
+ it 'has more than 10 stack frames' do
53
+ expect { subject }
54
+ .to change { io.string }
55
+ .from('')
56
+ .to(match(%r{^ 11\. }))
57
+ end
58
+
59
+ it 'does not contain a see-more explanation' do
60
+ subject
61
+ expect(io.string).not_to match(/crash\.log/)
62
+ end
63
+ end
64
+
65
+ context 'not verbose' do
66
+ let(:verbose) { false }
67
+
68
+ it 'starts with zero' do
69
+ expect { subject }
70
+ .to change { io.string }
71
+ .from('')
72
+ .to(start_with(' 0. '))
73
+ end
74
+
75
+ it 'has more recent stack frames at the top' do
76
+ expect { subject }
77
+ .to change { io.string }
78
+ .from('')
79
+ .to(match(%r{^ 0\. /.+/spec/nanoc/cli/stack_trace_writer_spec\.rb:\d+.*$\n 1\. /.+/spec/nanoc/cli/stack_trace_writer_spec\.rb:\d}m))
80
+ end
81
+
82
+ it 'has not more than 10 stack frames' do
83
+ subject
84
+ expect(io.string).not_to match(/^ 11\. /)
85
+ end
86
+
87
+ it 'does not contain a see-more explanation' do
88
+ subject
89
+ expect(io.string).to include(" lines omitted (see crash.log for details)\n")
90
+ end
91
+ end
92
+ end
93
+
94
+ context 'forwards' do
95
+ let(:forwards) { true }
96
+
97
+ context 'verbose' do
98
+ let(:verbose) { true }
99
+
100
+ it 'ends with most recent line' do
101
+ expect { subject }
102
+ .to change { io.string }
103
+ .from('')
104
+ .to(match(%r{^ 1\. from /.+/spec/nanoc/cli/stack_trace_writer_spec\.rb:\d+.*$\n /.+/spec/nanoc/cli}m))
105
+ end
106
+
107
+ it 'has more recent stack frames at the bottom' do
108
+ expect { subject }
109
+ .to change { io.string }
110
+ .from('')
111
+ .to(match(%r{^ 2\. from /.+/spec/nanoc/cli/stack_trace_writer_spec\.rb:\d+.*$\n 1\. from /.+/spec/nanoc/cli/stack_trace_writer_spec\.rb:\d}m))
112
+ end
113
+
114
+ it 'has more than 10 stack frames' do
115
+ expect { subject }
116
+ .to change { io.string }
117
+ .from('')
118
+ .to(match(%r{^ 11\. from }))
119
+ end
120
+
121
+ it 'does not contain a see-more explanation' do
122
+ subject
123
+ expect(io.string).not_to match(/crash\.log/)
124
+ end
125
+ end
126
+
127
+ context 'not verbose' do
128
+ let(:verbose) { false }
129
+
130
+ it 'ends with most recent line' do
131
+ expect { subject }
132
+ .to change { io.string }
133
+ .from('')
134
+ .to(match(%r{^ 1\. from /.+/spec/nanoc/cli/stack_trace_writer_spec\.rb:\d+.*$\n /.+/spec/nanoc/cli}m))
135
+ end
136
+
137
+ it 'has more recent stack frames at the top' do
138
+ expect { subject }
139
+ .to change { io.string }
140
+ .from('')
141
+ .to(match(%r{^ 2\. from /.+/spec/nanoc/cli/stack_trace_writer_spec\.rb:\d+.*$\n 1\. from /.+/spec/nanoc/cli/stack_trace_writer_spec\.rb:\d}m))
142
+ end
143
+
144
+ it 'has not more than 10 stack frames' do
145
+ subject
146
+ expect(io.string).not_to match(/^ 11\. from /)
147
+ end
148
+
149
+ it 'does not contain a see-more explanation' do
150
+ subject
151
+ expect(io.string).to include(" lines omitted (see crash.log for details)\n")
152
+ end
153
+ end
154
+ end
155
+ end
156
+ end
@@ -79,4 +79,50 @@ describe Nanoc::DataSources::Filesystem do
79
79
  end
80
80
  end
81
81
  end
82
+
83
+ describe '#item_changes' do
84
+ subject { data_source.item_changes }
85
+
86
+ it 'returns a stream' do
87
+ expect(subject).to be_a(Nanoc::ChangesStream)
88
+ end
89
+
90
+ it 'contains one element after changing' do
91
+ FileUtils.mkdir_p('content')
92
+
93
+ enum = DDBuffer.new(1).call(subject.to_enum)
94
+ q = SizedQueue.new(1)
95
+ Thread.new { q << enum.take(1).first }
96
+
97
+ # FIXME: sleep is ugly
98
+ sleep 0.3
99
+ File.write('content/wat.md', 'stuff')
100
+
101
+ expect(q.pop).to eq(:unknown)
102
+ subject.stop
103
+ end
104
+ end
105
+
106
+ describe '#layout_changes' do
107
+ subject { data_source.layout_changes }
108
+
109
+ it 'returns a stream' do
110
+ expect(subject).to be_a(Nanoc::ChangesStream)
111
+ end
112
+
113
+ it 'contains one element after changing' do
114
+ FileUtils.mkdir_p('layouts')
115
+
116
+ enum = DDBuffer.new(1).call(subject.to_enum)
117
+ q = SizedQueue.new(1)
118
+ Thread.new { q << enum.take(1).first }
119
+
120
+ # FIXME: sleep is ugly
121
+ sleep 0.3
122
+ File.write('layouts/wat.md', 'stuff')
123
+
124
+ expect(q.pop).to eq(:unknown)
125
+ subject.stop
126
+ end
127
+ end
82
128
  end
@@ -0,0 +1,129 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Nanoc::Extra::LiveRecompiler, site: true, stdio: true do
4
+ it 'detects content changes' do
5
+ command = nil
6
+ command_runner = Nanoc::CLI::CommandRunner.new({}, [], command)
7
+ live_recompiler = described_class.new(command_runner: command_runner)
8
+
9
+ pid = fork do
10
+ trap(:INT) { exit(0) }
11
+ live_recompiler.run
12
+ end
13
+
14
+ # FIXME: wait is ugly
15
+ sleep 0.5
16
+
17
+ File.write('content/lol.html', 'hej')
18
+ sleep 0.1 until File.file?('output/lol.html')
19
+ expect(File.read('output/lol.html')).to eq('hej')
20
+
21
+ sleep 1.0 # HFS+ mtime resolution is 1s
22
+ File.write('content/lol.html', 'bye')
23
+ sleep 0.1 until File.read('output/lol.html') == 'bye'
24
+
25
+ # Stop
26
+ Process.kill('INT', pid)
27
+ Process.waitpid(pid)
28
+ end
29
+
30
+ it 'detects rules changes' do
31
+ command = nil
32
+ command_runner = Nanoc::CLI::CommandRunner.new({}, [], command)
33
+ live_recompiler = described_class.new(command_runner: command_runner)
34
+
35
+ pid = fork do
36
+ trap(:INT) { exit(0) }
37
+ live_recompiler.run
38
+ end
39
+
40
+ # FIXME: wait is ugly
41
+ sleep 0.5
42
+
43
+ File.write('content/lol.html', '<%= "hej" %>')
44
+ sleep 0.1 until File.file?('output/lol.html')
45
+ expect(File.read('output/lol.html')).to eq('<%= "hej" %>')
46
+
47
+ sleep 1.0 # HFS+ mtime resolution is 1s
48
+ File.write('Rules', <<~RULES)
49
+ compile '/**/*' do
50
+ filter :erb
51
+ write item.identifier
52
+ end
53
+ RULES
54
+ sleep 0.1 until File.read('output/lol.html') == 'hej'
55
+
56
+ # Stop
57
+ Process.kill('INT', pid)
58
+ Process.waitpid(pid)
59
+ end
60
+
61
+ it 'detects config changes' do
62
+ command = nil
63
+ command_runner = Nanoc::CLI::CommandRunner.new({}, [], command)
64
+ live_recompiler = described_class.new(command_runner: command_runner)
65
+
66
+ File.write('nanoc.yaml', 'site_name: Oldz')
67
+ File.write('content/lol.html', '<%= @config[:site_name] %>')
68
+ File.write('Rules', <<~RULES)
69
+ compile '/**/*' do
70
+ filter :erb
71
+ write item.identifier
72
+ end
73
+ RULES
74
+
75
+ pid = fork do
76
+ trap(:INT) { exit(0) }
77
+ live_recompiler.run
78
+ end
79
+
80
+ # FIXME: wait is ugly
81
+ sleep 0.5
82
+
83
+ sleep 0.1 until File.file?('output/lol.html')
84
+ expect(File.read('output/lol.html')).to eq('Oldz')
85
+
86
+ sleep 1.0 # HFS+ mtime resolution is 1s
87
+ File.write('nanoc.yaml', 'site_name: Newz')
88
+ sleep 0.1 until File.read('output/lol.html') == 'Newz'
89
+
90
+ # Stop
91
+ Process.kill('INT', pid)
92
+ Process.waitpid(pid)
93
+ end
94
+
95
+ it 'detects config changes' do
96
+ command = nil
97
+ command_runner = Nanoc::CLI::CommandRunner.new({}, [], command)
98
+ live_recompiler = described_class.new(command_runner: command_runner)
99
+
100
+ FileUtils.mkdir_p('lib')
101
+ File.write('lib/lol.rb', 'def greeting; "hi"; end')
102
+ File.write('content/lol.html', '<%= greeting %>')
103
+ File.write('Rules', <<~RULES)
104
+ compile '/**/*' do
105
+ filter :erb
106
+ write item.identifier
107
+ end
108
+ RULES
109
+
110
+ pid = fork do
111
+ trap(:INT) { exit(0) }
112
+ live_recompiler.run
113
+ end
114
+
115
+ # FIXME: wait is ugly
116
+ sleep 0.5
117
+
118
+ sleep 0.1 until File.file?('output/lol.html')
119
+ expect(File.read('output/lol.html')).to eq('hi')
120
+
121
+ sleep 1.0 # HFS+ mtime resolution is 1s
122
+ File.write('lib/lol.rb', 'def greeting; "yo"; end')
123
+ sleep 0.1 until File.read('output/lol.html') == 'yo'
124
+
125
+ # Stop
126
+ Process.kill('INT', pid)
127
+ Process.waitpid(pid)
128
+ end
129
+ end