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
@@ -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