jekyll-relationships 0.1.0.alpha → 0.2.0.alpha
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/jekyll-relationships/configuration/debug_setting.rb +72 -12
- data/lib/jekyll-relationships/configuration/defaults.rb +3 -1
- data/lib/jekyll-relationships/configuration/frontmatter.rb +28 -1
- data/lib/jekyll-relationships/configuration/parser.rb +20 -3
- data/lib/jekyll-relationships/configuration/prune_rule_settings.rb +33 -5
- data/lib/jekyll-relationships/configuration/tree_frontmatter.rb +8 -2
- data/lib/jekyll-relationships/configuration.rb +43 -8
- data/lib/jekyll-relationships/debug_logger.rb +94 -3
- data/lib/jekyll-relationships/definitions/normal_relationship.rb +13 -2
- data/lib/jekyll-relationships/definitions/prune_rule.rb +19 -3
- data/lib/jekyll-relationships/definitions/tree_relationship.rb +8 -2
- data/lib/jekyll-relationships/documents/registry.rb +182 -33
- data/lib/jekyll-relationships/engine/normal_seed.rb +222 -0
- data/lib/jekyll-relationships/engine/persisted_links.rb +365 -0
- data/lib/jekyll-relationships/engine/raw_path_state.rb +46 -116
- data/lib/jekyll-relationships/engine/relationship_state.rb +67 -56
- data/lib/jekyll-relationships/engine/session.rb +159 -19
- data/lib/jekyll-relationships/engine/write_back.rb +3 -42
- data/lib/jekyll-relationships/engine.rb +168 -46
- data/lib/jekyll-relationships/pruning/rule_pruner.rb +7 -7
- data/lib/jekyll-relationships/pruning/tree_phase.rb +128 -124
- data/lib/jekyll-relationships/pruning/tree_provenance.rb +1 -1
- data/lib/jekyll-relationships/references/accumulator.rb +87 -9
- data/lib/jekyll-relationships/references/template.rb +46 -11
- data/lib/jekyll-relationships/resolvers/base.rb +59 -2
- data/lib/jekyll-relationships/support/frontmatter_matcher.rb +76 -0
- data/lib/jekyll-relationships/support/placeholders.rb +1 -0
- data/lib/jekyll-relationships/trees/edge_builder.rb +43 -11
- data/lib/jekyll-relationships/trees/graph.rb +135 -17
- data/lib/jekyll-relationships/trees/root_distances.rb +44 -0
- data/lib/jekyll-relationships/version.rb +1 -1
- data/lib/jekyll-relationships.rb +1 -0
- data/readme.md +185 -26
- metadata +6 -2
|
@@ -20,7 +20,7 @@ class Engine
|
|
|
20
20
|
@document = document
|
|
21
21
|
@definition = definition
|
|
22
22
|
@status = :unseen
|
|
23
|
-
@
|
|
23
|
+
@seeded = false
|
|
24
24
|
@links = Jekyll::Plugins::Relationships::References::Accumulator.new(
|
|
25
25
|
reference_template: @engine.configuration.reference_template,
|
|
26
26
|
multiple_settings: @engine.configuration.multiple_settings
|
|
@@ -37,6 +37,16 @@ class Engine
|
|
|
37
37
|
@status == :resolved
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
+
# Seeds the state from the normal seed graph once, without running resolvers.
|
|
41
|
+
def ensure_seeded!
|
|
42
|
+
seed_initial_links!
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Returns true when one document is active in this session.
|
|
46
|
+
def active_document?(document = @document)
|
|
47
|
+
@engine.active_document?(document)
|
|
48
|
+
end
|
|
49
|
+
|
|
40
50
|
# Resolves the state unless it has already been handled.
|
|
41
51
|
def resolve!
|
|
42
52
|
return if resolved?
|
|
@@ -50,8 +60,10 @@ class Engine
|
|
|
50
60
|
bidirectional: @definition.bidirectional,
|
|
51
61
|
resolvers: @definition.resolver_classes.map { |resolver_class| @engine.debug_logger.resolver_name(resolver_class) }
|
|
52
62
|
})
|
|
53
|
-
|
|
63
|
+
ensure_seeded!
|
|
54
64
|
run_resolvers!
|
|
65
|
+
@engine.reapply_persisted_links(state: self)
|
|
66
|
+
@engine.refresh_persisted_positions(state: self)
|
|
55
67
|
@status = :resolved
|
|
56
68
|
debug('resolution', 'resolve_finish', {
|
|
57
69
|
references: current_references
|
|
@@ -59,10 +71,13 @@ class Engine
|
|
|
59
71
|
end
|
|
60
72
|
|
|
61
73
|
# Adds one link to the state and mirrors it when required.
|
|
62
|
-
def link(reference, metadata: nil, count: 1, reflect: true, origin: 'resolver')
|
|
74
|
+
def link(reference, metadata: nil, count: 1, reflect: true, origin: 'resolver', persist: false)
|
|
63
75
|
target_document = @engine.resolve_reference_document(
|
|
64
76
|
reference,
|
|
65
77
|
primary_path: @definition.primary_path,
|
|
78
|
+
scope_fields: @definition.scope_fields,
|
|
79
|
+
referring_document: @document,
|
|
80
|
+
relationship: relationship_description,
|
|
66
81
|
collection_hint: @definition.to_collection
|
|
67
82
|
)
|
|
68
83
|
unless @engine.active_document?(target_document)
|
|
@@ -74,35 +89,50 @@ class Engine
|
|
|
74
89
|
count: count,
|
|
75
90
|
metadata: sanitised_metadata(metadata)
|
|
76
91
|
})
|
|
77
|
-
return
|
|
92
|
+
return {
|
|
93
|
+
action: :inactive,
|
|
94
|
+
entry: nil,
|
|
95
|
+
target_document: target_document
|
|
96
|
+
}
|
|
78
97
|
end
|
|
79
98
|
unless target_document.collection.label == @definition.to_collection
|
|
80
99
|
raise ResolutionError, "Resolver attempted to link `#{target_document.relative_path}` outside the allowed target collection `#{@definition.to_collection}`."
|
|
81
100
|
end
|
|
82
101
|
|
|
83
|
-
|
|
102
|
+
result = @links.add_detailed_result(
|
|
84
103
|
document: target_document,
|
|
85
104
|
key: @engine.registry.key_for(target_document, primary_path: @definition.primary_path),
|
|
105
|
+
scope: @engine.registry.scope_for(target_document, scope_fields: @definition.scope_fields, relationship: relationship_description),
|
|
86
106
|
metadata: sanitised_metadata(metadata),
|
|
87
107
|
count: count
|
|
88
108
|
)
|
|
89
109
|
debug('mutations', 'link', {
|
|
90
|
-
action: action,
|
|
110
|
+
action: result.fetch(:action),
|
|
91
111
|
origin: origin,
|
|
92
112
|
target: target_document,
|
|
93
113
|
target_key: @engine.registry.key_for(target_document, primary_path: @definition.primary_path),
|
|
94
114
|
count: count,
|
|
95
115
|
metadata: sanitised_metadata(metadata)
|
|
96
116
|
})
|
|
97
|
-
|
|
98
|
-
|
|
117
|
+
if persist && result.fetch(:action) != :ignored
|
|
118
|
+
@engine.persist_link(
|
|
119
|
+
source_state: self,
|
|
120
|
+
target_document: target_document,
|
|
121
|
+
metadata: metadata,
|
|
122
|
+
count: count
|
|
123
|
+
)
|
|
124
|
+
end
|
|
125
|
+
if reflect && @definition.bidirectional && result.fetch(:action) != :ignored
|
|
126
|
+
@engine.mirror_add(source_state: self, target_document: target_document, metadata: metadata, count: count)
|
|
127
|
+
end
|
|
99
128
|
|
|
100
|
-
|
|
129
|
+
result.merge(target_document: target_document)
|
|
101
130
|
end
|
|
102
131
|
|
|
103
132
|
# Removes one link, or every link when no reference is given.
|
|
104
|
-
def unlink(reference = nil, reflect: true, origin: 'resolver')
|
|
133
|
+
def unlink(reference = nil, reflect: true, origin: 'resolver', clear_persisted: true)
|
|
105
134
|
if reference.nil?
|
|
135
|
+
@engine.clear_all_persisted_links(source_state: self) if clear_persisted
|
|
106
136
|
if current_link_entries.empty?
|
|
107
137
|
debug('mutations', 'unlink_all', {
|
|
108
138
|
action: :missing,
|
|
@@ -119,8 +149,12 @@ class Engine
|
|
|
119
149
|
target_document = @engine.resolve_reference_document(
|
|
120
150
|
reference,
|
|
121
151
|
primary_path: @definition.primary_path,
|
|
152
|
+
scope_fields: @definition.scope_fields,
|
|
153
|
+
referring_document: @document,
|
|
154
|
+
relationship: relationship_description,
|
|
122
155
|
collection_hint: @definition.to_collection
|
|
123
156
|
)
|
|
157
|
+
@engine.clear_persisted_link(source_state: self, target_document: target_document) if clear_persisted
|
|
124
158
|
remove_document(document: target_document, reflect: reflect, origin: origin)
|
|
125
159
|
end
|
|
126
160
|
|
|
@@ -134,56 +168,28 @@ class Engine
|
|
|
134
168
|
@links.encounter_entries
|
|
135
169
|
end
|
|
136
170
|
|
|
137
|
-
|
|
171
|
+
# Repositions reinserted persisted entries after they have been added.
|
|
172
|
+
def reposition_entries!(reinsertions:, base_count:)
|
|
173
|
+
@links.reposition_entries!(reinsertions: reinsertions, base_count: base_count)
|
|
174
|
+
end
|
|
138
175
|
|
|
139
|
-
|
|
140
|
-
def seed_raw_links!
|
|
141
|
-
return unless @definition.reads_frontmatter
|
|
142
|
-
return if @raw_seeded
|
|
143
|
-
|
|
144
|
-
@definition.foreign_paths.each do |path|
|
|
145
|
-
raw_state = @engine.raw_path_state(@document, path)
|
|
146
|
-
debug('upgrading', 'raw_path', {
|
|
147
|
-
path: path,
|
|
148
|
-
present: raw_state.present?,
|
|
149
|
-
value: raw_state.raw_value
|
|
150
|
-
})
|
|
151
|
-
next unless raw_state.present?
|
|
176
|
+
private
|
|
152
177
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
178
|
+
# Seeds the state from the source-derived normal seed graph.
|
|
179
|
+
def seed_initial_links!
|
|
180
|
+
return if @seeded
|
|
181
|
+
|
|
182
|
+
@engine.seed_entries_for(@document, @definition.to_collection).each do |entry|
|
|
183
|
+
link(
|
|
184
|
+
entry.fetch(:document),
|
|
185
|
+
metadata: entry.fetch(:metadata),
|
|
186
|
+
count: entry.fetch(:count),
|
|
187
|
+
reflect: false,
|
|
188
|
+
origin: 'seed graph',
|
|
189
|
+
persist: false
|
|
157
190
|
)
|
|
158
|
-
debug('upgrading', 'raw_path_resolved', {
|
|
159
|
-
path: path,
|
|
160
|
-
entries: resolved_entries.compact
|
|
161
|
-
})
|
|
162
|
-
|
|
163
|
-
resolved_entries.each do |entry|
|
|
164
|
-
next unless entry
|
|
165
|
-
if entry.fetch(:document).collection.label != @definition.to_collection
|
|
166
|
-
debug('upgrading', 'raw_path_skipped', {
|
|
167
|
-
path: path,
|
|
168
|
-
target: entry.fetch(:document),
|
|
169
|
-
target_key: entry.fetch(:key),
|
|
170
|
-
actual_collection: entry.fetch(:document).collection.label,
|
|
171
|
-
expected_collection: @definition.to_collection
|
|
172
|
-
})
|
|
173
|
-
next
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
link(
|
|
177
|
-
entry.fetch(:document),
|
|
178
|
-
metadata: entry.fetch(:metadata),
|
|
179
|
-
count: entry.fetch(:count),
|
|
180
|
-
reflect: @definition.bidirectional,
|
|
181
|
-
origin: "frontmatter #{path}"
|
|
182
|
-
)
|
|
183
|
-
end
|
|
184
191
|
end
|
|
185
|
-
|
|
186
|
-
@raw_seeded = true
|
|
192
|
+
@seeded = true
|
|
187
193
|
end
|
|
188
194
|
|
|
189
195
|
# Instantiates and runs every configured resolver class.
|
|
@@ -232,6 +238,11 @@ class Engine
|
|
|
232
238
|
"`#{@document.relative_path}` (#{@definition.from_collection} -> #{@definition.to_collection})"
|
|
233
239
|
end
|
|
234
240
|
|
|
241
|
+
# Returns the concrete relationship label used in validation diagnostics.
|
|
242
|
+
def relationship_description
|
|
243
|
+
"#{@definition.from_collection} -> #{@definition.to_collection}"
|
|
244
|
+
end
|
|
245
|
+
|
|
235
246
|
# Emits one debug event for this state when the definition enables it.
|
|
236
247
|
def debug(area, event, details)
|
|
237
248
|
@engine.debug_logger.relationship_event(
|
|
@@ -11,12 +11,13 @@ class Engine
|
|
|
11
11
|
#
|
|
12
12
|
# The main engine creates a fresh session for each rebuild round so raw-path
|
|
13
13
|
# caches, resolver state, and relationship accumulators never outlive the
|
|
14
|
-
# graph they were built against.
|
|
14
|
+
# graph they were built against. Every round reseeds from the current source
|
|
15
|
+
# graph plus any explicit persisted-link overlay.
|
|
15
16
|
class Session
|
|
16
17
|
attr_reader :site, :configuration, :registry, :tree_graph, :data_path, :debug_logger
|
|
17
18
|
|
|
18
19
|
# Builds one new resolution session.
|
|
19
|
-
def initialize(engine:, active_document_ids:, tree_graph:)
|
|
20
|
+
def initialize(engine:, active_document_ids:, tree_graph:, normal_seed:, persisted_links:)
|
|
20
21
|
@engine = engine
|
|
21
22
|
@site = engine.site
|
|
22
23
|
@configuration = engine.configuration
|
|
@@ -24,6 +25,9 @@ class Engine
|
|
|
24
25
|
@tree_graph = tree_graph
|
|
25
26
|
@data_path = engine.data_path
|
|
26
27
|
@debug_logger = engine.debug_logger
|
|
28
|
+
@normal_seed = normal_seed
|
|
29
|
+
@persisted_links = persisted_links
|
|
30
|
+
@persisted_links.start_round!
|
|
27
31
|
@active_document_ids = active_document_ids.each_with_object({}) do |document_id, active_ids|
|
|
28
32
|
active_ids[document_id] = true
|
|
29
33
|
end
|
|
@@ -31,6 +35,8 @@ class Engine
|
|
|
31
35
|
@raw_path_states = {}
|
|
32
36
|
@relationship_states = {}
|
|
33
37
|
@document_resolution_states = {}
|
|
38
|
+
@initial_links_seeded = false
|
|
39
|
+
@defer_bidirectional_mirror_resolvers = false
|
|
34
40
|
end
|
|
35
41
|
|
|
36
42
|
# Returns true when the document is active in this session.
|
|
@@ -52,8 +58,7 @@ class Engine
|
|
|
52
58
|
path: path,
|
|
53
59
|
data_path: @data_path,
|
|
54
60
|
string_array: @string_array,
|
|
55
|
-
reference_template: @configuration.reference_template
|
|
56
|
-
multiple_settings: @configuration.multiple_settings
|
|
61
|
+
reference_template: @configuration.reference_template
|
|
57
62
|
)
|
|
58
63
|
end
|
|
59
64
|
|
|
@@ -66,6 +71,9 @@ class Engine
|
|
|
66
71
|
raise ResolutionError, "No relationship is defined from collection `#{document.collection.label}` to `#{to_collection}`."
|
|
67
72
|
end
|
|
68
73
|
|
|
74
|
+
ensure_initial_links_seeded!
|
|
75
|
+
return state.current_references if defer_bidirectional_mirror_resolver_for?(state)
|
|
76
|
+
|
|
69
77
|
resolve_state(state)
|
|
70
78
|
state.current_references
|
|
71
79
|
end
|
|
@@ -89,19 +97,86 @@ class Engine
|
|
|
89
97
|
end
|
|
90
98
|
|
|
91
99
|
# Resolves one helper or resolver reference to a document, even if inactive.
|
|
92
|
-
def resolve_reference_document(reference, primary_path:, collection_hint: nil)
|
|
93
|
-
|
|
100
|
+
def resolve_reference_document(reference, primary_path:, scope_fields: [], referring_document: nil, relationship: nil, collection_hint: nil)
|
|
101
|
+
if reference.is_a?(Jekyll::Document)
|
|
102
|
+
@registry.scope_for(reference, scope_fields: scope_fields, relationship: relationship)
|
|
103
|
+
return reference
|
|
104
|
+
end
|
|
94
105
|
|
|
95
|
-
parsed_reference = @configuration.reference_template.parse(
|
|
96
|
-
|
|
106
|
+
parsed_reference = @configuration.reference_template.parse(
|
|
107
|
+
reference,
|
|
108
|
+
context: reference_context(relationship: relationship, document: referring_document)
|
|
109
|
+
)
|
|
110
|
+
effective_scope = @registry.effective_scope_for(
|
|
111
|
+
reference_scope: parsed_reference.scope,
|
|
112
|
+
referring_document: referring_document,
|
|
113
|
+
scope_fields: scope_fields,
|
|
114
|
+
relationship: relationship
|
|
115
|
+
)
|
|
116
|
+
if parsed_reference.document?
|
|
117
|
+
@registry.validate_scope_match!(
|
|
118
|
+
document: parsed_reference.page,
|
|
119
|
+
scope: effective_scope,
|
|
120
|
+
scope_fields: scope_fields,
|
|
121
|
+
relationship: relationship,
|
|
122
|
+
referring_document: referring_document
|
|
123
|
+
)
|
|
124
|
+
return parsed_reference.page
|
|
125
|
+
end
|
|
97
126
|
|
|
98
127
|
@registry.lookup(
|
|
99
128
|
key: parsed_reference.key,
|
|
100
129
|
primary_path: primary_path,
|
|
101
|
-
collection: collection_hint || (parsed_reference.collection.nil? ? nil : parsed_reference.collection.to_s)
|
|
130
|
+
collection: collection_hint || (parsed_reference.collection.nil? ? nil : parsed_reference.collection.to_s),
|
|
131
|
+
scope_fields: scope_fields,
|
|
132
|
+
scope: effective_scope,
|
|
133
|
+
relationship: relationship,
|
|
134
|
+
referring_document: referring_document
|
|
135
|
+
)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Returns the source-derived seed entries for one concrete pair.
|
|
139
|
+
def seed_entries_for(document, to_collection)
|
|
140
|
+
return [] unless active_document?(document)
|
|
141
|
+
|
|
142
|
+
@normal_seed.entries_for(document, to_collection).select do |entry|
|
|
143
|
+
active_document?(entry.fetch(:document))
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# Records one persisted resolver link so it can survive later rounds.
|
|
148
|
+
def persist_link(source_state:, target_document:, metadata:, count:)
|
|
149
|
+
@persisted_links.persist_link(
|
|
150
|
+
source_state: source_state,
|
|
151
|
+
target_document: target_document,
|
|
152
|
+
metadata: metadata,
|
|
153
|
+
count: count
|
|
102
154
|
)
|
|
103
155
|
end
|
|
104
156
|
|
|
157
|
+
# Removes one persisted resolver link.
|
|
158
|
+
def clear_persisted_link(source_state:, target_document:)
|
|
159
|
+
@persisted_links.clear_link(
|
|
160
|
+
source_state: source_state,
|
|
161
|
+
target_document: target_document
|
|
162
|
+
)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Removes every persisted resolver link on one state.
|
|
166
|
+
def clear_all_persisted_links(source_state:)
|
|
167
|
+
@persisted_links.clear_all(source_state: source_state)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Reapplies persisted links only when resolvers did not recreate them.
|
|
171
|
+
def reapply_persisted_links(state:)
|
|
172
|
+
@persisted_links.reapply_missing_links(state: state)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Refreshes remembered persisted-link positions from the final current ordering.
|
|
176
|
+
def refresh_persisted_positions(state:)
|
|
177
|
+
@persisted_links.refresh_positions(state: state)
|
|
178
|
+
end
|
|
179
|
+
|
|
105
180
|
# Mirrors one bidirectional add into the reverse state.
|
|
106
181
|
def mirror_add(source_state:, target_document:, metadata:, count:)
|
|
107
182
|
return unless active_document?(target_document)
|
|
@@ -151,16 +226,12 @@ class Engine
|
|
|
151
226
|
|
|
152
227
|
# Resolves every configured normal relationship state once.
|
|
153
228
|
def resolve_all_relationships!
|
|
154
|
-
@
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
resolve_relationships(document, definition.to_collection)
|
|
161
|
-
end
|
|
162
|
-
end
|
|
163
|
-
end
|
|
229
|
+
@defer_bidirectional_mirror_resolvers = true
|
|
230
|
+
resolve_all_relationships_pass!
|
|
231
|
+
@defer_bidirectional_mirror_resolvers = false
|
|
232
|
+
resolve_deferred_bidirectional_mirror_resolvers!
|
|
233
|
+
ensure
|
|
234
|
+
@defer_bidirectional_mirror_resolvers = false
|
|
164
235
|
end
|
|
165
236
|
|
|
166
237
|
# Writes the current session's resolved relationships back to frontmatter.
|
|
@@ -170,6 +241,33 @@ class Engine
|
|
|
170
241
|
|
|
171
242
|
private
|
|
172
243
|
|
|
244
|
+
# Builds consistent relationship and source-document details for reference parsing errors.
|
|
245
|
+
def reference_context(relationship:, document:)
|
|
246
|
+
parts = []
|
|
247
|
+
parts << "Relationship `#{relationship}`" unless relationship.nil? || relationship.to_s.empty?
|
|
248
|
+
parts << "reference on document `#{document.relative_path}`" if document
|
|
249
|
+
parts.join(' ')
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
# Seeds every normal relationship state before any resolver mutates the graph.
|
|
253
|
+
#
|
|
254
|
+
# Bidirectional removals can target reverse states that have not been
|
|
255
|
+
# resolved yet. Seeding the whole current graph up front ensures those
|
|
256
|
+
# reverse states already contain their direct seed links before any resolver
|
|
257
|
+
# adds or removes mirrored relationships.
|
|
258
|
+
def ensure_initial_links_seeded!
|
|
259
|
+
return if @initial_links_seeded
|
|
260
|
+
|
|
261
|
+
@configuration.collections.each do |collection|
|
|
262
|
+
@configuration.normal_relationships_for(collection).each do |definition|
|
|
263
|
+
documents_for(collection).each do |document|
|
|
264
|
+
relationship_state(document, definition.to_collection)&.ensure_seeded!
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
@initial_links_seeded = true
|
|
269
|
+
end
|
|
270
|
+
|
|
173
271
|
# Resolves one relationship state with cycle detection.
|
|
174
272
|
def resolve_state(state)
|
|
175
273
|
return state if state.resolved?
|
|
@@ -178,6 +276,48 @@ class Engine
|
|
|
178
276
|
state.resolve!
|
|
179
277
|
state
|
|
180
278
|
end
|
|
279
|
+
|
|
280
|
+
# Resolves every configured normal relationship state in collection and sequence order.
|
|
281
|
+
def resolve_all_relationships_pass!
|
|
282
|
+
@configuration.collections.each do |collection|
|
|
283
|
+
definitions = @configuration.normal_relationships_for(collection)
|
|
284
|
+
next if definitions.empty?
|
|
285
|
+
|
|
286
|
+
documents_for(collection).each do |document|
|
|
287
|
+
definitions.each do |definition|
|
|
288
|
+
resolve_relationships(document, definition.to_collection)
|
|
289
|
+
end
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
# Runs deferred resolvers for inverse bidirectional mirrors after forward resolution settles.
|
|
295
|
+
def resolve_deferred_bidirectional_mirror_resolvers!
|
|
296
|
+
@configuration.collections.each do |collection|
|
|
297
|
+
definitions = @configuration.normal_relationships_for(collection).select do |definition|
|
|
298
|
+
deferred_bidirectional_mirror_definition?(definition)
|
|
299
|
+
end
|
|
300
|
+
next if definitions.empty?
|
|
301
|
+
|
|
302
|
+
documents_for(collection).each do |document|
|
|
303
|
+
definitions.each do |definition|
|
|
304
|
+
resolve_relationships(document, definition.to_collection)
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
end
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
# Returns true when one definition should run in the deferred mirror-resolver pass.
|
|
311
|
+
def deferred_bidirectional_mirror_definition?(definition)
|
|
312
|
+
definition.bidirectional_mirror? && !definition.resolver_classes.empty?
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
# Returns true when this state should expose current links now but defer running its resolver.
|
|
316
|
+
def defer_bidirectional_mirror_resolver_for?(state)
|
|
317
|
+
return false unless @defer_bidirectional_mirror_resolvers
|
|
318
|
+
|
|
319
|
+
deferred_bidirectional_mirror_definition?(state.definition)
|
|
320
|
+
end
|
|
181
321
|
end
|
|
182
322
|
end
|
|
183
323
|
|
|
@@ -9,8 +9,8 @@ class Engine
|
|
|
9
9
|
|
|
10
10
|
# Writes resolved normal relationships back into document frontmatter.
|
|
11
11
|
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
12
|
+
# Each relationship writes one final resolved link set to its configured
|
|
13
|
+
# output path. Any separate ingestion paths are left untouched.
|
|
14
14
|
class WriteBack
|
|
15
15
|
# Builds one write-back helper for the current engine instance.
|
|
16
16
|
def initialize(engine:)
|
|
@@ -37,7 +37,6 @@ class Engine
|
|
|
37
37
|
# Writes every normal relationship path for one document.
|
|
38
38
|
def write_document_relationships(document:, definitions:)
|
|
39
39
|
output_paths = {}
|
|
40
|
-
input_paths = {}
|
|
41
40
|
|
|
42
41
|
definitions.each do |definition|
|
|
43
42
|
state = @engine.relationship_state(document, definition.to_collection)
|
|
@@ -46,13 +45,6 @@ class Engine
|
|
|
46
45
|
output_path = definition.final_output_path
|
|
47
46
|
output_paths[output_path] ||= []
|
|
48
47
|
output_paths[output_path] << state
|
|
49
|
-
definition.foreign_paths.each do |path|
|
|
50
|
-
input_paths[path] ||= {
|
|
51
|
-
raw_state: @engine.raw_path_state(document, path),
|
|
52
|
-
definitions: []
|
|
53
|
-
}
|
|
54
|
-
input_paths[path][:definitions] << definition
|
|
55
|
-
end
|
|
56
48
|
end
|
|
57
49
|
|
|
58
50
|
output_paths.each do |path, states|
|
|
@@ -74,31 +66,6 @@ class Engine
|
|
|
74
66
|
}
|
|
75
67
|
)
|
|
76
68
|
end
|
|
77
|
-
|
|
78
|
-
input_paths.each do |path, input_path_state|
|
|
79
|
-
raw_state = input_path_state.fetch(:raw_state)
|
|
80
|
-
next if output_paths.key?(path)
|
|
81
|
-
next unless raw_state.present?
|
|
82
|
-
|
|
83
|
-
raw_state.write_upgraded_input!(
|
|
84
|
-
registry: @registry,
|
|
85
|
-
active_document_checker: proc { |resolved_document| active_document?(resolved_document) }
|
|
86
|
-
)
|
|
87
|
-
value = raw_state.upgraded_raw_value(
|
|
88
|
-
registry: @registry,
|
|
89
|
-
active_document_checker: proc { |resolved_document| active_document?(resolved_document) }
|
|
90
|
-
)
|
|
91
|
-
@engine.debug_logger.document_event(
|
|
92
|
-
document: document,
|
|
93
|
-
definitions: input_path_state.fetch(:definitions),
|
|
94
|
-
area: 'upgrading',
|
|
95
|
-
event: 'upgrade_input',
|
|
96
|
-
details: {
|
|
97
|
-
path: path,
|
|
98
|
-
value: value
|
|
99
|
-
}
|
|
100
|
-
)
|
|
101
|
-
end
|
|
102
69
|
end
|
|
103
70
|
|
|
104
71
|
# Rejects any consolidated output path that expands through arrays.
|
|
@@ -123,6 +90,7 @@ class Engine
|
|
|
123
90
|
accumulator.add(
|
|
124
91
|
document: entry.fetch(:document),
|
|
125
92
|
key: entry.fetch(:key),
|
|
93
|
+
scope: entry.fetch(:scope),
|
|
126
94
|
metadata: entry.fetch(:metadata),
|
|
127
95
|
count: entry.fetch(:count)
|
|
128
96
|
)
|
|
@@ -138,13 +106,6 @@ class Engine
|
|
|
138
106
|
|
|
139
107
|
@registry.documents_for(collection)
|
|
140
108
|
end
|
|
141
|
-
|
|
142
|
-
# Returns true when one document is active in the current engine or session.
|
|
143
|
-
def active_document?(document)
|
|
144
|
-
return @engine.active_document?(document) if @engine.respond_to?(:active_document?)
|
|
145
|
-
|
|
146
|
-
true
|
|
147
|
-
end
|
|
148
109
|
end
|
|
149
110
|
end
|
|
150
111
|
|