nanoc 4.6.3 → 4.6.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +5 -5
- data/NEWS.md +6 -0
- data/lib/nanoc.rb +1 -0
- data/lib/nanoc/cli/commands/compile.rb +28 -61
- data/lib/nanoc/cli/stream_cleaners/utf8.rb +1 -1
- data/lib/nanoc/spec.rb +3 -3
- data/lib/nanoc/telemetry.rb +17 -0
- data/lib/nanoc/telemetry/counter.rb +13 -0
- data/lib/nanoc/telemetry/labelled_counter.rb +27 -0
- data/lib/nanoc/telemetry/labelled_summary.rb +31 -0
- data/lib/nanoc/telemetry/registry.rb +16 -0
- data/lib/nanoc/telemetry/stopwatch.rb +41 -0
- data/lib/nanoc/telemetry/summary.rb +53 -0
- data/lib/nanoc/version.rb +1 -1
- data/spec/nanoc/cli/commands/compile/timing_recorder_spec.rb +35 -4
- data/spec/nanoc/cli/stream_cleaners/utf8_spec.rb +7 -0
- data/spec/nanoc/helpers/blogging_spec.rb +7 -6
- data/spec/nanoc/helpers/capturing_spec.rb +5 -4
- data/spec/nanoc/helpers/child_parent_spec.rb +12 -43
- data/spec/nanoc/helpers/filtering_spec.rb +5 -2
- data/spec/nanoc/helpers/link_to_spec.rb +66 -23
- data/spec/nanoc/helpers/rendering_spec.rb +3 -7
- data/spec/nanoc/spec_spec.rb +68 -0
- data/spec/nanoc/telemetry/counter_spec.rb +18 -0
- data/spec/nanoc/telemetry/labelled_counter_spec.rb +56 -0
- data/spec/nanoc/telemetry/labelled_summary_spec.rb +63 -0
- data/spec/nanoc/telemetry/stopwatch_spec.rb +59 -0
- data/spec/nanoc/telemetry/summary_spec.rb +66 -0
- data/spec/nanoc/telemetry_spec.rb +26 -0
- metadata +17 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 45e73cb6f9f73af2a760146464d15b5537744e16
|
4
|
+
data.tar.gz: e5c219a107840118bb1fa45ba1396d302dbc5049
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 88453d81285519f1ee03821c835f9744096623f07a887f012dc66404bd68342429b211202cc9ec686b430da2d56ffbe2e6c23ec166fded484674512ab1c4c611
|
7
|
+
data.tar.gz: 39bb9372f9c71984af7726da2a1a2290dc85a89af3c7006073265d19620a4fb58f66616badd98ed97651a5006c71be82b0c9a00822727b91c94855cf368e3342
|
data/Gemfile.lock
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
GIT
|
2
2
|
remote: https://github.com/bbatsov/rubocop.git
|
3
|
-
revision:
|
3
|
+
revision: 70f6d9191e2cc1d9726d4a4107f5efc5a797e310
|
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.6.
|
30
|
+
nanoc (4.6.4)
|
31
31
|
cri (~> 2.3)
|
32
32
|
ddplugin (~> 1.0)
|
33
33
|
hamster (~> 3.0)
|
@@ -246,7 +246,7 @@ GEM
|
|
246
246
|
kramdown (1.13.2)
|
247
247
|
less (2.6.0)
|
248
248
|
commonjs (~> 0.2.7)
|
249
|
-
libv8 (3.16.14.
|
249
|
+
libv8 (3.16.14.19)
|
250
250
|
listen (3.1.5)
|
251
251
|
rb-fsevent (~> 0.9, >= 0.9.4)
|
252
252
|
rb-inotify (~> 0.9, >= 0.9.7)
|
@@ -342,7 +342,7 @@ GEM
|
|
342
342
|
trollop (2.1.2)
|
343
343
|
typogruby (1.0.18)
|
344
344
|
rubypants
|
345
|
-
uglifier (3.1.
|
345
|
+
uglifier (3.1.5)
|
346
346
|
execjs (>= 0.3.0, < 3)
|
347
347
|
unicode-display_width (1.1.3)
|
348
348
|
vcr (3.0.3)
|
@@ -425,4 +425,4 @@ DEPENDENCIES
|
|
425
425
|
yuicompressor
|
426
426
|
|
427
427
|
BUNDLED WITH
|
428
|
-
1.14.
|
428
|
+
1.14.6
|
data/NEWS.md
CHANGED
data/lib/nanoc.rb
CHANGED
@@ -151,55 +151,36 @@ module Nanoc::CLI::Commands
|
|
151
151
|
|
152
152
|
# @param [Enumerable<Nanoc::Int::ItemRep>] reps
|
153
153
|
def initialize(reps:)
|
154
|
-
# rep ->
|
155
|
-
# filter_name ->
|
156
|
-
# accum -> 0.0
|
157
|
-
# last_start -> nil
|
158
|
-
@times_per_rep = {}
|
159
|
-
|
160
154
|
@reps = reps
|
161
155
|
end
|
162
156
|
|
163
157
|
# @see Listener#start
|
164
158
|
def start
|
165
|
-
Nanoc::
|
166
|
-
|
167
|
-
|
159
|
+
@telemetry = Nanoc::Telemetry.new
|
160
|
+
|
161
|
+
stopwatches = {}
|
168
162
|
|
169
|
-
|
170
|
-
|
171
|
-
|
163
|
+
Nanoc::Int::NotificationCenter.on(:filtering_started) do |rep, _filter_name|
|
164
|
+
stopwatch_stack = stopwatches.fetch(rep) { stopwatches[rep] = [] }
|
165
|
+
stopwatch_stack << Nanoc::Telemetry::Stopwatch.new
|
166
|
+
stopwatch_stack.last.start
|
172
167
|
end
|
173
168
|
|
174
169
|
Nanoc::Int::NotificationCenter.on(:filtering_ended) do |rep, filter_name|
|
175
|
-
|
176
|
-
|
170
|
+
stopwatch = stopwatches.fetch(rep).pop
|
171
|
+
stopwatch.stop
|
177
172
|
|
178
|
-
|
179
|
-
@times_per_rep[rep][filter_name].delete(:last_start)
|
173
|
+
@telemetry.summary(:filter_total).observe(stopwatch.duration, filter_name: filter_name)
|
180
174
|
end
|
181
175
|
|
182
176
|
Nanoc::Int::NotificationCenter.on(:compilation_suspended) do |rep, _exception|
|
183
|
-
|
184
|
-
|
185
|
-
times[:accum] << (Time.now - times[:last_start])
|
186
|
-
times.delete(:last_start)
|
187
|
-
times[:suspended] = true
|
188
|
-
|
189
|
-
break
|
190
|
-
end
|
191
|
-
end
|
177
|
+
stopwatch = stopwatches.fetch(rep).last
|
178
|
+
stopwatch.stop if stopwatch && stopwatch.running?
|
192
179
|
end
|
193
180
|
|
194
181
|
Nanoc::Int::NotificationCenter.on(:compilation_started) do |rep|
|
195
|
-
|
196
|
-
|
197
|
-
@times_per_rep[rep][filter_name][:last_start] = Time.now
|
198
|
-
times[:suspended] = false
|
199
|
-
|
200
|
-
break
|
201
|
-
end
|
202
|
-
end
|
182
|
+
stopwatch = stopwatches.fetch(rep, []).last
|
183
|
+
stopwatch.start if stopwatch
|
203
184
|
end
|
204
185
|
end
|
205
186
|
|
@@ -214,25 +195,26 @@ module Nanoc::CLI::Commands
|
|
214
195
|
def profiling_table
|
215
196
|
headers = ['', 'count', 'min', 'avg', 'max', 'tot']
|
216
197
|
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
tot = samples.reduce(0, &:+)
|
222
|
-
avg = tot / count
|
223
|
-
max = samples.max
|
198
|
+
metric_set = @telemetry.summary(:filter_total)
|
199
|
+
rows = metric_set.labels.map do |label|
|
200
|
+
metric = metric_set.get(label)
|
201
|
+
filter_name = label[:filter_name].to_s
|
224
202
|
|
225
|
-
|
203
|
+
count = metric.count
|
204
|
+
min = metric.min
|
205
|
+
avg = metric.avg
|
206
|
+
tot = metric.sum
|
207
|
+
max = metric.max
|
208
|
+
|
209
|
+
[filter_name, count.to_s] + [min, avg, max, tot].map { |r| "#{format('%4.2f', r)}s" }
|
226
210
|
end
|
227
211
|
|
228
212
|
[headers] + rows
|
229
213
|
end
|
230
214
|
|
231
215
|
def print_profiling_feedback
|
232
|
-
|
233
|
-
return if durations_per_filter.empty?
|
216
|
+
return if @telemetry.summary(:filter_total).labels.empty?
|
234
217
|
|
235
|
-
# Print warning if necessary
|
236
218
|
if @reps.any? { |r| !r.compiled? }
|
237
219
|
$stderr.puts
|
238
220
|
$stderr.puts 'Warning: profiling information may not be accurate because ' \
|
@@ -248,7 +230,7 @@ module Nanoc::CLI::Commands
|
|
248
230
|
|
249
231
|
print_row(table[0], lengths)
|
250
232
|
|
251
|
-
puts "#{'
|
233
|
+
puts "#{'─' * lengths[0]}─┼─#{lengths[1..-1].map { |length| '─' * length }.join('───')}"
|
252
234
|
|
253
235
|
table[1..-1].each { |row| print_row(row, lengths) }
|
254
236
|
end
|
@@ -256,22 +238,7 @@ module Nanoc::CLI::Commands
|
|
256
238
|
def print_row(row, lengths)
|
257
239
|
values = row.zip(lengths).map { |text, length| text.rjust length }
|
258
240
|
|
259
|
-
puts values[0] + '
|
260
|
-
end
|
261
|
-
|
262
|
-
def durations_per_filter
|
263
|
-
@_durations_per_filter ||= begin
|
264
|
-
result = {}
|
265
|
-
|
266
|
-
@times_per_rep.each do |_rep, times_per_filter|
|
267
|
-
times_per_filter.each do |filter_name, data|
|
268
|
-
result[filter_name] ||= []
|
269
|
-
result[filter_name].concat(data[:accum])
|
270
|
-
end
|
271
|
-
end
|
272
|
-
|
273
|
-
result
|
274
|
-
end
|
241
|
+
puts values[0] + ' │ ' + values[1..-1].join(' ')
|
275
242
|
end
|
276
243
|
end
|
277
244
|
|
@@ -6,7 +6,7 @@ module Nanoc::CLI::StreamCleaners
|
|
6
6
|
# @see Nanoc::CLI::StreamCleaners::Abstract#clean
|
7
7
|
def clean(s)
|
8
8
|
# FIXME: this decomposition is not generally usable
|
9
|
-
s.
|
9
|
+
s.tr('─┼“”‘’', '-+""\'\'').gsub('…', '...').gsub('©', '(c)')
|
10
10
|
end
|
11
11
|
end
|
12
12
|
end
|
data/lib/nanoc/spec.rb
CHANGED
@@ -32,7 +32,7 @@ module Nanoc
|
|
32
32
|
def create_item(content, attributes, identifier)
|
33
33
|
item = Nanoc::Int::Item.new(content, attributes, identifier)
|
34
34
|
@items = @items.add(item)
|
35
|
-
|
35
|
+
self
|
36
36
|
end
|
37
37
|
|
38
38
|
# Creates a new layout and adds it to the site’s collection of layouts.
|
@@ -47,7 +47,7 @@ module Nanoc
|
|
47
47
|
def create_layout(content, attributes, identifier)
|
48
48
|
layout = Nanoc::Int::Layout.new(content, attributes, identifier)
|
49
49
|
@layouts = @layouts.add(layout)
|
50
|
-
|
50
|
+
self
|
51
51
|
end
|
52
52
|
|
53
53
|
# Creates a new representation for the given item.
|
@@ -60,7 +60,7 @@ module Nanoc
|
|
60
60
|
rep = Nanoc::Int::ItemRep.new(item.unwrap, rep)
|
61
61
|
rep.paths[:last] = [path]
|
62
62
|
@reps << rep
|
63
|
-
|
63
|
+
self
|
64
64
|
end
|
65
65
|
|
66
66
|
# @return [Object] An object that includes the helper functions
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Nanoc
|
2
|
+
# @api private
|
3
|
+
module Telemetry
|
4
|
+
def self.new
|
5
|
+
Registry.new
|
6
|
+
end
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
require_relative 'telemetry/counter'
|
11
|
+
require_relative 'telemetry/summary'
|
12
|
+
|
13
|
+
require_relative 'telemetry/labelled_counter'
|
14
|
+
require_relative 'telemetry/labelled_summary'
|
15
|
+
|
16
|
+
require_relative 'telemetry/registry'
|
17
|
+
require_relative 'telemetry/stopwatch'
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Nanoc::Telemetry
|
2
|
+
class LabelledCounter
|
3
|
+
def initialize
|
4
|
+
@counters = {}
|
5
|
+
end
|
6
|
+
|
7
|
+
def increment(labels)
|
8
|
+
get(labels).increment
|
9
|
+
end
|
10
|
+
|
11
|
+
def get(labels)
|
12
|
+
@counters.fetch(labels) { @counters[labels] = Counter.new }
|
13
|
+
end
|
14
|
+
|
15
|
+
# TODO: add #labels
|
16
|
+
|
17
|
+
def value(labels)
|
18
|
+
get(labels).value
|
19
|
+
end
|
20
|
+
|
21
|
+
def values
|
22
|
+
@counters.each_with_object({}) do |(labels, counter), res|
|
23
|
+
res[labels] = counter.value
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Nanoc::Telemetry
|
2
|
+
class LabelledSummary
|
3
|
+
def initialize
|
4
|
+
@summaries = {}
|
5
|
+
end
|
6
|
+
|
7
|
+
def observe(value, labels)
|
8
|
+
get(labels).observe(value)
|
9
|
+
end
|
10
|
+
|
11
|
+
def get(labels)
|
12
|
+
@summaries.fetch(labels) { @summaries[labels] = Summary.new }
|
13
|
+
end
|
14
|
+
|
15
|
+
def labels
|
16
|
+
@summaries.keys
|
17
|
+
end
|
18
|
+
|
19
|
+
def quantile(fraction, labels)
|
20
|
+
get(labels).quantile(fraction)
|
21
|
+
end
|
22
|
+
|
23
|
+
# TODO: add quantiles(fraction)
|
24
|
+
# TODO: add min(labels)
|
25
|
+
# TODO: add mins
|
26
|
+
# TODO: add max(labels)
|
27
|
+
# TODO: add maxs
|
28
|
+
# TODO: add sum(labels)
|
29
|
+
# TODO: add sums
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Nanoc::Telemetry
|
2
|
+
class Registry
|
3
|
+
def initialize
|
4
|
+
@counters = {}
|
5
|
+
@summaries = {}
|
6
|
+
end
|
7
|
+
|
8
|
+
def counter(name)
|
9
|
+
@counters.fetch(name) { @counters[name] = LabelledCounter.new }
|
10
|
+
end
|
11
|
+
|
12
|
+
def summary(name)
|
13
|
+
@summaries.fetch(name) { @summaries[name] = LabelledSummary.new }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Nanoc::Telemetry
|
2
|
+
class Stopwatch
|
3
|
+
attr_reader :duration
|
4
|
+
|
5
|
+
class AlreadyRunningError < StandardError
|
6
|
+
def message
|
7
|
+
'Cannot start, because stopwatch is already running'
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class NotRunningError < StandardError
|
12
|
+
def message
|
13
|
+
'Cannot stop, because stopwatch is not running'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
@duration = 0.0
|
19
|
+
@last_start = nil
|
20
|
+
end
|
21
|
+
|
22
|
+
def start
|
23
|
+
raise AlreadyRunningError if running?
|
24
|
+
@last_start = Time.now
|
25
|
+
end
|
26
|
+
|
27
|
+
def stop
|
28
|
+
raise NotRunningError unless running?
|
29
|
+
@duration += (Time.now - @last_start)
|
30
|
+
@last_start = nil
|
31
|
+
end
|
32
|
+
|
33
|
+
def running?
|
34
|
+
!@last_start.nil?
|
35
|
+
end
|
36
|
+
|
37
|
+
def stopped?
|
38
|
+
!running?
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Nanoc::Telemetry
|
2
|
+
class Summary
|
3
|
+
class EmptySummaryError < StandardError
|
4
|
+
def message
|
5
|
+
'Cannot calculate quantile for empty summary'
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@values = []
|
11
|
+
end
|
12
|
+
|
13
|
+
def observe(value)
|
14
|
+
@values << value
|
15
|
+
@sorted_values = nil
|
16
|
+
end
|
17
|
+
|
18
|
+
def count
|
19
|
+
@values.size
|
20
|
+
end
|
21
|
+
|
22
|
+
def sum
|
23
|
+
raise EmptySummaryError if @values.empty?
|
24
|
+
@values.reduce(:+)
|
25
|
+
end
|
26
|
+
|
27
|
+
def avg
|
28
|
+
sum / count
|
29
|
+
end
|
30
|
+
|
31
|
+
def min
|
32
|
+
quantile(0.0)
|
33
|
+
end
|
34
|
+
|
35
|
+
def max
|
36
|
+
quantile(1.0)
|
37
|
+
end
|
38
|
+
|
39
|
+
def quantile(fraction)
|
40
|
+
raise EmptySummaryError if @values.empty?
|
41
|
+
|
42
|
+
target = (@values.size - 1) * fraction.to_f
|
43
|
+
interp = target % 1.0
|
44
|
+
sorted_values[target.floor] * (1.0 - interp) + sorted_values[target.ceil] * interp
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def sorted_values
|
50
|
+
@sorted_values ||= @values.sort
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|