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,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe 'manifest', chdir: false do
4
+ let(:manifest_lines) do
5
+ File.readlines('nanoc.manifest').map(&:chomp).reject(&:empty?)
6
+ end
7
+
8
+ let(:gemspec_lines) do
9
+ gemspec = eval(File.read('nanoc.gemspec'), binding, 'nanoc.gemspec')
10
+ gemspec.files
11
+ end
12
+
13
+ it 'contains all files in gemspec' do
14
+ missing_from_manifest = gemspec_lines - manifest_lines
15
+ expect(missing_from_manifest).to be_empty
16
+ end
17
+
18
+ it 'contains no files not in gemspec' do
19
+ extra_in_manifest = manifest_lines - gemspec_lines
20
+ expect(extra_in_manifest).to be_empty
21
+ end
22
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Nanoc::ChangesStream do
4
+ let(:simple_stream) do
5
+ described_class.new do |cl|
6
+ cl.unknown
7
+ sleep
8
+ end
9
+ end
10
+
11
+ it 'returns a stream of events generated by the listener' do
12
+ buffered_stream = DDBuffer.new(1).call(simple_stream)
13
+ expect(buffered_stream.take(1).to_a).to eq([:unknown])
14
+ end
15
+
16
+ describe '#map' do
17
+ it 'returns a new maped enum' do
18
+ stream = simple_stream.map { |e| e.to_s.upcase }
19
+ buffered_stream = DDBuffer.new(1).call(stream)
20
+ expect(buffered_stream.take(1).to_a).to eq(['UNKNOWN'])
21
+ end
22
+ end
23
+
24
+ describe '#to_enum' do
25
+ it 'returns an enumerator corresponding to itself' do
26
+ buffered_stream = DDBuffer.new(1).call(simple_stream.to_enum)
27
+ expect(buffered_stream.take(1).to_a).to eq([:unknown])
28
+ end
29
+ end
30
+
31
+ describe '#stop' do
32
+ let(:simple_stream) do
33
+ described_class.new do |cl|
34
+ cl.to_stop { $changes_stream_stopped = true }
35
+ sleep
36
+ end
37
+ end
38
+
39
+ example do
40
+ DDBuffer.new(1).call(simple_stream)
41
+ sleep 0.1
42
+ expect { simple_stream.stop }.to change { $changes_stream_stopped }.from(nil).to(true)
43
+ end
44
+ end
45
+ end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'tempfile'
4
-
5
3
  describe Nanoc::Int::Checksummer::VerboseDigest do
6
4
  let(:digest) { described_class.new }
7
5
 
@@ -51,6 +51,37 @@ describe Nanoc::Int::DirectedGraph do
51
51
  end
52
52
  end
53
53
 
54
+ it 'has correct examples' do
55
+ expect('Nanoc::Int::DirectedGraph')
56
+ .to have_correct_yard_examples
57
+ .in_file('lib/nanoc/base/entities/directed_graph.rb')
58
+ end
59
+
60
+ describe '#vertices' do
61
+ subject { graph.vertices }
62
+
63
+ it { is_expected.to include('1') }
64
+ it { is_expected.not_to include('4') }
65
+ end
66
+
67
+ describe '#add_edge' do
68
+ subject { graph.add_edge('1', '4') }
69
+
70
+ it 'adds vertex' do
71
+ expect { subject }
72
+ .to change { graph.vertices.include?('4') }
73
+ .from(false)
74
+ .to(true)
75
+ end
76
+
77
+ it 'changes direct predecessors' do
78
+ expect { subject }
79
+ .to change { graph.direct_predecessors_of('4') }
80
+ .from([])
81
+ .to(['1'])
82
+ end
83
+ end
84
+
54
85
  describe '#props_for' do
55
86
  subject { graph.props_for('1', '2') }
56
87
 
@@ -148,6 +179,41 @@ describe Nanoc::Int::DirectedGraph do
148
179
  end
149
180
  end
150
181
 
182
+ describe '#delete_edges_to' do
183
+ before do
184
+ graph.add_edge('1', '2')
185
+ graph.add_edge('2', '1')
186
+ graph.add_edge('2', '3')
187
+ graph.add_edge('3', '2')
188
+ graph.add_edge('1', '3')
189
+ graph.add_edge('3', '1')
190
+ end
191
+
192
+ subject { graph.delete_edges_to('1') }
193
+
194
+ it 'deletes edges to 1' do
195
+ expect { subject }
196
+ .to change { graph.direct_predecessors_of('1') }
197
+ .from(%w[2 3])
198
+ .to([])
199
+ end
200
+
201
+ it 'keeps edges to 2' do
202
+ expect { subject }
203
+ .not_to change { graph.direct_predecessors_of('2') }
204
+ end
205
+
206
+ it 'keeps edges to 3' do
207
+ expect { subject }
208
+ .not_to change { graph.direct_predecessors_of('3') }
209
+ end
210
+
211
+ it 'keeps edges to 4' do
212
+ expect { subject }
213
+ .not_to change { graph.direct_predecessors_of('4') }
214
+ end
215
+ end
216
+
151
217
  describe '#inspect' do
152
218
  subject { graph.inspect }
153
219
 
@@ -47,5 +47,14 @@ describe Nanoc::Int::CodeSnippet do
47
47
  .to([true, false])
48
48
  end
49
49
  end
50
+
51
+ it 'defines at top level' do
52
+ @foo = 'meow'
53
+
54
+ code_snippet = Nanoc::Int::CodeSnippet.new("@foo = 'woof'", 'dog.rb')
55
+ code_snippet.load
56
+
57
+ expect(@foo).to eq('meow')
58
+ end
50
59
  end
51
60
  end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Nanoc::Int::Context do
4
+ let(:context) do
5
+ Nanoc::Int::Context.new(foo: 'bar', baz: 'quux')
6
+ end
7
+
8
+ it 'provides instance variables' do
9
+ expect(eval('@foo', context.get_binding)).to eq('bar')
10
+ end
11
+
12
+ it 'provides instance methods' do
13
+ expect(eval('foo', context.get_binding)).to eq('bar')
14
+ end
15
+
16
+ it 'supports #include' do
17
+ eval('include Nanoc::Helpers::HTMLEscape', context.get_binding)
18
+ expect(eval('h("<>")', context.get_binding)).to eq('&lt;&gt;')
19
+ end
20
+
21
+ it 'has correct examples' do
22
+ expect('Nanoc::Int::Context#initialize')
23
+ .to have_correct_yard_examples
24
+ .in_file('lib/nanoc/base/entities/context.rb')
25
+ end
26
+ end
@@ -142,6 +142,59 @@ describe Nanoc::Int::IdentifiableCollection do
142
142
  subject { identifiable_collection.reference }
143
143
  it { is_expected.to eql(expected_reference) }
144
144
  end
145
+
146
+ describe 'changing identifiers' do
147
+ let(:objects) do
148
+ [
149
+ Nanoc::Int::Item.new('Foo', {}, '/foo'),
150
+ ]
151
+ end
152
+
153
+ subject { objects[0].identifier = '/bar' }
154
+
155
+ it 'makes /foo nil' do
156
+ expect { subject }
157
+ .to change { identifiable_collection['/foo'] }
158
+ .from(objects[0])
159
+ .to(nil)
160
+ end
161
+
162
+ it 'makes /bar non-nil' do
163
+ expect { subject }
164
+ .to change { identifiable_collection['/bar'] }
165
+ .from(nil)
166
+ .to(objects[0])
167
+ end
168
+ end
169
+
170
+ describe '#each' do
171
+ let(:objects) do
172
+ [
173
+ Nanoc::Int::Item.new('Foo', {}, '/foo'),
174
+ Nanoc::Int::Item.new('Bar', {}, '/bar'),
175
+ ]
176
+ end
177
+
178
+ it 'loops' do
179
+ res = []
180
+ identifiable_collection.each { |i| res << i.identifier.to_s }
181
+ expect(res).to match_array(['/foo', '/bar'])
182
+ end
183
+ end
184
+
185
+ describe '#map' do
186
+ let(:objects) do
187
+ [
188
+ Nanoc::Int::Item.new('Foo', {}, '/foo'),
189
+ Nanoc::Int::Item.new('Bar', {}, '/bar'),
190
+ ]
191
+ end
192
+
193
+ it 'loops' do
194
+ res = identifiable_collection.map { |i| i.identifier.to_s }
195
+ expect(res).to match_array(['/foo', '/bar'])
196
+ end
197
+ end
145
198
  end
146
199
 
147
200
  describe Nanoc::Int::ItemCollection do
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Nanoc::Int::AggregateDataSource, stdio: true do
4
+ let(:klass_1) do
5
+ Class.new(Nanoc::DataSource) do
6
+ def items
7
+ [Nanoc::Int::Item.new('One', {}, '/one.md')]
8
+ end
9
+
10
+ def item_changes
11
+ %i[one_foo one_bar]
12
+ end
13
+
14
+ def layouts
15
+ [Nanoc::Int::Layout.new('One', {}, '/one.md')]
16
+ end
17
+
18
+ def layout_changes
19
+ %i[one_foo one_bar]
20
+ end
21
+ end
22
+ end
23
+
24
+ let(:klass_2) do
25
+ Class.new(Nanoc::DataSource) do
26
+ def items
27
+ [Nanoc::Int::Item.new('Two', {}, '/two.md')]
28
+ end
29
+
30
+ def item_changes
31
+ %i[two_foo two_bar]
32
+ end
33
+
34
+ def layouts
35
+ [Nanoc::Int::Layout.new('Two', {}, '/two.md')]
36
+ end
37
+
38
+ def layout_changes
39
+ %i[two_foo two_bar]
40
+ end
41
+ end
42
+ end
43
+
44
+ let(:data_source_1) do
45
+ klass_1.new({}, nil, nil, {})
46
+ end
47
+
48
+ let(:data_source_2) do
49
+ klass_2.new({}, nil, nil, {})
50
+ end
51
+
52
+ subject(:data_source) do
53
+ described_class.new([data_source_1, data_source_2], {})
54
+ end
55
+
56
+ describe '#items' do
57
+ subject { data_source.items }
58
+
59
+ it 'contains all items' do
60
+ expect(subject).to match_array(data_source_1.items + data_source_2.items)
61
+ end
62
+ end
63
+
64
+ describe '#layouts' do
65
+ subject { data_source.layouts }
66
+
67
+ it 'contains all layouts' do
68
+ expect(subject).to match_array(data_source_1.layouts + data_source_2.layouts)
69
+ end
70
+ end
71
+
72
+ describe '#item_changes' do
73
+ subject { data_source.item_changes }
74
+
75
+ it 'yields changes from both' do
76
+ expect(subject).to match_array(data_source_1.item_changes + data_source_2.item_changes)
77
+ end
78
+ end
79
+
80
+ describe '#layout_changes' do
81
+ subject { data_source.layout_changes }
82
+
83
+ it 'yields changes from both' do
84
+ expect(subject).to match_array(data_source_1.layout_changes + data_source_2.layout_changes)
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Nanoc::DataSource, stdio: true do
4
+ subject(:data_source) do
5
+ described_class.new({}, nil, nil, {})
6
+ end
7
+
8
+ it 'has an empty #up implementation' do
9
+ data_source.up
10
+ end
11
+
12
+ it 'has an empty #down implementation' do
13
+ data_source.down
14
+ end
15
+
16
+ it 'returns empty #items' do
17
+ expect(data_source.items).to be_empty
18
+ end
19
+
20
+ it 'returns empty #layouts' do
21
+ expect(data_source.layouts).to be_empty
22
+ end
23
+
24
+ describe '#new_item' do
25
+ it 'supports checksum data' do
26
+ item = data_source.new_item('stuff', { title: 'Stuff!' }, '/asdf', checksum_data: 'abcdef')
27
+
28
+ expect(item.content.string).to eql('stuff')
29
+ expect(item.attributes[:title]).to eql('Stuff!')
30
+ expect(item.identifier).to eql(Nanoc::Identifier.new('/asdf'))
31
+ expect(item.checksum_data).to eql('abcdef')
32
+ end
33
+
34
+ it 'supports content/attributes checksum data' do
35
+ item = data_source.new_item('stuff', { title: 'Stuff!' }, '/asdf', content_checksum_data: 'con-cs', attributes_checksum_data: 'attr-cs')
36
+
37
+ expect(item.content.string).to eql('stuff')
38
+ expect(item.attributes[:title]).to eql('Stuff!')
39
+ expect(item.identifier).to eql(Nanoc::Identifier.new('/asdf'))
40
+ expect(item.content_checksum_data).to eql('con-cs')
41
+ expect(item.attributes_checksum_data).to eql('attr-cs')
42
+ end
43
+ end
44
+
45
+ describe '#new_layout' do
46
+ it 'supports checksum data' do
47
+ layout = data_source.new_layout('stuff', { title: 'Stuff!' }, '/asdf', checksum_data: 'abcdef')
48
+
49
+ expect(layout.content.string).to eql('stuff')
50
+ expect(layout.attributes[:title]).to eql('Stuff!')
51
+ expect(layout.identifier).to eql(Nanoc::Identifier.new('/asdf'))
52
+ expect(layout.checksum_data).to eql('abcdef')
53
+ end
54
+
55
+ it 'supports content/attributes checksum data' do
56
+ layout = data_source.new_layout('stuff', { title: 'Stuff!' }, '/asdf', content_checksum_data: 'con-cs', attributes_checksum_data: 'attr-cs')
57
+
58
+ expect(layout.content.string).to eql('stuff')
59
+ expect(layout.attributes[:title]).to eql('Stuff!')
60
+ expect(layout.identifier).to eql(Nanoc::Identifier.new('/asdf'))
61
+ expect(layout.content_checksum_data).to eql('con-cs')
62
+ expect(layout.attributes_checksum_data).to eql('attr-cs')
63
+ end
64
+ end
65
+
66
+ describe '#item_changes' do
67
+ subject { data_source.item_changes }
68
+
69
+ it 'warns' do
70
+ expect { subject }.to output("Caution: Data source nil does not implement #item_changes; live compilation will not pick up changes in this data source.\n").to_stderr
71
+ end
72
+
73
+ it 'never yields anything' do
74
+ q = SizedQueue.new(1)
75
+ Thread.new { subject.each { |c| q << c } }
76
+ sleep 0.1
77
+ expect { q.pop(true) }.to raise_error(ThreadError)
78
+ end
79
+ end
80
+
81
+ describe '#layout_changes' do
82
+ subject { data_source.layout_changes }
83
+
84
+ it 'warns' do
85
+ expect { subject }.to output("Caution: Data source nil does not implement #layout_changes; live compilation will not pick up changes in this data source.\n").to_stderr
86
+ end
87
+
88
+ it 'never yields anything' do
89
+ q = SizedQueue.new(1)
90
+ Thread.new { subject.each { |c| q << c } }
91
+ sleep 0.1
92
+ expect { q.pop(true) }.to raise_error(ThreadError)
93
+ end
94
+ end
95
+ end