nanoc 4.5.2 → 4.5.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +7 -7
  3. data/NEWS.md +7 -0
  4. data/lib/nanoc/base/entities/item_rep.rb +0 -46
  5. data/lib/nanoc/base/repos.rb +1 -0
  6. data/lib/nanoc/base/repos/snapshot_repo.rb +60 -0
  7. data/lib/nanoc/base/services/compiler.rb +30 -17
  8. data/lib/nanoc/base/services/executor.rb +17 -8
  9. data/lib/nanoc/base/services/item_rep_writer.rb +3 -3
  10. data/lib/nanoc/base/views/item_rep_collection_view.rb +1 -1
  11. data/lib/nanoc/base/views/item_rep_view.rb +2 -2
  12. data/lib/nanoc/base/views/post_compile_item_rep_view.rb +9 -7
  13. data/lib/nanoc/base/views/view_context.rb +3 -1
  14. data/lib/nanoc/cli/commands/compile.rb +3 -3
  15. data/lib/nanoc/cli/commands/shell.rb +1 -0
  16. data/lib/nanoc/extra/parallel_collection.rb +1 -1
  17. data/lib/nanoc/helpers/capturing.rb +9 -5
  18. data/lib/nanoc/rule_dsl/action_provider.rb +2 -0
  19. data/lib/nanoc/rule_dsl/rule_memory_calculator.rb +4 -1
  20. data/lib/nanoc/spec.rb +17 -18
  21. data/lib/nanoc/version.rb +1 -1
  22. data/spec/nanoc/base/compiler_spec.rb +9 -8
  23. data/spec/nanoc/base/entities/item_rep_spec.rb +0 -222
  24. data/spec/nanoc/base/filter_spec.rb +1 -0
  25. data/spec/nanoc/base/item_rep_writer_spec.rb +9 -4
  26. data/spec/nanoc/base/repos/snapshot_repo_spec.rb +314 -0
  27. data/spec/nanoc/base/services/compiler/phases/cache_spec.rb +107 -0
  28. data/spec/nanoc/base/services/executor_spec.rb +230 -44
  29. data/spec/nanoc/base/views/document_view_spec.rb +1 -0
  30. data/spec/nanoc/base/views/item_rep_view_spec.rb +18 -5
  31. data/spec/nanoc/base/views/item_view_spec.rb +18 -7
  32. data/spec/nanoc/base/views/mutable_document_view_spec.rb +6 -5
  33. data/spec/nanoc/base/views/post_compile_item_rep_view_spec.rb +8 -3
  34. data/spec/nanoc/cli/commands/compile/file_action_printer_spec.rb +3 -3
  35. data/spec/nanoc/helpers/capturing_spec.rb +8 -5
  36. data/spec/nanoc/regressions/gh_1064_spec.rb +18 -0
  37. data/spec/nanoc/rule_dsl/rule_context_spec.rb +2 -1
  38. data/spec/nanoc/rule_dsl/rule_memory_calculator_spec.rb +15 -3
  39. data/spec/spec_helper.rb +43 -0
  40. data/test/cli/commands/test_compile.rb +1 -1
  41. data/test/filters/test_xsl.rb +1 -0
  42. data/test/helpers/test_capturing.rb +9 -2
  43. data/test/helpers/test_xml_sitemap.rb +1 -1
  44. metadata +6 -2
@@ -53,6 +53,7 @@ describe Nanoc::Filter do
53
53
  items: double(:items),
54
54
  dependency_tracker: dependency_tracker,
55
55
  compilation_context: double(:compilation_context),
56
+ snapshot_repo: double(:snapshot_repo),
56
57
  )
57
58
  end
58
59
 
@@ -6,7 +6,6 @@ describe Nanoc::Int::ItemRepWriter do
6
6
 
7
7
  let(:item_rep) do
8
8
  Nanoc::Int::ItemRep.new(item, :default).tap do |ir|
9
- ir.snapshot_contents = snapshot_contents
10
9
  ir.raw_paths = raw_paths
11
10
  end
12
11
  end
@@ -24,10 +23,16 @@ describe Nanoc::Int::ItemRepWriter do
24
23
  { snapshot_name => raw_path }
25
24
  end
26
25
 
27
- subject { described_class.new.write(item_rep, snapshot_name) }
26
+ let(:snapshot_repo) { Nanoc::Int::SnapshotRepo.new }
27
+
28
+ subject { described_class.new.write(item_rep, snapshot_repo, snapshot_name) }
28
29
 
29
30
  before do
30
31
  expect(File.directory?('output')).to be_falsy
32
+
33
+ snapshot_contents.each_pair do |key, value|
34
+ snapshot_repo.set(item_rep, key, value)
35
+ end
31
36
  end
32
37
 
33
38
  context 'binary item rep' do
@@ -49,7 +54,7 @@ describe Nanoc::Int::ItemRepWriter do
49
54
  expect(Nanoc::Int::NotificationCenter).to receive(:post)
50
55
  .with(:will_write_rep, item_rep, 'output/blah.dat')
51
56
  expect(Nanoc::Int::NotificationCenter).to receive(:post)
52
- .with(:rep_written, item_rep, 'output/blah.dat', true, true)
57
+ .with(:rep_written, item_rep, true, 'output/blah.dat', true, true)
53
58
 
54
59
  subject
55
60
 
@@ -92,7 +97,7 @@ describe Nanoc::Int::ItemRepWriter do
92
97
  expect(Nanoc::Int::NotificationCenter).to receive(:post)
93
98
  .with(:will_write_rep, item_rep, 'output/blah.dat')
94
99
  expect(Nanoc::Int::NotificationCenter).to receive(:post)
95
- .with(:rep_written, item_rep, 'output/blah.dat', true, true)
100
+ .with(:rep_written, item_rep, false, 'output/blah.dat', true, true)
96
101
 
97
102
  subject
98
103
 
@@ -0,0 +1,314 @@
1
+ describe Nanoc::Int::SnapshotRepo do
2
+ subject(:repo) { described_class.new }
3
+
4
+ describe '#get' do
5
+ subject { repo.get(rep, snapshot_name) }
6
+
7
+ let(:item) { Nanoc::Int::Item.new('contentz', {}, '/foo.md') }
8
+ let(:rep) { Nanoc::Int::ItemRep.new(item, :foo) }
9
+ let(:snapshot_name) { :donkey }
10
+
11
+ context 'rep does not exist in repo' do
12
+ it { is_expected.to be_nil }
13
+ end
14
+
15
+ context 'rep exists in repo' do
16
+ before { repo.set(rep, :foobar, Nanoc::Int::TextualContent.new('other content')) }
17
+
18
+ context 'snapshot does not exist in repo' do
19
+ it { is_expected.to be_nil }
20
+ end
21
+
22
+ context 'snapshot exists in repo' do
23
+ before { repo.set(rep, :donkey, Nanoc::Int::TextualContent.new('donkey')) }
24
+ it { is_expected.to be_some_textual_content('donkey') }
25
+ end
26
+ end
27
+ end
28
+
29
+ describe '#get_all' do
30
+ subject { repo.get_all(rep) }
31
+
32
+ let(:item) { Nanoc::Int::Item.new('contentz', {}, '/foo.md') }
33
+ let(:rep) { Nanoc::Int::ItemRep.new(item, :foo) }
34
+
35
+ context 'rep does not exist in repo' do
36
+ it { is_expected.to eq({}) }
37
+ end
38
+
39
+ context 'rep exists in repo' do
40
+ before { repo.set(rep, :foobar, Nanoc::Int::TextualContent.new('donkey')) }
41
+ it { is_expected.to match(foobar: some_textual_content('donkey')) }
42
+ end
43
+ end
44
+
45
+ describe '#set' do
46
+ subject { repo.set(rep, snapshot_name, contents) }
47
+
48
+ let(:item) { Nanoc::Int::Item.new('contentz', {}, '/foo.md') }
49
+ let(:rep) { Nanoc::Int::ItemRep.new(item, :foo) }
50
+ let(:snapshot_name) { :donkey }
51
+ let(:contents) { Nanoc::Int::TextualContent.new('donkey') }
52
+
53
+ it 'changes the given rep+snapshot' do
54
+ expect { subject }
55
+ .to change { repo.get(rep, snapshot_name) }
56
+ .from(nil)
57
+ .to(some_textual_content('donkey'))
58
+ end
59
+ end
60
+
61
+ describe '#set_all' do
62
+ subject { repo.set_all(rep, contents_by_snapshot) }
63
+
64
+ let(:other_item) { Nanoc::Int::Item.new('contentz', {}, '/foo.md') }
65
+ let(:other_rep) { Nanoc::Int::ItemRep.new(other_item, :foo) }
66
+
67
+ let(:item) { Nanoc::Int::Item.new('contentz', {}, '/foo.md') }
68
+ let(:rep) { Nanoc::Int::ItemRep.new(item, :foo) }
69
+ let(:contents_by_snapshot) { { donkey: Nanoc::Int::TextualContent.new('donkey') } }
70
+
71
+ it 'changes the given rep+snapshot' do
72
+ expect { subject }
73
+ .to change { repo.get(rep, :donkey) }
74
+ .from(nil)
75
+ .to(some_textual_content('donkey'))
76
+ end
77
+
78
+ it 'leaves other reps intact' do
79
+ expect { subject }
80
+ .not_to change { repo.get(other_rep, :donkey) }
81
+ end
82
+
83
+ it 'leaves other snapshots intact' do
84
+ expect { subject }
85
+ .not_to change { repo.get(rep, :giraffe) }
86
+ end
87
+ end
88
+
89
+ describe '#compiled_content' do
90
+ subject { repo.compiled_content(rep: rep, snapshot: snapshot_name) }
91
+
92
+ let(:snapshot_name) { raise 'override me' }
93
+
94
+ let(:item) { Nanoc::Int::Item.new('contentz', {}, '/foo.md') }
95
+ let(:rep) { Nanoc::Int::ItemRep.new(item, :foo) }
96
+
97
+ shared_examples 'a non-moving snapshot with content' do
98
+ context 'no snapshot def' do
99
+ it 'raises' do
100
+ expect { subject }.to raise_error(Nanoc::Int::Errors::NoSuchSnapshot)
101
+ end
102
+ end
103
+
104
+ context 'snapshot def exists' do
105
+ before do
106
+ rep.snapshot_defs = [Nanoc::Int::SnapshotDef.new(snapshot_name)]
107
+ repo.set_all(rep, snapshot_name => content)
108
+ end
109
+
110
+ context 'content is textual' do
111
+ let(:content) { Nanoc::Int::TextualContent.new('hellos') }
112
+ it { is_expected.to eql('hellos') }
113
+ end
114
+
115
+ context 'content is binary' do
116
+ before { File.write('donkey.dat', 'binary data') }
117
+ let(:content) { Nanoc::Int::BinaryContent.new(File.expand_path('donkey.dat')) }
118
+
119
+ it 'raises' do
120
+ expect { subject }.to raise_error(Nanoc::Int::Errors::CannotGetCompiledContentOfBinaryItem)
121
+ end
122
+ end
123
+ end
124
+ end
125
+
126
+ shared_examples 'a non-moving snapshot' do
127
+ include_examples 'a non-moving snapshot with content'
128
+
129
+ context 'snapshot def exists, but not content' do
130
+ before do
131
+ rep.snapshot_defs = [Nanoc::Int::SnapshotDef.new(snapshot_name)]
132
+ repo.set_all(rep, {})
133
+ end
134
+
135
+ it 'errors' do
136
+ expect { subject }.to yield_from_fiber(an_instance_of(Nanoc::Int::Errors::UnmetDependency))
137
+ end
138
+ end
139
+ end
140
+
141
+ shared_examples 'snapshot :last' do
142
+ context 'no snapshot def' do
143
+ it 'errors' do
144
+ expect { subject }.to raise_error(Nanoc::Int::Errors::NoSuchSnapshot)
145
+ end
146
+ end
147
+
148
+ context 'snapshot exists' do
149
+ context 'snapshot is not final' do
150
+ before do
151
+ rep.snapshot_defs = [Nanoc::Int::SnapshotDef.new(snapshot_name)]
152
+ end
153
+
154
+ context 'snapshot content does not exist' do
155
+ before do
156
+ repo.set_all(rep, {})
157
+ end
158
+
159
+ it 'errors' do
160
+ expect { subject }.to yield_from_fiber(an_instance_of(Nanoc::Int::Errors::UnmetDependency))
161
+ end
162
+ end
163
+
164
+ context 'snapshot content exists' do
165
+ context 'content is textual' do
166
+ before do
167
+ repo.set(rep, snapshot_name, Nanoc::Int::TextualContent.new('hellos'))
168
+ end
169
+
170
+ context 'not compiled' do
171
+ before { rep.compiled = false }
172
+
173
+ it 'raises' do
174
+ expect { subject }.to yield_from_fiber(an_instance_of(Nanoc::Int::Errors::UnmetDependency))
175
+ end
176
+ end
177
+
178
+ context 'compiled' do
179
+ before { rep.compiled = true }
180
+
181
+ it { is_expected.to eql('hellos') }
182
+ end
183
+ end
184
+
185
+ context 'content is binary' do
186
+ before do
187
+ File.write('donkey.dat', 'binary data')
188
+ repo.set(rep, snapshot_name, Nanoc::Int::BinaryContent.new(File.expand_path('donkey.dat')))
189
+ end
190
+
191
+ context 'not compiled' do
192
+ before { rep.compiled = false }
193
+
194
+ it 'raises' do
195
+ expect { subject }.to yield_from_fiber(an_instance_of(Nanoc::Int::Errors::UnmetDependency))
196
+ end
197
+ end
198
+
199
+ context 'compiled' do
200
+ before { rep.compiled = true }
201
+
202
+ it 'raises' do
203
+ expect { subject }.to raise_error(Nanoc::Int::Errors::CannotGetCompiledContentOfBinaryItem)
204
+ end
205
+ end
206
+ end
207
+ end
208
+ end
209
+
210
+ context 'snapshot is final' do
211
+ before do
212
+ rep.snapshot_defs = [Nanoc::Int::SnapshotDef.new(snapshot_name)]
213
+ end
214
+
215
+ context 'snapshot content does not exist' do
216
+ before do
217
+ repo.set_all(rep, {})
218
+ end
219
+
220
+ it 'errors' do
221
+ expect { subject }.to yield_from_fiber(an_instance_of(Nanoc::Int::Errors::UnmetDependency))
222
+ end
223
+ end
224
+
225
+ context 'snapshot content exists' do
226
+ context 'content is textual' do
227
+ before do
228
+ repo.set(rep, snapshot_name, Nanoc::Int::TextualContent.new('hellos'))
229
+ end
230
+
231
+ context 'not compiled' do
232
+ before { rep.compiled = false }
233
+
234
+ it 'errors' do
235
+ expect { subject }.to yield_from_fiber(an_instance_of(Nanoc::Int::Errors::UnmetDependency))
236
+ end
237
+ end
238
+
239
+ context 'compiled' do
240
+ before { rep.compiled = true }
241
+
242
+ it { is_expected.to eql('hellos') }
243
+ end
244
+ end
245
+
246
+ context 'content is binary' do
247
+ before do
248
+ File.write('donkey.dat', 'binary data')
249
+ repo.set(rep, snapshot_name, Nanoc::Int::BinaryContent.new(File.expand_path('donkey.dat')))
250
+ end
251
+
252
+ context 'not compiled' do
253
+ before { rep.compiled = false }
254
+
255
+ it 'raises' do
256
+ expect { subject }.to yield_from_fiber(an_instance_of(Nanoc::Int::Errors::UnmetDependency))
257
+ end
258
+ end
259
+
260
+ context 'compiled' do
261
+ before { rep.compiled = true }
262
+
263
+ it 'raises' do
264
+ expect { subject }.to raise_error(Nanoc::Int::Errors::CannotGetCompiledContentOfBinaryItem)
265
+ end
266
+ end
267
+ end
268
+ end
269
+ end
270
+ end
271
+ end
272
+
273
+ context 'snapshot nil' do
274
+ let(:snapshot_name) { :last }
275
+ subject { repo.compiled_content(rep: rep, snapshot: nil) }
276
+ include_examples 'snapshot :last'
277
+ end
278
+
279
+ context 'snapshot not specified' do
280
+ subject { repo.compiled_content(rep: rep) }
281
+
282
+ context 'pre exists' do
283
+ before { repo.set(rep, :pre, Nanoc::Int::TextualContent.new('omg')) }
284
+ let(:snapshot_name) { :pre }
285
+ include_examples 'a non-moving snapshot with content'
286
+ end
287
+
288
+ context 'pre does not exist' do
289
+ let(:snapshot_name) { :last }
290
+ include_examples 'snapshot :last'
291
+ end
292
+ end
293
+
294
+ context 'snapshot :pre specified' do
295
+ let(:snapshot_name) { :pre }
296
+ include_examples 'a non-moving snapshot'
297
+ end
298
+
299
+ context 'snapshot :post specified' do
300
+ let(:snapshot_name) { :post }
301
+ include_examples 'a non-moving snapshot'
302
+ end
303
+
304
+ context 'snapshot :last specified' do
305
+ let(:snapshot_name) { :last }
306
+ include_examples 'snapshot :last'
307
+ end
308
+
309
+ context 'snapshot :donkey specified' do
310
+ let(:snapshot_name) { :donkey }
311
+ include_examples 'a non-moving snapshot'
312
+ end
313
+ end
314
+ end
@@ -0,0 +1,107 @@
1
+ describe Nanoc::Int::Compiler::Phases::Cache do
2
+ subject(:phase) do
3
+ described_class.new(
4
+ compiled_content_cache: compiled_content_cache,
5
+ snapshot_repo: snapshot_repo,
6
+ wrapped: wrapped,
7
+ )
8
+ end
9
+
10
+ let(:compiled_content_cache) do
11
+ Nanoc::Int::CompiledContentCache.new(env_name: nil, items: [item])
12
+ end
13
+
14
+ let(:snapshot_repo) { Nanoc::Int::SnapshotRepo.new }
15
+
16
+ let(:wrapped_class) do
17
+ Class.new do
18
+ def initialize(snapshot_repo)
19
+ @snapshot_repo = snapshot_repo
20
+ end
21
+
22
+ def run(rep, is_outdated:) # rubocop:disable Lint/UnusedMethodArgument
23
+ @snapshot_repo.set(rep, :last, Nanoc::Int::TextualContent.new('wrapped content'))
24
+ end
25
+ end
26
+ end
27
+
28
+ let(:wrapped) { wrapped_class.new(snapshot_repo) }
29
+
30
+ let(:item) { Nanoc::Int::Item.new('item content', {}, '/donkey.md') }
31
+ let(:rep) { Nanoc::Int::ItemRep.new(item, :latex) }
32
+
33
+ describe '#run' do
34
+ subject { phase.run(rep, is_outdated: is_outdated) }
35
+
36
+ let(:is_outdated) { raise 'override me' }
37
+
38
+ shared_examples 'calls wrapped' do
39
+ it 'delegates to wrapped' do
40
+ expect(wrapped).to receive(:run).with(rep, is_outdated: is_outdated)
41
+ subject
42
+ end
43
+
44
+ it 'marks rep as compiled' do
45
+ expect { subject }
46
+ .to change { rep.compiled? }
47
+ .from(false)
48
+ .to(true)
49
+ end
50
+
51
+ it 'sends no notifications' do
52
+ expect(Nanoc::Int::NotificationCenter).not_to receive(:post)
53
+ subject
54
+ end
55
+
56
+ it 'updates compiled content cache' do
57
+ expect { subject }
58
+ .to change { compiled_content_cache[rep] }
59
+ .from(nil)
60
+ .to(last: some_textual_content('wrapped content'))
61
+ end
62
+ end
63
+
64
+ context 'outdated' do
65
+ let(:is_outdated) { true }
66
+ include_examples 'calls wrapped'
67
+ end
68
+
69
+ context 'not outdated' do
70
+ let(:is_outdated) { false }
71
+
72
+ context 'cached compiled content available' do
73
+ before do
74
+ compiled_content_cache[rep] = { last: Nanoc::Int::TextualContent.new('cached') }
75
+ end
76
+
77
+ it 'reuses content from cache' do
78
+ expect { subject }
79
+ .to change { snapshot_repo.get(rep, :last) }
80
+ .from(nil)
81
+ .to(some_textual_content('cached'))
82
+ end
83
+
84
+ it 'marks rep as compiled' do
85
+ expect { subject }
86
+ .to change { rep.compiled? }
87
+ .from(false)
88
+ .to(true)
89
+ end
90
+
91
+ it 'does not change compiled content cache' do
92
+ expect { subject }
93
+ .not_to change { compiled_content_cache[rep] }
94
+ end
95
+
96
+ it 'sends notification' do
97
+ expect(Nanoc::Int::NotificationCenter).to receive(:post).with(:cached_content_used, rep)
98
+ subject
99
+ end
100
+ end
101
+
102
+ context 'no cached compiled content available' do
103
+ include_examples 'calls wrapped'
104
+ end
105
+ end
106
+ end
107
+ end