nanoc-core 4.12.10 → 4.12.12
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/action_sequence.rb +1 -4
- data/lib/nanoc/core/action_sequence_builder.rb +8 -10
- data/lib/nanoc/core/basic_item_view.rb +1 -1
- data/lib/nanoc/core/basic_outdatedness_checker.rb +135 -0
- data/lib/nanoc/core/compilation_stages/determine_outdatedness.rb +6 -1
- data/lib/nanoc/core/compilation_stages/load_stores.rb +5 -12
- data/lib/nanoc/core/configuration.rb +15 -0
- data/lib/nanoc/core/errors.rb +1 -4
- data/lib/nanoc/core/executor.rb +2 -1
- data/lib/nanoc/core/identifiable_collection.rb +16 -33
- data/lib/nanoc/core/identifiable_collection_view.rb +54 -10
- data/lib/nanoc/core/item_collection.rb +0 -6
- data/lib/nanoc/core/item_rep.rb +18 -0
- data/lib/nanoc/core/item_rep_builder.rb +4 -4
- data/lib/nanoc/core/item_rep_selector.rb +89 -22
- data/lib/nanoc/core/layout_collection.rb +0 -6
- data/lib/nanoc/core/outdatedness_checker.rb +57 -119
- data/lib/nanoc/core/outdatedness_rules/attributes_modified.rb +6 -6
- data/lib/nanoc/core/outdatedness_rules/code_snippets_modified.rb +6 -6
- data/lib/nanoc/core/outdatedness_rules/content_modified.rb +3 -3
- data/lib/nanoc/core/outdatedness_rules/item_added.rb +3 -3
- data/lib/nanoc/core/outdatedness_rules/layout_added.rb +3 -3
- data/lib/nanoc/core/outdatedness_rules/not_written.rb +9 -9
- data/lib/nanoc/core/outdatedness_rules/rules_modified.rb +12 -10
- data/lib/nanoc/core/outdatedness_rules/uses_always_outdated_filter.rb +2 -2
- data/lib/nanoc/core/store.rb +56 -23
- data/lib/nanoc/core/version.rb +1 -1
- data/lib/nanoc/core.rb +8 -0
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5f5cba2759d0a1a3fed3b3ecd60b4d48fe74e4e47d9855f585731a52436b407c
|
4
|
+
data.tar.gz: cbe6128977455e8109c3991059521a6f97053eda60eccaa4597637c680f89a00
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 488a4b2b75e2a0a49f1a66f810003f5f11961e156013852cd97e1495bc1b66a0906b7efe1816a760127ed4759cd8926426d0dccc9776d12bb159ac15be3eabc6
|
7
|
+
data.tar.gz: 32cc8224e2aaf4abff07af2fb0401211f927bfa1e057c3641acdf043097df4554f4a70142cfcbac37f791f54dfefd196d10e0f48c7637d2692a94134b4850658
|
@@ -7,11 +7,9 @@ module Nanoc
|
|
7
7
|
include Enumerable
|
8
8
|
prepend MemoWise
|
9
9
|
|
10
|
-
attr_reader :item_rep
|
11
10
|
attr_reader :actions
|
12
11
|
|
13
|
-
def initialize(
|
14
|
-
@item_rep = item_rep
|
12
|
+
def initialize(actions: [])
|
15
13
|
@actions = actions
|
16
14
|
end
|
17
15
|
|
@@ -54,7 +52,6 @@ module Nanoc
|
|
54
52
|
# contract C::Func[Nanoc::Core::ProcessingAction => C::Any] => self
|
55
53
|
def map(&block)
|
56
54
|
self.class.new(
|
57
|
-
@item_rep,
|
58
55
|
actions: @actions.map(&block),
|
59
56
|
)
|
60
57
|
end
|
@@ -15,14 +15,13 @@ module Nanoc
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
-
def self.build
|
19
|
-
builder = new
|
18
|
+
def self.build
|
19
|
+
builder = new
|
20
20
|
yield(builder)
|
21
21
|
builder.action_sequence
|
22
22
|
end
|
23
23
|
|
24
|
-
def initialize
|
25
|
-
@item_rep = item_rep
|
24
|
+
def initialize
|
26
25
|
@actions = []
|
27
26
|
end
|
28
27
|
|
@@ -38,24 +37,23 @@ module Nanoc
|
|
38
37
|
self
|
39
38
|
end
|
40
39
|
|
41
|
-
|
42
|
-
|
43
|
-
will_add_snapshot(snapshot_name)
|
40
|
+
def add_snapshot(snapshot_name, path, rep)
|
41
|
+
will_add_snapshot(snapshot_name, rep)
|
44
42
|
@actions << Nanoc::Core::ProcessingActions::Snapshot.new([snapshot_name], path ? [path] : [])
|
45
43
|
self
|
46
44
|
end
|
47
45
|
|
48
46
|
contract C::None => Nanoc::Core::ActionSequence
|
49
47
|
def action_sequence
|
50
|
-
Nanoc::Core::ActionSequence.new(
|
48
|
+
Nanoc::Core::ActionSequence.new(actions: @actions)
|
51
49
|
end
|
52
50
|
|
53
51
|
private
|
54
52
|
|
55
|
-
def will_add_snapshot(name)
|
53
|
+
def will_add_snapshot(name, rep)
|
56
54
|
@_snapshot_names ||= Set.new
|
57
55
|
if @_snapshot_names.include?(name)
|
58
|
-
raise CannotCreateMultipleSnapshotsWithSameNameError.new(
|
56
|
+
raise CannotCreateMultipleSnapshotsWithSameNameError.new(rep, name)
|
59
57
|
else
|
60
58
|
@_snapshot_names << name
|
61
59
|
end
|
@@ -34,7 +34,7 @@ module Nanoc
|
|
34
34
|
parent_identifier = '/' + _unwrap.identifier.components[0..-2].join('/') + '/'
|
35
35
|
parent_identifier = '/' if parent_identifier == '//'
|
36
36
|
|
37
|
-
parent = @context.items
|
37
|
+
parent = @context.items.object_with_identifier(parent_identifier)
|
38
38
|
|
39
39
|
parent && self.class.new(parent, @context)
|
40
40
|
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Nanoc
|
4
|
+
module Core
|
5
|
+
class BasicOutdatednessChecker
|
6
|
+
include Nanoc::Core::ContractsSupport
|
7
|
+
|
8
|
+
attr_reader :site
|
9
|
+
attr_reader :checksum_store
|
10
|
+
attr_reader :checksums
|
11
|
+
attr_reader :dependency_store
|
12
|
+
attr_reader :action_sequence_store
|
13
|
+
attr_reader :action_sequences
|
14
|
+
|
15
|
+
Rules = Nanoc::Core::OutdatednessRules
|
16
|
+
|
17
|
+
RULES_FOR_ITEM_REP =
|
18
|
+
[
|
19
|
+
Rules::ItemAdded,
|
20
|
+
Rules::RulesModified,
|
21
|
+
Rules::ContentModified,
|
22
|
+
Rules::AttributesModified,
|
23
|
+
Rules::NotWritten,
|
24
|
+
Rules::CodeSnippetsModified,
|
25
|
+
Rules::UsesAlwaysOutdatedFilter,
|
26
|
+
].freeze
|
27
|
+
|
28
|
+
RULES_FOR_LAYOUT =
|
29
|
+
[
|
30
|
+
Rules::LayoutAdded,
|
31
|
+
Rules::RulesModified,
|
32
|
+
Rules::ContentModified,
|
33
|
+
Rules::AttributesModified,
|
34
|
+
Rules::UsesAlwaysOutdatedFilter,
|
35
|
+
].freeze
|
36
|
+
|
37
|
+
RULES_FOR_CONFIG =
|
38
|
+
[
|
39
|
+
Rules::AttributesModified,
|
40
|
+
].freeze
|
41
|
+
|
42
|
+
C_OBJ = C::Or[
|
43
|
+
Nanoc::Core::Item,
|
44
|
+
Nanoc::Core::ItemRep,
|
45
|
+
Nanoc::Core::Configuration,
|
46
|
+
Nanoc::Core::Layout,
|
47
|
+
Nanoc::Core::ItemCollection,
|
48
|
+
]
|
49
|
+
|
50
|
+
C_OBJ_MAYBE_REP = C::Or[
|
51
|
+
Nanoc::Core::Item,
|
52
|
+
Nanoc::Core::ItemRep,
|
53
|
+
Nanoc::Core::Configuration,
|
54
|
+
Nanoc::Core::Layout,
|
55
|
+
Nanoc::Core::ItemCollection,
|
56
|
+
Nanoc::Core::LayoutCollection,
|
57
|
+
]
|
58
|
+
|
59
|
+
C_ACTION_SEQUENCES = C::HashOf[C_OBJ => Nanoc::Core::ActionSequence]
|
60
|
+
|
61
|
+
contract C::KeywordArgs[
|
62
|
+
site: Nanoc::Core::Site,
|
63
|
+
checksum_store: Nanoc::Core::ChecksumStore,
|
64
|
+
checksums: Nanoc::Core::ChecksumCollection,
|
65
|
+
dependency_store: Nanoc::Core::DependencyStore,
|
66
|
+
action_sequence_store: Nanoc::Core::ActionSequenceStore,
|
67
|
+
action_sequences: C_ACTION_SEQUENCES,
|
68
|
+
reps: Nanoc::Core::ItemRepRepo,
|
69
|
+
] => C::Any
|
70
|
+
def initialize(site:, checksum_store:, checksums:, dependency_store:, action_sequence_store:, action_sequences:, reps:)
|
71
|
+
@reps = reps
|
72
|
+
@site = site
|
73
|
+
@checksum_store = checksum_store
|
74
|
+
@checksums = checksums
|
75
|
+
@dependency_store = dependency_store
|
76
|
+
@action_sequence_store = action_sequence_store
|
77
|
+
@action_sequences = action_sequences
|
78
|
+
|
79
|
+
# Memoize
|
80
|
+
@_outdatedness_status_for = {}
|
81
|
+
end
|
82
|
+
|
83
|
+
contract C_OBJ_MAYBE_REP => C::Maybe[Nanoc::Core::OutdatednessStatus]
|
84
|
+
def outdatedness_status_for(obj)
|
85
|
+
# TODO: remove memoization (no longer needed)
|
86
|
+
@_outdatedness_status_for[obj] ||=
|
87
|
+
case obj
|
88
|
+
when Nanoc::Core::ItemRep
|
89
|
+
apply_rules(RULES_FOR_ITEM_REP, obj)
|
90
|
+
when Nanoc::Core::Item
|
91
|
+
apply_rules_multi(RULES_FOR_ITEM_REP, @reps[obj])
|
92
|
+
when Nanoc::Core::Layout
|
93
|
+
apply_rules(RULES_FOR_LAYOUT, obj)
|
94
|
+
when Nanoc::Core::Configuration
|
95
|
+
apply_rules(RULES_FOR_CONFIG, obj)
|
96
|
+
when Nanoc::Core::ItemCollection, Nanoc::Core::LayoutCollection
|
97
|
+
# Collections are never outdated. Objects inside them might be,
|
98
|
+
# however.
|
99
|
+
apply_rules([], obj)
|
100
|
+
else
|
101
|
+
raise Nanoc::Core::Errors::InternalInconsistency, "do not know how to check outdatedness of #{obj.inspect}"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def action_sequence_for(rep)
|
106
|
+
@action_sequences.fetch(rep)
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
contract C::ArrayOf[Class], C_OBJ_MAYBE_REP, Nanoc::Core::OutdatednessStatus => C::Maybe[Nanoc::Core::OutdatednessStatus]
|
112
|
+
def apply_rules(rules, obj, status = Nanoc::Core::OutdatednessStatus.new)
|
113
|
+
rules.inject(status) do |acc, rule|
|
114
|
+
if acc.useful_to_apply?(rule)
|
115
|
+
reason = rule.instance.call(obj, self)
|
116
|
+
if reason
|
117
|
+
acc.update(reason)
|
118
|
+
else
|
119
|
+
acc
|
120
|
+
end
|
121
|
+
else
|
122
|
+
acc
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
contract C::ArrayOf[Class], C::ArrayOf[C_OBJ_MAYBE_REP] => C::Maybe[Nanoc::Core::OutdatednessStatus]
|
128
|
+
def apply_rules_multi(rules, objs)
|
129
|
+
objs.inject(Nanoc::Core::OutdatednessStatus.new) do |acc, elem|
|
130
|
+
apply_rules(rules, elem, acc)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -41,7 +41,12 @@ module Nanoc
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def outdated?(rep)
|
44
|
-
@outdatedness_store.include?(rep)
|
44
|
+
if @outdatedness_store.include?(rep)
|
45
|
+
# We determined previously that this rep is outdated.
|
46
|
+
true
|
47
|
+
else
|
48
|
+
!@outdatedness_checker.outdatedness_reasons_for(rep).empty?
|
49
|
+
end
|
45
50
|
end
|
46
51
|
end
|
47
52
|
end
|
@@ -16,18 +16,11 @@ module Nanoc
|
|
16
16
|
|
17
17
|
contract C::None => C::Any
|
18
18
|
def run
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
end
|
25
|
-
|
26
|
-
contract Nanoc::Core::Store => C::Any
|
27
|
-
def load_store(store)
|
28
|
-
Nanoc::Core::Instrumentor.call(:store_loaded, store.class) do
|
29
|
-
store.load
|
30
|
-
end
|
19
|
+
@checksum_store.load
|
20
|
+
@compiled_content_cache.load
|
21
|
+
@dependency_store.load
|
22
|
+
@action_sequence_store.load
|
23
|
+
@outdatedness_store.load
|
31
24
|
end
|
32
25
|
end
|
33
26
|
end
|
@@ -176,6 +176,21 @@ module Nanoc
|
|
176
176
|
"<#{self.class}>"
|
177
177
|
end
|
178
178
|
|
179
|
+
contract C::None => C::Num
|
180
|
+
def hash
|
181
|
+
[@dir, @env_name].hash
|
182
|
+
end
|
183
|
+
|
184
|
+
contract C::Any => C::Bool
|
185
|
+
def ==(other)
|
186
|
+
eql?(other)
|
187
|
+
end
|
188
|
+
|
189
|
+
contract C::Any => C::Bool
|
190
|
+
def eql?(other)
|
191
|
+
other.is_a?(self.class) && @dir == other.dir && @env_name == other.env_name
|
192
|
+
end
|
193
|
+
|
179
194
|
private
|
180
195
|
|
181
196
|
def make_absolute(path)
|
data/lib/nanoc/core/errors.rb
CHANGED
@@ -72,10 +72,7 @@ module Nanoc
|
|
72
72
|
# Error that is raised during site compilation when an item (directly or
|
73
73
|
# indirectly) includes its own item content, leading to endless recursion.
|
74
74
|
class DependencyCycle < ::Nanoc::Core::Error
|
75
|
-
def initialize(
|
76
|
-
start_idx = stack.index(stack.last)
|
77
|
-
cycle = stack[start_idx..-2]
|
78
|
-
|
75
|
+
def initialize(cycle)
|
79
76
|
msg_bits = []
|
80
77
|
msg_bits << 'The site cannot be compiled because there is a dependency cycle:'
|
81
78
|
msg_bits << ''
|
data/lib/nanoc/core/executor.rb
CHANGED
@@ -90,10 +90,11 @@ module Nanoc
|
|
90
90
|
|
91
91
|
def find_layout(arg)
|
92
92
|
req_id = arg.__nanoc_cleaned_identifier
|
93
|
-
layout = layouts
|
93
|
+
layout = layouts.object_with_identifier(req_id)
|
94
94
|
return layout if layout
|
95
95
|
|
96
96
|
if use_globs?
|
97
|
+
# TODO: use object_matching_glob instead
|
97
98
|
pat = Nanoc::Core::Pattern.from(arg)
|
98
99
|
layout = layouts.find { |l| pat.match?(l.identifier) }
|
99
100
|
return layout if layout
|
@@ -37,15 +37,6 @@ module Nanoc
|
|
37
37
|
super
|
38
38
|
end
|
39
39
|
|
40
|
-
# contract C::Any => C::Maybe[C::RespondTo[:identifier]]
|
41
|
-
def [](arg)
|
42
|
-
if frozen?
|
43
|
-
get_memoized(arg)
|
44
|
-
else
|
45
|
-
get_unmemoized(arg)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
40
|
# contract C::Any => C::IterOf[C::RespondTo[:identifier]]
|
50
41
|
def find_all(arg)
|
51
42
|
if frozen?
|
@@ -75,7 +66,6 @@ module Nanoc
|
|
75
66
|
self.class.new(@config, @objects.reject(&block))
|
76
67
|
end
|
77
68
|
|
78
|
-
# contract C::Any => C::Maybe[C::RespondTo[:identifier]]
|
79
69
|
def object_with_identifier(identifier)
|
80
70
|
if frozen?
|
81
71
|
@mapping[identifier.to_s]
|
@@ -84,26 +74,26 @@ module Nanoc
|
|
84
74
|
end
|
85
75
|
end
|
86
76
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
def get_unmemoized(arg)
|
91
|
-
case arg
|
92
|
-
when Nanoc::Core::Identifier
|
93
|
-
object_with_identifier(arg)
|
94
|
-
when String
|
95
|
-
object_with_identifier(arg) || object_matching_glob(arg)
|
96
|
-
when Regexp
|
97
|
-
find { |i| i.identifier.to_s =~ arg }
|
77
|
+
def object_matching_glob(glob)
|
78
|
+
if frozen?
|
79
|
+
object_matching_glob_memoized(glob)
|
98
80
|
else
|
99
|
-
|
81
|
+
object_matching_glob_unmemoized(glob)
|
100
82
|
end
|
101
83
|
end
|
102
84
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
85
|
+
protected
|
86
|
+
|
87
|
+
def object_matching_glob_memoized(glob)
|
88
|
+
object_matching_glob_unmemoized(glob)
|
89
|
+
end
|
90
|
+
memo_wise :object_matching_glob_memoized
|
91
|
+
|
92
|
+
def object_matching_glob_unmemoized(glob)
|
93
|
+
if use_globs?
|
94
|
+
pat = Pattern.from(glob)
|
95
|
+
find { |i| pat.match?(i.identifier) }
|
96
|
+
end
|
107
97
|
end
|
108
98
|
|
109
99
|
# contract C::Any => C::IterOf[C::RespondTo[:identifier]]
|
@@ -118,13 +108,6 @@ module Nanoc
|
|
118
108
|
end
|
119
109
|
memo_wise :find_all_memoized
|
120
110
|
|
121
|
-
def object_matching_glob(glob)
|
122
|
-
if use_globs?
|
123
|
-
pat = Pattern.from(glob)
|
124
|
-
find { |i| pat.match?(i.identifier) }
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
111
|
def build_mapping
|
129
112
|
@mapping = {}
|
130
113
|
each do |object|
|
@@ -120,19 +120,63 @@ module Nanoc
|
|
120
120
|
#
|
121
121
|
# @return [#identifier] if an object was found
|
122
122
|
def [](arg)
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
123
|
+
# The argument to #[] fall in two categories: exact matches, and
|
124
|
+
# patterns. An example of an exact match is '/about.md', while an
|
125
|
+
# example of a pattern is '/about.*'.
|
126
|
+
#
|
127
|
+
# If a Nanoc::Core::Identifier is given, we know it will need to be an
|
128
|
+
# exact match. If a String is given, it could be either. If a Regexp is
|
129
|
+
# given, we know it’s a pattern match.
|
130
|
+
#
|
131
|
+
# If we have a pattern match, create a dependency on the item
|
132
|
+
# collection, with a `raw_content` property that contains the pattern.
|
133
|
+
# If we have an exact match, do nothing -- there is no reason to create
|
134
|
+
# a dependency on the item itself, because accessing that item
|
135
|
+
# (attributes, compiled content, …) will create the dependency later.
|
136
|
+
|
137
|
+
object_from_exact_match = nil
|
138
|
+
object_from_pattern_match = nil
|
139
|
+
|
140
|
+
case arg
|
141
|
+
when Nanoc::Core::Identifier
|
142
|
+
# Can only be an exact match
|
143
|
+
object_from_exact_match = @objects.object_with_identifier(arg)
|
144
|
+
when String
|
145
|
+
# Can be an exact match, or a pattern match
|
146
|
+
tmp = @objects.object_with_identifier(arg)
|
147
|
+
if tmp
|
148
|
+
object_from_exact_match = tmp
|
129
149
|
else
|
130
|
-
|
150
|
+
object_from_pattern_match = @objects.object_matching_glob(arg)
|
131
151
|
end
|
152
|
+
when Regexp
|
153
|
+
# Can only be a pattern match
|
154
|
+
object_from_pattern_match = @objects.find { |i| i.identifier.to_s =~ arg }
|
155
|
+
else
|
156
|
+
raise ArgumentError, "Unexpected argument #{arg.class} to []: Can only pass strings, identfiers, and regular expressions"
|
157
|
+
end
|
132
158
|
|
133
|
-
|
134
|
-
|
135
|
-
|
159
|
+
unless object_from_exact_match
|
160
|
+
# We got this object from a pattern match. Create a dependency with
|
161
|
+
# this pattern, because if the objects matching this pattern change,
|
162
|
+
# then the result of #[] will change too.
|
163
|
+
#
|
164
|
+
# NOTE: object_from_exact_match can also be nil, but in that case
|
165
|
+
# we still need to create a dependency.
|
166
|
+
|
167
|
+
prop_attribute =
|
168
|
+
case arg
|
169
|
+
when Identifier
|
170
|
+
[arg.to_s]
|
171
|
+
when String, Regexp
|
172
|
+
[arg]
|
173
|
+
end
|
174
|
+
|
175
|
+
@context.dependency_tracker.bounce(_unwrap, raw_content: prop_attribute)
|
176
|
+
end
|
177
|
+
|
178
|
+
object = object_from_exact_match || object_from_pattern_match
|
179
|
+
object && view_class.new(object, @context)
|
136
180
|
end
|
137
181
|
end
|
138
182
|
end
|
data/lib/nanoc/core/item_rep.rb
CHANGED
@@ -42,6 +42,9 @@ module Nanoc
|
|
42
42
|
# Reset flags
|
43
43
|
@compiled = false
|
44
44
|
@modified = false
|
45
|
+
|
46
|
+
# Precalculate for performance
|
47
|
+
@hash = [self.class, item, name].hash
|
45
48
|
end
|
46
49
|
|
47
50
|
contract C::HashOf[Symbol => C::IterOf[C::AbsolutePathString]] => C::HashOf[Symbol => C::IterOf[C::AbsolutePathString]]
|
@@ -75,6 +78,21 @@ module Nanoc
|
|
75
78
|
@paths.fetch(snapshot, []).first
|
76
79
|
end
|
77
80
|
|
81
|
+
contract C::None => C::Num
|
82
|
+
def hash
|
83
|
+
@hash
|
84
|
+
end
|
85
|
+
|
86
|
+
contract C::Any => C::Bool
|
87
|
+
def ==(other)
|
88
|
+
eql?(other)
|
89
|
+
end
|
90
|
+
|
91
|
+
contract C::Any => C::Bool
|
92
|
+
def eql?(other)
|
93
|
+
other.is_a?(self.class) && item == other.item && name == other.name
|
94
|
+
end
|
95
|
+
|
78
96
|
# Returns an object that can be used for uniquely identifying objects.
|
79
97
|
def reference
|
80
98
|
"item_rep:#{item.identifier}:#{name}"
|
@@ -25,15 +25,15 @@ module Nanoc
|
|
25
25
|
action_sequences = Nanoc::Core::ItemRepRouter.new(@reps, @action_provider, @site).run
|
26
26
|
|
27
27
|
@reps.each do |rep|
|
28
|
-
rep.snapshot_defs = self.class.snapshot_defs_for(action_sequences[rep])
|
28
|
+
rep.snapshot_defs = self.class.snapshot_defs_for(action_sequences[rep], rep)
|
29
29
|
end
|
30
30
|
|
31
31
|
action_sequences
|
32
32
|
end
|
33
33
|
|
34
|
-
contract Nanoc::Core::ActionSequence => C::ArrayOf[Nanoc::Core::SnapshotDef]
|
35
|
-
def self.snapshot_defs_for(action_sequence)
|
36
|
-
is_binary =
|
34
|
+
contract Nanoc::Core::ActionSequence, Nanoc::Core::ItemRep => C::ArrayOf[Nanoc::Core::SnapshotDef]
|
35
|
+
def self.snapshot_defs_for(action_sequence, rep)
|
36
|
+
is_binary = rep.item.content.binary?
|
37
37
|
snapshot_defs = []
|
38
38
|
|
39
39
|
action_sequence.each do |action|
|
@@ -8,54 +8,121 @@ module Nanoc
|
|
8
8
|
@reps = reps
|
9
9
|
end
|
10
10
|
|
11
|
-
|
11
|
+
# An iterator (FIFO) over an array, with ability to ignore certain
|
12
|
+
# elements.
|
13
|
+
class ItemRepIgnorableIterator
|
14
|
+
def initialize(array)
|
15
|
+
@array = array.dup
|
16
|
+
end
|
17
|
+
|
18
|
+
def next_ignoring(ignored)
|
19
|
+
elem = @array.shift
|
20
|
+
elem = @array.shift while ignored.include?(elem)
|
21
|
+
elem
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# A priority queue that tracks dependencies and can detect circular
|
26
|
+
# dependencies.
|
27
|
+
class ItemRepPriorityQueue
|
12
28
|
def initialize(reps)
|
13
|
-
|
14
|
-
@
|
29
|
+
# Prio A: most important; prio C: least important.
|
30
|
+
@prio_a = []
|
31
|
+
@prio_b = ItemRepIgnorableIterator.new(reps)
|
32
|
+
@prio_c = []
|
33
|
+
|
34
|
+
# List of reps that we’ve already seen. Reps from `reps` will end up
|
35
|
+
# in here. Reps can end up in here even *before* they come from
|
36
|
+
# `reps`, when they are part of a dependency.
|
37
|
+
@seen = Set.new
|
38
|
+
|
39
|
+
# Record (hard) dependencies. Used for detecting cycles.
|
40
|
+
@dependencies = Hash.new { |hash, key| hash[key] = Set.new }
|
15
41
|
end
|
16
42
|
|
17
43
|
def next
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
@
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
44
|
+
# Read prio A
|
45
|
+
@this = @prio_a.pop
|
46
|
+
if @this
|
47
|
+
return @this
|
48
|
+
end
|
49
|
+
|
50
|
+
# Read prio B
|
51
|
+
@this = @prio_b.next_ignoring(@seen)
|
52
|
+
if @this
|
53
|
+
return @this
|
27
54
|
end
|
55
|
+
|
56
|
+
# Read prio C
|
57
|
+
@this = @prio_c.pop
|
58
|
+
if @this
|
59
|
+
return @this
|
60
|
+
end
|
61
|
+
|
62
|
+
nil
|
28
63
|
end
|
29
64
|
|
30
65
|
def mark_ok
|
31
|
-
|
66
|
+
# Nothing to do
|
32
67
|
end
|
33
68
|
|
34
69
|
def mark_failed(dep)
|
35
|
-
|
36
|
-
|
37
|
-
|
70
|
+
record_dependency(dep)
|
71
|
+
|
72
|
+
# `@this` depends on `dep`, so `dep` has to be compiled first. Thus,
|
73
|
+
# move `@this` into priority C, and `dep` into priority A.
|
74
|
+
|
75
|
+
# Put `@this` (item rep that needs `dep` to be compiled first) into
|
76
|
+
# priority C (lowest prio).
|
77
|
+
@prio_c.push(@this) unless @prio_c.include?(@this)
|
78
|
+
|
79
|
+
# Put `dep` (item rep that needs to be compiled first, before
|
80
|
+
# `@this`) into priority A (highest prio).
|
81
|
+
@prio_a.push(dep)
|
82
|
+
|
83
|
+
# Remember that we’ve prioritised `dep`. This particular element will
|
84
|
+
# come from @prio_b at some point in the future, so we’ll have to skip
|
85
|
+
# it then.
|
86
|
+
@seen << dep
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
38
90
|
|
39
|
-
|
40
|
-
@
|
91
|
+
def record_dependency(dep)
|
92
|
+
@dependencies[@this] << dep
|
93
|
+
|
94
|
+
find_cycle(@this, [@this])
|
95
|
+
end
|
96
|
+
|
97
|
+
def find_cycle(dep, path)
|
98
|
+
@dependencies[dep].each do |dep1|
|
99
|
+
# Check whether this dependency path ends in `@this` again. If it
|
100
|
+
# does, we have a cycle (because we started from `@this`).
|
101
|
+
if dep1 == @this
|
102
|
+
raise Nanoc::Core::Errors::DependencyCycle.new(path)
|
103
|
+
end
|
104
|
+
|
105
|
+
# Continue checking, starting from `dep1` this time.
|
106
|
+
find_cycle(dep1, [*path, dep1])
|
107
|
+
end
|
41
108
|
end
|
42
109
|
end
|
43
110
|
|
44
111
|
def each
|
45
|
-
|
112
|
+
pq = ItemRepPriorityQueue.new(@reps)
|
46
113
|
|
47
114
|
loop do
|
48
|
-
rep =
|
115
|
+
rep = pq.next
|
49
116
|
break if rep.nil?
|
50
117
|
|
51
118
|
begin
|
52
119
|
yield(rep)
|
53
|
-
|
120
|
+
pq.mark_ok
|
54
121
|
rescue => e
|
55
122
|
actual_error = e.is_a?(Nanoc::Core::Errors::CompilationError) ? e.unwrap : e
|
56
123
|
|
57
124
|
if actual_error.is_a?(Nanoc::Core::Errors::UnmetDependency)
|
58
|
-
|
125
|
+
pq.mark_failed(actual_error.rep)
|
59
126
|
else
|
60
127
|
raise(e)
|
61
128
|
end
|
@@ -6,92 +6,6 @@ module Nanoc
|
|
6
6
|
#
|
7
7
|
# @api private
|
8
8
|
class OutdatednessChecker
|
9
|
-
class Basic
|
10
|
-
include Nanoc::Core::ContractsSupport
|
11
|
-
|
12
|
-
Rules = Nanoc::Core::OutdatednessRules
|
13
|
-
|
14
|
-
RULES_FOR_ITEM_REP =
|
15
|
-
[
|
16
|
-
Rules::ItemAdded,
|
17
|
-
Rules::RulesModified,
|
18
|
-
Rules::ContentModified,
|
19
|
-
Rules::AttributesModified,
|
20
|
-
Rules::NotWritten,
|
21
|
-
Rules::CodeSnippetsModified,
|
22
|
-
Rules::UsesAlwaysOutdatedFilter,
|
23
|
-
].freeze
|
24
|
-
|
25
|
-
RULES_FOR_LAYOUT =
|
26
|
-
[
|
27
|
-
Rules::LayoutAdded,
|
28
|
-
Rules::RulesModified,
|
29
|
-
Rules::ContentModified,
|
30
|
-
Rules::AttributesModified,
|
31
|
-
Rules::UsesAlwaysOutdatedFilter,
|
32
|
-
].freeze
|
33
|
-
|
34
|
-
RULES_FOR_CONFIG =
|
35
|
-
[
|
36
|
-
Rules::AttributesModified,
|
37
|
-
].freeze
|
38
|
-
|
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]
|
40
|
-
|
41
|
-
contract C::KeywordArgs[outdatedness_checker: OutdatednessChecker, reps: Nanoc::Core::ItemRepRepo] => C::Any
|
42
|
-
def initialize(outdatedness_checker:, reps:)
|
43
|
-
@outdatedness_checker = outdatedness_checker
|
44
|
-
@reps = reps
|
45
|
-
|
46
|
-
# Memoize
|
47
|
-
@_outdatedness_status_for = {}
|
48
|
-
end
|
49
|
-
|
50
|
-
contract C_OBJ_MAYBE_REP => C::Maybe[Nanoc::Core::OutdatednessStatus]
|
51
|
-
def outdatedness_status_for(obj)
|
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
|
69
|
-
end
|
70
|
-
|
71
|
-
private
|
72
|
-
|
73
|
-
contract C::ArrayOf[Class], C_OBJ_MAYBE_REP, Nanoc::Core::OutdatednessStatus => C::Maybe[Nanoc::Core::OutdatednessStatus]
|
74
|
-
def apply_rules(rules, obj, status = Nanoc::Core::OutdatednessStatus.new)
|
75
|
-
rules.inject(status) do |acc, rule|
|
76
|
-
if acc.useful_to_apply?(rule)
|
77
|
-
reason = rule.instance.call(obj, @outdatedness_checker)
|
78
|
-
if reason
|
79
|
-
acc.update(reason)
|
80
|
-
else
|
81
|
-
acc
|
82
|
-
end
|
83
|
-
else
|
84
|
-
acc
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
contract C::ArrayOf[Class], C::ArrayOf[C_OBJ_MAYBE_REP] => C::Maybe[Nanoc::Core::OutdatednessStatus]
|
90
|
-
def apply_rules_multi(rules, objs)
|
91
|
-
objs.inject(Nanoc::Core::OutdatednessStatus.new) { |acc, elem| apply_rules(rules, elem, acc) }
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
9
|
include Nanoc::Core::ContractsSupport
|
96
10
|
|
97
11
|
attr_reader :checksum_store
|
@@ -107,7 +21,15 @@ module Nanoc
|
|
107
21
|
C_ITEM_OR_REP = C::Or[Nanoc::Core::Item, Nanoc::Core::ItemRep]
|
108
22
|
C_ACTION_SEQUENCES = C::HashOf[C_OBJ => Nanoc::Core::ActionSequence]
|
109
23
|
|
110
|
-
contract C::KeywordArgs[
|
24
|
+
contract C::KeywordArgs[
|
25
|
+
site: Nanoc::Core::Site,
|
26
|
+
checksum_store: Nanoc::Core::ChecksumStore,
|
27
|
+
checksums: Nanoc::Core::ChecksumCollection,
|
28
|
+
dependency_store: Nanoc::Core::DependencyStore,
|
29
|
+
action_sequence_store: Nanoc::Core::ActionSequenceStore,
|
30
|
+
action_sequences: C_ACTION_SEQUENCES,
|
31
|
+
reps: Nanoc::Core::ItemRepRepo
|
32
|
+
] => C::Any
|
111
33
|
def initialize(site:, checksum_store:, checksums:, dependency_store:, action_sequence_store:, action_sequences:, reps:)
|
112
34
|
@site = site
|
113
35
|
@checksum_store = checksum_store
|
@@ -120,20 +42,11 @@ module Nanoc
|
|
120
42
|
@objects_outdated_due_to_dependencies = {}
|
121
43
|
end
|
122
44
|
|
123
|
-
def action_sequence_for(rep)
|
124
|
-
@action_sequences.fetch(rep)
|
125
|
-
end
|
126
|
-
|
127
|
-
contract C_OBJ => C::Bool
|
128
|
-
def outdated?(obj)
|
129
|
-
outdatedness_reasons_for(obj).any?
|
130
|
-
end
|
131
|
-
|
132
45
|
contract C_OBJ => C::IterOf[Reasons::Generic]
|
133
46
|
def outdatedness_reasons_for(obj)
|
134
|
-
|
135
|
-
if
|
136
|
-
|
47
|
+
basic_reasons = basic_outdatedness_statuses.fetch(obj).reasons
|
48
|
+
if basic_reasons.any?
|
49
|
+
basic_reasons
|
137
50
|
elsif outdated_due_to_dependencies?(obj)
|
138
51
|
[Reasons::DependenciesOutdated]
|
139
52
|
else
|
@@ -143,9 +56,28 @@ module Nanoc
|
|
143
56
|
|
144
57
|
private
|
145
58
|
|
146
|
-
|
59
|
+
def basic_outdatedness_statuses
|
60
|
+
@_basic_outdatedness_statuses ||= {}.tap do |tmp|
|
61
|
+
collections = [[@site.config], @site.layouts, @site.items, @reps]
|
62
|
+
collections.each do |collection|
|
63
|
+
collection.each do |obj|
|
64
|
+
tmp[obj] = basic.outdatedness_status_for(obj)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
contract C::None => BasicOutdatednessChecker
|
147
71
|
def basic
|
148
|
-
@_basic ||=
|
72
|
+
@_basic ||= BasicOutdatednessChecker.new(
|
73
|
+
site: @site,
|
74
|
+
checksum_store: @checksum_store,
|
75
|
+
checksums: @checksums,
|
76
|
+
dependency_store: @dependency_store,
|
77
|
+
action_sequence_store: @action_sequence_store,
|
78
|
+
action_sequences: @action_sequences,
|
79
|
+
reps: @reps,
|
80
|
+
)
|
149
81
|
end
|
150
82
|
|
151
83
|
contract C_OBJ, Hamster::Set => C::Bool
|
@@ -191,7 +123,7 @@ module Nanoc
|
|
191
123
|
raw_content_prop_causes_outdatedness?(all_objects, dependency.props.raw_content) ||
|
192
124
|
attributes_prop_causes_outdatedness?(all_objects, dependency.props.attributes)
|
193
125
|
else
|
194
|
-
status =
|
126
|
+
status = basic_outdatedness_statuses.fetch(dependency.from)
|
195
127
|
|
196
128
|
active = status.props.active & dependency.props.active
|
197
129
|
active.delete(:attributes) if attributes_unaffected?(status, dependency)
|
@@ -217,8 +149,7 @@ module Nanoc
|
|
217
149
|
when Enumerable
|
218
150
|
# If the `raw_content` dependency prop is a collection, then this
|
219
151
|
# is a dependency on specific objects, given by the patterns.
|
220
|
-
|
221
|
-
patterns.flat_map { |pat| objects.select { |obj| pat.match?(obj.identifier) } }
|
152
|
+
raw_content_prop.flat_map { |pat| objects.find_all(pat) }
|
222
153
|
else
|
223
154
|
raise(
|
224
155
|
Nanoc::Core::Errors::InternalInconsistency,
|
@@ -236,7 +167,7 @@ module Nanoc
|
|
236
167
|
# accessed), then another dependency will exist that will cause
|
237
168
|
# outdatedness.
|
238
169
|
matching_objects.any? do |obj|
|
239
|
-
status =
|
170
|
+
status = basic_outdatedness_statuses.fetch(obj)
|
240
171
|
status.reasons.any? { |r| Nanoc::Core::OutdatednessReasons::DocumentAdded == r }
|
241
172
|
end
|
242
173
|
end
|
@@ -265,23 +196,30 @@ module Nanoc
|
|
265
196
|
objects.any? do |object|
|
266
197
|
# Find old and new attribute checksums for the object
|
267
198
|
old_object_checksums = checksum_store.attributes_checksum_for(object)
|
268
|
-
next false unless old_object_checksums
|
269
|
-
|
270
199
|
new_object_checksums = checksums.attributes_checksum_for(object)
|
271
200
|
|
272
201
|
dep_checksums.any? do |key, dep_value|
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
202
|
+
if old_object_checksums
|
203
|
+
# Get old and new checksum for this particular attribute
|
204
|
+
old_value = old_object_checksums[key]
|
205
|
+
new_value = new_object_checksums[key]
|
206
|
+
|
207
|
+
# If the old and new checksums are identical, then the attribute
|
208
|
+
# is unchanged and can’t cause outdatedness.
|
209
|
+
next false unless old_value != new_value
|
210
|
+
|
211
|
+
# We already know that the old value and new value are different.
|
212
|
+
# This attribute will cause outdatedness if either of those
|
213
|
+
# checksums is identical to the one in the prop.
|
214
|
+
old_value == dep_value || new_value == dep_value
|
215
|
+
else
|
216
|
+
# We don’t have the previous checksums, which means this item is
|
217
|
+
# newly added. In this case, we can compare the value in the
|
218
|
+
# dependency with the new checksum.
|
219
|
+
|
220
|
+
new_value = new_object_checksums[key]
|
221
|
+
new_value == dep_value
|
222
|
+
end
|
285
223
|
end
|
286
224
|
end
|
287
225
|
end
|
@@ -8,22 +8,22 @@ module Nanoc
|
|
8
8
|
|
9
9
|
affects_props :attributes, :compiled_content
|
10
10
|
|
11
|
-
contract C::Or[Nanoc::Core::ItemRep, Nanoc::Core::Item, Nanoc::Core::Configuration, Nanoc::Core::Layout], C::Named['Nanoc::Core::
|
12
|
-
def apply(obj,
|
11
|
+
contract C::Or[Nanoc::Core::ItemRep, Nanoc::Core::Item, Nanoc::Core::Configuration, Nanoc::Core::Layout], C::Named['Nanoc::Core::BasicOutdatednessChecker'] => C::Maybe[Nanoc::Core::OutdatednessReasons::Generic]
|
12
|
+
def apply(obj, basic_outdatedness_checker)
|
13
13
|
case obj
|
14
14
|
when Nanoc::Core::ItemRep
|
15
|
-
apply(obj.item,
|
15
|
+
apply(obj.item, basic_outdatedness_checker)
|
16
16
|
when Nanoc::Core::Item, Nanoc::Core::Layout, Nanoc::Core::Configuration
|
17
|
-
if
|
17
|
+
if basic_outdatedness_checker.checksum_store[obj] == basic_outdatedness_checker.checksums.checksum_for(obj)
|
18
18
|
return nil
|
19
19
|
end
|
20
20
|
|
21
|
-
old_checksums =
|
21
|
+
old_checksums = basic_outdatedness_checker.checksum_store.attributes_checksum_for(obj)
|
22
22
|
unless old_checksums
|
23
23
|
return Nanoc::Core::OutdatednessReasons::AttributesModified.new(true)
|
24
24
|
end
|
25
25
|
|
26
|
-
new_checksums =
|
26
|
+
new_checksums = basic_outdatedness_checker.checksums.attributes_checksum_for(obj)
|
27
27
|
|
28
28
|
attributes = Set.new(old_checksums.keys) + Set.new(new_checksums.keys)
|
29
29
|
changed_attributes = attributes.reject { |a| old_checksums[a] == new_checksums[a] }
|
@@ -10,18 +10,18 @@ module Nanoc
|
|
10
10
|
|
11
11
|
affects_props :raw_content, :attributes, :compiled_content, :path
|
12
12
|
|
13
|
-
def apply(_obj,
|
14
|
-
if any_snippets_modified?(
|
13
|
+
def apply(_obj, basic_outdatedness_checker)
|
14
|
+
if any_snippets_modified?(basic_outdatedness_checker)
|
15
15
|
Nanoc::Core::OutdatednessReasons::CodeSnippetsModified
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
19
|
private
|
20
20
|
|
21
|
-
def any_snippets_modified?(
|
22
|
-
|
23
|
-
ch_old =
|
24
|
-
ch_new =
|
21
|
+
def any_snippets_modified?(basic_outdatedness_checker)
|
22
|
+
basic_outdatedness_checker.site.code_snippets.any? do |cs|
|
23
|
+
ch_old = basic_outdatedness_checker.checksum_store[cs]
|
24
|
+
ch_new = basic_outdatedness_checker.checksums.checksum_for(cs)
|
25
25
|
ch_old != ch_new
|
26
26
|
end
|
27
27
|
end
|
@@ -6,11 +6,11 @@ module Nanoc
|
|
6
6
|
class ContentModified < Nanoc::Core::OutdatednessRule
|
7
7
|
affects_props :raw_content, :compiled_content
|
8
8
|
|
9
|
-
def apply(obj,
|
9
|
+
def apply(obj, basic_outdatedness_checker)
|
10
10
|
obj = obj.item if obj.is_a?(Nanoc::Core::ItemRep)
|
11
11
|
|
12
|
-
ch_old =
|
13
|
-
ch_new =
|
12
|
+
ch_old = basic_outdatedness_checker.checksum_store.content_checksum_for(obj)
|
13
|
+
ch_new = basic_outdatedness_checker.checksums.content_checksum_for(obj)
|
14
14
|
if ch_old != ch_new
|
15
15
|
Nanoc::Core::OutdatednessReasons::ContentModified
|
16
16
|
end
|
@@ -6,9 +6,9 @@ module Nanoc
|
|
6
6
|
class ItemAdded < Nanoc::Core::OutdatednessRule
|
7
7
|
affects_props :raw_content
|
8
8
|
|
9
|
-
contract Nanoc::Core::ItemRep, C::Named['Nanoc::Core::
|
10
|
-
def apply(obj,
|
11
|
-
if
|
9
|
+
contract Nanoc::Core::ItemRep, C::Named['Nanoc::Core::BasicOutdatednessChecker'] => C::Maybe[Nanoc::Core::OutdatednessReasons::Generic]
|
10
|
+
def apply(obj, basic_outdatedness_checker)
|
11
|
+
if basic_outdatedness_checker.dependency_store.new_items.include?(obj.item)
|
12
12
|
Nanoc::Core::OutdatednessReasons::DocumentAdded
|
13
13
|
end
|
14
14
|
end
|
@@ -6,9 +6,9 @@ module Nanoc
|
|
6
6
|
class LayoutAdded < Nanoc::Core::OutdatednessRule
|
7
7
|
affects_props :raw_content
|
8
8
|
|
9
|
-
contract Nanoc::Core::Layout, C::Named['Nanoc::Core::
|
10
|
-
def apply(obj,
|
11
|
-
if
|
9
|
+
contract Nanoc::Core::Layout, C::Named['Nanoc::Core::BasicOutdatednessChecker'] => C::Maybe[Nanoc::Core::OutdatednessReasons::Generic]
|
10
|
+
def apply(obj, basic_outdatedness_checker)
|
11
|
+
if basic_outdatedness_checker.dependency_store.new_layouts.include?(obj)
|
12
12
|
Nanoc::Core::OutdatednessReasons::DocumentAdded
|
13
13
|
end
|
14
14
|
end
|
@@ -6,30 +6,30 @@ 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| !exist?(fn,
|
9
|
+
def apply(obj, basic_outdatedness_checker)
|
10
|
+
if obj.raw_paths.values.flatten.compact.any? { |fn| !exist?(fn, basic_outdatedness_checker) }
|
11
11
|
Nanoc::Core::OutdatednessReasons::NotWritten
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
15
|
private
|
16
16
|
|
17
|
-
def exist?(fn,
|
18
|
-
all(
|
17
|
+
def exist?(fn, basic_outdatedness_checker)
|
18
|
+
all(basic_outdatedness_checker).include?(fn)
|
19
19
|
end
|
20
20
|
|
21
|
-
def all(
|
21
|
+
def all(basic_outdatedness_checker)
|
22
22
|
# NOTE: Cached per outdatedness checker, so that unrelated invocations
|
23
23
|
# later on don’t reuse an old cache.
|
24
24
|
|
25
25
|
@all ||= {}
|
26
|
-
@all[
|
27
|
-
Dir.glob("#{site_root(
|
26
|
+
@all[basic_outdatedness_checker] ||= Set.new(
|
27
|
+
Dir.glob("#{site_root(basic_outdatedness_checker)}/**/*", File::FNM_DOTMATCH),
|
28
28
|
)
|
29
29
|
end
|
30
30
|
|
31
|
-
def site_root(
|
32
|
-
|
31
|
+
def site_root(basic_outdatedness_checker)
|
32
|
+
basic_outdatedness_checker.site.config.output_dir
|
33
33
|
end
|
34
34
|
end
|
35
35
|
end
|
@@ -6,15 +6,15 @@ module Nanoc
|
|
6
6
|
class RulesModified < Nanoc::Core::OutdatednessRule
|
7
7
|
affects_props :compiled_content, :path
|
8
8
|
|
9
|
-
def apply(obj,
|
9
|
+
def apply(obj, basic_outdatedness_checker)
|
10
10
|
# Check rules of obj itself
|
11
|
-
if rules_modified?(obj,
|
11
|
+
if rules_modified?(obj, basic_outdatedness_checker)
|
12
12
|
return Nanoc::Core::OutdatednessReasons::RulesModified
|
13
13
|
end
|
14
14
|
|
15
15
|
# Check rules of layouts used by obj
|
16
|
-
layouts = layouts_touched_by(obj,
|
17
|
-
if layouts.any? { |layout| rules_modified?(layout,
|
16
|
+
layouts = layouts_touched_by(obj, basic_outdatedness_checker)
|
17
|
+
if layouts.any? { |layout| rules_modified?(layout, basic_outdatedness_checker) }
|
18
18
|
return Nanoc::Core::OutdatednessReasons::RulesModified
|
19
19
|
end
|
20
20
|
|
@@ -23,20 +23,22 @@ module Nanoc
|
|
23
23
|
|
24
24
|
private
|
25
25
|
|
26
|
-
def rules_modified?(obj,
|
27
|
-
seq_old =
|
28
|
-
seq_new =
|
26
|
+
def rules_modified?(obj, basic_outdatedness_checker)
|
27
|
+
seq_old = basic_outdatedness_checker.action_sequence_store[obj]
|
28
|
+
seq_new = basic_outdatedness_checker.action_sequence_for(obj).serialize
|
29
29
|
|
30
30
|
!seq_old.eql?(seq_new)
|
31
31
|
end
|
32
32
|
|
33
|
-
def layouts_touched_by(obj,
|
34
|
-
actions =
|
33
|
+
def layouts_touched_by(obj, basic_outdatedness_checker)
|
34
|
+
actions = basic_outdatedness_checker.action_sequence_store[obj]
|
35
35
|
layout_actions = actions.select { |a| a.first == :layout }
|
36
36
|
|
37
|
+
layouts = basic_outdatedness_checker.site.layouts
|
38
|
+
|
37
39
|
layout_actions.map do |layout_action|
|
38
40
|
layout_pattern = layout_action[1]
|
39
|
-
|
41
|
+
layouts.object_with_identifier(layout_pattern) || layouts.object_matching_glob(layout_pattern)
|
40
42
|
end.compact
|
41
43
|
end
|
42
44
|
end
|
@@ -6,8 +6,8 @@ module Nanoc
|
|
6
6
|
class UsesAlwaysOutdatedFilter < Nanoc::Core::OutdatednessRule
|
7
7
|
affects_props :raw_content, :attributes, :path
|
8
8
|
|
9
|
-
def apply(obj,
|
10
|
-
seq =
|
9
|
+
def apply(obj, basic_outdatedness_checker)
|
10
|
+
seq = basic_outdatedness_checker.action_sequence_for(obj)
|
11
11
|
if any_always_outdated?(seq)
|
12
12
|
Nanoc::Core::OutdatednessReasons::UsesAlwaysOutdatedFilter
|
13
13
|
end
|
data/lib/nanoc/core/store.rb
CHANGED
@@ -7,11 +7,7 @@ module Nanoc
|
|
7
7
|
# graphs.
|
8
8
|
#
|
9
9
|
# Each store has a version number. When attempting to load data from a store
|
10
|
-
# that has an incompatible version number, no data will be loaded
|
11
|
-
# {#version_mismatch_detected} will be called.
|
12
|
-
#
|
13
|
-
# @abstract Subclasses must implement {#data} and {#data=}, and may
|
14
|
-
# implement {#no_data_found} and {#version_mismatch_detected}.
|
10
|
+
# that has an incompatible version number, no data will be loaded.
|
15
11
|
#
|
16
12
|
# @api private
|
17
13
|
class Store
|
@@ -78,17 +74,8 @@ module Nanoc
|
|
78
74
|
#
|
79
75
|
# @return [void]
|
80
76
|
def load
|
81
|
-
|
82
|
-
|
83
|
-
begin
|
84
|
-
pstore.transaction(true) do
|
85
|
-
return if pstore[:version] != version
|
86
|
-
|
87
|
-
self.data = pstore[:data]
|
88
|
-
end
|
89
|
-
rescue
|
90
|
-
FileUtils.rm_f(filename)
|
91
|
-
load
|
77
|
+
Nanoc::Core::Instrumentor.call(:store_loaded, self.class) do
|
78
|
+
load_uninstrumented
|
92
79
|
end
|
93
80
|
end
|
94
81
|
|
@@ -97,18 +84,64 @@ module Nanoc
|
|
97
84
|
#
|
98
85
|
# @return [void]
|
99
86
|
def store
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
pstore[:version] = version
|
87
|
+
# NOTE: Yes, the “store stored” name is a little silly. Maybe stores
|
88
|
+
# need to be renamed to databases or so.
|
89
|
+
Nanoc::Core::Instrumentor.call(:store_stored, self.class) do
|
90
|
+
store_uninstrumented
|
105
91
|
end
|
106
92
|
end
|
107
93
|
|
108
94
|
private
|
109
95
|
|
110
|
-
def
|
111
|
-
|
96
|
+
def load_uninstrumented
|
97
|
+
unsafe_load_uninstrumented
|
98
|
+
rescue
|
99
|
+
# An error occurred! Remove the database and try again
|
100
|
+
FileUtils.rm_f(version_filename)
|
101
|
+
FileUtils.rm_f(data_filename)
|
102
|
+
|
103
|
+
# Try again
|
104
|
+
unsafe_load_uninstrumented
|
105
|
+
end
|
106
|
+
|
107
|
+
def store_uninstrumented
|
108
|
+
FileUtils.mkdir_p(File.dirname(filename))
|
109
|
+
|
110
|
+
write_obj_to_file(version_filename, version)
|
111
|
+
write_obj_to_file(data_filename, data)
|
112
|
+
|
113
|
+
# Remove old file (back from the PStore days), if there are any.
|
114
|
+
FileUtils.rm_f(filename)
|
115
|
+
end
|
116
|
+
|
117
|
+
# Unsafe, because it can throw exceptions.
|
118
|
+
def unsafe_load_uninstrumented
|
119
|
+
# If there is no database, no point in loading anything
|
120
|
+
return unless File.file?(version_filename)
|
121
|
+
|
122
|
+
# Check if store version is the expected version. If it is not, don’t
|
123
|
+
# load.
|
124
|
+
read_version = read_obj_from_file(version_filename)
|
125
|
+
return if read_version != version
|
126
|
+
|
127
|
+
# Load data
|
128
|
+
self.data = read_obj_from_file(data_filename)
|
129
|
+
end
|
130
|
+
|
131
|
+
def write_obj_to_file(fn, obj)
|
132
|
+
File.binwrite(fn, Marshal.dump(obj))
|
133
|
+
end
|
134
|
+
|
135
|
+
def read_obj_from_file(fn)
|
136
|
+
Marshal.load(File.binread(fn))
|
137
|
+
end
|
138
|
+
|
139
|
+
def version_filename
|
140
|
+
"#{filename}.version.db"
|
141
|
+
end
|
142
|
+
|
143
|
+
def data_filename
|
144
|
+
"#{filename}.data.db"
|
112
145
|
end
|
113
146
|
end
|
114
147
|
end
|
data/lib/nanoc/core/version.rb
CHANGED
data/lib/nanoc/core.rb
CHANGED
@@ -33,6 +33,14 @@ module Nanoc
|
|
33
33
|
# thus cannot be used to mean the presence of nothing.
|
34
34
|
UNDEFINED = Object.new
|
35
35
|
|
36
|
+
def UNDEFINED.inspect
|
37
|
+
'<UNDEFINED>'
|
38
|
+
end
|
39
|
+
|
40
|
+
def UNDEFINED.to_s
|
41
|
+
inspect
|
42
|
+
end
|
43
|
+
|
36
44
|
# @return [String] A string containing information about this Nanoc version
|
37
45
|
# and its environment (Ruby engine and version, Rubygems version if any).
|
38
46
|
#
|
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.12
|
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-11-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -169,6 +169,7 @@ files:
|
|
169
169
|
- lib/nanoc/core/basic_item_rep_collection_view.rb
|
170
170
|
- lib/nanoc/core/basic_item_rep_view.rb
|
171
171
|
- lib/nanoc/core/basic_item_view.rb
|
172
|
+
- lib/nanoc/core/basic_outdatedness_checker.rb
|
172
173
|
- lib/nanoc/core/binary_compiled_content_cache.rb
|
173
174
|
- lib/nanoc/core/binary_content.rb
|
174
175
|
- lib/nanoc/core/changes_stream.rb
|
@@ -315,7 +316,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
315
316
|
- !ruby/object:Gem::Version
|
316
317
|
version: '0'
|
317
318
|
requirements: []
|
318
|
-
rubygems_version: 3.3.
|
319
|
+
rubygems_version: 3.3.25
|
319
320
|
signing_key:
|
320
321
|
specification_version: 4
|
321
322
|
summary: Core of Nanoc
|