nanoc 4.6.3 → 4.6.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2be981505c54e8c257e032a6bd08d7be8be95135
4
- data.tar.gz: db527143c17a425809740641b4344b225c35fc43
3
+ metadata.gz: 45e73cb6f9f73af2a760146464d15b5537744e16
4
+ data.tar.gz: e5c219a107840118bb1fa45ba1396d302dbc5049
5
5
  SHA512:
6
- metadata.gz: f1b3ed7df2c66ed6eab2b67081b1b828942fa5ce474e85d2c088dc6b439ccffbf36a1cedad89fef84334b2e5033ce561a98e1bc48b5f94f176358a9b913d1426
7
- data.tar.gz: ad5c1d8f749f3b10f8167f4faa45a41cbcc18ba64ca2ad73220a4afc49f606399349de87d05599d462d4eebf819f5ead745e3c9c42883ba3b71bf2c2038b54e0
6
+ metadata.gz: 88453d81285519f1ee03821c835f9744096623f07a887f012dc66404bd68342429b211202cc9ec686b430da2d56ffbe2e6c23ec166fded484674512ab1c4c611
7
+ data.tar.gz: 39bb9372f9c71984af7726da2a1a2290dc85a89af3c7006073265d19620a4fb58f66616badd98ed97651a5006c71be82b0c9a00822727b91c94855cf368e3342
@@ -1,6 +1,6 @@
1
1
  GIT
2
2
  remote: https://github.com/bbatsov/rubocop.git
3
- revision: c52c7ea94d1d99f2d384f562b36dbed0f546f628
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.3)
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.17)
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.3)
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.5
428
+ 1.14.6
data/NEWS.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Nanoc news
2
2
 
3
+ ## 4.6.4 (2017-03-10)
4
+
5
+ Fixes:
6
+
7
+ * Fixed issue where `compile --verbose` would show misleading timing information (#1113)
8
+
3
9
  ## 4.6.3 (2017-03-05)
4
10
 
5
11
  Fixes:
@@ -45,6 +45,7 @@ require 'English'
45
45
  # Load Nanoc
46
46
  require 'nanoc/version'
47
47
  require 'nanoc/base'
48
+ require 'nanoc/telemetry'
48
49
  require 'nanoc/checking'
49
50
  require 'nanoc/deploying'
50
51
  require 'nanoc/extra'
@@ -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::Int::NotificationCenter.on(:filtering_started) do |rep, filter_name|
166
- @times_per_rep[rep] ||= {}
167
- @times_per_rep[rep][filter_name] ||= {}
159
+ @telemetry = Nanoc::Telemetry.new
160
+
161
+ stopwatches = {}
168
162
 
169
- @times_per_rep[rep][filter_name][:last_start] = Time.now
170
- @times_per_rep[rep][filter_name][:accum] ||= []
171
- @times_per_rep[rep][filter_name][:suspended] = false
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
- times = @times_per_rep[rep][filter_name]
176
- last_start = @times_per_rep[rep][filter_name][:last_start]
170
+ stopwatch = stopwatches.fetch(rep).pop
171
+ stopwatch.stop
177
172
 
178
- times[:accum] << (Time.now - last_start)
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
- @times_per_rep.fetch(rep, {}).each do |_filter_name, times|
184
- if times[:last_start]
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
- @times_per_rep.fetch(rep, {}).each do |filter_name, times|
196
- if times[:suspended]
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
- rows = durations_per_filter.to_a.sort_by { |r| r[1] }.map do |row|
218
- filter_name, samples = *row
219
- count = samples.size
220
- min = samples.min
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
- [filter_name.to_s, count.to_s] + [min, avg, max, tot].map { |r| "#{format('%4.2f', r)}s" }
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
- # Get max filter length
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 "#{'-' * lengths[0]}-+-#{lengths[1..-1].map { |length| '-' * length }.join('---')}"
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] + ' | ' + values[1..-1].join(' ')
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.gsub(/“|”/, '"').gsub(/‘|’/, '\'').gsub('…', '...').gsub('©', '(c)')
9
+ s.tr('─┼“”‘’', '-+""\'\'').gsub('…', '...').gsub('©', '(c)')
10
10
  end
11
11
  end
12
12
  end
@@ -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
- Nanoc::ItemWithRepsView.new(item, view_context)
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
- Nanoc::LayoutView.new(layout, view_context)
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
- Nanoc::ItemRepView.new(rep, view_context)
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,13 @@
1
+ module Nanoc::Telemetry
2
+ class Counter
3
+ attr_reader :value
4
+
5
+ def initialize
6
+ @value = 0
7
+ end
8
+
9
+ def increment
10
+ @value += 1
11
+ end
12
+ end
13
+ end
@@ -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