nanoc 4.7.10 → 4.7.11

Sign up to get free protection for your applications and to get access to all the features.
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