nanoc 4.6.4 → 4.7.0

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