nanoc 4.6.4 → 4.7.0

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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -0
  3. data/Gemfile.lock +12 -4
  4. data/NEWS.md +7 -0
  5. data/README.md +1 -1
  6. data/lib/nanoc/base/feature.rb +1 -1
  7. data/lib/nanoc/base/services/compiler.rb +18 -11
  8. data/lib/nanoc/base/services/compiler/phases.rb +2 -0
  9. data/lib/nanoc/base/services/compiler/phases/abstract.rb +34 -0
  10. data/lib/nanoc/base/services/compiler/phases/cache.rb +8 -5
  11. data/lib/nanoc/base/services/compiler/phases/mark_done.rb +8 -5
  12. data/lib/nanoc/base/services/compiler/phases/recalculate.rb +6 -2
  13. data/lib/nanoc/base/services/compiler/phases/resume.rb +9 -7
  14. data/lib/nanoc/base/services/compiler/phases/write.rb +8 -5
  15. data/lib/nanoc/base/services/compiler/stages/compile_reps.rb +1 -1
  16. data/lib/nanoc/cli.rb +8 -0
  17. data/lib/nanoc/cli/commands/compile.rb +81 -27
  18. data/lib/nanoc/cli/commands/nanoc.rb +2 -1
  19. data/lib/nanoc/cli/error_handler.rb +1 -0
  20. data/lib/nanoc/filters.rb +1 -0
  21. data/lib/nanoc/filters/erubi.rb +27 -0
  22. data/lib/nanoc/rule_dsl/rule_context.rb +16 -2
  23. data/lib/nanoc/telemetry/labelled_counter.rb +9 -9
  24. data/lib/nanoc/telemetry/labelled_summary.rb +15 -11
  25. data/lib/nanoc/version.rb +1 -1
  26. data/spec/nanoc/base/compiler_spec.rb +2 -0
  27. data/spec/nanoc/base/services/compiler/phases/abstract_spec.rb +49 -0
  28. data/spec/nanoc/base/services/compiler/phases/cache_spec.rb +13 -9
  29. data/spec/nanoc/cli/commands/compile/timing_recorder_spec.rb +101 -5
  30. data/spec/nanoc/rule_dsl/rule_context_spec.rb +63 -13
  31. data/spec/nanoc/telemetry/labelled_counter_spec.rb +10 -10
  32. data/spec/nanoc/telemetry/labelled_summary_spec.rb +36 -23
  33. data/spec/nanoc/telemetry/stopwatch_spec.rb +2 -0
  34. data/spec/nanoc/telemetry_spec.rb +14 -14
  35. data/spec/spec_helper.rb +3 -0
  36. data/test/base/test_compiler.rb +0 -38
  37. data/test/checking/checks/test_html.rb +0 -4
  38. data/test/filters/test_erubi.rb +73 -0
  39. data/test/fixtures/vcr_cassettes/html_run_ok.yml +27 -98
  40. data/test/helper.rb +3 -0
  41. metadata +6 -2
@@ -24,8 +24,9 @@ opt :C, :'no-color', 'disable color' do
24
24
  $stderr.add_stream_cleaner(Nanoc::CLI::StreamCleaners::ANSIColors)
25
25
  end
26
26
 
27
- opt :V, :verbose, 'make output more detailed' do
27
+ opt :V, :verbose, 'make output more detailed', multiple: true do |val|
28
28
  Nanoc::CLI::Logger.instance.level = :low
29
+ Nanoc::CLI.verbosity = val.size
29
30
  end
30
31
 
31
32
  opt :v, :version, 'show version information and quit' do
@@ -179,6 +179,7 @@ module Nanoc::CLI
179
179
  'builder' => 'builder',
180
180
  'coderay' => 'coderay',
181
181
  'cri' => 'cri',
182
+ 'erubi' => 'erubi',
182
183
  'erubis' => 'erubis',
183
184
  'escape' => 'escape',
184
185
  'fog' => 'fog',
@@ -7,6 +7,7 @@ require_relative 'filters/bluecloth'
7
7
  require_relative 'filters/colorize_syntax'
8
8
  require_relative 'filters/coffeescript'
9
9
  require_relative 'filters/erb'
10
+ require_relative 'filters/erubi'
10
11
  require_relative 'filters/erubis'
11
12
  require_relative 'filters/haml'
12
13
  require_relative 'filters/handlebars'
@@ -0,0 +1,27 @@
1
+ module Nanoc::Filters
2
+ # @api private
3
+ class Erubi < Nanoc::Filter
4
+ identifier :erubi
5
+
6
+ requires 'erubi'
7
+
8
+ # Runs the content through [Erubi](https://github.com/jeremyevans/erubi).
9
+ # To prevent single quote escaping use :escapefunc => 'Nanoc::Helpers::HTMLEscape.html_escape'
10
+ # See the Erubi documentation for more options.
11
+ #
12
+ # @param [String] content The content to filter
13
+ #
14
+ # @return [String] The filtered content
15
+ def run(content, params = {})
16
+ # Create context
17
+ context = ::Nanoc::Int::Context.new(assigns)
18
+
19
+ # Get binding
20
+ proc = assigns[:content] ? -> { assigns[:content] } : nil
21
+ assigns_binding = context.get_binding(&proc)
22
+
23
+ # Get result
24
+ eval(::Erubi::Engine.new(content, { bufvar: '_erbout', filename: filename }.merge(params)).src, assigns_binding)
25
+ end
26
+ end
27
+ end
@@ -73,11 +73,25 @@ module Nanoc::RuleDSL
73
73
  # @param [String] path
74
74
  #
75
75
  # @return [void]
76
- def write(path)
76
+ def write(arg)
77
77
  @_write_snapshot_counter ||= 0
78
78
  snapshot_name = "_#{@_write_snapshot_counter}".to_sym
79
79
  @_write_snapshot_counter += 1
80
- snapshot(snapshot_name, path: path)
80
+
81
+ case arg
82
+ when String, Nanoc::Identifier
83
+ snapshot(snapshot_name, path: arg)
84
+ when Hash
85
+ if arg.key?(:ext)
86
+ ext = arg[:ext].sub(/\A\./, '')
87
+ path = @item.identifier.without_exts + '.' + ext
88
+ snapshot(snapshot_name, path: path)
89
+ else
90
+ raise ArgumentError, 'Cannot call #write this way (need path or :ext)'
91
+ end
92
+ else
93
+ raise ArgumentError, 'Cannot call #write this way (need path or :ext)'
94
+ end
81
95
  end
82
96
  end
83
97
  end
@@ -4,23 +4,23 @@ module Nanoc::Telemetry
4
4
  @counters = {}
5
5
  end
6
6
 
7
- def increment(labels)
8
- get(labels).increment
7
+ def increment(label)
8
+ get(label).increment
9
9
  end
10
10
 
11
- def get(labels)
12
- @counters.fetch(labels) { @counters[labels] = Counter.new }
11
+ def get(label)
12
+ @counters.fetch(label) { @counters[label] = Counter.new }
13
13
  end
14
14
 
15
- # TODO: add #labels
15
+ # TODO: add #empty?
16
16
 
17
- def value(labels)
18
- get(labels).value
17
+ def value(label)
18
+ get(label).value
19
19
  end
20
20
 
21
21
  def values
22
- @counters.each_with_object({}) do |(labels, counter), res|
23
- res[labels] = counter.value
22
+ @counters.each_with_object({}) do |(label, counter), res|
23
+ res[label] = counter.value
24
24
  end
25
25
  end
26
26
  end
@@ -4,28 +4,32 @@ module Nanoc::Telemetry
4
4
  @summaries = {}
5
5
  end
6
6
 
7
- def observe(value, labels)
8
- get(labels).observe(value)
7
+ def observe(value, label)
8
+ get(label).observe(value)
9
9
  end
10
10
 
11
- def get(labels)
12
- @summaries.fetch(labels) { @summaries[labels] = Summary.new }
11
+ def get(label)
12
+ @summaries.fetch(label) { @summaries[label] = Summary.new }
13
13
  end
14
14
 
15
- def labels
16
- @summaries.keys
15
+ def empty?
16
+ @summaries.empty?
17
17
  end
18
18
 
19
- def quantile(fraction, labels)
20
- get(labels).quantile(fraction)
19
+ def quantile(fraction, label)
20
+ get(label).quantile(fraction)
21
+ end
22
+
23
+ def map
24
+ @summaries.map { |(label, summary)| yield(label, summary) }
21
25
  end
22
26
 
23
27
  # TODO: add quantiles(fraction)
24
- # TODO: add min(labels)
28
+ # TODO: add min(label)
25
29
  # TODO: add mins
26
- # TODO: add max(labels)
30
+ # TODO: add max(label)
27
31
  # TODO: add maxs
28
- # TODO: add sum(labels)
32
+ # TODO: add sum(label)
29
33
  # TODO: add sums
30
34
  end
31
35
  end
@@ -1,4 +1,4 @@
1
1
  module Nanoc
2
2
  # The current Nanoc version.
3
- VERSION = '4.6.4'.freeze
3
+ VERSION = '4.7.0'.freeze
4
4
  end
@@ -70,6 +70,8 @@ describe Nanoc::Int::Compiler do
70
70
 
71
71
  allow(action_provider).to receive(:memory_for).with(rep).and_return(memory)
72
72
  allow(action_provider).to receive(:memory_for).with(other_rep).and_return(memory)
73
+
74
+ allow(Nanoc::Int::NotificationCenter).to receive(:post)
73
75
  end
74
76
 
75
77
  describe '#compile_rep' do
@@ -0,0 +1,49 @@
1
+ describe Nanoc::Int::Compiler::Phases::Abstract do
2
+ subject(:phase) do
3
+ described_class.new(wrapped: wrapped, name: 'my_phase')
4
+ end
5
+
6
+ let(:item) { Nanoc::Int::Item.new('foo', {}, '/stuff.md') }
7
+ let(:rep) { Nanoc::Int::ItemRep.new(item, :default) }
8
+
9
+ let(:wrapped) { nil }
10
+
11
+ describe '#run' do
12
+ subject { phase.run(rep, is_outdated: false) {} }
13
+
14
+ it 'raises' do
15
+ expect { subject }.to raise_error(NotImplementedError)
16
+ end
17
+ end
18
+
19
+ describe '#call' do
20
+ subject { phase.call(rep, is_outdated: false) }
21
+
22
+ let(:phase) do
23
+ Class.new(described_class) do
24
+ def run(_rep, is_outdated:) # rubocop:disable Lint/UnusedMethodArgument
25
+ yield
26
+ end
27
+ end.new(wrapped: wrapped, name: 'my_phase')
28
+ end
29
+
30
+ let(:wrapped) do
31
+ Class.new(described_class) do
32
+ def run(_rep, is_outdated:); end
33
+ end.new(wrapped: nil, name: 'wrapped_phase')
34
+ end
35
+
36
+ it 'sends the proper notifications' do
37
+ expect(Nanoc::Int::NotificationCenter).to receive(:post).with(:phase_started, 'my_phase', rep).ordered
38
+ expect(Nanoc::Int::NotificationCenter).to receive(:post).with(:phase_yielded, 'my_phase', rep).ordered
39
+
40
+ expect(Nanoc::Int::NotificationCenter).to receive(:post).with(:phase_started, 'wrapped_phase', rep).ordered
41
+ expect(Nanoc::Int::NotificationCenter).to receive(:post).with(:phase_ended, 'wrapped_phase', rep).ordered
42
+
43
+ expect(Nanoc::Int::NotificationCenter).to receive(:post).with(:phase_resumed, 'my_phase', rep).ordered
44
+ expect(Nanoc::Int::NotificationCenter).to receive(:post).with(:phase_ended, 'my_phase', rep).ordered
45
+
46
+ subject
47
+ end
48
+ end
49
+ end
@@ -14,7 +14,7 @@ describe Nanoc::Int::Compiler::Phases::Cache do
14
14
  let(:snapshot_repo) { Nanoc::Int::SnapshotRepo.new }
15
15
 
16
16
  let(:wrapped_class) do
17
- Class.new do
17
+ Class.new(Nanoc::Int::Compiler::Phases::Abstract) do
18
18
  def initialize(snapshot_repo)
19
19
  @snapshot_repo = snapshot_repo
20
20
  end
@@ -31,10 +31,17 @@ describe Nanoc::Int::Compiler::Phases::Cache do
31
31
  let(:rep) { Nanoc::Int::ItemRep.new(item, :latex) }
32
32
 
33
33
  describe '#run' do
34
- subject { phase.run(rep, is_outdated: is_outdated) }
34
+ subject { phase.call(rep, is_outdated: is_outdated) }
35
35
 
36
36
  let(:is_outdated) { raise 'override me' }
37
37
 
38
+ before do
39
+ allow(Nanoc::Int::NotificationCenter).to receive(:post).with(:phase_started, anything, anything)
40
+ allow(Nanoc::Int::NotificationCenter).to receive(:post).with(:phase_yielded, anything, anything)
41
+ allow(Nanoc::Int::NotificationCenter).to receive(:post).with(:phase_resumed, anything, anything)
42
+ allow(Nanoc::Int::NotificationCenter).to receive(:post).with(:phase_ended, anything, anything)
43
+ end
44
+
38
45
  shared_examples 'calls wrapped' do
39
46
  it 'delegates to wrapped' do
40
47
  expect(wrapped).to receive(:run).with(rep, is_outdated: is_outdated)
@@ -48,8 +55,7 @@ describe Nanoc::Int::Compiler::Phases::Cache do
48
55
  .to(true)
49
56
  end
50
57
 
51
- it 'sends no notifications' do
52
- expect(Nanoc::Int::NotificationCenter).not_to receive(:post)
58
+ it 'sends no other notifications' do
53
59
  subject
54
60
  end
55
61
 
@@ -75,6 +81,7 @@ describe Nanoc::Int::Compiler::Phases::Cache do
75
81
  end
76
82
 
77
83
  it 'writes content to cache' do
84
+ expect(Nanoc::Int::NotificationCenter).to receive(:post).with(:cached_content_used, rep)
78
85
  expect { subject }
79
86
  .to change { snapshot_repo.get(rep, :last) }
80
87
  .from(nil)
@@ -82,6 +89,7 @@ describe Nanoc::Int::Compiler::Phases::Cache do
82
89
  end
83
90
 
84
91
  it 'marks rep as compiled' do
92
+ expect(Nanoc::Int::NotificationCenter).to receive(:post).with(:cached_content_used, rep)
85
93
  expect { subject }
86
94
  .to change { rep.compiled? }
87
95
  .from(false)
@@ -89,14 +97,10 @@ describe Nanoc::Int::Compiler::Phases::Cache do
89
97
  end
90
98
 
91
99
  it 'does not change compiled content cache' do
100
+ expect(Nanoc::Int::NotificationCenter).to receive(:post).with(:cached_content_used, rep)
92
101
  expect { subject }
93
102
  .not_to change { compiled_content_cache[rep] }
94
103
  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
104
  end
101
105
 
102
106
  context 'binary cached compiled content available' do
@@ -4,6 +4,8 @@ describe Nanoc::CLI::Commands::Compile::TimingRecorder, stdio: true do
4
4
  before { Timecop.freeze(Time.local(2008, 1, 2, 14, 5, 0)) }
5
5
  after { Timecop.return }
6
6
 
7
+ before { Nanoc::CLI.verbosity = 2 }
8
+
7
9
  let(:reps) do
8
10
  Nanoc::Int::ItemRepRepo.new.tap do |reps|
9
11
  reps << rep
@@ -27,7 +29,7 @@ describe Nanoc::CLI::Commands::Compile::TimingRecorder, stdio: true do
27
29
  Nanoc::Int::NotificationCenter.post(:filtering_ended, rep, :erb)
28
30
 
29
31
  expect { listener.stop }
30
- .to output(/^erb │ 1 1\.00s 1\.00s 1\.00s 1\.00s$/).to_stdout
32
+ .to output(/^\s*erb │ 1 1\.00s 1\.00s 1\.00s 1\.00s$/).to_stdout
31
33
  end
32
34
 
33
35
  it 'records multiple from filtering_started to filtering_ended' do
@@ -43,7 +45,7 @@ describe Nanoc::CLI::Commands::Compile::TimingRecorder, stdio: true do
43
45
  Nanoc::Int::NotificationCenter.post(:filtering_ended, rep, :erb)
44
46
 
45
47
  expect { listener.stop }
46
- .to output(/^erb │ 2 1\.00s 1\.50s 2\.00s 3\.00s$/).to_stdout
48
+ .to output(/^\s*erb │ 2 1\.00s 1\.50s 2\.00s 3\.00s$/).to_stdout
47
49
  end
48
50
 
49
51
  it 'records inner filters in nested filtering_started/filtering_ended' do
@@ -59,7 +61,7 @@ describe Nanoc::CLI::Commands::Compile::TimingRecorder, stdio: true do
59
61
  Nanoc::Int::NotificationCenter.post(:filtering_ended, rep, :outer)
60
62
 
61
63
  expect { listener.stop }
62
- .to output(/^inner │ 1 2\.00s 2\.00s 2\.00s 2\.00s$/).to_stdout
64
+ .to output(/^\s*inner │ 1 2\.00s 2\.00s 2\.00s 2\.00s$/).to_stdout
63
65
  end
64
66
 
65
67
  it 'records outer filters in nested filtering_started/filtering_ended' do
@@ -75,7 +77,27 @@ describe Nanoc::CLI::Commands::Compile::TimingRecorder, stdio: true do
75
77
  Nanoc::Int::NotificationCenter.post(:filtering_ended, rep, :outer)
76
78
 
77
79
  expect { listener.stop }
78
- .to output(/^outer │ 1 6\.00s 6\.00s 6\.00s 6\.00s$/).to_stdout
80
+ .to output(/^\s*outer │ 1 6\.00s 6\.00s 6\.00s 6\.00s$/).to_stdout
81
+ end
82
+
83
+ it 'pauses outer stopwatch when suspended' do
84
+ listener.start
85
+
86
+ Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 0))
87
+ Nanoc::Int::NotificationCenter.post(:compilation_started, rep)
88
+ Nanoc::Int::NotificationCenter.post(:filtering_started, rep, :outer)
89
+ Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 1))
90
+ Nanoc::Int::NotificationCenter.post(:filtering_started, rep, :inner)
91
+ Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 3))
92
+ Nanoc::Int::NotificationCenter.post(:compilation_suspended, rep, :__anything__)
93
+ Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 6))
94
+ Nanoc::Int::NotificationCenter.post(:compilation_started, rep)
95
+ Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 10))
96
+ Nanoc::Int::NotificationCenter.post(:filtering_ended, rep, :inner)
97
+ Nanoc::Int::NotificationCenter.post(:filtering_ended, rep, :outer)
98
+
99
+ expect { listener.stop }
100
+ .to output(/^\s*outer │ 1 7\.00s 7\.00s 7\.00s 7\.00s$/).to_stdout
79
101
  end
80
102
 
81
103
  it 'records single from filtering_started over compilation_{suspended,started} to filtering_ended' do
@@ -92,6 +114,80 @@ describe Nanoc::CLI::Commands::Compile::TimingRecorder, stdio: true do
92
114
  Nanoc::Int::NotificationCenter.post(:filtering_ended, rep, :erb)
93
115
 
94
116
  expect { listener.stop }
95
- .to output(/^erb │ 1 5\.00s 5\.00s 5\.00s 5\.00s$/).to_stdout
117
+ .to output(/^\s*erb │ 1 5\.00s 5\.00s 5\.00s 5\.00s$/).to_stdout
118
+ end
119
+
120
+ it 'records single phase start+stop' do
121
+ listener.start
122
+
123
+ Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 0))
124
+ Nanoc::Int::NotificationCenter.post(:phase_started, 'donkey', rep)
125
+ Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 1))
126
+ Nanoc::Int::NotificationCenter.post(:phase_ended, 'donkey', rep)
127
+
128
+ expect { listener.stop }
129
+ .to output(/^\s*donkey │ 1 1\.00s 1\.00s 1\.00s 1\.00s$/).to_stdout
130
+ end
131
+
132
+ it 'records multiple phase start+stop' do
133
+ listener.start
134
+
135
+ Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 0))
136
+ Nanoc::Int::NotificationCenter.post(:phase_started, 'donkey', rep)
137
+ Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 1))
138
+ Nanoc::Int::NotificationCenter.post(:phase_ended, 'donkey', rep)
139
+ Timecop.freeze(Time.local(2008, 9, 1, 11, 6, 0))
140
+ Nanoc::Int::NotificationCenter.post(:phase_started, 'donkey', rep)
141
+ Timecop.freeze(Time.local(2008, 9, 1, 11, 6, 2))
142
+ Nanoc::Int::NotificationCenter.post(:phase_ended, 'donkey', rep)
143
+
144
+ expect { listener.stop }
145
+ .to output(/^\s*donkey │ 2 1\.00s 1\.50s 2\.00s 3\.00s$/).to_stdout
146
+ end
147
+
148
+ it 'records single phase start+yield+resume+stop' do
149
+ listener.start
150
+
151
+ Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 0))
152
+ Nanoc::Int::NotificationCenter.post(:phase_started, 'donkey', rep)
153
+ Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 1))
154
+ Nanoc::Int::NotificationCenter.post(:phase_yielded, 'donkey', rep)
155
+ Timecop.freeze(Time.local(2008, 9, 1, 11, 6, 0))
156
+ Nanoc::Int::NotificationCenter.post(:phase_resumed, 'donkey', rep)
157
+ Timecop.freeze(Time.local(2008, 9, 1, 11, 6, 2))
158
+ Nanoc::Int::NotificationCenter.post(:phase_ended, 'donkey', rep)
159
+
160
+ expect { listener.stop }
161
+ .to output(/^\s*donkey │ 1 3\.00s 3\.00s 3\.00s 3\.00s$/).to_stdout
162
+ end
163
+
164
+ it 'records single phase start+yield+abort+start+stop' do
165
+ listener.start
166
+
167
+ Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 0))
168
+ Nanoc::Int::NotificationCenter.post(:phase_started, 'donkey', rep)
169
+ Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 1))
170
+ Nanoc::Int::NotificationCenter.post(:phase_yielded, 'donkey', rep)
171
+ Timecop.freeze(Time.local(2008, 9, 1, 11, 6, 0))
172
+ Nanoc::Int::NotificationCenter.post(:phase_aborted, 'donkey', rep)
173
+ Timecop.freeze(Time.local(2008, 9, 1, 12, 7, 2))
174
+ Nanoc::Int::NotificationCenter.post(:phase_started, 'donkey', rep)
175
+ Timecop.freeze(Time.local(2008, 9, 1, 12, 7, 5))
176
+ Nanoc::Int::NotificationCenter.post(:phase_ended, 'donkey', rep)
177
+
178
+ expect { listener.stop }
179
+ .to output(/^\s*donkey │ 2 1\.00s 2\.00s 3\.00s 4\.00s$/).to_stdout
180
+ end
181
+
182
+ it 'records stage duration' do
183
+ listener.start
184
+
185
+ Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 0))
186
+ Nanoc::Int::NotificationCenter.post(:stage_started, 'donkey', rep)
187
+ Timecop.freeze(Time.local(2008, 9, 1, 10, 5, 1))
188
+ Nanoc::Int::NotificationCenter.post(:stage_ended, 'donkey', rep)
189
+
190
+ expect { listener.stop }
191
+ .to output(/^\s*donkey │ 1\.00s$/).to_stdout
96
192
  end
97
193
  end
@@ -162,25 +162,75 @@ describe(Nanoc::RuleDSL::RuleContext) do
162
162
  end
163
163
 
164
164
  describe '#write' do
165
- context 'calling once' do
166
- subject { rule_context.write('/foo.html') }
165
+ context 'with path' do
166
+ context 'calling once' do
167
+ subject { rule_context.write('/foo.html') }
168
+
169
+ it 'makes a request to the executor' do
170
+ expect(executor).to receive(:snapshot).with(:_0, path: '/foo.html')
171
+ subject
172
+ end
173
+ end
174
+
175
+ context 'calling twice' do
176
+ subject do
177
+ rule_context.write('/foo.html')
178
+ rule_context.write('/bar.html')
179
+ end
180
+
181
+ it 'makes two requests to the executor with unique snapshot names' do
182
+ expect(executor).to receive(:snapshot).with(:_0, path: '/foo.html')
183
+ expect(executor).to receive(:snapshot).with(:_1, path: '/bar.html')
184
+ subject
185
+ end
186
+ end
187
+ end
167
188
 
168
- it 'makes a request to the executor' do
169
- expect(executor).to receive(:snapshot).with(:_0, path: '/foo.html')
170
- subject
189
+ context 'with :ext, without period' do
190
+ context 'calling once' do
191
+ subject { rule_context.write(ext: 'html') }
192
+
193
+ it 'makes a request to the executor' do
194
+ expect(executor).to receive(:snapshot).with(:_0, path: '/foo.html')
195
+ subject
196
+ end
197
+ end
198
+
199
+ context 'calling twice' do
200
+ subject do
201
+ rule_context.write(ext: 'html')
202
+ rule_context.write(ext: 'htm')
203
+ end
204
+
205
+ it 'makes a request to the executor' do
206
+ expect(executor).to receive(:snapshot).with(:_0, path: '/foo.html')
207
+ expect(executor).to receive(:snapshot).with(:_1, path: '/foo.htm')
208
+ subject
209
+ end
171
210
  end
172
211
  end
173
212
 
174
- context 'calling twice' do
175
- subject do
176
- rule_context.write('/foo.html')
177
- rule_context.write('/bar.html')
213
+ context 'with :ext, without period' do
214
+ context 'calling once' do
215
+ subject { rule_context.write(ext: '.html') }
216
+
217
+ it 'makes a request to the executor' do
218
+ expect(executor).to receive(:snapshot).with(:_0, path: '/foo.html')
219
+ subject
220
+ end
178
221
  end
179
222
 
180
- it 'makes two requests to the executor with unique snapshot names' do
181
- expect(executor).to receive(:snapshot).with(:_0, path: '/foo.html')
182
- expect(executor).to receive(:snapshot).with(:_1, path: '/bar.html')
183
- subject
223
+ context 'calling twice' do
224
+ subject do
225
+ rule_context.write(ext: '.html')
226
+ rule_context.write(ext: '.htm')
227
+ end
228
+
229
+ it 'makes a request to the executor' do
230
+ expect(executor).to receive(:snapshot).with(:_0, path: '/foo.html')
231
+ expect(executor).to receive(:snapshot).with(:_1, path: '/foo.htm')
232
+ subject
233
+ end
184
234
  end
185
235
  end
186
236
  end