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 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