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.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -3
  3. data/Gemfile.lock +4 -4
  4. data/NEWS.md +6 -0
  5. data/Rakefile +2 -2
  6. data/lib/nanoc/base/memoization.rb +3 -0
  7. data/lib/nanoc/base/services/outdatedness_rules.rb +17 -5
  8. data/lib/nanoc/base/views/post_compile_item_rep_view.rb +4 -0
  9. data/lib/nanoc/checking/check.rb +1 -1
  10. data/lib/nanoc/cli/commands/compile_listeners/timing_recorder.rb +36 -0
  11. data/lib/nanoc/cli/commands/show-plugins.rb +1 -1
  12. data/lib/nanoc/filters/colorize_syntax.rb +1 -1
  13. data/lib/nanoc/telemetry/labelled_counter.rb +7 -1
  14. data/lib/nanoc/version.rb +1 -1
  15. data/spec/nanoc/base/entities/outdatedness_status_spec.rb +1 -1
  16. data/spec/nanoc/base/entities/processing_actions/snapshot_spec.rb +1 -1
  17. data/spec/nanoc/base/entities/props_spec.rb +8 -8
  18. data/spec/nanoc/base/entities/rule_memory_spec.rb +2 -2
  19. data/spec/nanoc/base/memoization_spec.rb +75 -0
  20. data/spec/nanoc/base/services/item_rep_selector_spec.rb +12 -12
  21. data/spec/nanoc/base/views/post_compile_item_rep_view_spec.rb +58 -0
  22. data/spec/nanoc/cli/commands/compile/timing_recorder_spec.rb +22 -0
  23. data/spec/nanoc/helpers/tagging_spec.rb +1 -1
  24. data/spec/nanoc/regressions/gh_1130_spec.rb +19 -0
  25. data/spec/nanoc/rule_dsl/rule_memory_calculator_spec.rb +4 -4
  26. data/spec/nanoc/telemetry/labelled_counter_spec.rb +36 -0
  27. data/spec/spec_helper.rb +31 -0
  28. data/test/base/core_ext/array_spec.rb +2 -2
  29. data/test/cli/test_cleaning_stream.rb +1 -1
  30. data/test/filters/colorize_syntax/test_common.rb +1 -1
  31. metadata +4 -3
  32. data/test/base/test_memoization.rb +0 -71
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3697f07907752b5c0010bbf3fb01e701d6128bba
4
- data.tar.gz: 70dfb18ac4973838113380a201ddde4468f2b13b
3
+ metadata.gz: db20da1ff9f55058bb62c68ac59ec1abd74e5afe
4
+ data.tar.gz: fc4c46dfd186f22446ad8a39c457fc72fcd93994
5
5
  SHA512:
6
- metadata.gz: e8810bd89fcd0e3c65f647ec3b99a76454dc2fa35169b5b4e66317f178b260e2e8229686342aee8c83929e7de04b323c98137c8c8b690c7b1259fabed99e2147
7
- data.tar.gz: 144aacfa4f5981109e514c8c33cde796598e86ec27c2b6d115ff2c4b497dc25b5d7a5950a071ad4e23dce5c68063b699b3fb42258f6b0f7c31ded9a81c311177
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: [:ruby, :mswin]
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: [:ruby, :mswin]
56
- gem 'redcarpet', platforms: [:ruby, :mswin]
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'
@@ -1,6 +1,6 @@
1
1
  GIT
2
2
  remote: https://github.com/bbatsov/rubocop.git
3
- revision: a047a11633e104d5ee9ca4d2957e4b1cf571f30b
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.1)
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.6)
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.8)
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
@@ -1,5 +1,11 @@
1
1
  # Nanoc news
2
2
 
3
+ ## 4.7.2 (2017-03-21)
4
+
5
+ Fixes:
6
+
7
+ * Fixed crash when calling `#raw_path` in the Checks file (#1130, #1131)
8
+
3
9
  ## 4.7.1 (2017-03-19)
4
10
 
5
11
  Fixes:
data/Rakefile CHANGED
@@ -14,7 +14,7 @@ end
14
14
 
15
15
  RSpec::Core::RakeTask.new(:spec)
16
16
 
17
- task test: [:spec, :test_all, :rubocop]
18
- task test_ci: [:test, :'coveralls:push']
17
+ task test: %i(spec test_all rubocop)
18
+ task test_ci: %i(test coveralls:push)
19
19
 
20
20
  task default: :test
@@ -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
- obj = obj.item if obj.is_a?(Nanoc::Int::ItemRep)
83
-
84
- ch_old = outdatedness_checker.checksum_store.attributes_checksum_for(obj)
85
- ch_new = Nanoc::Int::Checksummer.calc_for_attributes_of(obj)
86
- ch_old != ch_new
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
@@ -16,5 +16,9 @@ module Nanoc
16
16
 
17
17
  content.string
18
18
  end
19
+
20
+ def raw_path(snapshot: :last)
21
+ @item_rep.raw_path(snapshot: snapshot)
22
+ end
19
23
  end
20
24
  end
@@ -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::ItemCollectionWithRepsView.new(site.items, view_context),
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
 
@@ -41,7 +41,7 @@ module Nanoc::CLI::Commands
41
41
  puts
42
42
 
43
43
  # Print plugins organised by subtype
44
- [:builtin, :custom].each do |type|
44
+ %i(builtin custom).each do |type|
45
45
  # Find relevant plugins
46
46
  relevant_plugins = plugins_with_this_superclass[type]
47
47
 
@@ -358,7 +358,7 @@ module Nanoc::Filters
358
358
 
359
359
  protected
360
360
 
361
- KNOWN_COLORIZERS = [:coderay, :dummy, :pygmentize, :pygmentsrb, :simon_highlight, :rouge].freeze
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
- # TODO: add #empty?
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
@@ -1,4 +1,4 @@
1
1
  module Nanoc
2
2
  # The current Nanoc version.
3
- VERSION = '4.7.1'.freeze
3
+ VERSION = '4.7.2'.freeze
4
4
  end
@@ -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([:raw_content, :attributes, :compiled_content]))
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([:before_layout, :zebra]) }
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([:raw_content, :attributes, :compiled_content, :path])) }
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([:raw_content, :compiled_content])) }
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([:raw_content, :attributes, :compiled_content, :path])) }
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([:raw_content, :attributes, :compiled_content, :path])) }
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([:raw_content, :attributes, :compiled_content, :path])) }
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([:raw_content, :attributes, :compiled_content, :path])) }
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([:attributes, :compiled_content])) }
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([:raw_content, :attributes, :compiled_content, :path])) }
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([:a1, :a2, :a3])
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([:b1, :b2, :b3])
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 [:e, :d, :c, :b, :a]
126
- expect(tentatively_yielded).to eq [:a, :b, :c, :d, :e, :d, :c, :b, :a]
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 [:a, :b, :c, :d, :e]
137
- expect(tentatively_yielded).to eq [:a, :b, :c, :d, :e]
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: [:b, :c, :d, :e],
144
+ a: %i(b c d e),
145
145
  }
146
146
  end
147
147
 
148
148
  example do
149
- expect(successfully_yielded).to eq [:b, :c, :d, :e, :a]
150
- expect(tentatively_yielded).to eq [:a, :b, :a, :c, :a, :d, :a, :e, :a]
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: [:b, :c, :d, :e],
159
+ a: %i(b c d e),
160
160
  }
161
161
  end
162
162
 
163
163
  example do
164
- expect(successfully_yielded).to eq [:b, :c, :d, :e, :a]
165
- expect(tentatively_yielded).to eq [:a, :b, :a, :c, :a, :d, :a, :e, :a]
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 [:d, :a, :e, :b, :c]
180
- expect(tentatively_yielded).to eq [:a, :d, :a, :b, :e, :b, :c]
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: [:foo, :bar] }, '/item3.md')
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([:post, :last])
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([:raw, :last, :pre])
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([:raw, :last, :pre])
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([:raw, :last, :pre])
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
@@ -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, [:b, :c], :d]
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, [:b, :c], :d]
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 = [:write, :<<, :tty?, :tty?, :flush, :tell, :print, :puts, :string, :reopen, :exist?, :exists?, :close]
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
- [:albino, :pygmentize, :simon_highlight].each do |colorizer|
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.1
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-19 00:00:00.000000000 Z
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