nanoc 4.7.3 → 4.7.4

Sign up to get free protection for your applications and to get access to all the features.
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