nanoc 4.7.3 → 4.7.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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +5 -5
  3. data/NEWS.md +7 -0
  4. data/lib/nanoc/base/entities.rb +1 -1
  5. data/lib/nanoc/base/entities/{rule_memory.rb → action_sequence.rb} +19 -1
  6. data/lib/nanoc/base/entities/outdatedness_reasons.rb +12 -4
  7. data/lib/nanoc/base/entities/outdatedness_status.rb +1 -1
  8. data/lib/nanoc/base/repos.rb +1 -1
  9. data/lib/nanoc/base/repos/{rule_memory_store.rb → action_sequence_store.rb} +12 -12
  10. data/lib/nanoc/base/repos/checksum_store.rb +3 -3
  11. data/lib/nanoc/base/services/action_provider.rb +1 -9
  12. data/lib/nanoc/base/services/checksummer.rb +4 -2
  13. data/lib/nanoc/base/services/compiler.rb +11 -11
  14. data/lib/nanoc/base/services/compiler/phases/recalculate.rb +3 -3
  15. data/lib/nanoc/base/services/compiler/stages/cleanup.rb +1 -1
  16. data/lib/nanoc/base/services/compiler/stages/compile_reps.rb +3 -3
  17. data/lib/nanoc/base/services/compiler/stages/determine_outdatedness.rb +7 -2
  18. data/lib/nanoc/base/services/compiler_loader.rb +3 -3
  19. data/lib/nanoc/base/services/item_rep_builder.rb +4 -2
  20. data/lib/nanoc/base/services/item_rep_router.rb +7 -2
  21. data/lib/nanoc/base/services/outdatedness_checker.rb +30 -7
  22. data/lib/nanoc/base/services/outdatedness_rule.rb +7 -4
  23. data/lib/nanoc/base/services/outdatedness_rules.rb +9 -161
  24. data/lib/nanoc/base/services/outdatedness_rules/attributes_modified.rb +34 -0
  25. data/lib/nanoc/base/services/outdatedness_rules/code_snippets_modified.rb +26 -0
  26. data/lib/nanoc/base/services/outdatedness_rules/configuration_modified.rb +23 -0
  27. data/lib/nanoc/base/services/outdatedness_rules/content_modified.rb +15 -0
  28. data/lib/nanoc/base/services/outdatedness_rules/not_written.rb +11 -0
  29. data/lib/nanoc/base/services/outdatedness_rules/paths_modified.rb +20 -0
  30. data/lib/nanoc/base/services/outdatedness_rules/rules_modified.rb +13 -0
  31. data/lib/nanoc/base/services/outdatedness_rules/uses_always_outdated_filter.rb +20 -0
  32. data/lib/nanoc/rule_dsl.rb +1 -1
  33. data/lib/nanoc/rule_dsl/action_provider.rb +7 -11
  34. data/lib/nanoc/rule_dsl/{rule_memory_calculator.rb → action_sequence_calculator.rb} +21 -39
  35. data/lib/nanoc/rule_dsl/recording_executor.rb +7 -7
  36. data/lib/nanoc/spec.rb +7 -7
  37. data/lib/nanoc/version.rb +1 -1
  38. data/spec/nanoc/base/checksummer_spec.rb +20 -0
  39. data/spec/nanoc/base/compiler_spec.rb +7 -10
  40. data/spec/nanoc/base/entities/action_sequence_spec.rb +278 -0
  41. data/spec/nanoc/base/repos/checksum_store_spec.rb +22 -2
  42. data/spec/nanoc/base/services/compiler/stages/cleanup_spec.rb +2 -2
  43. data/spec/nanoc/base/services/compiler/stages/compile_reps_spec.rb +5 -9
  44. data/spec/nanoc/base/services/executor_spec.rb +5 -5
  45. data/spec/nanoc/base/services/item_rep_router_spec.rb +36 -18
  46. data/spec/nanoc/base/services/outdatedness_checker_spec.rb +74 -30
  47. data/spec/nanoc/base/services/outdatedness_rules_spec.rb +78 -18
  48. data/spec/nanoc/helpers/rendering_spec.rb +4 -4
  49. data/spec/nanoc/rule_dsl/{rule_memory_calculator_spec.rb → action_sequence_calculator_spec.rb} +6 -61
  50. data/spec/nanoc/rule_dsl/recording_executor_spec.rb +45 -45
  51. data/test/base/test_outdatedness_checker.rb +1 -1
  52. data/test/rule_dsl/test_action_provider.rb +3 -3
  53. metadata +15 -7
  54. data/spec/nanoc/base/entities/rule_memory_spec.rb +0 -167
@@ -16,11 +16,13 @@ module Nanoc::Int
16
16
  end
17
17
  end
18
18
 
19
- Nanoc::Int::ItemRepRouter.new(@reps, @action_provider, @site).run
19
+ action_sequences = Nanoc::Int::ItemRepRouter.new(@reps, @action_provider, @site).run
20
20
 
21
21
  @reps.each do |rep|
22
- rep.snapshot_defs = @action_provider.snapshots_defs_for(rep)
22
+ rep.snapshot_defs = action_sequences[rep].snapshots_defs
23
23
  end
24
+
25
+ action_sequences
24
26
  end
25
27
  end
26
28
  end
@@ -24,6 +24,7 @@ module Nanoc::Int
24
24
  end
25
25
 
26
26
  def run
27
+ action_sequences = {}
27
28
  assigned_paths = {}
28
29
  @reps.each do |rep|
29
30
  # Sigh. We route reps twice, because the first time, the paths might not have converged
@@ -31,16 +32,20 @@ module Nanoc::Int
31
32
  # I can think of. For details, see
32
33
  # https://github.com/nanoc/nanoc/pull/1085#issuecomment-280628426.
33
34
 
34
- @action_provider.paths_for(rep).each do |(snapshot_names, paths)|
35
+ @action_provider.action_sequence_for(rep).paths.each do |(snapshot_names, paths)|
35
36
  route_rep(rep, paths, snapshot_names, {})
36
37
  end
37
38
 
38
- @action_provider.paths_for(rep).each do |(snapshot_names, paths)|
39
+ mem = @action_provider.action_sequence_for(rep)
40
+ action_sequences[rep] = mem
41
+ mem.paths.each do |(snapshot_names, paths)|
39
42
  route_rep(rep, paths, snapshot_names, assigned_paths)
40
43
  end
41
44
 
42
45
  # TODO: verify that paths converge
43
46
  end
47
+
48
+ action_sequences
44
49
  end
45
50
 
46
51
  contract Nanoc::Int::ItemRep, C::IterOf[String], C::IterOf[Symbol], C::HashOf[String => Nanoc::Int::ItemRep] => C::Any
@@ -81,26 +81,34 @@ module Nanoc::Int
81
81
 
82
82
  attr_reader :checksum_store
83
83
  attr_reader :dependency_store
84
- attr_reader :rule_memory_store
84
+ attr_reader :action_sequence_store
85
85
  attr_reader :action_provider
86
86
  attr_reader :site
87
87
 
88
88
  Reasons = Nanoc::Int::OutdatednessReasons
89
89
 
90
+ C_OBJ = C::Or[Nanoc::Int::Item, Nanoc::Int::ItemRep, Nanoc::Int::Layout]
91
+
90
92
  # FIXME: Replace C::Any with proper types
91
- contract C::KeywordArgs[site: Nanoc::Int::Site, checksum_store: Nanoc::Int::ChecksumStore, dependency_store: Nanoc::Int::DependencyStore, rule_memory_store: Nanoc::Int::RuleMemoryStore, action_provider: C::Any, reps: Nanoc::Int::ItemRepRepo] => C::Any
92
- def initialize(site:, checksum_store:, dependency_store:, rule_memory_store:, action_provider:, reps:)
93
+ contract C::KeywordArgs[site: Nanoc::Int::Site, checksum_store: Nanoc::Int::ChecksumStore, dependency_store: Nanoc::Int::DependencyStore, action_sequence_store: Nanoc::Int::ActionSequenceStore, action_provider: C::Any, reps: Nanoc::Int::ItemRepRepo] => C::Any
94
+ def initialize(site:, checksum_store:, dependency_store:, action_sequence_store:, action_provider:, reps:)
93
95
  @site = site
94
96
  @checksum_store = checksum_store
95
97
  @dependency_store = dependency_store
96
- @rule_memory_store = rule_memory_store
98
+ @action_sequence_store = action_sequence_store
97
99
  @action_provider = action_provider
98
100
  @reps = reps
99
101
 
100
102
  @objects_outdated_due_to_dependencies = {}
101
103
  end
102
104
 
103
- contract C::Or[Nanoc::Int::Item, Nanoc::Int::ItemRep, Nanoc::Int::Layout] => C::Bool
105
+ def action_sequence_for(rep)
106
+ # TODO: Pass in action_sequences instead
107
+ @action_provider.action_sequence_for(rep)
108
+ end
109
+ memoize :action_sequence_for
110
+
111
+ contract C_OBJ, C::Maybe[C::HashOf[C_OBJ => Nanoc::Int::ActionSequence]] => C::Bool
104
112
  # Checks whether the given object is outdated and therefore needs to be
105
113
  # recompiled.
106
114
  #
@@ -108,7 +116,8 @@ module Nanoc::Int
108
116
  # whose outdatedness should be checked.
109
117
  #
110
118
  # @return [Boolean] true if the object is outdated, false otherwise
111
- def outdated?(obj)
119
+ def outdated?(obj, _action_sequences = nil)
120
+ # TODO: use action_sequences
112
121
  !outdatedness_reason_for(obj).nil?
113
122
  end
114
123
 
@@ -176,7 +185,21 @@ module Nanoc::Int
176
185
  return true if dependency.from.nil?
177
186
 
178
187
  status = basic.outdatedness_status_for(dependency.from)
179
- (status.props.active & dependency.props.active).any?
188
+
189
+ active = status.props.active & dependency.props.active
190
+ if attributes_unaffected?(status, dependency)
191
+ active.delete(:attributes)
192
+ end
193
+
194
+ active.any?
195
+ end
196
+
197
+ def attributes_unaffected?(status, dependency)
198
+ attr_reason = status.reasons.find do |r|
199
+ r.is_a?(Nanoc::Int::OutdatednessReasons::AttributesModified)
200
+ end
201
+
202
+ attr_reason && dependency.props.attributes.is_a?(Enumerable) && (dependency.props.attributes & attr_reason.attributes).empty?
180
203
  end
181
204
  end
182
205
  end
@@ -12,7 +12,7 @@ module Nanoc::Int
12
12
  end
13
13
 
14
14
  def apply(_obj, _outdatedness_checker)
15
- raise NotImplementedError.new('Nanoc::Int::OutdatednessRule subclasses must implement ##reason, and #apply')
15
+ raise NotImplementedError.new('Nanoc::Int::OutdatednessRule subclasses must implement #apply')
16
16
  end
17
17
 
18
18
  contract C::None => String
@@ -20,9 +20,12 @@ module Nanoc::Int
20
20
  "#{self.class.name}(#{reason})"
21
21
  end
22
22
 
23
- # TODO: remove
24
- def reason
25
- raise NotImplementedError.new('Nanoc::Int::OutdatednessRule subclasses must implement ##reason, and #apply')
23
+ def self.affects_props(*names)
24
+ @affected_props = Set.new(names)
25
+ end
26
+
27
+ def self.affected_props
28
+ @affected_props
26
29
  end
27
30
  end
28
31
  end
@@ -1,166 +1,14 @@
1
1
  module Nanoc::Int
2
2
  # @api private
3
3
  module OutdatednessRules
4
- class CodeSnippetsModified < OutdatednessRule
5
- extend Nanoc::Int::Memoization
6
-
7
- include Nanoc::Int::ContractsSupport
8
-
9
- def reason
10
- Nanoc::Int::OutdatednessReasons::CodeSnippetsModified
11
- end
12
-
13
- def apply(_obj, outdatedness_checker)
14
- if any_snippets_modified?(outdatedness_checker)
15
- Nanoc::Int::OutdatednessReasons::CodeSnippetsModified
16
- end
17
- end
18
-
19
- private
20
-
21
- def any_snippets_modified?(outdatedness_checker)
22
- outdatedness_checker.site.code_snippets.any? do |cs|
23
- ch_old = outdatedness_checker.checksum_store[cs]
24
- ch_new = Nanoc::Int::Checksummer.calc(cs)
25
- ch_old != ch_new
26
- end
27
- end
28
- memoize :any_snippets_modified?
29
- end
30
-
31
- class ConfigurationModified < OutdatednessRule
32
- extend Nanoc::Int::Memoization
33
-
34
- def reason
35
- Nanoc::Int::OutdatednessReasons::ConfigurationModified
36
- end
37
-
38
- def apply(_obj, outdatedness_checker)
39
- if config_modified?(outdatedness_checker)
40
- Nanoc::Int::OutdatednessReasons::ConfigurationModified
41
- end
42
- end
43
-
44
- private
45
-
46
- def config_modified?(outdatedness_checker)
47
- obj = outdatedness_checker.site.config
48
- ch_old = outdatedness_checker.checksum_store[obj]
49
- ch_new = Nanoc::Int::Checksummer.calc(obj)
50
- ch_old != ch_new
51
- end
52
- memoize :config_modified?
53
- end
54
-
55
- class NotWritten < OutdatednessRule
56
- def reason
57
- Nanoc::Int::OutdatednessReasons::NotWritten
58
- end
59
-
60
- def apply(obj, _outdatedness_checker)
61
- if obj.raw_paths.values.flatten.compact.any? { |fn| !File.file?(fn) }
62
- Nanoc::Int::OutdatednessReasons::NotWritten
63
- end
64
- end
65
- end
66
-
67
- class ContentModified < OutdatednessRule
68
- def reason
69
- Nanoc::Int::OutdatednessReasons::ContentModified
70
- end
71
-
72
- def apply(obj, outdatedness_checker)
73
- obj = obj.item if obj.is_a?(Nanoc::Int::ItemRep)
74
-
75
- ch_old = outdatedness_checker.checksum_store.content_checksum_for(obj)
76
- ch_new = Nanoc::Int::Checksummer.calc_for_content_of(obj)
77
- if ch_old != ch_new
78
- Nanoc::Int::OutdatednessReasons::ContentModified
79
- end
80
- end
81
- end
82
-
83
- class AttributesModified < OutdatednessRule
84
- extend Nanoc::Int::Memoization
85
-
86
- include Nanoc::Int::ContractsSupport
87
-
88
- def reason
89
- Nanoc::Int::OutdatednessReasons::AttributesModified
90
- end
91
-
92
- contract C::Or[Nanoc::Int::ItemRep, Nanoc::Int::Item, Nanoc::Int::Layout], C::Named['Nanoc::Int::OutdatednessChecker'] => C::Maybe[Nanoc::Int::OutdatednessReasons::Generic]
93
- def apply(obj, outdatedness_checker)
94
- case obj
95
- when Nanoc::Int::ItemRep
96
- apply(obj.item, outdatedness_checker)
97
- when Nanoc::Int::Item, Nanoc::Int::Layout
98
- ch_old = outdatedness_checker.checksum_store.attributes_checksum_for(obj)
99
- ch_new = Nanoc::Int::Checksummer.calc_for_attributes_of(obj)
100
- if ch_old != ch_new
101
- Nanoc::Int::OutdatednessReasons::AttributesModified
102
- end
103
- else
104
- raise ArgumentError
105
- end
106
- end
107
- memoize :apply
108
- end
109
-
110
- class RulesModified < OutdatednessRule
111
- def reason
112
- Nanoc::Int::OutdatednessReasons::RulesModified
113
- end
114
-
115
- def apply(obj, outdatedness_checker)
116
- mem_old = outdatedness_checker.rule_memory_store[obj]
117
- mem_new = outdatedness_checker.action_provider.memory_for(obj).serialize
118
- unless mem_old.eql?(mem_new)
119
- Nanoc::Int::OutdatednessReasons::RulesModified
120
- end
121
- end
122
- end
123
-
124
- class PathsModified < OutdatednessRule
125
- def reason
126
- Nanoc::Int::OutdatednessReasons::PathsModified
127
- end
128
-
129
- def apply(obj, outdatedness_checker)
130
- # FIXME: Prefer to not work on serialised version
131
-
132
- mem_old = outdatedness_checker.rule_memory_store[obj]
133
- mem_new = outdatedness_checker.action_provider.memory_for(obj).serialize
134
- return true if mem_old.nil?
135
-
136
- paths_old = mem_old.select { |pa| pa[0] == :snapshot }
137
- paths_new = mem_new.select { |pa| pa[0] == :snapshot }
138
-
139
- if paths_old != paths_new
140
- Nanoc::Int::OutdatednessReasons::PathsModified
141
- end
142
- end
143
- end
144
-
145
- class UsesAlwaysOutdatedFilter < OutdatednessRule
146
- def reason
147
- Nanoc::Int::OutdatednessReasons::UsesAlwaysOutdatedFilter
148
- end
149
-
150
- def apply(obj, outdatedness_checker)
151
- mem = outdatedness_checker.action_provider.memory_for(obj)
152
- if any_always_outdated?(mem)
153
- Nanoc::Int::OutdatednessReasons::UsesAlwaysOutdatedFilter
154
- end
155
- end
156
-
157
- def any_always_outdated?(mem)
158
- mem
159
- .select { |a| a.is_a?(Nanoc::Int::ProcessingActions::Filter) }
160
- .map { |a| Nanoc::Filter.named(a.filter_name) }
161
- .compact
162
- .any?(&:always_outdated?)
163
- end
164
- end
165
4
  end
166
5
  end
6
+
7
+ require_relative 'outdatedness_rules/attributes_modified'
8
+ require_relative 'outdatedness_rules/code_snippets_modified'
9
+ require_relative 'outdatedness_rules/configuration_modified'
10
+ require_relative 'outdatedness_rules/content_modified'
11
+ require_relative 'outdatedness_rules/not_written'
12
+ require_relative 'outdatedness_rules/paths_modified'
13
+ require_relative 'outdatedness_rules/rules_modified'
14
+ require_relative 'outdatedness_rules/uses_always_outdated_filter'
@@ -0,0 +1,34 @@
1
+ module Nanoc::Int::OutdatednessRules
2
+ class AttributesModified < Nanoc::Int::OutdatednessRule
3
+ extend Nanoc::Int::Memoization
4
+
5
+ include Nanoc::Int::ContractsSupport
6
+
7
+ affects_props :attributes, :compiled_content
8
+
9
+ contract C::Or[Nanoc::Int::ItemRep, Nanoc::Int::Item, Nanoc::Int::Layout], C::Named['Nanoc::Int::OutdatednessChecker'] => C::Maybe[Nanoc::Int::OutdatednessReasons::Generic]
10
+ def apply(obj, outdatedness_checker)
11
+ case obj
12
+ when Nanoc::Int::ItemRep
13
+ apply(obj.item, outdatedness_checker)
14
+ when Nanoc::Int::Item, Nanoc::Int::Layout
15
+ old_checksums = outdatedness_checker.checksum_store.attributes_checksum_for(obj)
16
+ unless old_checksums
17
+ return Nanoc::Int::OutdatednessReasons::AttributesModified.new(true)
18
+ end
19
+
20
+ new_checksums = Nanoc::Int::Checksummer.calc_for_each_attribute_of(obj)
21
+
22
+ attributes = Set.new(old_checksums.keys) + Set.new(new_checksums.keys)
23
+ changed_attributes = attributes.reject { |a| old_checksums[a] == new_checksums[a] }
24
+
25
+ if changed_attributes.any?
26
+ Nanoc::Int::OutdatednessReasons::AttributesModified.new(changed_attributes)
27
+ end
28
+ else
29
+ raise ArgumentError
30
+ end
31
+ end
32
+ memoize :apply
33
+ end
34
+ end
@@ -0,0 +1,26 @@
1
+ module Nanoc::Int::OutdatednessRules
2
+ class CodeSnippetsModified < Nanoc::Int::OutdatednessRule
3
+ extend Nanoc::Int::Memoization
4
+
5
+ include Nanoc::Int::ContractsSupport
6
+
7
+ affects_props :raw_content, :attributes, :compiled_content, :path
8
+
9
+ def apply(_obj, outdatedness_checker)
10
+ if any_snippets_modified?(outdatedness_checker)
11
+ Nanoc::Int::OutdatednessReasons::CodeSnippetsModified
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def any_snippets_modified?(outdatedness_checker)
18
+ outdatedness_checker.site.code_snippets.any? do |cs|
19
+ ch_old = outdatedness_checker.checksum_store[cs]
20
+ ch_new = Nanoc::Int::Checksummer.calc(cs)
21
+ ch_old != ch_new
22
+ end
23
+ end
24
+ memoize :any_snippets_modified?
25
+ end
26
+ end
@@ -0,0 +1,23 @@
1
+ module Nanoc::Int::OutdatednessRules
2
+ class ConfigurationModified < Nanoc::Int::OutdatednessRule
3
+ extend Nanoc::Int::Memoization
4
+
5
+ affects_props :raw_content, :attributes, :compiled_content, :path
6
+
7
+ def apply(_obj, outdatedness_checker)
8
+ if config_modified?(outdatedness_checker)
9
+ Nanoc::Int::OutdatednessReasons::ConfigurationModified
10
+ end
11
+ end
12
+
13
+ private
14
+
15
+ def config_modified?(outdatedness_checker)
16
+ obj = outdatedness_checker.site.config
17
+ ch_old = outdatedness_checker.checksum_store[obj]
18
+ ch_new = Nanoc::Int::Checksummer.calc(obj)
19
+ ch_old != ch_new
20
+ end
21
+ memoize :config_modified?
22
+ end
23
+ end
@@ -0,0 +1,15 @@
1
+ module Nanoc::Int::OutdatednessRules
2
+ class ContentModified < Nanoc::Int::OutdatednessRule
3
+ affects_props :raw_content, :compiled_content
4
+
5
+ def apply(obj, outdatedness_checker)
6
+ obj = obj.item if obj.is_a?(Nanoc::Int::ItemRep)
7
+
8
+ ch_old = outdatedness_checker.checksum_store.content_checksum_for(obj)
9
+ ch_new = Nanoc::Int::Checksummer.calc_for_content_of(obj)
10
+ if ch_old != ch_new
11
+ Nanoc::Int::OutdatednessReasons::ContentModified
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,11 @@
1
+ module Nanoc::Int::OutdatednessRules
2
+ class NotWritten < Nanoc::Int::OutdatednessRule
3
+ affects_props :raw_content, :attributes, :compiled_content, :path
4
+
5
+ def apply(obj, _outdatedness_checker)
6
+ if obj.raw_paths.values.flatten.compact.any? { |fn| !File.file?(fn) }
7
+ Nanoc::Int::OutdatednessReasons::NotWritten
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,20 @@
1
+ module Nanoc::Int::OutdatednessRules
2
+ class PathsModified < Nanoc::Int::OutdatednessRule
3
+ affects_props :path
4
+
5
+ def apply(obj, outdatedness_checker)
6
+ # FIXME: Prefer to not work on serialised version
7
+
8
+ mem_old = outdatedness_checker.action_sequence_store[obj]
9
+ mem_new = outdatedness_checker.action_sequence_for(obj).serialize
10
+ return true if mem_old.nil?
11
+
12
+ paths_old = mem_old.select { |pa| pa[0] == :snapshot }
13
+ paths_new = mem_new.select { |pa| pa[0] == :snapshot }
14
+
15
+ if paths_old != paths_new
16
+ Nanoc::Int::OutdatednessReasons::PathsModified
17
+ end
18
+ end
19
+ end
20
+ end