nanoc 4.4.6 → 4.4.7
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.
- checksums.yaml +4 -4
- data/Gemfile +0 -1
- data/Gemfile.lock +11 -13
- data/LICENSE +1 -1
- data/NEWS.md +11 -0
- data/README.md +1 -1
- data/lib/nanoc.rb +1 -1
- data/lib/nanoc/base/repos.rb +1 -0
- data/lib/nanoc/base/repos/outdatedness_store.rb +49 -0
- data/lib/nanoc/base/services/compiler.rb +286 -152
- data/lib/nanoc/base/services/compiler_loader.rb +4 -0
- data/lib/nanoc/base/services/outdatedness_checker.rb +2 -6
- data/lib/nanoc/checking/checks/external_links.rb +0 -2
- data/lib/nanoc/data_sources/filesystem.rb +13 -4
- data/lib/nanoc/version.rb +1 -1
- data/spec/nanoc/base/compiler_spec.rb +54 -14
- data/spec/nanoc/base/repos/outdatedness_store_spec.rb +100 -0
- data/spec/nanoc/base/services/outdatedness_checker_spec.rb +15 -6
- data/spec/nanoc/base/services/outdatedness_rules_spec.rb +12 -4
- data/spec/nanoc/cli/commands/view_spec.rb +2 -0
- data/spec/nanoc/data_sources/filesystem_spec.rb +13 -1
- data/spec/nanoc/extra/parallel_collection_spec.rb +2 -2
- data/spec/nanoc/integration/outdatedness_integration_spec.rb +8 -0
- data/spec/nanoc/integration/partial_recompilation_spec.rb +48 -0
- data/spec/nanoc/regressions/gh_1045_spec.rb +48 -0
- data/spec/nanoc/regressions/gh_1047_spec.rb +28 -0
- data/test/base/test_compiler.rb +1 -0
- metadata +7 -2
@@ -16,6 +16,9 @@ module Nanoc::Int
|
|
16
16
|
|
17
17
|
action_provider ||= Nanoc::Int::ActionProvider.named(:rule_dsl).for(site)
|
18
18
|
|
19
|
+
outdatedness_store =
|
20
|
+
Nanoc::Int::OutdatednessStore.new(site: site, reps: item_rep_repo)
|
21
|
+
|
19
22
|
outdatedness_checker =
|
20
23
|
Nanoc::Int::OutdatednessChecker.new(
|
21
24
|
site: site,
|
@@ -40,6 +43,7 @@ module Nanoc::Int
|
|
40
43
|
outdatedness_checker: outdatedness_checker,
|
41
44
|
reps: item_rep_repo,
|
42
45
|
action_provider: action_provider,
|
46
|
+
outdatedness_store: outdatedness_store,
|
43
47
|
}
|
44
48
|
|
45
49
|
Nanoc::Int::Compiler.new(site, params)
|
@@ -82,12 +82,8 @@ module Nanoc::Int
|
|
82
82
|
|
83
83
|
Reasons = Nanoc::Int::OutdatednessReasons
|
84
84
|
|
85
|
-
#
|
86
|
-
|
87
|
-
# @param [Nanoc::Int::DependencyStore] dependency_store
|
88
|
-
# @param [Nanoc::Int::RuleMemoryStore] rule_memory_store
|
89
|
-
# @param [Nanoc::Int::ActionProvider] action_provider
|
90
|
-
# @param [Nanoc::Int::ItemRepRepo] reps
|
85
|
+
# FIXME: Replace C::Any with proper types
|
86
|
+
contract C::KeywordArgs[site: Nanoc::Int::Site, checksum_store: Nanoc::Int::ChecksumStore, dependency_store: Nanoc::Int::DependencyStore, rule_memory_store: Nanoc::Int::RuleMemoryStore, action_provider: C::Any, reps: Nanoc::Int::ItemRepRepo] => C::Any
|
91
87
|
def initialize(site:, checksum_store:, dependency_store:, rule_memory_store:, action_provider:, reps:)
|
92
88
|
@site = site
|
93
89
|
@checksum_store = checksum_store
|
@@ -12,8 +12,6 @@ module ::Nanoc::Checking::Checks
|
|
12
12
|
identifiers :external_links, :elinks
|
13
13
|
|
14
14
|
def run
|
15
|
-
require 'parallel'
|
16
|
-
|
17
15
|
# Find all broken external hrefs
|
18
16
|
# TODO: de-duplicate this (duplicated in internal links check)
|
19
17
|
filenames = output_filenames.select { |f| File.extname(f) == '.html' && !excluded_file?(f) }
|
@@ -163,7 +163,7 @@ module Nanoc::DataSources
|
|
163
163
|
attributes,
|
164
164
|
identifier,
|
165
165
|
content_checksum_data: proto_doc.content_checksum_data,
|
166
|
-
attributes_checksum_data: proto_doc
|
166
|
+
attributes_checksum_data: attributes_checksum_data_for(proto_doc, content_filename, meta_filename),
|
167
167
|
)
|
168
168
|
end
|
169
169
|
end
|
@@ -171,16 +171,25 @@ module Nanoc::DataSources
|
|
171
171
|
res
|
172
172
|
end
|
173
173
|
|
174
|
-
def
|
175
|
-
|
174
|
+
def attributes_checksum_data_for(proto_doc, content_filename, meta_filename)
|
175
|
+
YAML.dump(
|
176
|
+
attributes: proto_doc.attributes_checksum_data,
|
177
|
+
extra_attributes: extra_attributes_for(content_filename, meta_filename),
|
178
|
+
)
|
179
|
+
end
|
180
|
+
|
181
|
+
def extra_attributes_for(content_filename, meta_filename)
|
182
|
+
{
|
176
183
|
filename: content_filename,
|
177
184
|
content_filename: content_filename,
|
178
185
|
meta_filename: meta_filename,
|
179
186
|
extension: content_filename ? ext_of(content_filename)[1..-1] : nil,
|
180
187
|
mtime: mtime_of(content_filename, meta_filename),
|
181
188
|
}
|
189
|
+
end
|
182
190
|
|
183
|
-
|
191
|
+
def attributes_for(proto_doc, content_filename, meta_filename)
|
192
|
+
extra_attributes_for(content_filename, meta_filename).merge(proto_doc.attributes)
|
184
193
|
end
|
185
194
|
|
186
195
|
def identifier_for(content_filename, meta_filename, dir_name)
|
data/lib/nanoc/version.rb
CHANGED
@@ -9,16 +9,18 @@ describe Nanoc::Int::Compiler do
|
|
9
9
|
dependency_store: dependency_store,
|
10
10
|
outdatedness_checker: outdatedness_checker,
|
11
11
|
reps: reps,
|
12
|
+
outdatedness_store: outdatedness_store,
|
12
13
|
)
|
13
14
|
end
|
14
15
|
|
15
|
-
let(:checksum_store)
|
16
|
-
let(:rule_memory_store)
|
16
|
+
let(:checksum_store) { Nanoc::Int::ChecksumStore.new(objects: items) }
|
17
|
+
let(:rule_memory_store) { Nanoc::Int::RuleMemoryStore.new }
|
17
18
|
|
18
19
|
let(:dependency_store) { Nanoc::Int::DependencyStore.new(items.to_a) }
|
19
20
|
let(:reps) { Nanoc::Int::ItemRepRepo.new }
|
20
21
|
|
21
22
|
let(:outdatedness_checker) { double(:outdatedness_checker) }
|
23
|
+
let(:outdatedness_store) { Nanoc::Int::OutdatednessStore.new(site: site, reps: reps) }
|
22
24
|
let(:action_provider) { double(:action_provider) }
|
23
25
|
|
24
26
|
let(:compiled_content_cache) { Nanoc::Int::CompiledContentCache.new(items: items) }
|
@@ -50,10 +52,13 @@ describe Nanoc::Int::Compiler do
|
|
50
52
|
end
|
51
53
|
|
52
54
|
let(:memory) do
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
55
|
+
actions =
|
56
|
+
[
|
57
|
+
Nanoc::Int::ProcessingActions::Filter.new(:erb, {}),
|
58
|
+
Nanoc::Int::ProcessingActions::Snapshot.new(:last, nil),
|
59
|
+
]
|
60
|
+
|
61
|
+
Nanoc::Int::RuleMemory.new(nil, actions: actions)
|
57
62
|
end
|
58
63
|
|
59
64
|
before do
|
@@ -72,7 +77,10 @@ describe Nanoc::Int::Compiler do
|
|
72
77
|
end
|
73
78
|
|
74
79
|
describe '#compile_reps' do
|
75
|
-
subject
|
80
|
+
subject do
|
81
|
+
compiler.send(:determine_outdatedness)
|
82
|
+
compiler.send(:compile_reps)
|
83
|
+
end
|
76
84
|
|
77
85
|
before do
|
78
86
|
allow(action_provider).to receive(:snapshots_defs_for).with(rep).and_return(snapshot_defs_for_rep)
|
@@ -93,6 +101,21 @@ describe Nanoc::Int::Compiler do
|
|
93
101
|
.to('3')
|
94
102
|
end
|
95
103
|
|
104
|
+
it 'removes the item rep from the outdatedness store' do
|
105
|
+
expect(compiler.outdatedness_store.include?(rep)).not_to be
|
106
|
+
expect { subject }.not_to change { compiler.outdatedness_store.include?(rep) }
|
107
|
+
end
|
108
|
+
|
109
|
+
context 'rep in outdatedness store' do
|
110
|
+
before do
|
111
|
+
compiler.outdatedness_store.add(rep)
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'removes the item rep from the outdatedness store' do
|
115
|
+
expect { subject }.to change { compiler.outdatedness_store.include?(rep) }.from(true).to(false)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
96
119
|
context 'exception' do
|
97
120
|
let(:item) { Nanoc::Int::Item.new('<%= raise "lol" %>', {}, '/hi.md') }
|
98
121
|
|
@@ -112,11 +135,28 @@ describe Nanoc::Int::Compiler do
|
|
112
135
|
expect(err.unwrap.message).to eq('lol')
|
113
136
|
end
|
114
137
|
end
|
138
|
+
|
139
|
+
it 'adds the item rep to the outdatedness store' do
|
140
|
+
expect { subject rescue nil }.to change { compiler.outdatedness_store.include?(rep) }.from(false).to(true)
|
141
|
+
end
|
142
|
+
|
143
|
+
context 'rep in outdatedness store' do
|
144
|
+
before do
|
145
|
+
compiler.outdatedness_store.add(rep)
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'keeps the item rep in the outdatedness store' do
|
149
|
+
expect(compiler.outdatedness_store.include?(rep)).to be
|
150
|
+
expect { subject rescue nil }.not_to change { compiler.outdatedness_store.include?(rep) }
|
151
|
+
end
|
152
|
+
end
|
115
153
|
end
|
116
154
|
end
|
117
155
|
|
118
156
|
describe '#compile_rep' do
|
119
|
-
|
157
|
+
let(:stage) { compiler.send(:compile_reps_stage) }
|
158
|
+
|
159
|
+
subject { stage.send(:compile_rep, rep, is_outdated: is_outdated) }
|
120
160
|
|
121
161
|
let(:is_outdated) { true }
|
122
162
|
|
@@ -145,10 +185,10 @@ describe Nanoc::Int::Compiler do
|
|
145
185
|
it 'generates expected output' do
|
146
186
|
expect(rep.snapshot_contents[:last].string).to eql(item.content.string)
|
147
187
|
|
148
|
-
expect {
|
188
|
+
expect { stage.send(:compile_rep, rep, is_outdated: true) }
|
149
189
|
.to raise_error(Nanoc::Int::Errors::UnmetDependency)
|
150
|
-
|
151
|
-
|
190
|
+
stage.send(:compile_rep, other_rep, is_outdated: true)
|
191
|
+
stage.send(:compile_rep, rep, is_outdated: true)
|
152
192
|
|
153
193
|
expect(rep.snapshot_contents[:last].string).to eql('other=other content')
|
154
194
|
end
|
@@ -171,10 +211,10 @@ describe Nanoc::Int::Compiler do
|
|
171
211
|
expect(Nanoc::Int::NotificationCenter).to receive(:post).with(:filtering_ended, rep, :erb).ordered
|
172
212
|
expect(Nanoc::Int::NotificationCenter).to receive(:post).with(:compilation_ended, rep).ordered
|
173
213
|
|
174
|
-
expect {
|
214
|
+
expect { stage.send(:compile_rep, rep, is_outdated: true) }
|
175
215
|
.to raise_error(Nanoc::Int::Errors::UnmetDependency)
|
176
|
-
|
177
|
-
|
216
|
+
stage.send(:compile_rep, other_rep, is_outdated: true)
|
217
|
+
stage.send(:compile_rep, rep, is_outdated: true)
|
178
218
|
end
|
179
219
|
end
|
180
220
|
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
describe Nanoc::Int::OutdatednessStore do
|
2
|
+
subject(:store) { described_class.new(site: site, reps: reps) }
|
3
|
+
|
4
|
+
let(:site) { double(:site) }
|
5
|
+
let(:reps) { Nanoc::Int::ItemRepRepo.new }
|
6
|
+
|
7
|
+
let(:item) { Nanoc::Int::Item.new('foo', {}, '/foo.md') }
|
8
|
+
let(:rep) { Nanoc::Int::ItemRep.new(item, :foo) }
|
9
|
+
|
10
|
+
let(:site) do
|
11
|
+
Nanoc::Int::Site.new(
|
12
|
+
config: config,
|
13
|
+
code_snippets: code_snippets,
|
14
|
+
items: items,
|
15
|
+
layouts: layouts,
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
let(:config) { Nanoc::Int::Configuration.new.with_defaults }
|
20
|
+
let(:items) { [] }
|
21
|
+
let(:layouts) { [] }
|
22
|
+
let(:code_snippets) { [] }
|
23
|
+
|
24
|
+
describe '#include?, #add and #remove' do
|
25
|
+
subject { store.include?(rep) }
|
26
|
+
|
27
|
+
context 'nothing added' do
|
28
|
+
it { is_expected.not_to be }
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'rep added' do
|
32
|
+
before { store.add(rep) }
|
33
|
+
it { is_expected.to be }
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'rep added and removed' do
|
37
|
+
before do
|
38
|
+
store.add(rep)
|
39
|
+
store.remove(rep)
|
40
|
+
end
|
41
|
+
|
42
|
+
it { is_expected.not_to be }
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'rep added, removed, and added again' do
|
46
|
+
before do
|
47
|
+
store.add(rep)
|
48
|
+
store.remove(rep)
|
49
|
+
store.add(rep)
|
50
|
+
end
|
51
|
+
|
52
|
+
it { is_expected.to be }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe '#to_a' do
|
57
|
+
subject { store.to_a }
|
58
|
+
|
59
|
+
context 'nothing added' do
|
60
|
+
it { is_expected.to be_empty }
|
61
|
+
end
|
62
|
+
|
63
|
+
context 'one rep added' do
|
64
|
+
before { store.add(rep) }
|
65
|
+
it { is_expected.to eql([rep]) }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe 'reloading' do
|
70
|
+
subject do
|
71
|
+
store.store
|
72
|
+
store.load
|
73
|
+
store.include?(rep)
|
74
|
+
end
|
75
|
+
|
76
|
+
context 'not added' do
|
77
|
+
context 'rep part of new reps' do
|
78
|
+
before { reps << rep }
|
79
|
+
it { is_expected.not_to be }
|
80
|
+
end
|
81
|
+
|
82
|
+
context 'rep not part of new reps' do
|
83
|
+
it { is_expected.not_to be }
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
context 'added' do
|
88
|
+
before { store.add(rep) }
|
89
|
+
|
90
|
+
context 'rep part of new reps' do
|
91
|
+
before { reps << rep }
|
92
|
+
it { is_expected.to be }
|
93
|
+
end
|
94
|
+
|
95
|
+
context 'rep not part of new reps' do
|
96
|
+
it { is_expected.not_to be }
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -10,9 +10,22 @@ describe Nanoc::Int::OutdatednessChecker do
|
|
10
10
|
)
|
11
11
|
end
|
12
12
|
|
13
|
-
let(:site) { double(:site) }
|
14
13
|
let(:checksum_store) { double(:checksum_store) }
|
15
|
-
|
14
|
+
|
15
|
+
let(:dependency_store) do
|
16
|
+
Nanoc::Int::DependencyStore.new(objects)
|
17
|
+
end
|
18
|
+
|
19
|
+
let(:objects) { [item] }
|
20
|
+
|
21
|
+
let(:site) do
|
22
|
+
Nanoc::Int::Site.new(
|
23
|
+
config: config,
|
24
|
+
items: [],
|
25
|
+
layouts: [],
|
26
|
+
code_snippets: [],
|
27
|
+
)
|
28
|
+
end
|
16
29
|
|
17
30
|
let(:rule_memory_store) do
|
18
31
|
Nanoc::Int::RuleMemoryStore.new
|
@@ -102,10 +115,6 @@ describe Nanoc::Int::OutdatednessChecker do
|
|
102
115
|
describe '#outdated_due_to_dependencies?' do
|
103
116
|
subject { outdatedness_checker.send(:outdated_due_to_dependencies?, item) }
|
104
117
|
|
105
|
-
let(:dependency_store) do
|
106
|
-
Nanoc::Int::DependencyStore.new(objects)
|
107
|
-
end
|
108
|
-
|
109
118
|
let(:checksum_store) { Nanoc::Int::ChecksumStore.new(objects: objects) }
|
110
119
|
|
111
120
|
let(:other_item) { Nanoc::Int::Item.new('other stuff', {}, '/other.md') }
|
@@ -18,11 +18,19 @@ describe Nanoc::Int::OutdatednessRules do
|
|
18
18
|
let(:item_rep) { Nanoc::Int::ItemRep.new(item, :default) }
|
19
19
|
let(:item) { Nanoc::Int::Item.new('stuff', {}, '/foo.md') }
|
20
20
|
|
21
|
-
let(:site) { double(:site) }
|
22
21
|
let(:config) { Nanoc::Int::Configuration.new }
|
23
22
|
let(:code_snippets) { [] }
|
24
23
|
let(:objects) { [config] + code_snippets + [item] }
|
25
24
|
|
25
|
+
let(:site) do
|
26
|
+
Nanoc::Int::Site.new(
|
27
|
+
config: config,
|
28
|
+
items: [],
|
29
|
+
layouts: [],
|
30
|
+
code_snippets: code_snippets,
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
26
34
|
let(:action_provider) { double(:action_provider) }
|
27
35
|
let(:reps) { Nanoc::Int::ItemRepRepo.new }
|
28
36
|
let(:dependency_store) { Nanoc::Int::DependencyStore.new(dependency_store_objects) }
|
@@ -67,15 +75,15 @@ describe Nanoc::Int::OutdatednessRules do
|
|
67
75
|
context 'ConfigurationModified' do
|
68
76
|
let(:rule_class) { Nanoc::Int::OutdatednessRules::ConfigurationModified }
|
69
77
|
|
70
|
-
context '
|
71
|
-
let(:config) { Nanoc::Int::
|
78
|
+
context 'non-outdated' do
|
79
|
+
let(:config) { Nanoc::Int::Configuration.new }
|
72
80
|
|
73
81
|
before { checksum_store.add(config) }
|
74
82
|
|
75
83
|
it { is_expected.not_to be }
|
76
84
|
end
|
77
85
|
|
78
|
-
context '
|
86
|
+
context 'outdated' do
|
79
87
|
let(:config) { Nanoc::Int::Configuration.new }
|
80
88
|
let(:config_old) { Nanoc::Int::Configuration.new(hash: { foo: 125 }) }
|
81
89
|
|
@@ -48,7 +48,19 @@ describe Nanoc::DataSources::Filesystem do
|
|
48
48
|
expect(subject[0].identifier).to eq(Nanoc::Identifier.new('/bar/'))
|
49
49
|
expect(subject[0].checksum_data).to be_nil
|
50
50
|
expect(subject[0].content_checksum_data).to eq('test 1')
|
51
|
-
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'has the right attributes checksum data' do
|
54
|
+
cs = YAML.load(subject[0].attributes_checksum_data)
|
55
|
+
|
56
|
+
expect(cs[:attributes]).to eq("num: 1\n")
|
57
|
+
expect(cs[:extra_attributes]).to eq(
|
58
|
+
filename: 'foo/bar.html',
|
59
|
+
content_filename: 'foo/bar.html',
|
60
|
+
meta_filename: nil,
|
61
|
+
extension: 'html',
|
62
|
+
mtime: now,
|
63
|
+
)
|
52
64
|
end
|
53
65
|
end
|
54
66
|
end
|
@@ -13,7 +13,7 @@ describe Nanoc::Extra::ParallelCollection do
|
|
13
13
|
let!(:out) { [] }
|
14
14
|
|
15
15
|
it 'is fast' do
|
16
|
-
expect { subject }.to finish_in_under(0.
|
16
|
+
expect { subject }.to finish_in_under(0.25).seconds
|
17
17
|
end
|
18
18
|
|
19
19
|
it 'is correct' do
|
@@ -66,7 +66,7 @@ describe Nanoc::Extra::ParallelCollection do
|
|
66
66
|
end
|
67
67
|
|
68
68
|
it 'is fast' do
|
69
|
-
expect { subject }.to finish_in_under(0.
|
69
|
+
expect { subject }.to finish_in_under(0.25).seconds
|
70
70
|
end
|
71
71
|
|
72
72
|
it 'does not leave threads lingering' do
|
@@ -1,9 +1,14 @@
|
|
1
1
|
describe 'Outdatedness integration', site: true, stdio: true do
|
2
2
|
context 'only attribute dependency' do
|
3
|
+
let(:time) { Time.now }
|
4
|
+
|
3
5
|
before do
|
4
6
|
File.write('content/foo.md', "---\ntitle: hello\n---\n\nfoo")
|
5
7
|
File.write('content/bar.md', '<%= @items["/foo.*"][:title] %>')
|
6
8
|
|
9
|
+
FileUtils.touch('content/foo.md', mtime: time)
|
10
|
+
FileUtils.touch('content/bar.md', mtime: time)
|
11
|
+
|
7
12
|
File.write('Rules', <<EOS)
|
8
13
|
compile '/foo.*' do
|
9
14
|
write '/foo.html'
|
@@ -29,6 +34,7 @@ EOS
|
|
29
34
|
|
30
35
|
it 'shows file as outdated after modification' do
|
31
36
|
File.write('content/bar.md', 'JUST BAR!')
|
37
|
+
FileUtils.touch('content/bar.md', mtime: time)
|
32
38
|
|
33
39
|
expect { Nanoc::CLI.run(%w(show-data --no-color)) }.to(
|
34
40
|
output(/^item \/foo\.md, rep default:\n is not outdated/).to_stdout,
|
@@ -40,6 +46,7 @@ EOS
|
|
40
46
|
|
41
47
|
it 'shows file and dependencies as not outdated after content modification' do
|
42
48
|
File.write('content/foo.md', "---\ntitle: hello\n---\n\nfoooOoooOOoooOooo")
|
49
|
+
FileUtils.touch('content/foo.md', mtime: time)
|
43
50
|
|
44
51
|
expect { Nanoc::CLI.run(%w(show-data --no-color)) }.to(
|
45
52
|
output(/^item \/foo\.md, rep default:\n is outdated: /).to_stdout,
|
@@ -51,6 +58,7 @@ EOS
|
|
51
58
|
|
52
59
|
it 'shows file and dependencies as outdated after title modification' do
|
53
60
|
File.write('content/foo.md', "---\ntitle: bye\n---\n\nfoo")
|
61
|
+
FileUtils.touch('content/foo.md', mtime: time)
|
54
62
|
|
55
63
|
expect { Nanoc::CLI.run(%w(show-data --no-color)) }.to(
|
56
64
|
output(/^item \/foo\.md, rep default:\n is outdated: /).to_stdout,
|