nanoc-core 4.12.7 → 4.12.9
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 +4 -4
- data/lib/nanoc/core/checksummer.rb +11 -3
- data/lib/nanoc/core/contracts_support.rb +2 -2
- data/lib/nanoc/core/dependency_props.rb +63 -7
- data/lib/nanoc/core/dependency_store.rb +47 -13
- data/lib/nanoc/core/dependency_tracker.rb +3 -2
- data/lib/nanoc/core/feature.rb +2 -0
- data/lib/nanoc/core/identifiable_collection_view.rb +28 -0
- data/lib/nanoc/core/instrumentor.rb +29 -6
- data/lib/nanoc/core/outdatedness_checker.rb +135 -64
- data/lib/nanoc/core/outdatedness_reasons.rb +9 -19
- data/lib/nanoc/core/outdatedness_rules/item_added.rb +18 -0
- data/lib/nanoc/core/outdatedness_rules/layout_added.rb +18 -0
- data/lib/nanoc/core/outdatedness_rules/not_written.rb +22 -2
- data/lib/nanoc/core/outdatedness_status.rb +4 -0
- data/lib/nanoc/core/store.rb +1 -1
- data/lib/nanoc/core/temp_filename_factory.rb +2 -3
- data/lib/nanoc/core/version.rb +1 -1
- metadata +5 -5
- data/lib/nanoc/core/outdatedness_rules/item_collection_extended.rb +0 -20
- data/lib/nanoc/core/outdatedness_rules/layout_collection_extended.rb +0 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 835f6e4a74f07183143caa0c2d27f304fa5a712c3231dddc2dd28974f714b117
|
4
|
+
data.tar.gz: 71331c08284b2f0da1b6410c8a5c17230efa1606640c1ca7e8724fb97e037951
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1c3b6430105aab73a5e9efff550b3519bd3e19cf99cb9b70f7a3b42916fd48becc091ea7325740673936373650e0d2950f986e507128aa1a7156f0e95bbb1520
|
7
|
+
data.tar.gz: 24ef14012f2ed47f9dd77f885601c395b58fb4d50fb61afa2196fedeecac2571d3702ad87f0e7d80d8806a07b6712364774cf6012650595f4a0cae072e85353e
|
@@ -81,7 +81,8 @@ module Nanoc
|
|
81
81
|
# NOTE: Other behaviors are registered elsewhere
|
82
82
|
# (search for `define_behavior`).
|
83
83
|
|
84
|
-
define_behavior(Array,
|
84
|
+
define_behavior(Array, CollectionUpdateBehavior)
|
85
|
+
define_behavior(Set, SetUpdateBehavior)
|
85
86
|
define_behavior(FalseClass, NoUpdateBehavior)
|
86
87
|
define_behavior(Hash, HashUpdateBehavior)
|
87
88
|
define_behavior(NilClass, NoUpdateBehavior)
|
@@ -96,7 +97,7 @@ module Nanoc
|
|
96
97
|
define_behavior(Nanoc::Core::Configuration, HashUpdateBehavior)
|
97
98
|
define_behavior(Nanoc::Core::Context, ContextUpdateBehavior)
|
98
99
|
define_behavior(Nanoc::Core::CodeSnippet, DataUpdateBehavior)
|
99
|
-
define_behavior(Nanoc::Core::IdentifiableCollection,
|
100
|
+
define_behavior(Nanoc::Core::IdentifiableCollection, CollectionUpdateBehavior)
|
100
101
|
define_behavior(Nanoc::Core::Identifier, ToSUpdateBehavior)
|
101
102
|
define_behavior(Nanoc::Core::Item, DocumentUpdateBehavior)
|
102
103
|
define_behavior(Nanoc::Core::ItemRep, ItemRepUpdateBehavior)
|
@@ -194,7 +195,7 @@ module Nanoc
|
|
194
195
|
end
|
195
196
|
end
|
196
197
|
|
197
|
-
class
|
198
|
+
class CollectionUpdateBehavior < UpdateBehavior
|
198
199
|
def self.update(obj, digest)
|
199
200
|
obj.each do |el|
|
200
201
|
yield(el)
|
@@ -203,6 +204,13 @@ module Nanoc
|
|
203
204
|
end
|
204
205
|
end
|
205
206
|
|
207
|
+
class SetUpdateBehavior < CollectionUpdateBehavior
|
208
|
+
def self.update(obj, digest)
|
209
|
+
# Similar to CollectionUpdateBehavior, but sorted for consistency.
|
210
|
+
super(obj.sort { |a, b| (a <=> b) || 0 }, digest)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
206
214
|
class HashUpdateBehavior < UpdateBehavior
|
207
215
|
def self.update(obj, digest)
|
208
216
|
obj.each do |key, value|
|
@@ -25,6 +25,7 @@ module Nanoc
|
|
25
25
|
Maybe = Ignorer.instance
|
26
26
|
None = Ignorer.instance
|
27
27
|
ArrayOf = Ignorer.instance
|
28
|
+
SetOf = Ignorer.instance
|
28
29
|
Or = Ignorer.instance
|
29
30
|
Func = Ignorer.instance
|
30
31
|
RespondTo = Ignorer.instance
|
@@ -96,8 +97,7 @@ module Nanoc
|
|
96
97
|
false
|
97
98
|
end
|
98
99
|
|
99
|
-
|
100
|
-
@_contracts_support__should_enable = contracts_loadable && !RUBY_VERSION.start_with?('3') && !ENV.key?('DISABLE_CONTRACTS')
|
100
|
+
@_contracts_support__should_enable = contracts_loadable && !ENV.key?('DISABLE_CONTRACTS')
|
101
101
|
|
102
102
|
if @_contracts_support__should_enable
|
103
103
|
# FIXME: ugly
|
@@ -10,9 +10,39 @@ module Nanoc
|
|
10
10
|
attr_reader :raw_content
|
11
11
|
|
12
12
|
# TODO: Split raw_content for documents and collections
|
13
|
-
C_RAW_CONTENT =
|
14
|
-
|
15
|
-
|
13
|
+
C_RAW_CONTENT =
|
14
|
+
C::Or[
|
15
|
+
C::SetOf[C::Or[String, Regexp]],
|
16
|
+
C::ArrayOf[C::Or[String, Regexp]],
|
17
|
+
C::Bool
|
18
|
+
]
|
19
|
+
|
20
|
+
C_ATTR =
|
21
|
+
C::Or[
|
22
|
+
C::SetOf[
|
23
|
+
C::Or[
|
24
|
+
Symbol, # any value
|
25
|
+
[Symbol, C::Any] # pair (specific value)
|
26
|
+
],
|
27
|
+
],
|
28
|
+
C::ArrayOf[
|
29
|
+
C::Or[
|
30
|
+
Symbol, # any value
|
31
|
+
[Symbol, C::Any] # pair (specific value)
|
32
|
+
],
|
33
|
+
],
|
34
|
+
C::Bool
|
35
|
+
]
|
36
|
+
|
37
|
+
C_ARGS =
|
38
|
+
C::KeywordArgs[
|
39
|
+
raw_content: C::Optional[C_RAW_CONTENT],
|
40
|
+
attributes: C::Optional[C_ATTR],
|
41
|
+
compiled_content: C::Optional[C::Bool],
|
42
|
+
path: C::Optional[C::Bool]
|
43
|
+
]
|
44
|
+
|
45
|
+
contract C_ARGS => C::Any
|
16
46
|
def initialize(raw_content: false, attributes: false, compiled_content: false, path: false)
|
17
47
|
@compiled_content = compiled_content
|
18
48
|
@path = path
|
@@ -21,7 +51,7 @@ module Nanoc
|
|
21
51
|
case attributes
|
22
52
|
when Set
|
23
53
|
attributes
|
24
|
-
when
|
54
|
+
when Array
|
25
55
|
Set.new(attributes)
|
26
56
|
else
|
27
57
|
attributes
|
@@ -31,7 +61,7 @@ module Nanoc
|
|
31
61
|
case raw_content
|
32
62
|
when Set
|
33
63
|
raw_content
|
34
|
-
when
|
64
|
+
when Array
|
35
65
|
Set.new(raw_content)
|
36
66
|
else
|
37
67
|
raw_content
|
@@ -46,6 +76,23 @@ module Nanoc
|
|
46
76
|
s << (attributes? ? 'a' : '_')
|
47
77
|
s << (compiled_content? ? 'c' : '_')
|
48
78
|
s << (path? ? 'p' : '_')
|
79
|
+
|
80
|
+
if @raw_content.is_a?(Set)
|
81
|
+
@raw_content.each do |elem|
|
82
|
+
s << '; raw_content('
|
83
|
+
s << elem.inspect
|
84
|
+
s << ')'
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
if @attributes.is_a?(Set)
|
89
|
+
@attributes.each do |elem|
|
90
|
+
s << '; attr('
|
91
|
+
s << elem.inspect
|
92
|
+
s << ')'
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
49
96
|
s << ')'
|
50
97
|
end
|
51
98
|
end
|
@@ -63,7 +110,7 @@ module Nanoc
|
|
63
110
|
contract C::None => C::Bool
|
64
111
|
def raw_content?
|
65
112
|
case @raw_content
|
66
|
-
when
|
113
|
+
when Set
|
67
114
|
@raw_content.any?
|
68
115
|
else
|
69
116
|
@raw_content
|
@@ -73,7 +120,7 @@ module Nanoc
|
|
73
120
|
contract C::None => C::Bool
|
74
121
|
def attributes?
|
75
122
|
case @attributes
|
76
|
-
when
|
123
|
+
when Set
|
77
124
|
@attributes.any?
|
78
125
|
else
|
79
126
|
@attributes
|
@@ -136,6 +183,15 @@ module Nanoc
|
|
136
183
|
end
|
137
184
|
end
|
138
185
|
|
186
|
+
def attribute_keys
|
187
|
+
case @attributes
|
188
|
+
when Enumerable
|
189
|
+
@attributes.map { |a| a.is_a?(Array) ? a.first : a }
|
190
|
+
else
|
191
|
+
[]
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
139
195
|
contract C::None => Hash
|
140
196
|
def to_h
|
141
197
|
{
|
@@ -6,11 +6,36 @@ module Nanoc
|
|
6
6
|
class DependencyStore < ::Nanoc::Core::Store
|
7
7
|
include Nanoc::Core::ContractsSupport
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
C_RAW_CONTENT =
|
10
|
+
C::Or[
|
11
|
+
C::ArrayOf[C::Or[String, Regexp]],
|
12
|
+
C::Bool
|
13
|
+
]
|
14
|
+
|
15
|
+
C_ATTR =
|
16
|
+
C::Or[
|
17
|
+
C::ArrayOf[Symbol],
|
18
|
+
C::HashOf[Symbol => C::Any],
|
19
|
+
C::Bool
|
20
|
+
]
|
21
|
+
|
22
|
+
C_KEYWORD_PROPS =
|
23
|
+
C::KeywordArgs[
|
24
|
+
raw_content: C::Optional[C_RAW_CONTENT],
|
25
|
+
attributes: C::Optional[C_ATTR],
|
26
|
+
compiled_content: C::Optional[C::Bool],
|
27
|
+
path: C::Optional[C::Bool]
|
28
|
+
]
|
29
|
+
|
12
30
|
C_OBJ_SRC = Nanoc::Core::Item
|
13
|
-
|
31
|
+
|
32
|
+
C_OBJ_DST =
|
33
|
+
C::Or[
|
34
|
+
Nanoc::Core::Item,
|
35
|
+
Nanoc::Core::Layout,
|
36
|
+
Nanoc::Core::Configuration,
|
37
|
+
Nanoc::Core::IdentifiableCollection
|
38
|
+
]
|
14
39
|
|
15
40
|
attr_reader :items
|
16
41
|
attr_reader :layouts
|
@@ -116,11 +141,16 @@ module Nanoc
|
|
116
141
|
src_ref = obj2ref(src)
|
117
142
|
dst_ref = obj2ref(dst)
|
118
143
|
|
119
|
-
|
144
|
+
# Convert attributes into key-value pairs, if necessary
|
145
|
+
if attributes.is_a?(Hash)
|
146
|
+
attributes = attributes.to_a
|
147
|
+
end
|
148
|
+
|
149
|
+
existing_props = @graph.props_for(dst_ref, src_ref)
|
120
150
|
new_props = Nanoc::Core::DependencyProps.new(raw_content: raw_content, attributes: attributes, compiled_content: compiled_content, path: path)
|
121
|
-
props = existing_props.merge(new_props)
|
151
|
+
props = existing_props ? existing_props.merge(new_props) : new_props
|
122
152
|
|
123
|
-
@graph.add_edge(dst_ref, src_ref, props: props
|
153
|
+
@graph.add_edge(dst_ref, src_ref, props: props)
|
124
154
|
end
|
125
155
|
|
126
156
|
def add_vertex_for(obj)
|
@@ -162,11 +192,12 @@ module Nanoc
|
|
162
192
|
refs.map { |r| ref2obj(r) }
|
163
193
|
end
|
164
194
|
|
195
|
+
# TODO: Return not a Hash, but a DependencyProps instead
|
165
196
|
def props_for(from, to)
|
166
|
-
props = @graph.props_for(obj2ref(from), obj2ref(to))
|
197
|
+
props = @graph.props_for(obj2ref(from), obj2ref(to))
|
167
198
|
|
168
|
-
if props
|
169
|
-
props
|
199
|
+
if props&.active&.any?
|
200
|
+
props.to_h
|
170
201
|
else
|
171
202
|
{ raw_content: true, attributes: true, compiled_content: true, path: true }
|
172
203
|
end
|
@@ -174,7 +205,7 @@ module Nanoc
|
|
174
205
|
|
175
206
|
def data
|
176
207
|
{
|
177
|
-
edges: @graph.edges,
|
208
|
+
edges: @graph.edges.map { |arr| [arr[0], arr[1], arr[2].to_h] },
|
178
209
|
vertices: @graph.vertices,
|
179
210
|
}
|
180
211
|
end
|
@@ -193,8 +224,11 @@ module Nanoc
|
|
193
224
|
# Load edges
|
194
225
|
new_data[:edges].each do |edge|
|
195
226
|
from_index, to_index, props = *edge
|
196
|
-
|
197
|
-
|
227
|
+
|
228
|
+
from = from_index && previous_refs[from_index]
|
229
|
+
to = to_index && previous_refs[to_index]
|
230
|
+
props = Nanoc::Core::DependencyProps.new(**props)
|
231
|
+
|
198
232
|
@graph.add_edge(from, to, props: props)
|
199
233
|
end
|
200
234
|
|
@@ -16,13 +16,14 @@ module Nanoc
|
|
16
16
|
|
17
17
|
C_RAW_CONTENT =
|
18
18
|
C::Or[
|
19
|
-
C::
|
19
|
+
C::ArrayOf[C::Or[String, Regexp]],
|
20
20
|
C::Bool
|
21
21
|
]
|
22
22
|
|
23
23
|
C_ATTR =
|
24
24
|
C::Or[
|
25
|
-
C::
|
25
|
+
C::ArrayOf[Symbol],
|
26
|
+
C::HashOf[Symbol => C::Any],
|
26
27
|
C::Bool
|
27
28
|
]
|
28
29
|
|
data/lib/nanoc/core/feature.rb
CHANGED
@@ -69,6 +69,34 @@ module Nanoc
|
|
69
69
|
@objects.find_all(arg).map { |i| view_class.new(i, @context) }
|
70
70
|
end
|
71
71
|
|
72
|
+
# Finds all objects that have the given attribute key/value pair.
|
73
|
+
#
|
74
|
+
# @example
|
75
|
+
#
|
76
|
+
# @items.where(kind: 'article')
|
77
|
+
# @items.where(kind: 'article', year: 2020)
|
78
|
+
#
|
79
|
+
# @return [Enumerable]
|
80
|
+
def where(**hash)
|
81
|
+
unless Nanoc::Core::Feature.enabled?(Nanoc::Core::Feature::WHERE)
|
82
|
+
raise(
|
83
|
+
Nanoc::Core::TrivialError,
|
84
|
+
'#where is experimental, and not yet available unless the corresponding feature flag is turned on. Set the `NANOC_FEATURES` environment variable to `where` to enable its usage. (Alternatively, set the environment variable to `all` to turn on all feature flags.)',
|
85
|
+
)
|
86
|
+
end
|
87
|
+
|
88
|
+
@context.dependency_tracker.bounce(_unwrap, attributes: hash)
|
89
|
+
|
90
|
+
# IDEA: Nanoc could remember (from the previous compilation) how many
|
91
|
+
# times #where is called with a given attribute key, and memoize the
|
92
|
+
# key-to-identifiers list.
|
93
|
+
found_objects = @objects.select do |i|
|
94
|
+
hash.all? { |k, v| i.attributes[k] == v }
|
95
|
+
end
|
96
|
+
|
97
|
+
found_objects.map { |i| view_class.new(i, @context) }
|
98
|
+
end
|
99
|
+
|
72
100
|
# @overload [](string)
|
73
101
|
#
|
74
102
|
# Finds the object whose identifier matches the given string.
|
@@ -4,13 +4,36 @@ module Nanoc
|
|
4
4
|
module Core
|
5
5
|
# @api private
|
6
6
|
class Instrumentor
|
7
|
+
@enabled = false
|
8
|
+
|
9
|
+
def self.enable
|
10
|
+
if block_given?
|
11
|
+
begin
|
12
|
+
enable
|
13
|
+
yield
|
14
|
+
ensure
|
15
|
+
disable
|
16
|
+
end
|
17
|
+
else
|
18
|
+
@enabled = true
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.disable
|
23
|
+
@enabled = false
|
24
|
+
end
|
25
|
+
|
7
26
|
def self.call(key, *args)
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
27
|
+
return yield unless @enabled
|
28
|
+
|
29
|
+
begin
|
30
|
+
stopwatch = DDMetrics::Stopwatch.new
|
31
|
+
stopwatch.start
|
32
|
+
yield
|
33
|
+
ensure
|
34
|
+
stopwatch.stop
|
35
|
+
Nanoc::Core::NotificationCenter.post(key, stopwatch.duration, *args)
|
36
|
+
end
|
14
37
|
end
|
15
38
|
end
|
16
39
|
end
|
@@ -7,14 +7,13 @@ module Nanoc
|
|
7
7
|
# @api private
|
8
8
|
class OutdatednessChecker
|
9
9
|
class Basic
|
10
|
-
|
11
|
-
|
12
|
-
# include Nanoc::Core::ContractsSupport
|
10
|
+
include Nanoc::Core::ContractsSupport
|
13
11
|
|
14
12
|
Rules = Nanoc::Core::OutdatednessRules
|
15
13
|
|
16
14
|
RULES_FOR_ITEM_REP =
|
17
15
|
[
|
16
|
+
Rules::ItemAdded,
|
18
17
|
Rules::RulesModified,
|
19
18
|
Rules::ContentModified,
|
20
19
|
Rules::AttributesModified,
|
@@ -25,6 +24,7 @@ module Nanoc
|
|
25
24
|
|
26
25
|
RULES_FOR_LAYOUT =
|
27
26
|
[
|
27
|
+
Rules::LayoutAdded,
|
28
28
|
Rules::RulesModified,
|
29
29
|
Rules::ContentModified,
|
30
30
|
Rules::AttributesModified,
|
@@ -36,48 +36,41 @@ module Nanoc
|
|
36
36
|
Rules::AttributesModified,
|
37
37
|
].freeze
|
38
38
|
|
39
|
-
|
40
|
-
[
|
41
|
-
Rules::ItemCollectionExtended,
|
42
|
-
].freeze
|
43
|
-
|
44
|
-
RULES_FOR_LAYOUT_COLLECTION =
|
45
|
-
[
|
46
|
-
Rules::LayoutCollectionExtended,
|
47
|
-
].freeze
|
48
|
-
|
49
|
-
# C_OBJ_MAYBE_REP = C::Or[Nanoc::Core::Item, Nanoc::Core::ItemRep, Nanoc::Core::Configuration, Nanoc::Core::Layout, Nanoc::Core::ItemCollection, Nanoc::Core::LayoutCollection]
|
39
|
+
C_OBJ_MAYBE_REP = C::Or[Nanoc::Core::Item, Nanoc::Core::ItemRep, Nanoc::Core::Configuration, Nanoc::Core::Layout, Nanoc::Core::ItemCollection, Nanoc::Core::LayoutCollection]
|
50
40
|
|
51
|
-
|
41
|
+
contract C::KeywordArgs[outdatedness_checker: OutdatednessChecker, reps: Nanoc::Core::ItemRepRepo] => C::Any
|
52
42
|
def initialize(outdatedness_checker:, reps:)
|
53
43
|
@outdatedness_checker = outdatedness_checker
|
54
44
|
@reps = reps
|
45
|
+
|
46
|
+
# Memoize
|
47
|
+
@_outdatedness_status_for = {}
|
55
48
|
end
|
56
49
|
|
57
|
-
|
50
|
+
contract C_OBJ_MAYBE_REP => C::Maybe[Nanoc::Core::OutdatednessStatus]
|
58
51
|
def outdatedness_status_for(obj)
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
52
|
+
@_outdatedness_status_for[obj] ||=
|
53
|
+
case obj
|
54
|
+
when Nanoc::Core::ItemRep
|
55
|
+
apply_rules(RULES_FOR_ITEM_REP, obj)
|
56
|
+
when Nanoc::Core::Item
|
57
|
+
apply_rules_multi(RULES_FOR_ITEM_REP, @reps[obj])
|
58
|
+
when Nanoc::Core::Layout
|
59
|
+
apply_rules(RULES_FOR_LAYOUT, obj)
|
60
|
+
when Nanoc::Core::Configuration
|
61
|
+
apply_rules(RULES_FOR_CONFIG, obj)
|
62
|
+
when Nanoc::Core::ItemCollection, Nanoc::Core::LayoutCollection
|
63
|
+
# Collections are never outdated. Objects inside them might be,
|
64
|
+
# however.
|
65
|
+
apply_rules([], obj)
|
66
|
+
else
|
67
|
+
raise Nanoc::Core::Errors::InternalInconsistency, "do not know how to check outdatedness of #{obj.inspect}"
|
68
|
+
end
|
75
69
|
end
|
76
|
-
memo_wise :outdatedness_status_for
|
77
70
|
|
78
71
|
private
|
79
72
|
|
80
|
-
|
73
|
+
contract C::ArrayOf[Class], C_OBJ_MAYBE_REP, Nanoc::Core::OutdatednessStatus => C::Maybe[Nanoc::Core::OutdatednessStatus]
|
81
74
|
def apply_rules(rules, obj, status = Nanoc::Core::OutdatednessStatus.new)
|
82
75
|
rules.inject(status) do |acc, rule|
|
83
76
|
if acc.useful_to_apply?(rule)
|
@@ -93,15 +86,13 @@ module Nanoc
|
|
93
86
|
end
|
94
87
|
end
|
95
88
|
|
96
|
-
|
89
|
+
contract C::ArrayOf[Class], C::ArrayOf[C_OBJ_MAYBE_REP] => C::Maybe[Nanoc::Core::OutdatednessStatus]
|
97
90
|
def apply_rules_multi(rules, objs)
|
98
91
|
objs.inject(Nanoc::Core::OutdatednessStatus.new) { |acc, elem| apply_rules(rules, elem, acc) }
|
99
92
|
end
|
100
93
|
end
|
101
94
|
|
102
|
-
|
103
|
-
|
104
|
-
# include Nanoc::Core::ContractsSupport
|
95
|
+
include Nanoc::Core::ContractsSupport
|
105
96
|
|
106
97
|
attr_reader :checksum_store
|
107
98
|
attr_reader :checksums
|
@@ -112,11 +103,11 @@ module Nanoc
|
|
112
103
|
|
113
104
|
Reasons = Nanoc::Core::OutdatednessReasons
|
114
105
|
|
115
|
-
|
116
|
-
|
117
|
-
|
106
|
+
C_OBJ = C::Or[Nanoc::Core::Item, Nanoc::Core::ItemRep, Nanoc::Core::Configuration, Nanoc::Core::Layout, Nanoc::Core::ItemCollection]
|
107
|
+
C_ITEM_OR_REP = C::Or[Nanoc::Core::Item, Nanoc::Core::ItemRep]
|
108
|
+
C_ACTION_SEQUENCES = C::HashOf[C_OBJ => Nanoc::Core::ActionSequence]
|
118
109
|
|
119
|
-
|
110
|
+
contract C::KeywordArgs[site: Nanoc::Core::Site, checksum_store: Nanoc::Core::ChecksumStore, checksums: Nanoc::Core::ChecksumCollection, dependency_store: Nanoc::Core::DependencyStore, action_sequence_store: Nanoc::Core::ActionSequenceStore, action_sequences: C_ACTION_SEQUENCES, reps: Nanoc::Core::ItemRepRepo] => C::Any
|
120
111
|
def initialize(site:, checksum_store:, checksums:, dependency_store:, action_sequence_store:, action_sequences:, reps:)
|
121
112
|
@site = site
|
122
113
|
@checksum_store = checksum_store
|
@@ -133,12 +124,12 @@ module Nanoc
|
|
133
124
|
@action_sequences.fetch(rep)
|
134
125
|
end
|
135
126
|
|
136
|
-
|
127
|
+
contract C_OBJ => C::Bool
|
137
128
|
def outdated?(obj)
|
138
129
|
outdatedness_reasons_for(obj).any?
|
139
130
|
end
|
140
131
|
|
141
|
-
|
132
|
+
contract C_OBJ => C::IterOf[Reasons::Generic]
|
142
133
|
def outdatedness_reasons_for(obj)
|
143
134
|
reasons = basic.outdatedness_status_for(obj).reasons
|
144
135
|
if reasons.any?
|
@@ -152,12 +143,12 @@ module Nanoc
|
|
152
143
|
|
153
144
|
private
|
154
145
|
|
155
|
-
|
146
|
+
contract C::None => Basic
|
156
147
|
def basic
|
157
148
|
@_basic ||= Basic.new(outdatedness_checker: self, reps: @reps)
|
158
149
|
end
|
159
150
|
|
160
|
-
|
151
|
+
contract C_OBJ, Hamster::Set => C::Bool
|
161
152
|
def outdated_due_to_dependencies?(obj, processed = Hamster::Set.new)
|
162
153
|
# Convert from rep to item if necessary
|
163
154
|
obj = obj.item if obj.is_a?(Nanoc::Core::ItemRep)
|
@@ -189,33 +180,113 @@ module Nanoc
|
|
189
180
|
is_outdated
|
190
181
|
end
|
191
182
|
|
192
|
-
|
183
|
+
contract Nanoc::Core::Dependency => C::Bool
|
193
184
|
def dependency_causes_outdatedness?(dependency)
|
194
|
-
|
195
|
-
|
196
|
-
|
185
|
+
case dependency.from
|
186
|
+
when nil
|
187
|
+
true
|
188
|
+
when Nanoc::Core::ItemCollection, Nanoc::Core::LayoutCollection
|
189
|
+
all_objects = dependency.from
|
190
|
+
|
191
|
+
raw_content_prop_causes_outdatedness?(all_objects, dependency.props.raw_content) ||
|
192
|
+
attributes_prop_causes_outdatedness?(all_objects, dependency.props.attributes)
|
193
|
+
else
|
194
|
+
status = basic.outdatedness_status_for(dependency.from)
|
197
195
|
|
198
|
-
|
199
|
-
|
200
|
-
active.delete(:raw_content) if raw_content_unaffected?(status, dependency)
|
196
|
+
active = status.props.active & dependency.props.active
|
197
|
+
active.delete(:attributes) if attributes_unaffected?(status, dependency)
|
201
198
|
|
202
|
-
|
199
|
+
active.any?
|
200
|
+
end
|
203
201
|
end
|
204
202
|
|
205
203
|
def attributes_unaffected?(status, dependency)
|
206
204
|
reason = status.reasons.find { |r| r.is_a?(Nanoc::Core::OutdatednessReasons::AttributesModified) }
|
207
|
-
reason && dependency.props.
|
205
|
+
reason && dependency.props.attribute_keys.any? && (dependency.props.attribute_keys & reason.attributes).empty?
|
208
206
|
end
|
209
207
|
|
210
|
-
def
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
208
|
+
def raw_content_prop_causes_outdatedness?(objects, raw_content_prop)
|
209
|
+
return false unless raw_content_prop
|
210
|
+
|
211
|
+
matching_objects =
|
212
|
+
case raw_content_prop
|
213
|
+
when true
|
214
|
+
# If the `raw_content` dependency prop is `true`, then this is a
|
215
|
+
# dependency on all *objects* (items or layouts).
|
216
|
+
objects
|
217
|
+
when Enumerable
|
218
|
+
# If the `raw_content` dependency prop is a collection, then this
|
219
|
+
# is a dependency on specific objects, given by the patterns.
|
220
|
+
patterns = raw_content_prop.map { |r| Nanoc::Core::Pattern.from(r) }
|
221
|
+
patterns.flat_map { |pat| objects.select { |obj| pat.match?(obj.identifier) } }
|
222
|
+
else
|
223
|
+
raise(
|
224
|
+
Nanoc::Core::Errors::InternalInconsistency,
|
225
|
+
"Unexpected type of raw_content: #{raw_content_prop.inspect}",
|
226
|
+
)
|
227
|
+
end
|
228
|
+
|
229
|
+
# For all objects matching the `raw_content` dependency prop:
|
230
|
+
# If the object is outdated because it is newly added,
|
231
|
+
# then this dependency causes outdatedness.
|
232
|
+
#
|
233
|
+
# Note that these objects might be modified but *not* newly added,
|
234
|
+
# in which case this dependency will *not* cause outdatedness.
|
235
|
+
# However, when the object is used later (e.g. attributes are
|
236
|
+
# accessed), then another dependency will exist that will cause
|
237
|
+
# outdatedness.
|
238
|
+
matching_objects.any? do |obj|
|
239
|
+
status = basic.outdatedness_status_for(obj)
|
240
|
+
status.reasons.any? { |r| Nanoc::Core::OutdatednessReasons::DocumentAdded == r }
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
def attributes_prop_causes_outdatedness?(objects, attributes_prop)
|
245
|
+
return false unless attributes_prop
|
246
|
+
|
247
|
+
unless attributes_prop.is_a?(Set)
|
248
|
+
raise(
|
249
|
+
Nanoc::Core::Errors::InternalInconsistency,
|
250
|
+
'expected attributes_prop to be a Set',
|
251
|
+
)
|
252
|
+
end
|
253
|
+
|
254
|
+
pairs = attributes_prop.select { |a| a.is_a?(Array) }.to_h
|
255
|
+
|
256
|
+
unless pairs.any?
|
257
|
+
raise(
|
258
|
+
Nanoc::Core::Errors::InternalInconsistency,
|
259
|
+
'expected attributes_prop not to be empty',
|
260
|
+
)
|
261
|
+
end
|
262
|
+
|
263
|
+
dep_checksums = pairs.transform_values { |value| Nanoc::Core::Checksummer.calc(value) }
|
264
|
+
|
265
|
+
objects.any? do |object|
|
266
|
+
# Find old and new attribute checksums for the object
|
267
|
+
old_object_checksums = checksum_store.attributes_checksum_for(object)
|
268
|
+
next false unless old_object_checksums
|
269
|
+
|
270
|
+
new_object_checksums = checksums.attributes_checksum_for(object)
|
271
|
+
|
272
|
+
# Ignore any attribute not mentioned in the dependency
|
273
|
+
old_object_checksums = old_object_checksums.select { |k, _v| dep_checksums.key?(k) }
|
274
|
+
new_object_checksums = new_object_checksums.select { |k, _v| dep_checksums.key?(k) }
|
275
|
+
|
276
|
+
dep_checksums.any? do |key, dep_value|
|
277
|
+
# Get old and new checksum for this particular attribute
|
278
|
+
old_value = old_object_checksums[key]
|
279
|
+
new_value = new_object_checksums[key]
|
280
|
+
|
281
|
+
# If either the old or new vale match the value in the dependency,
|
282
|
+
# then a potential change is relevant to us, and can cause
|
283
|
+
# outdatedness.
|
284
|
+
is_match = [old_value, new_value].include?(dep_value)
|
285
|
+
|
286
|
+
is_changed = old_value != new_value
|
287
|
+
|
288
|
+
is_match && is_changed
|
289
|
+
end
|
219
290
|
end
|
220
291
|
end
|
221
292
|
end
|
@@ -18,6 +18,10 @@ module Nanoc
|
|
18
18
|
# @param [String] message The descriptive message for this outdatedness
|
19
19
|
# reason
|
20
20
|
def initialize(message, props = Nanoc::Core::DependencyProps.new)
|
21
|
+
# TODO: Replace `DependencyProps` with its own `OutdatednessProps`
|
22
|
+
# type. For `OutdatednessProps`, the only values are true/false;
|
23
|
+
# giving a collection for `raw_content` makes no sense (anymore).
|
24
|
+
|
21
25
|
@message = message
|
22
26
|
@props = props
|
23
27
|
end
|
@@ -42,30 +46,16 @@ module Nanoc
|
|
42
46
|
Nanoc::Core::DependencyProps.new(compiled_content: true, path: true),
|
43
47
|
)
|
44
48
|
|
49
|
+
DocumentAdded = Generic.new(
|
50
|
+
'The item or layout is newly added to the site.',
|
51
|
+
Nanoc::Core::DependencyProps.new, # NOTE: empty props, because they’re not relevant
|
52
|
+
)
|
53
|
+
|
45
54
|
ContentModified = Generic.new(
|
46
55
|
'The content of this item has been modified since the last time the site was compiled.',
|
47
56
|
Nanoc::Core::DependencyProps.new(raw_content: true, compiled_content: true),
|
48
57
|
)
|
49
58
|
|
50
|
-
class DocumentCollectionExtended < Generic
|
51
|
-
attr_reader :objects
|
52
|
-
|
53
|
-
def initialize(objects)
|
54
|
-
super(
|
55
|
-
'New items/layouts have been added to the site.',
|
56
|
-
Nanoc::Core::DependencyProps.new(raw_content: true),
|
57
|
-
)
|
58
|
-
|
59
|
-
@objects = objects
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
class ItemCollectionExtended < DocumentCollectionExtended
|
64
|
-
end
|
65
|
-
|
66
|
-
class LayoutCollectionExtended < DocumentCollectionExtended
|
67
|
-
end
|
68
|
-
|
69
59
|
class AttributesModified < Generic
|
70
60
|
attr_reader :attributes
|
71
61
|
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Nanoc
|
4
|
+
module Core
|
5
|
+
module OutdatednessRules
|
6
|
+
class ItemAdded < Nanoc::Core::OutdatednessRule
|
7
|
+
affects_props :raw_content
|
8
|
+
|
9
|
+
contract Nanoc::Core::ItemRep, C::Named['Nanoc::Core::OutdatednessChecker'] => C::Maybe[Nanoc::Core::OutdatednessReasons::Generic]
|
10
|
+
def apply(obj, outdatedness_checker)
|
11
|
+
if outdatedness_checker.dependency_store.new_items.include?(obj.item)
|
12
|
+
Nanoc::Core::OutdatednessReasons::DocumentAdded
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Nanoc
|
4
|
+
module Core
|
5
|
+
module OutdatednessRules
|
6
|
+
class LayoutAdded < Nanoc::Core::OutdatednessRule
|
7
|
+
affects_props :raw_content
|
8
|
+
|
9
|
+
contract Nanoc::Core::Layout, C::Named['Nanoc::Core::OutdatednessChecker'] => C::Maybe[Nanoc::Core::OutdatednessReasons::Generic]
|
10
|
+
def apply(obj, outdatedness_checker)
|
11
|
+
if outdatedness_checker.dependency_store.new_layouts.include?(obj)
|
12
|
+
Nanoc::Core::OutdatednessReasons::DocumentAdded
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -6,11 +6,31 @@ module Nanoc
|
|
6
6
|
class NotWritten < Nanoc::Core::OutdatednessRule
|
7
7
|
affects_props :raw_content, :attributes, :compiled_content, :path
|
8
8
|
|
9
|
-
def apply(obj,
|
10
|
-
if obj.raw_paths.values.flatten.compact.any? { |fn| !
|
9
|
+
def apply(obj, outdatedness_checker)
|
10
|
+
if obj.raw_paths.values.flatten.compact.any? { |fn| !exist?(fn, outdatedness_checker) }
|
11
11
|
Nanoc::Core::OutdatednessReasons::NotWritten
|
12
12
|
end
|
13
13
|
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def exist?(fn, outdatedness_checker)
|
18
|
+
all(outdatedness_checker).include?(fn)
|
19
|
+
end
|
20
|
+
|
21
|
+
def all(outdatedness_checker)
|
22
|
+
# NOTE: Cached per outdatedness checker, so that unrelated invocations
|
23
|
+
# later on don’t reuse an old cache.
|
24
|
+
|
25
|
+
@all ||= {}
|
26
|
+
@all[outdatedness_checker] ||= Set.new(
|
27
|
+
Dir.glob("#{site_root(outdatedness_checker)}/**/*", File::FNM_DOTMATCH),
|
28
|
+
)
|
29
|
+
end
|
30
|
+
|
31
|
+
def site_root(outdatedness_checker)
|
32
|
+
outdatedness_checker.site.config.output_dir
|
33
|
+
end
|
14
34
|
end
|
15
35
|
end
|
16
36
|
end
|
data/lib/nanoc/core/store.rb
CHANGED
data/lib/nanoc/core/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nanoc-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.12.
|
4
|
+
version: 4.12.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Denis Defreyne
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-10-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -261,8 +261,8 @@ files:
|
|
261
261
|
- lib/nanoc/core/outdatedness_rules/attributes_modified.rb
|
262
262
|
- lib/nanoc/core/outdatedness_rules/code_snippets_modified.rb
|
263
263
|
- lib/nanoc/core/outdatedness_rules/content_modified.rb
|
264
|
-
- lib/nanoc/core/outdatedness_rules/
|
265
|
-
- lib/nanoc/core/outdatedness_rules/
|
264
|
+
- lib/nanoc/core/outdatedness_rules/item_added.rb
|
265
|
+
- lib/nanoc/core/outdatedness_rules/layout_added.rb
|
266
266
|
- lib/nanoc/core/outdatedness_rules/not_written.rb
|
267
267
|
- lib/nanoc/core/outdatedness_rules/rules_modified.rb
|
268
268
|
- lib/nanoc/core/outdatedness_rules/uses_always_outdated_filter.rb
|
@@ -315,7 +315,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
315
315
|
- !ruby/object:Gem::Version
|
316
316
|
version: '0'
|
317
317
|
requirements: []
|
318
|
-
rubygems_version: 3.3.
|
318
|
+
rubygems_version: 3.3.22
|
319
319
|
signing_key:
|
320
320
|
specification_version: 4
|
321
321
|
summary: Core of Nanoc
|
@@ -1,20 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Nanoc
|
4
|
-
module Core
|
5
|
-
module OutdatednessRules
|
6
|
-
class ItemCollectionExtended < Nanoc::Core::OutdatednessRule
|
7
|
-
affects_props :raw_content
|
8
|
-
|
9
|
-
contract Nanoc::Core::ItemCollection, C::Named['Nanoc::Core::OutdatednessChecker'] => C::Maybe[Nanoc::Core::OutdatednessReasons::Generic]
|
10
|
-
def apply(_obj, outdatedness_checker)
|
11
|
-
new_items = outdatedness_checker.dependency_store.new_items
|
12
|
-
|
13
|
-
if new_items.any?
|
14
|
-
Nanoc::Core::OutdatednessReasons::ItemCollectionExtended.new(new_items)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
@@ -1,20 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Nanoc
|
4
|
-
module Core
|
5
|
-
module OutdatednessRules
|
6
|
-
class LayoutCollectionExtended < Nanoc::Core::OutdatednessRule
|
7
|
-
affects_props :raw_content
|
8
|
-
|
9
|
-
contract Nanoc::Core::LayoutCollection, C::Named['Nanoc::Core::OutdatednessChecker'] => C::Maybe[Nanoc::Core::OutdatednessReasons::Generic]
|
10
|
-
def apply(_obj, outdatedness_checker)
|
11
|
-
new_layouts = outdatedness_checker.dependency_store.new_layouts
|
12
|
-
|
13
|
-
if new_layouts.any?
|
14
|
-
Nanoc::Core::OutdatednessReasons::LayoutCollectionExtended.new(new_layouts)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|