nanoc 4.7.10 → 4.7.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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/.github/CONTRIBUTING.md +17 -0
  3. data/.github/ISSUE_TEMPLATE.md +23 -0
  4. data/.github/PULL_REQUEST_TEMPLATE.md +18 -0
  5. data/.gitignore +8 -0
  6. data/.rspec +3 -0
  7. data/.rubocop.yml +174 -0
  8. data/.travis.yml +27 -0
  9. data/Gemfile +4 -3
  10. data/NEWS.md +11 -0
  11. data/Rakefile +5 -2
  12. data/lib/nanoc/base/entities/dependency.rb +5 -3
  13. data/lib/nanoc/base/entities/layout.rb +1 -1
  14. data/lib/nanoc/base/repos/dependency_store.rb +64 -28
  15. data/lib/nanoc/base/services/dependency_tracker.rb +1 -1
  16. data/lib/nanoc/base/views/config_view.rb +4 -0
  17. data/lib/nanoc/checking/checks/external_links.rb +3 -6
  18. data/lib/nanoc/cli.rb +0 -2
  19. data/lib/nanoc/cli/commands/shell.rb +2 -3
  20. data/lib/nanoc/filters/colorize_syntax/colorizers.rb +4 -1
  21. data/lib/nanoc/telemetry/table.rb +1 -1
  22. data/lib/nanoc/version.rb +1 -1
  23. data/nanoc.gemspec +1 -5
  24. data/scripts/release +95 -0
  25. data/{test → spec/nanoc}/base/core_ext/array_spec.rb +5 -14
  26. data/{test → spec/nanoc}/base/core_ext/hash_spec.rb +6 -15
  27. data/{test → spec/nanoc}/base/core_ext/pathname_spec.rb +0 -0
  28. data/spec/nanoc/base/core_ext/string_spec.rb +23 -0
  29. data/spec/nanoc/base/directed_graph_spec.rb +291 -0
  30. data/spec/nanoc/base/entities/identifiable_collection_spec.rb +56 -0
  31. data/spec/nanoc/base/entities/item_spec.rb +8 -0
  32. data/spec/nanoc/base/entities/layout_spec.rb +8 -0
  33. data/spec/nanoc/base/repos/dependency_store_spec.rb +166 -21
  34. data/spec/nanoc/base/views/config_view_spec.rb +29 -1
  35. data/spec/nanoc/cli/commands/shell_spec.rb +23 -8
  36. data/spec/nanoc/filters/less_spec.rb +1 -1
  37. data/spec/nanoc/regressions/gh_1185_spec.rb +22 -0
  38. data/spec/nanoc/telemetry/table_spec.rb +22 -0
  39. data/spec/spec_helper.rb +5 -6
  40. data/test/base/test_item_array.rb +0 -35
  41. data/test/checking/checks/test_external_links.rb +0 -14
  42. data/test/filters/test_coffeescript.rb +0 -2
  43. data/test/filters/test_handlebars.rb +0 -4
  44. data/test/filters/test_uglify_js.rb +0 -4
  45. data/test/helper.rb +0 -6
  46. data/test/helpers/test_blogging.rb +66 -26
  47. data/test/helpers/test_xml_sitemap.rb +23 -7
  48. metadata +16 -9
  49. data/Gemfile.lock +0 -433
  50. data/test/base/core_ext/string_spec.rb +0 -25
  51. data/test/base/test_item.rb +0 -40
  52. data/test/base/test_layout.rb +0 -16
@@ -12,6 +12,62 @@ describe Nanoc::Int::IdentifiableCollection do
12
12
  it { is_expected.to be_a(described_class) }
13
13
  end
14
14
 
15
+ describe '#[]' do
16
+ let(:objects) do
17
+ [
18
+ Nanoc::Int::Item.new('asdf', {}, Nanoc::Identifier.new('/one')),
19
+ Nanoc::Int::Item.new('asdf', {}, Nanoc::Identifier.new('/two')),
20
+ ]
21
+ end
22
+
23
+ context 'string pattern style is glob' do
24
+ let(:config) { Nanoc::Int::Configuration.new.with_defaults }
25
+
26
+ it 'handles glob' do
27
+ expect(identifiable_collection['/on*']).to equal(objects[0])
28
+ expect(identifiable_collection['/*wo']).to equal(objects[1])
29
+ end
30
+ end
31
+
32
+ context 'string pattern style is glob' do
33
+ let(:config) { Nanoc::Int::Configuration.new }
34
+
35
+ it 'does not handle glob' do
36
+ expect(identifiable_collection['/on*']).to be_nil
37
+ expect(identifiable_collection['/*wo']).to be_nil
38
+ end
39
+ end
40
+
41
+ it 'handles identifier' do
42
+ expect(identifiable_collection['/one']).to equal(objects[0])
43
+ expect(identifiable_collection['/two']).to equal(objects[1])
44
+ end
45
+
46
+ it 'handles malformed identifier' do
47
+ expect(identifiable_collection['one/']).to be_nil
48
+ expect(identifiable_collection['/one/']).to be_nil
49
+ expect(identifiable_collection['one']).to be_nil
50
+ expect(identifiable_collection['//one']).to be_nil
51
+ expect(identifiable_collection['/one//']).to be_nil
52
+ end
53
+
54
+ it 'handles regex' do
55
+ expect(identifiable_collection[/one/]).to equal(objects[0])
56
+ expect(identifiable_collection[/on/]).to equal(objects[0])
57
+ expect(identifiable_collection[/\/o/]).to equal(objects[0])
58
+ expect(identifiable_collection[/e$/]).to equal(objects[0])
59
+ end
60
+
61
+ context 'frozen' do
62
+ before { identifiable_collection.freeze }
63
+
64
+ example do
65
+ expect(identifiable_collection['/one']).to equal(objects[0])
66
+ expect(identifiable_collection['/fifty']).to be_nil
67
+ end
68
+ end
69
+ end
70
+
15
71
  describe '#find_all' do
16
72
  let(:objects) do
17
73
  [
@@ -2,4 +2,12 @@
2
2
 
3
3
  describe Nanoc::Int::Item do
4
4
  it_behaves_like 'a document'
5
+
6
+ describe '#reference' do
7
+ let(:item) { described_class.new('hi', {}, '/foo.md') }
8
+
9
+ it 'has the proper reference' do
10
+ expect(item.reference).to eql([:item, '/foo.md'])
11
+ end
12
+ end
5
13
  end
@@ -2,4 +2,12 @@
2
2
 
3
3
  describe Nanoc::Int::Layout do
4
4
  it_behaves_like 'a document'
5
+
6
+ describe '#reference' do
7
+ let(:layout) { described_class.new('hi', {}, '/foo.md') }
8
+
9
+ it 'has the proper reference' do
10
+ expect(layout.reference).to eql([:layout, '/foo.md'])
11
+ end
12
+ end
5
13
  end
@@ -7,8 +7,11 @@ describe Nanoc::Int::DependencyStore do
7
7
  let(:item_b) { Nanoc::Int::Item.new('b', {}, '/b.md') }
8
8
  let(:item_c) { Nanoc::Int::Item.new('c', {}, '/c.md') }
9
9
 
10
+ let(:layout_a) { Nanoc::Int::Layout.new('la', {}, '/la.md') }
11
+ let(:layout_b) { Nanoc::Int::Layout.new('lb', {}, '/lb.md') }
12
+
10
13
  let(:items) { Nanoc::Int::IdentifiableCollection.new(config, [item_a, item_b, item_c]) }
11
- let(:layouts) { Nanoc::Int::IdentifiableCollection.new(config) }
14
+ let(:layouts) { Nanoc::Int::IdentifiableCollection.new(config, [layout_a, layout_b]) }
12
15
  let(:config) { Nanoc::Int::Configuration.new }
13
16
 
14
17
  describe '#dependencies_causing_outdatedness_of' do
@@ -21,9 +24,75 @@ describe Nanoc::Int::DependencyStore do
21
24
  end
22
25
 
23
26
  context 'one dependency' do
27
+ context 'dependency on config, no props' do
28
+ before do
29
+ store.record_dependency(item_a, config)
30
+ end
31
+
32
+ it 'returns one dependency' do
33
+ deps = store.dependencies_causing_outdatedness_of(item_a)
34
+ expect(deps.size).to eql(1)
35
+ end
36
+
37
+ it 'returns dependency from a onto config' do
38
+ deps = store.dependencies_causing_outdatedness_of(item_a)
39
+ expect(deps[0].from).to eql(config)
40
+ expect(deps[0].to).to eql(item_a)
41
+ end
42
+
43
+ it 'returns true for all props by default' do
44
+ deps = store.dependencies_causing_outdatedness_of(item_a)
45
+ expect(deps[0].props.raw_content?).to eq(true)
46
+ expect(deps[0].props.attributes?).to eq(true)
47
+ expect(deps[0].props.compiled_content?).to eq(true)
48
+ expect(deps[0].props.path?).to eq(true)
49
+ end
50
+
51
+ it 'returns nothing for the others' do
52
+ expect(store.dependencies_causing_outdatedness_of(item_b)).to be_empty
53
+ expect(store.dependencies_causing_outdatedness_of(item_c)).to be_empty
54
+ end
55
+ end
56
+
57
+ context 'dependency on config, generic attributes prop' do
58
+ before do
59
+ store.record_dependency(item_a, config, attributes: true)
60
+ end
61
+
62
+ it 'returns false for all unspecified props' do
63
+ deps = store.dependencies_causing_outdatedness_of(item_a)
64
+ expect(deps[0].props.raw_content?).to eq(false)
65
+ expect(deps[0].props.compiled_content?).to eq(false)
66
+ expect(deps[0].props.path?).to eq(false)
67
+ end
68
+
69
+ it 'returns the specified props' do
70
+ deps = store.dependencies_causing_outdatedness_of(item_a)
71
+ expect(deps[0].props.attributes?).to eq(true)
72
+ end
73
+ end
74
+
75
+ context 'dependency on config, specific attributes prop' do
76
+ before do
77
+ store.record_dependency(item_a, config, attributes: [:donkey])
78
+ end
79
+
80
+ it 'returns false for all unspecified props' do
81
+ deps = store.dependencies_causing_outdatedness_of(item_a)
82
+ expect(deps[0].props.raw_content?).to eq(false)
83
+ expect(deps[0].props.compiled_content?).to eq(false)
84
+ expect(deps[0].props.path?).to eq(false)
85
+ end
86
+
87
+ it 'returns the specified props' do
88
+ deps = store.dependencies_causing_outdatedness_of(item_a)
89
+ expect(deps[0].props.attributes?).to eq(true)
90
+ expect(deps[0].props.attributes).to contain_exactly(:donkey)
91
+ end
92
+ end
93
+
24
94
  context 'no props' do
25
95
  before do
26
- # FIXME: weird argument order (item_b depends on item_a, not th other way around)
27
96
  store.record_dependency(item_a, item_b)
28
97
  end
29
98
 
@@ -54,7 +123,6 @@ describe Nanoc::Int::DependencyStore do
54
123
 
55
124
  context 'one prop' do
56
125
  before do
57
- # FIXME: weird argument order (item_b depends on item_a, not th other way around)
58
126
  store.record_dependency(item_a, item_b, compiled_content: true)
59
127
  end
60
128
 
@@ -73,7 +141,6 @@ describe Nanoc::Int::DependencyStore do
73
141
 
74
142
  context 'two props' do
75
143
  before do
76
- # FIXME: weird argument order (item_b depends on item_a, not th other way around)
77
144
  store.record_dependency(item_a, item_b, compiled_content: true)
78
145
  store.record_dependency(item_a, item_b, attributes: true)
79
146
  end
@@ -94,7 +161,6 @@ describe Nanoc::Int::DependencyStore do
94
161
 
95
162
  context 'two dependency in a chain' do
96
163
  before do
97
- # FIXME: weird argument order (item_b depends on item_a, not th other way around)
98
164
  store.record_dependency(item_a, item_b)
99
165
  store.record_dependency(item_b, item_c)
100
166
  end
@@ -117,7 +183,7 @@ describe Nanoc::Int::DependencyStore do
117
183
  end
118
184
  end
119
185
 
120
- describe 'reloading' do
186
+ describe 'reloading - item a -> b' do
121
187
  before do
122
188
  store.record_dependency(item_a, item_b, compiled_content: true)
123
189
  store.record_dependency(item_a, item_b, attributes: true)
@@ -195,75 +261,154 @@ describe Nanoc::Int::DependencyStore do
195
261
  end
196
262
  end
197
263
 
198
- describe '#record_dependency' do
264
+ describe 'reloading - item a -> config' do
265
+ before do
266
+ store.record_dependency(item_a, config, attributes: [:donkey])
267
+
268
+ store.store
269
+ store.load
270
+ end
271
+
272
+ it 'has the right dependencies for item A' do
273
+ deps = store.dependencies_causing_outdatedness_of(item_a)
274
+ expect(deps.size).to eql(1)
275
+
276
+ expect(deps[0].from).to eql(config)
277
+ expect(deps[0].to).to eql(item_a)
278
+
279
+ expect(deps[0].props.raw_content?).to eq(false)
280
+ expect(deps[0].props.attributes?).to eq(true)
281
+ expect(deps[0].props.attributes).to contain_exactly(:donkey)
282
+ expect(deps[0].props.compiled_content?).to eq(false)
283
+ expect(deps[0].props.path?).to eq(false)
284
+ end
285
+
286
+ it 'has the right dependencies for item B' do
287
+ deps = store.dependencies_causing_outdatedness_of(item_b)
288
+ expect(deps).to be_empty
289
+ end
290
+
291
+ it 'has the right dependencies for item C' do
292
+ deps = store.dependencies_causing_outdatedness_of(item_c)
293
+ expect(deps).to be_empty
294
+ end
295
+ end
296
+
297
+ shared_examples 'records dependencies' do
199
298
  context 'no props' do
200
- subject { store.record_dependency(item_a, item_b) }
299
+ subject { store.record_dependency(source_obj, item_b) }
201
300
 
202
301
  it 'records a dependency' do
203
302
  expect { subject }
204
- .to change { store.objects_causing_outdatedness_of(item_a) }
303
+ .to change { store.objects_causing_outdatedness_of(source_obj) }
205
304
  .from([])
206
305
  .to([item_b])
207
306
  end
307
+
308
+ it 'ignores all other objects' do
309
+ subject
310
+ expect(other_objs).to all(satisfy { |o| store.dependencies_causing_outdatedness_of(o).empty? })
311
+ end
208
312
  end
209
313
 
210
314
  context 'compiled content prop' do
211
- subject { store.record_dependency(item_a, item_b, compiled_content: true) }
315
+ subject { store.record_dependency(source_obj, target_obj, compiled_content: true) }
212
316
 
213
317
  it 'records a dependency' do
214
318
  expect { subject }
215
- .to change { store.objects_causing_outdatedness_of(item_a) }
319
+ .to change { store.objects_causing_outdatedness_of(source_obj) }
216
320
  .from([])
217
- .to([item_b])
321
+ .to([target_obj])
218
322
  end
219
323
 
220
324
  it 'records a dependency with the right props' do
221
325
  subject
222
- deps = store.dependencies_causing_outdatedness_of(item_a)
326
+ deps = store.dependencies_causing_outdatedness_of(source_obj)
223
327
 
224
328
  expect(deps.first.props.attributes?).not_to be
225
329
  expect(deps.first.props.compiled_content?).to be
226
330
  end
331
+
332
+ it 'ignores all other objects' do
333
+ subject
334
+ expect(other_objs).to all(satisfy { |o| store.dependencies_causing_outdatedness_of(o).empty? })
335
+ end
227
336
  end
228
337
 
229
338
  context 'attribute prop (true)' do
230
- subject { store.record_dependency(item_a, item_b, attributes: true) }
339
+ subject { store.record_dependency(source_obj, target_obj, attributes: true) }
231
340
 
232
341
  it 'records a dependency' do
233
342
  expect { subject }
234
- .to change { store.objects_causing_outdatedness_of(item_a) }
343
+ .to change { store.objects_causing_outdatedness_of(source_obj) }
235
344
  .from([])
236
- .to([item_b])
345
+ .to([target_obj])
237
346
  end
238
347
 
239
348
  it 'records a dependency with the right props' do
240
349
  subject
241
- deps = store.dependencies_causing_outdatedness_of(item_a)
350
+ deps = store.dependencies_causing_outdatedness_of(source_obj)
242
351
 
243
352
  expect(deps.first.props.attributes?).to be
244
353
  expect(deps.first.props.attributes).to be
245
354
  expect(deps.first.props.compiled_content?).not_to be
246
355
  end
356
+
357
+ it 'ignores all other objects' do
358
+ subject
359
+ expect(other_objs).to all(satisfy { |o| store.dependencies_causing_outdatedness_of(o).empty? })
360
+ end
247
361
  end
248
362
 
249
363
  context 'attribute prop (true)' do
250
- subject { store.record_dependency(item_a, item_b, attributes: [:giraffe]) }
364
+ subject { store.record_dependency(source_obj, target_obj, attributes: [:giraffe]) }
251
365
 
252
366
  it 'records a dependency' do
253
367
  expect { subject }
254
- .to change { store.objects_causing_outdatedness_of(item_a) }
368
+ .to change { store.objects_causing_outdatedness_of(source_obj) }
255
369
  .from([])
256
- .to([item_b])
370
+ .to([target_obj])
257
371
  end
258
372
 
259
373
  it 'records a dependency with the right props' do
260
374
  subject
261
- deps = store.dependencies_causing_outdatedness_of(item_a)
375
+ deps = store.dependencies_causing_outdatedness_of(source_obj)
262
376
 
263
377
  expect(deps.first.props.attributes?).to be
264
378
  expect(deps.first.props.attributes).to match_array([:giraffe])
265
379
  expect(deps.first.props.compiled_content?).not_to be
266
380
  end
381
+
382
+ it 'ignores all other objects' do
383
+ subject
384
+ expect(other_objs).to all(satisfy { |o| store.dependencies_causing_outdatedness_of(o).empty? })
385
+ end
386
+ end
387
+ end
388
+
389
+ describe '#record_dependency' do
390
+ context 'item on item' do
391
+ let(:source_obj) { item_a }
392
+ let(:target_obj) { item_b }
393
+ let(:other_objs) { [config, item_c, layout_a, layout_b] }
394
+
395
+ include_examples 'records dependencies'
396
+ end
397
+
398
+ context 'item on layout' do
399
+ let(:source_obj) { item_a }
400
+ let(:target_obj) { layout_a }
401
+ let(:other_objs) { [config, item_b, item_c, layout_b] }
402
+
403
+ include_examples 'records dependencies'
404
+ end
405
+
406
+ context 'item on config' do
407
+ let(:source_obj) { item_a }
408
+ let(:target_obj) { config }
409
+ let(:other_objs) { [item_b, item_c, layout_a, layout_b] }
410
+
411
+ include_examples 'records dependencies'
267
412
  end
268
413
  end
269
414
  end
@@ -7,7 +7,19 @@ describe Nanoc::ConfigView do
7
7
 
8
8
  let(:hash) { { amount: 9000, animal: 'donkey' } }
9
9
 
10
- let(:view) { described_class.new(config, nil) }
10
+ let(:view) { described_class.new(config, view_context) }
11
+
12
+ let(:view_context) do
13
+ Nanoc::ViewContext.new(
14
+ reps: double(:reps),
15
+ items: double(:items),
16
+ dependency_tracker: dependency_tracker,
17
+ compilation_context: double(:compilation_context),
18
+ snapshot_repo: double(:snapshot_repo),
19
+ )
20
+ end
21
+
22
+ let(:dependency_tracker) { double(:dependency_tracker) }
11
23
 
12
24
  describe '#frozen?' do
13
25
  subject { view.frozen? }
@@ -25,6 +37,10 @@ describe Nanoc::ConfigView do
25
37
  describe '#[]' do
26
38
  subject { view[key] }
27
39
 
40
+ before do
41
+ expect(dependency_tracker).to receive(:bounce).with(config, attributes: [key])
42
+ end
43
+
28
44
  context 'with existant key' do
29
45
  let(:key) { :animal }
30
46
  it { is_expected.to eql('donkey') }
@@ -37,6 +53,10 @@ describe Nanoc::ConfigView do
37
53
  end
38
54
 
39
55
  describe '#fetch' do
56
+ before do
57
+ expect(dependency_tracker).to receive(:bounce).with(config, attributes: [key])
58
+ end
59
+
40
60
  context 'with existant key' do
41
61
  let(:key) { :animal }
42
62
 
@@ -71,6 +91,10 @@ describe Nanoc::ConfigView do
71
91
  describe '#key?' do
72
92
  subject { view.key?(key) }
73
93
 
94
+ before do
95
+ expect(dependency_tracker).to receive(:bounce).with(config, attributes: [key])
96
+ end
97
+
74
98
  context 'with existant key' do
75
99
  let(:key) { :animal }
76
100
  it { is_expected.to eql(true) }
@@ -83,6 +107,10 @@ describe Nanoc::ConfigView do
83
107
  end
84
108
 
85
109
  describe '#each' do
110
+ before do
111
+ expect(dependency_tracker).to receive(:bounce).with(config, attributes: true)
112
+ end
113
+
86
114
  example do
87
115
  res = []
88
116
  view.each { |k, v| res << [k, v] }
@@ -5,20 +5,35 @@ describe Nanoc::CLI::Commands::Shell, site: true, stdio: true do
5
5
  before do
6
6
  # Prevent double-loading
7
7
  expect(Nanoc::CLI).to receive(:setup)
8
+
9
+ File.write('content/hello.md', 'Hello!')
10
+
11
+ File.write('Rules', <<~EOS)
12
+ preprocess do
13
+ @items['/hello.*'].raw_content = 'Better hello!'
14
+ end
15
+
16
+ compile '/**/*' do
17
+ end
18
+ EOS
8
19
  end
9
20
 
10
21
  it 'can be invoked' do
11
- rules_collection = Nanoc::RuleDSL::RulesCollection.new
12
- config = Nanoc::Int::Configuration.new.with_defaults
22
+ expect_any_instance_of(Nanoc::Int::Context).to receive(:pry) do |ctx|
23
+ expect(ctx.items.size).to eq(1)
24
+ expect(ctx.items.to_a[0].unwrap.content.string).to eq('Hello!')
25
+ end
13
26
 
14
- dsl = Nanoc::RuleDSL::CompilerDSL.new(rules_collection, config)
15
- allow(Nanoc::RuleDSL::CompilerDSL).to receive(:new).and_return(dsl)
27
+ Nanoc::CLI.run(['shell'])
28
+ end
16
29
 
17
- context = Object.new
18
- allow(Nanoc::Int::Context).to receive(:new).with(anything).and_return(context)
19
- expect(context).to receive(:pry)
30
+ it 'will preprocess if requested' do
31
+ expect_any_instance_of(Nanoc::Int::Context).to receive(:pry) do |ctx|
32
+ expect(ctx.items.size).to eq(1)
33
+ expect(ctx.items.to_a[0].unwrap.content.string).to eq('Better hello!')
34
+ end
20
35
 
21
- Nanoc::CLI.run(['shell'])
36
+ Nanoc::CLI.run(['shell', '--preprocess'])
22
37
  end
23
38
  end
24
39