nanoc 4.7.1 → 4.7.2
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 +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
|