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