nanoc 4.7.1 → 4.7.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +3 -3
- data/Gemfile.lock +4 -4
- data/NEWS.md +6 -0
- data/Rakefile +2 -2
- data/lib/nanoc/base/memoization.rb +3 -0
- data/lib/nanoc/base/services/outdatedness_rules.rb +17 -5
- data/lib/nanoc/base/views/post_compile_item_rep_view.rb +4 -0
- data/lib/nanoc/checking/check.rb +1 -1
- data/lib/nanoc/cli/commands/compile_listeners/timing_recorder.rb +36 -0
- data/lib/nanoc/cli/commands/show-plugins.rb +1 -1
- data/lib/nanoc/filters/colorize_syntax.rb +1 -1
- data/lib/nanoc/telemetry/labelled_counter.rb +7 -1
- data/lib/nanoc/version.rb +1 -1
- data/spec/nanoc/base/entities/outdatedness_status_spec.rb +1 -1
- data/spec/nanoc/base/entities/processing_actions/snapshot_spec.rb +1 -1
- data/spec/nanoc/base/entities/props_spec.rb +8 -8
- data/spec/nanoc/base/entities/rule_memory_spec.rb +2 -2
- data/spec/nanoc/base/memoization_spec.rb +75 -0
- data/spec/nanoc/base/services/item_rep_selector_spec.rb +12 -12
- data/spec/nanoc/base/views/post_compile_item_rep_view_spec.rb +58 -0
- data/spec/nanoc/cli/commands/compile/timing_recorder_spec.rb +22 -0
- data/spec/nanoc/helpers/tagging_spec.rb +1 -1
- data/spec/nanoc/regressions/gh_1130_spec.rb +19 -0
- data/spec/nanoc/rule_dsl/rule_memory_calculator_spec.rb +4 -4
- data/spec/nanoc/telemetry/labelled_counter_spec.rb +36 -0
- data/spec/spec_helper.rb +31 -0
- data/test/base/core_ext/array_spec.rb +2 -2
- data/test/cli/test_cleaning_stream.rb +1 -1
- data/test/filters/colorize_syntax/test_common.rb +1 -1
- metadata +4 -3
- data/test/base/test_memoization.rb +0 -71
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: db20da1ff9f55058bb62c68ac59ec1abd74e5afe
|
4
|
+
data.tar.gz: fc4c46dfd186f22446ad8a39c457fc72fcd93994
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b8f484dac9493470c64172ac234a026f17413a0fe688782426a606396e51b343c866165e24e58a574dfd948a86ea4ae1006114dbb1e495454fa27bd83f468e13
|
7
|
+
data.tar.gz: bc68076395418f75d02e6ece28d8af476423007b63d3badc18238c52c711352f02eab2ca1ec13ea79865154fca17ba15da4b0d2653bf1d32bc261ce615757ae0
|
data/Gemfile
CHANGED
@@ -49,11 +49,11 @@ group :plugins do
|
|
49
49
|
gem 'mustache', '~> 1.0'
|
50
50
|
gem 'nokogiri', '~> 1.6'
|
51
51
|
gem 'pandoc-ruby'
|
52
|
-
gem 'pygments.rb', github: 'tmm1/pygments.rb', platforms:
|
52
|
+
gem 'pygments.rb', github: 'tmm1/pygments.rb', platforms: %i(ruby mswin)
|
53
53
|
gem 'rack'
|
54
54
|
gem 'rainpress'
|
55
|
-
gem 'rdiscount', '~> 2.2', platforms:
|
56
|
-
gem 'redcarpet', platforms:
|
55
|
+
gem 'rdiscount', '~> 2.2', platforms: %i(ruby mswin)
|
56
|
+
gem 'redcarpet', platforms: %i(ruby mswin)
|
57
57
|
gem 'RedCloth', platforms: :ruby
|
58
58
|
gem 'rouge'
|
59
59
|
gem 'rubypants'
|
data/Gemfile.lock
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
GIT
|
2
2
|
remote: https://github.com/bbatsov/rubocop.git
|
3
|
-
revision:
|
3
|
+
revision: 7917f22cb5f1e2ce6c81a1cc07b2b4a14fcd4fa8
|
4
4
|
specs:
|
5
5
|
rubocop (0.47.1)
|
6
6
|
parser (>= 2.3.3.1, < 3.0)
|
@@ -27,7 +27,7 @@ GIT
|
|
27
27
|
PATH
|
28
28
|
remote: .
|
29
29
|
specs:
|
30
|
-
nanoc (4.7.
|
30
|
+
nanoc (4.7.2)
|
31
31
|
cri (~> 2.3)
|
32
32
|
ddplugin (~> 1.0)
|
33
33
|
hamster (~> 3.0)
|
@@ -341,13 +341,13 @@ GEM
|
|
341
341
|
term-ansicolor (1.4.0)
|
342
342
|
tins (~> 1.0)
|
343
343
|
thor (0.19.4)
|
344
|
-
tilt (2.0.
|
344
|
+
tilt (2.0.7)
|
345
345
|
timecop (0.8.1)
|
346
346
|
tins (1.13.2)
|
347
347
|
trollop (2.1.2)
|
348
348
|
typogruby (1.0.18)
|
349
349
|
rubypants
|
350
|
-
uglifier (3.1.
|
350
|
+
uglifier (3.1.9)
|
351
351
|
execjs (>= 0.3.0, < 3)
|
352
352
|
unicode-display_width (1.1.3)
|
353
353
|
url (0.3.2)
|
data/NEWS.md
CHANGED
data/Rakefile
CHANGED
@@ -63,11 +63,14 @@ module Nanoc::Int
|
|
63
63
|
value = object ? object.value : NONE
|
64
64
|
end
|
65
65
|
|
66
|
+
counter_label = is_a?(Class) ? "#{self}.#{method_name}" : "#{self.class}##{method_name}"
|
66
67
|
if value.equal?(NONE)
|
68
|
+
Nanoc::Int::NotificationCenter.post(:memoization_miss, counter_label)
|
67
69
|
send(original_method_name, *args).tap do |r|
|
68
70
|
instance_method_cache[args] = Ref::SoftReference.new(Value.new(r))
|
69
71
|
end
|
70
72
|
else
|
73
|
+
Nanoc::Int::NotificationCenter.post(:memoization_hit, counter_label)
|
71
74
|
value
|
72
75
|
end
|
73
76
|
end
|
@@ -74,17 +74,29 @@ module Nanoc::Int
|
|
74
74
|
end
|
75
75
|
|
76
76
|
class AttributesModified < OutdatednessRule
|
77
|
+
extend Nanoc::Int::Memoization
|
78
|
+
|
79
|
+
include Nanoc::Int::ContractsSupport
|
80
|
+
|
77
81
|
def reason
|
78
82
|
Nanoc::Int::OutdatednessReasons::AttributesModified
|
79
83
|
end
|
80
84
|
|
85
|
+
contract C::Or[Nanoc::Int::ItemRep, Nanoc::Int::Item, Nanoc::Int::Layout], C::Named['Nanoc::Int::OutdatednessChecker'] => C::Bool
|
81
86
|
def apply(obj, outdatedness_checker)
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
+
case obj
|
88
|
+
when Nanoc::Int::ItemRep
|
89
|
+
apply(obj.item, outdatedness_checker)
|
90
|
+
when Nanoc::Int::Item, Nanoc::Int::Layout
|
91
|
+
ch_old = outdatedness_checker.checksum_store.attributes_checksum_for(obj)
|
92
|
+
ch_new = Nanoc::Int::Checksummer.calc_for_attributes_of(obj)
|
93
|
+
res = ch_old != ch_new
|
94
|
+
res
|
95
|
+
else
|
96
|
+
raise ArgumentError
|
97
|
+
end
|
87
98
|
end
|
99
|
+
memoize :apply
|
88
100
|
end
|
89
101
|
|
90
102
|
class RulesModified < OutdatednessRule
|
data/lib/nanoc/checking/check.rb
CHANGED
@@ -23,7 +23,7 @@ module Nanoc::Checking
|
|
23
23
|
view_context = site.compiler.compilation_context.create_view_context(Nanoc::Int::DependencyTracker::Null.new)
|
24
24
|
|
25
25
|
context = {
|
26
|
-
items: Nanoc::
|
26
|
+
items: Nanoc::PostCompileItemCollectionView.new(site.items, view_context),
|
27
27
|
layouts: Nanoc::LayoutCollectionView.new(site.layouts, view_context),
|
28
28
|
config: Nanoc::ConfigView.new(site.config, view_context),
|
29
29
|
output_filenames: output_filenames,
|
@@ -96,6 +96,14 @@ module Nanoc::CLI::Commands::CompileListeners
|
|
96
96
|
|
97
97
|
@telemetry.summary(:phases).observe(stopwatch.duration, phase_name)
|
98
98
|
end
|
99
|
+
|
100
|
+
on(:memoization_miss) do |label|
|
101
|
+
@telemetry.counter(:memoization).increment([label, :miss])
|
102
|
+
end
|
103
|
+
|
104
|
+
on(:memoization_hit) do |label|
|
105
|
+
@telemetry.counter(:memoization).increment([label, :hit])
|
106
|
+
end
|
99
107
|
end
|
100
108
|
|
101
109
|
# @see Listener#stop
|
@@ -134,11 +142,32 @@ module Nanoc::CLI::Commands::CompileListeners
|
|
134
142
|
[headers] + rows
|
135
143
|
end
|
136
144
|
|
145
|
+
def table_for_memoization
|
146
|
+
headers = %w(memoization hit miss %)
|
147
|
+
|
148
|
+
rows_raw = @telemetry.counter(:memoization).map do |(name, type), counter|
|
149
|
+
{ name: name, type: type, count: counter.value }
|
150
|
+
end
|
151
|
+
|
152
|
+
rows = rows_raw.group_by { |r| r[:name] }.map do |name, rows_for_name|
|
153
|
+
rows_by_type = rows_for_name.group_by { |r| r[:type] }
|
154
|
+
|
155
|
+
num_hit = rows_by_type.fetch(:hit, []).fetch(0, {}).fetch(:count, 0)
|
156
|
+
num_miss = rows_by_type.fetch(:miss, []).fetch(0, {}).fetch(:count, 0)
|
157
|
+
pct = num_hit.to_f / (num_hit + num_miss).to_f
|
158
|
+
|
159
|
+
[name, num_hit.to_s, num_miss.to_s, "#{format('%3.1f', pct * 100)}%"]
|
160
|
+
end
|
161
|
+
|
162
|
+
[headers] + rows
|
163
|
+
end
|
164
|
+
|
137
165
|
def print_profiling_feedback
|
138
166
|
print_table_for_summary(:filters)
|
139
167
|
print_table_for_summary(:phases) if Nanoc::CLI.verbosity >= 2
|
140
168
|
print_table_for_summary_duration(:stages) if Nanoc::CLI.verbosity >= 2
|
141
169
|
print_table_for_summary(:outdatedness_rules) if Nanoc::CLI.verbosity >= 2
|
170
|
+
print_table_for_memoization if Nanoc::CLI.verbosity >= 2
|
142
171
|
end
|
143
172
|
|
144
173
|
def print_table_for_summary(name)
|
@@ -155,6 +184,13 @@ module Nanoc::CLI::Commands::CompileListeners
|
|
155
184
|
print_table(table_for_summary_durations(name))
|
156
185
|
end
|
157
186
|
|
187
|
+
def print_table_for_memoization
|
188
|
+
return if @telemetry.counter(:memoization).empty?
|
189
|
+
|
190
|
+
puts
|
191
|
+
print_table(table_for_memoization)
|
192
|
+
end
|
193
|
+
|
158
194
|
def print_table(table)
|
159
195
|
lengths = table.transpose.map { |r| r.map(&:size).max }
|
160
196
|
|
@@ -358,7 +358,7 @@ module Nanoc::Filters
|
|
358
358
|
|
359
359
|
protected
|
360
360
|
|
361
|
-
KNOWN_COLORIZERS =
|
361
|
+
KNOWN_COLORIZERS = %i(coderay dummy pygmentize pygmentsrb simon_highlight rouge).freeze
|
362
362
|
|
363
363
|
# Removes the first blank lines and any whitespace at the end.
|
364
364
|
def strip(s)
|
@@ -12,7 +12,9 @@ module Nanoc::Telemetry
|
|
12
12
|
@counters.fetch(label) { @counters[label] = Counter.new }
|
13
13
|
end
|
14
14
|
|
15
|
-
|
15
|
+
def empty?
|
16
|
+
@counters.empty?
|
17
|
+
end
|
16
18
|
|
17
19
|
def value(label)
|
18
20
|
get(label).value
|
@@ -23,5 +25,9 @@ module Nanoc::Telemetry
|
|
23
25
|
res[label] = counter.value
|
24
26
|
end
|
25
27
|
end
|
28
|
+
|
29
|
+
def map
|
30
|
+
@counters.map { |(label, counter)| yield(label, counter) }
|
31
|
+
end
|
26
32
|
end
|
27
33
|
end
|
data/lib/nanoc/version.rb
CHANGED
@@ -106,7 +106,7 @@ describe Nanoc::Int::OutdatednessStatus do
|
|
106
106
|
let(:status) { described_class.new(props: Nanoc::Int::Props.new(attributes: true)) }
|
107
107
|
|
108
108
|
it 'updates props' do
|
109
|
-
expect(subject.props.active).to eql(Set.new(
|
109
|
+
expect(subject.props.active).to eql(Set.new(%i(raw_content attributes compiled_content)))
|
110
110
|
end
|
111
111
|
end
|
112
112
|
end
|
@@ -25,7 +25,7 @@ describe Nanoc::Int::ProcessingActions::Snapshot do
|
|
25
25
|
|
26
26
|
context 'with snapshot name' do
|
27
27
|
subject { action.update(snapshot_names: [:zebra]) }
|
28
|
-
its(:snapshot_names) { is_expected.to eql(
|
28
|
+
its(:snapshot_names) { is_expected.to eql(%i(before_layout zebra)) }
|
29
29
|
its(:paths) { is_expected.to eql(['/foo.md']) }
|
30
30
|
end
|
31
31
|
|
@@ -89,7 +89,7 @@ describe Nanoc::Int::Props do
|
|
89
89
|
let(:props) { described_class.new }
|
90
90
|
let(:other_props) { props_all }
|
91
91
|
|
92
|
-
it { is_expected.to eql(Set.new(
|
92
|
+
it { is_expected.to eql(Set.new(%i(raw_content attributes compiled_content path))) }
|
93
93
|
end
|
94
94
|
|
95
95
|
context 'some + nothing' do
|
@@ -103,35 +103,35 @@ describe Nanoc::Int::Props do
|
|
103
103
|
let(:props) { described_class.new(compiled_content: true) }
|
104
104
|
let(:other_props) { described_class.new(raw_content: true) }
|
105
105
|
|
106
|
-
it { is_expected.to eql(Set.new(
|
106
|
+
it { is_expected.to eql(Set.new(%i(raw_content compiled_content))) }
|
107
107
|
end
|
108
108
|
|
109
109
|
context 'some + all' do
|
110
110
|
let(:props) { described_class.new(compiled_content: true) }
|
111
111
|
let(:other_props) { props_all }
|
112
112
|
|
113
|
-
it { is_expected.to eql(Set.new(
|
113
|
+
it { is_expected.to eql(Set.new(%i(raw_content attributes compiled_content path))) }
|
114
114
|
end
|
115
115
|
|
116
116
|
context 'all + nothing' do
|
117
117
|
let(:props) { props_all }
|
118
118
|
let(:other_props) { described_class.new }
|
119
119
|
|
120
|
-
it { is_expected.to eql(Set.new(
|
120
|
+
it { is_expected.to eql(Set.new(%i(raw_content attributes compiled_content path))) }
|
121
121
|
end
|
122
122
|
|
123
123
|
context 'some + all' do
|
124
124
|
let(:props) { props_all }
|
125
125
|
let(:other_props) { described_class.new(compiled_content: true) }
|
126
126
|
|
127
|
-
it { is_expected.to eql(Set.new(
|
127
|
+
it { is_expected.to eql(Set.new(%i(raw_content attributes compiled_content path))) }
|
128
128
|
end
|
129
129
|
|
130
130
|
context 'all + all' do
|
131
131
|
let(:props) { props_all }
|
132
132
|
let(:other_props) { props_all }
|
133
133
|
|
134
|
-
it { is_expected.to eql(Set.new(
|
134
|
+
it { is_expected.to eql(Set.new(%i(raw_content attributes compiled_content path))) }
|
135
135
|
end
|
136
136
|
end
|
137
137
|
|
@@ -165,12 +165,12 @@ describe Nanoc::Int::Props do
|
|
165
165
|
|
166
166
|
context 'attributes and compiled_content active' do
|
167
167
|
let(:props) { described_class.new(attributes: true, compiled_content: true) }
|
168
|
-
it { is_expected.to eql(Set.new(
|
168
|
+
it { is_expected.to eql(Set.new(%i(attributes compiled_content))) }
|
169
169
|
end
|
170
170
|
|
171
171
|
context 'all active' do
|
172
172
|
let(:props) { described_class.new(raw_content: true, attributes: true, compiled_content: true, path: true) }
|
173
|
-
it { is_expected.to eql(Set.new(
|
173
|
+
it { is_expected.to eql(Set.new(%i(raw_content attributes compiled_content path))) }
|
174
174
|
end
|
175
175
|
end
|
176
176
|
|
@@ -146,13 +146,13 @@ describe Nanoc::Int::RuleMemory do
|
|
146
146
|
|
147
147
|
example do
|
148
148
|
expect(subject[0]).to be_a(Nanoc::Int::ProcessingActions::Snapshot)
|
149
|
-
expect(subject[0].snapshot_names).to eql(
|
149
|
+
expect(subject[0].snapshot_names).to eql(%i(a1 a2 a3))
|
150
150
|
expect(subject[0].paths).to eql(['/a2.md'])
|
151
151
|
|
152
152
|
expect(subject[1]).to be_a(Nanoc::Int::ProcessingActions::Filter)
|
153
153
|
|
154
154
|
expect(subject[2]).to be_a(Nanoc::Int::ProcessingActions::Snapshot)
|
155
|
-
expect(subject[2].snapshot_names).to eql(
|
155
|
+
expect(subject[2].snapshot_names).to eql(%i(b1 b2 b3))
|
156
156
|
expect(subject[2].paths).to eql(['/b1.md', '/b3.md'])
|
157
157
|
|
158
158
|
expect(subject[3]).to be_a(Nanoc::Int::ProcessingActions::Filter)
|
@@ -0,0 +1,75 @@
|
|
1
|
+
describe Nanoc::Int::Memoization do
|
2
|
+
class MemoizationSpecSample1
|
3
|
+
extend Nanoc::Int::Memoization
|
4
|
+
|
5
|
+
def initialize(value)
|
6
|
+
@value = value
|
7
|
+
end
|
8
|
+
|
9
|
+
def run(n)
|
10
|
+
@value * 10 + n
|
11
|
+
end
|
12
|
+
memoize :run
|
13
|
+
end
|
14
|
+
|
15
|
+
class MemoizationSpecSample2
|
16
|
+
extend Nanoc::Int::Memoization
|
17
|
+
|
18
|
+
def initialize(value)
|
19
|
+
@value = value
|
20
|
+
end
|
21
|
+
|
22
|
+
def run(n)
|
23
|
+
@value * 100 + n
|
24
|
+
end
|
25
|
+
memoize :run
|
26
|
+
end
|
27
|
+
|
28
|
+
class MemoizationSpecUpcaser
|
29
|
+
extend Nanoc::Int::Memoization
|
30
|
+
|
31
|
+
def run(value)
|
32
|
+
value.upcase
|
33
|
+
end
|
34
|
+
memoize :run
|
35
|
+
end
|
36
|
+
|
37
|
+
example do
|
38
|
+
sample1a = MemoizationSpecSample1.new(10)
|
39
|
+
sample1b = MemoizationSpecSample1.new(15)
|
40
|
+
sample2a = MemoizationSpecSample2.new(20)
|
41
|
+
sample2b = MemoizationSpecSample2.new(25)
|
42
|
+
|
43
|
+
3.times do
|
44
|
+
expect(sample1a.run(5)).to eq(10 * 10 + 5)
|
45
|
+
expect(sample1b.run(7)).to eq(10 * 15 + 7)
|
46
|
+
expect(sample2a.run(5)).to eq(100 * 20 + 5)
|
47
|
+
expect(sample2b.run(7)).to eq(100 * 25 + 7)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'supports frozen objects' do
|
52
|
+
sample = MemoizationSpecSample1.new(10)
|
53
|
+
sample.freeze
|
54
|
+
sample.run(5)
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'does not crash on #inspect' do
|
58
|
+
upcaser = MemoizationSpecUpcaser.new
|
59
|
+
10_000.times do |i|
|
60
|
+
upcaser.run("hello world #{i}")
|
61
|
+
end
|
62
|
+
|
63
|
+
GC.start
|
64
|
+
GC.start
|
65
|
+
|
66
|
+
upcaser.inspect
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'sends notifications' do
|
70
|
+
sample = MemoizationSpecSample1.new(10)
|
71
|
+
expect { sample.run(5) }.to send_notification(:memoization_miss, 'MemoizationSpecSample1#run')
|
72
|
+
expect { sample.run(5) }.to send_notification(:memoization_hit, 'MemoizationSpecSample1#run')
|
73
|
+
expect { sample.run(5) }.to send_notification(:memoization_hit, 'MemoizationSpecSample1#run')
|
74
|
+
end
|
75
|
+
end
|
@@ -122,8 +122,8 @@ describe Nanoc::Int::ItemRepSelector do
|
|
122
122
|
end
|
123
123
|
|
124
124
|
example do
|
125
|
-
expect(successfully_yielded).to eq
|
126
|
-
expect(tentatively_yielded).to eq
|
125
|
+
expect(successfully_yielded).to eq %i(e d c b a)
|
126
|
+
expect(tentatively_yielded).to eq %i(a b c d e d c b a)
|
127
127
|
end
|
128
128
|
end
|
129
129
|
|
@@ -133,21 +133,21 @@ describe Nanoc::Int::ItemRepSelector do
|
|
133
133
|
end
|
134
134
|
|
135
135
|
example do
|
136
|
-
expect(successfully_yielded).to eq
|
137
|
-
expect(tentatively_yielded).to eq
|
136
|
+
expect(successfully_yielded).to eq %i(a b c d e)
|
137
|
+
expect(tentatively_yielded).to eq %i(a b c d e)
|
138
138
|
end
|
139
139
|
end
|
140
140
|
|
141
141
|
context 'star dependencies' do
|
142
142
|
let(:dependencies) do
|
143
143
|
{
|
144
|
-
a:
|
144
|
+
a: %i(b c d e),
|
145
145
|
}
|
146
146
|
end
|
147
147
|
|
148
148
|
example do
|
149
|
-
expect(successfully_yielded).to eq
|
150
|
-
expect(tentatively_yielded).to eq
|
149
|
+
expect(successfully_yielded).to eq %i(b c d e a)
|
150
|
+
expect(tentatively_yielded).to eq %i(a b a c a d a e a)
|
151
151
|
end
|
152
152
|
end
|
153
153
|
|
@@ -156,13 +156,13 @@ describe Nanoc::Int::ItemRepSelector do
|
|
156
156
|
|
157
157
|
let(:dependencies) do
|
158
158
|
{
|
159
|
-
a:
|
159
|
+
a: %i(b c d e),
|
160
160
|
}
|
161
161
|
end
|
162
162
|
|
163
163
|
example do
|
164
|
-
expect(successfully_yielded).to eq
|
165
|
-
expect(tentatively_yielded).to eq
|
164
|
+
expect(successfully_yielded).to eq %i(b c d e a)
|
165
|
+
expect(tentatively_yielded).to eq %i(a b a c a d a e a)
|
166
166
|
end
|
167
167
|
end
|
168
168
|
|
@@ -176,8 +176,8 @@ describe Nanoc::Int::ItemRepSelector do
|
|
176
176
|
end
|
177
177
|
|
178
178
|
it 'picks prioritised roots' do
|
179
|
-
expect(successfully_yielded).to eq
|
180
|
-
expect(tentatively_yielded).to eq
|
179
|
+
expect(successfully_yielded).to eq %i(d a e b c)
|
180
|
+
expect(tentatively_yielded).to eq %i(a d a b e b c)
|
181
181
|
end
|
182
182
|
end
|
183
183
|
end
|
@@ -34,6 +34,64 @@ describe Nanoc::PostCompileItemRepView do
|
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
|
+
describe '#raw_path' do
|
38
|
+
context 'no args' do
|
39
|
+
subject { view.raw_path }
|
40
|
+
|
41
|
+
it 'does not raise' do
|
42
|
+
subject
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'no path specified' do
|
46
|
+
it { is_expected.to be_nil }
|
47
|
+
end
|
48
|
+
|
49
|
+
context 'path for default snapshot specified' do
|
50
|
+
before do
|
51
|
+
item_rep.raw_paths = { last: ['output/about/index.html'] }
|
52
|
+
end
|
53
|
+
|
54
|
+
it { is_expected.to eql('output/about/index.html') }
|
55
|
+
end
|
56
|
+
|
57
|
+
context 'path specified, but not for default snapshot' do
|
58
|
+
before do
|
59
|
+
item_rep.raw_paths = { pre: ['output/about/index.html'] }
|
60
|
+
end
|
61
|
+
|
62
|
+
it { is_expected.to be_nil }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'snapshot arg' do
|
67
|
+
subject { view.raw_path(snapshot: :special) }
|
68
|
+
|
69
|
+
it 'does not raise' do
|
70
|
+
subject
|
71
|
+
end
|
72
|
+
|
73
|
+
context 'no path specified' do
|
74
|
+
it { is_expected.to be_nil }
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'path for default snapshot specified' do
|
78
|
+
before do
|
79
|
+
item_rep.raw_paths = { special: ['output/about/index.html'] }
|
80
|
+
end
|
81
|
+
|
82
|
+
it { is_expected.to eql('output/about/index.html') }
|
83
|
+
end
|
84
|
+
|
85
|
+
context 'path specified, but not for default snapshot' do
|
86
|
+
before do
|
87
|
+
item_rep.raw_paths = { pre: ['output/about/index.html'] }
|
88
|
+
end
|
89
|
+
|
90
|
+
it { is_expected.to be_nil }
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
37
95
|
describe '#compiled_content' do
|
38
96
|
subject { view.compiled_content }
|
39
97
|
|
@@ -260,4 +260,26 @@ describe Nanoc::CLI::Commands::CompileListeners::TimingRecorder, stdio: true do
|
|
260
260
|
expect(listener.telemetry.summary(:outdatedness_rules).get('CodeSnippetsModified').sum).to eq(4.00)
|
261
261
|
expect(listener.telemetry.summary(:outdatedness_rules).get('CodeSnippetsModified').count).to eq(2.00)
|
262
262
|
end
|
263
|
+
|
264
|
+
it 'records memoization usage' do
|
265
|
+
Nanoc::Int::NotificationCenter.post(:memoization_hit, 'Foo#bar', rep)
|
266
|
+
Nanoc::Int::NotificationCenter.post(:memoization_miss, 'Foo#bar', rep)
|
267
|
+
Nanoc::Int::NotificationCenter.post(:memoization_miss, 'Foo#bar', rep)
|
268
|
+
Nanoc::Int::NotificationCenter.post(:memoization_miss, 'Foo#bar', rep)
|
269
|
+
Nanoc::Int::NotificationCenter.post(:memoization_miss, 'Foo#bar', rep)
|
270
|
+
|
271
|
+
expect(listener.telemetry.counter(:memoization).get(['Foo#bar', :hit]).value).to eq(1)
|
272
|
+
expect(listener.telemetry.counter(:memoization).get(['Foo#bar', :miss]).value).to eq(4)
|
273
|
+
end
|
274
|
+
|
275
|
+
it 'prints memoization table' do
|
276
|
+
Nanoc::Int::NotificationCenter.post(:memoization_hit, 'Foo#bar', rep)
|
277
|
+
Nanoc::Int::NotificationCenter.post(:memoization_miss, 'Foo#bar', rep)
|
278
|
+
Nanoc::Int::NotificationCenter.post(:memoization_miss, 'Foo#bar', rep)
|
279
|
+
Nanoc::Int::NotificationCenter.post(:memoization_miss, 'Foo#bar', rep)
|
280
|
+
Nanoc::Int::NotificationCenter.post(:memoization_miss, 'Foo#bar', rep)
|
281
|
+
|
282
|
+
expect { listener.stop }
|
283
|
+
.to output(/^\s*Foo#bar │ 1 4 20\.0%$/).to_stdout
|
284
|
+
end
|
263
285
|
end
|
@@ -72,7 +72,7 @@ describe Nanoc::Helpers::Tagging, helper: true do
|
|
72
72
|
before do
|
73
73
|
ctx.create_item('item 1', { tags: [:foo] }, '/item1.md')
|
74
74
|
ctx.create_item('item 2', { tags: [:bar] }, '/item2.md')
|
75
|
-
ctx.create_item('item 3', { tags:
|
75
|
+
ctx.create_item('item 3', { tags: %i(foo bar) }, '/item3.md')
|
76
76
|
ctx.create_item('item 4', { tags: nil }, '/item4.md')
|
77
77
|
ctx.create_item('item 5', {}, '/item5.md')
|
78
78
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
describe 'GH-1130', site: true, stdio: true do
|
2
|
+
before do
|
3
|
+
File.write('content/foo', 'asdf')
|
4
|
+
|
5
|
+
File.write('Rules', <<EOS)
|
6
|
+
passthrough '/**/*'
|
7
|
+
EOS
|
8
|
+
|
9
|
+
File.write('Checks', <<EOS)
|
10
|
+
check :wat do
|
11
|
+
@items.flat_map(&:reps).map(&:raw_path)
|
12
|
+
end
|
13
|
+
EOS
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'does not raise fiber error' do
|
17
|
+
Nanoc::CLI.run(%w(check wat))
|
18
|
+
end
|
19
|
+
end
|
@@ -67,7 +67,7 @@ describe(Nanoc::RuleDSL::RuleMemoryCalculator) do
|
|
67
67
|
expect(subject[4].params).to eql({})
|
68
68
|
|
69
69
|
expect(subject[5]).to be_a(Nanoc::Int::ProcessingActions::Snapshot)
|
70
|
-
expect(subject[5].snapshot_names).to eql(
|
70
|
+
expect(subject[5].snapshot_names).to eql(%i(post last))
|
71
71
|
expect(subject[5].paths).to be_empty
|
72
72
|
|
73
73
|
expect(subject.size).to eql(6)
|
@@ -85,7 +85,7 @@ describe(Nanoc::RuleDSL::RuleMemoryCalculator) do
|
|
85
85
|
subject
|
86
86
|
|
87
87
|
expect(subject[0]).to be_a(Nanoc::Int::ProcessingActions::Snapshot)
|
88
|
-
expect(subject[0].snapshot_names).to eql(
|
88
|
+
expect(subject[0].snapshot_names).to eql(%i(raw last pre))
|
89
89
|
expect(subject[0].paths).to be_empty
|
90
90
|
|
91
91
|
expect(subject.size).to eql(1)
|
@@ -107,7 +107,7 @@ describe(Nanoc::RuleDSL::RuleMemoryCalculator) do
|
|
107
107
|
subject
|
108
108
|
|
109
109
|
expect(subject[0]).to be_a(Nanoc::Int::ProcessingActions::Snapshot)
|
110
|
-
expect(subject[0].snapshot_names).to eql(
|
110
|
+
expect(subject[0].snapshot_names).to eql(%i(raw last pre))
|
111
111
|
expect(subject[0].paths).to eq(['/foo.md'])
|
112
112
|
|
113
113
|
expect(subject.size).to eql(1)
|
@@ -129,7 +129,7 @@ describe(Nanoc::RuleDSL::RuleMemoryCalculator) do
|
|
129
129
|
subject
|
130
130
|
|
131
131
|
expect(subject[0]).to be_a(Nanoc::Int::ProcessingActions::Snapshot)
|
132
|
-
expect(subject[0].snapshot_names).to eql(
|
132
|
+
expect(subject[0].snapshot_names).to eql(%i(raw last pre))
|
133
133
|
expect(subject[0].paths).to be_empty
|
134
134
|
|
135
135
|
expect(subject.size).to eql(1)
|
@@ -53,4 +53,40 @@ describe Nanoc::Telemetry::LabelledCounter do
|
|
53
53
|
its(:value) { is_expected.to eq(0) }
|
54
54
|
end
|
55
55
|
end
|
56
|
+
|
57
|
+
describe '#empty?' do
|
58
|
+
subject { counter.empty? }
|
59
|
+
|
60
|
+
context 'not incremented' do
|
61
|
+
it { is_expected.to be }
|
62
|
+
end
|
63
|
+
|
64
|
+
context 'incremented' do
|
65
|
+
before { counter.increment(:erb) }
|
66
|
+
it { is_expected.not_to be }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe '#map' do
|
71
|
+
subject { counter.map { |label, counter| [label, counter.value] } }
|
72
|
+
|
73
|
+
context 'not incremented' do
|
74
|
+
it { is_expected.to be_empty }
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'incremented once' do
|
78
|
+
before { counter.increment(:erb) }
|
79
|
+
it { is_expected.to eq [[:erb, 1]] }
|
80
|
+
end
|
81
|
+
|
82
|
+
context 'both incremental multiple times' do
|
83
|
+
before do
|
84
|
+
counter.increment(:erb)
|
85
|
+
counter.increment(:erb)
|
86
|
+
counter.increment(:haml)
|
87
|
+
end
|
88
|
+
|
89
|
+
it { is_expected.to eq [[:erb, 2], [:haml, 1]] }
|
90
|
+
end
|
91
|
+
end
|
56
92
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -245,3 +245,34 @@ end
|
|
245
245
|
|
246
246
|
RSpec::Matchers.alias_matcher :some_textual_content, :be_some_textual_content
|
247
247
|
RSpec::Matchers.alias_matcher :some_binary_content, :be_some_binary_content
|
248
|
+
|
249
|
+
RSpec::Matchers.define :send_notification do |name, *expected_args|
|
250
|
+
supports_block_expectations
|
251
|
+
|
252
|
+
include RSpec::Matchers::Composable
|
253
|
+
|
254
|
+
match do |actual|
|
255
|
+
@actual_notifications = []
|
256
|
+
Nanoc::Int::NotificationCenter.on(name, self) do |*actual_args|
|
257
|
+
@actual_notifications << actual_args
|
258
|
+
end
|
259
|
+
actual.call
|
260
|
+
@actual_notifications.any? { |c| c == expected_args }
|
261
|
+
end
|
262
|
+
|
263
|
+
description do
|
264
|
+
"send notification #{name.inspect} with args #{expected_args.inspect}"
|
265
|
+
end
|
266
|
+
|
267
|
+
failure_message do |_actual|
|
268
|
+
s = "expected that proc would send notification #{name.inspect} with args #{expected_args.inspect}"
|
269
|
+
if @actual_notifications.any?
|
270
|
+
s << " (received #{@actual_notifications.size} times with other arguments: #{@actual_notifications.map(&:inspect).join(', ')})"
|
271
|
+
end
|
272
|
+
s
|
273
|
+
end
|
274
|
+
|
275
|
+
failure_message_when_negated do |_actual|
|
276
|
+
"expected that proc would not send notification #{name.inspect} with args #{expected_args.inspect}"
|
277
|
+
end
|
278
|
+
end
|
@@ -12,7 +12,7 @@ describe 'Array#__nanoc_freeze_recursively' do
|
|
12
12
|
include Nanoc::TestHelpers
|
13
13
|
|
14
14
|
it 'should prevent first-level elements from being modified' do
|
15
|
-
array = [:a,
|
15
|
+
array = [:a, %i(b c), :d]
|
16
16
|
array.__nanoc_freeze_recursively
|
17
17
|
|
18
18
|
assert_raises_frozen_error do
|
@@ -21,7 +21,7 @@ describe 'Array#__nanoc_freeze_recursively' do
|
|
21
21
|
end
|
22
22
|
|
23
23
|
it 'should prevent second-level elements from being modified' do
|
24
|
-
array = [:a,
|
24
|
+
array = [:a, %i(b c), :d]
|
25
25
|
array.__nanoc_freeze_recursively
|
26
26
|
|
27
27
|
assert_raises_frozen_error do
|
@@ -20,7 +20,7 @@ class Nanoc::CLI::CleaningStreamTest < Nanoc::TestCase
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def test_forward
|
23
|
-
methods =
|
23
|
+
methods = %i(write << tty? tty? flush tell print puts string reopen exist? exists? close)
|
24
24
|
|
25
25
|
s = Stream.new
|
26
26
|
cs = Nanoc::CLI::CleaningStream.new(s)
|
@@ -64,7 +64,7 @@ EOS
|
|
64
64
|
input = '<pre><code class="language-ruby">puts "foo"</code></pre>'
|
65
65
|
|
66
66
|
# Run filter
|
67
|
-
|
67
|
+
%i(albino pygmentize simon_highlight).each do |colorizer|
|
68
68
|
begin
|
69
69
|
input = '<pre><code class="language-ruby">puts "foo"</code></pre>'
|
70
70
|
filter.setup_and_run(
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nanoc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.7.
|
4
|
+
version: 4.7.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Denis Defreyne
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-03-
|
11
|
+
date: 2017-03-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: cri
|
@@ -377,6 +377,7 @@ files:
|
|
377
377
|
- spec/nanoc/base/feature_spec.rb
|
378
378
|
- spec/nanoc/base/filter_spec.rb
|
379
379
|
- spec/nanoc/base/item_rep_writer_spec.rb
|
380
|
+
- spec/nanoc/base/memoization_spec.rb
|
380
381
|
- spec/nanoc/base/repos/checksum_store_spec.rb
|
381
382
|
- spec/nanoc/base/repos/compiled_content_cache_spec.rb
|
382
383
|
- spec/nanoc/base/repos/config_loader_spec.rb
|
@@ -466,6 +467,7 @@ files:
|
|
466
467
|
- spec/nanoc/regressions/gh_1100_spec.rb
|
467
468
|
- spec/nanoc/regressions/gh_1102_spec.rb
|
468
469
|
- spec/nanoc/regressions/gh_1107_spec.rb
|
470
|
+
- spec/nanoc/regressions/gh_1130_spec.rb
|
469
471
|
- spec/nanoc/regressions/gh_761_spec.rb
|
470
472
|
- spec/nanoc/regressions/gh_767_spec.rb
|
471
473
|
- spec/nanoc/regressions/gh_769_spec.rb
|
@@ -524,7 +526,6 @@ files:
|
|
524
526
|
- test/base/test_item.rb
|
525
527
|
- test/base/test_item_array.rb
|
526
528
|
- test/base/test_layout.rb
|
527
|
-
- test/base/test_memoization.rb
|
528
529
|
- test/base/test_notification_center.rb
|
529
530
|
- test/base/test_outdatedness_checker.rb
|
530
531
|
- test/base/test_site.rb
|
@@ -1,71 +0,0 @@
|
|
1
|
-
require 'helper'
|
2
|
-
|
3
|
-
class Nanoc::Int::MemoizationTest < Nanoc::TestCase
|
4
|
-
class Sample1
|
5
|
-
extend Nanoc::Int::Memoization
|
6
|
-
|
7
|
-
def initialize(value)
|
8
|
-
@value = value
|
9
|
-
end
|
10
|
-
|
11
|
-
def run(n)
|
12
|
-
@value * 10 + n
|
13
|
-
end
|
14
|
-
memoize :run
|
15
|
-
end
|
16
|
-
|
17
|
-
class Sample2
|
18
|
-
extend Nanoc::Int::Memoization
|
19
|
-
|
20
|
-
def initialize(value)
|
21
|
-
@value = value
|
22
|
-
end
|
23
|
-
|
24
|
-
def run(n)
|
25
|
-
@value * 100 + n
|
26
|
-
end
|
27
|
-
memoize :run
|
28
|
-
end
|
29
|
-
|
30
|
-
class Upcaser
|
31
|
-
extend Nanoc::Int::Memoization
|
32
|
-
|
33
|
-
def run(value)
|
34
|
-
value.upcase
|
35
|
-
end
|
36
|
-
memoize :run
|
37
|
-
end
|
38
|
-
|
39
|
-
def test_normal
|
40
|
-
sample1a = Sample1.new(10)
|
41
|
-
sample1b = Sample1.new(15)
|
42
|
-
sample2a = Sample2.new(20)
|
43
|
-
sample2b = Sample2.new(25)
|
44
|
-
|
45
|
-
3.times do
|
46
|
-
assert_equal 10 * 10 + 5, sample1a.run(5)
|
47
|
-
assert_equal 10 * 15 + 7, sample1b.run(7)
|
48
|
-
assert_equal 100 * 20 + 5, sample2a.run(5)
|
49
|
-
assert_equal 100 * 25 + 7, sample2b.run(7)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def test_frozen
|
54
|
-
sample = Sample1.new(10)
|
55
|
-
sample.freeze
|
56
|
-
sample.run(5)
|
57
|
-
end
|
58
|
-
|
59
|
-
def test_weak_inspect
|
60
|
-
upcaser = Upcaser.new
|
61
|
-
10_000.times do |i|
|
62
|
-
upcaser.run("hello world #{i}")
|
63
|
-
end
|
64
|
-
|
65
|
-
GC.start
|
66
|
-
GC.start
|
67
|
-
|
68
|
-
# Should not raise
|
69
|
-
upcaser.inspect
|
70
|
-
end
|
71
|
-
end
|