jekyll-relationships 0.1.0.alpha → 0.1.1.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 +2 -1
- data/lib/jekyll-relationships/configuration/tree_frontmatter.rb +8 -2
- data/lib/jekyll-relationships/debug_logger.rb +94 -3
- data/lib/jekyll-relationships/definitions/normal_relationship.rb +5 -0
- data/lib/jekyll-relationships/definitions/tree_relationship.rb +5 -0
- data/lib/jekyll-relationships/engine/normal_seed.rb +217 -0
- data/lib/jekyll-relationships/engine/persisted_links.rb +365 -0
- data/lib/jekyll-relationships/engine/raw_path_state.rb +6 -98
- data/lib/jekyll-relationships/engine/relationship_state.rb +55 -56
- data/lib/jekyll-relationships/engine/session.rb +70 -4
- data/lib/jekyll-relationships/engine/write_back.rb +2 -42
- data/lib/jekyll-relationships/engine.rb +167 -45
- data/lib/jekyll-relationships/pruning/tree_phase.rb +102 -116
- data/lib/jekyll-relationships/references/accumulator.rb +71 -5
- data/lib/jekyll-relationships/resolvers/base.rb +56 -2
- data/lib/jekyll-relationships/trees/graph.rb +105 -4
- data/lib/jekyll-relationships/trees/root_distances.rb +44 -0
- data/lib/jekyll-relationships/version.rb +1 -1
- data/readme.md +33 -5
- metadata +5 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f4f49c60e8c57f7661b7fdec54b0d4d8ce2e9564dca858e86126ed2f810bfdec
|
|
4
|
+
data.tar.gz: f0c22961ff04cc0493e923676d45a9de3c69ff6720f72160e7897bf921272687
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d4b352ce61991bf340b239a9aa881e1314274371637a96e24f317597a6343fb5bebb763fbe6b60f31d8877b3e01f1e4bfc5076994382940f08a757906f632933
|
|
7
|
+
data.tar.gz: 1ec40c78e1bf65e5781b580c4effc37e5510424031267c120a08be6bb6a0c5ffc1f36ccffa1fc4817b6c1ddc451fe996947b4403af48e36db7112d875dcd0bc2
|
|
@@ -23,12 +23,32 @@ class Configuration
|
|
|
23
23
|
'resolvers' => 'resolver execution and helper calls',
|
|
24
24
|
'trees' => 'tree discovery, edge handling, and tree write-back'
|
|
25
25
|
}.freeze
|
|
26
|
+
HASH_KEYS = %w[ids log].freeze
|
|
26
27
|
|
|
27
28
|
ALL_AREAS = AREAS.keys.freeze
|
|
28
29
|
|
|
29
30
|
class << self
|
|
30
31
|
# Builds one explicit debug setting from a loose config value.
|
|
31
32
|
def build(value:, string_array:, context:)
|
|
33
|
+
return build_hash_value(value: value, string_array: string_array, context: context) if value.is_a?(Hash)
|
|
34
|
+
|
|
35
|
+
build_scalar_value(value: value, string_array: string_array, context: context)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Returns one disabled debug setting.
|
|
39
|
+
def disabled
|
|
40
|
+
new([])
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Returns one debug setting that enables every area.
|
|
44
|
+
def all
|
|
45
|
+
new(ALL_AREAS)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
# Builds one debug setting from the legacy scalar forms.
|
|
51
|
+
def build_scalar_value(value:, string_array:, context:)
|
|
32
52
|
return disabled if value.nil? || false_value?(value)
|
|
33
53
|
return all if true_value?(value)
|
|
34
54
|
|
|
@@ -45,18 +65,30 @@ class Configuration
|
|
|
45
65
|
new(areas.uniq)
|
|
46
66
|
end
|
|
47
67
|
|
|
48
|
-
#
|
|
49
|
-
def
|
|
50
|
-
|
|
51
|
-
|
|
68
|
+
# Builds one debug setting from the new hash form with optional ID filtering.
|
|
69
|
+
def build_hash_value(value:, string_array:, context:)
|
|
70
|
+
hash_value = stringify_hash(value)
|
|
71
|
+
unknown_keys = hash_value.keys - HASH_KEYS
|
|
72
|
+
unless unknown_keys.empty?
|
|
73
|
+
raise ConfigurationError, "`#{context}` contains unsupported keys #{unknown_keys.sort.join(', ')}. Supported keys: #{HASH_KEYS.join(', ')}."
|
|
74
|
+
end
|
|
52
75
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
76
|
+
log_context = hash_value.key?('log') ? "#{context}.log" : context
|
|
77
|
+
scalar_setting = build_scalar_value(
|
|
78
|
+
value: hash_value.key?('log') ? hash_value.fetch('log') : true,
|
|
79
|
+
string_array: string_array,
|
|
80
|
+
context: log_context
|
|
81
|
+
)
|
|
82
|
+
new(
|
|
83
|
+
scalar_setting.areas,
|
|
84
|
+
parse_ids(
|
|
85
|
+
value: hash_value['ids'],
|
|
86
|
+
string_array: string_array,
|
|
87
|
+
context: "#{context}.ids"
|
|
88
|
+
)
|
|
89
|
+
)
|
|
56
90
|
end
|
|
57
91
|
|
|
58
|
-
private
|
|
59
|
-
|
|
60
92
|
# Returns true when a loose value means "debug everything".
|
|
61
93
|
def true_value?(value)
|
|
62
94
|
value == true || value.to_s.strip.casecmp('true').zero?
|
|
@@ -83,13 +115,34 @@ class Configuration
|
|
|
83
115
|
raise ConfigurationError,
|
|
84
116
|
"`#{context}` must be true, false, `all`, or a comma-delimited string/array of debug areas: #{ALL_AREAS.join(', ')}."
|
|
85
117
|
end
|
|
118
|
+
|
|
119
|
+
# Parses one optional string-or-array ID filter.
|
|
120
|
+
def parse_ids(value:, string_array:, context:)
|
|
121
|
+
return [] if value.nil?
|
|
122
|
+
|
|
123
|
+
raw_ids = string_array.interpret(value, split: true, flatten: true)
|
|
124
|
+
raise ConfigurationError, "`#{context}` must be a string or array of strings." if raw_ids.empty?
|
|
125
|
+
|
|
126
|
+
ids = raw_ids.map { |raw_id| raw_id.to_s.strip }.reject(&:empty?).uniq
|
|
127
|
+
raise ConfigurationError, "`#{context}` must contain at least one non-blank ID." if ids.empty?
|
|
128
|
+
|
|
129
|
+
ids
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Converts one config hash to a shallow string-keyed copy.
|
|
133
|
+
def stringify_hash(hash)
|
|
134
|
+
hash.each_with_object({}) do |(key, value), stringified|
|
|
135
|
+
stringified[key.to_s] = value
|
|
136
|
+
end
|
|
137
|
+
end
|
|
86
138
|
end
|
|
87
139
|
|
|
88
|
-
attr_reader :areas
|
|
140
|
+
attr_reader :areas, :ids
|
|
89
141
|
|
|
90
|
-
# Captures one immutable set of enabled debug areas.
|
|
91
|
-
def initialize(areas)
|
|
142
|
+
# Captures one immutable set of enabled debug areas and optional ID filters.
|
|
143
|
+
def initialize(areas, ids = [])
|
|
92
144
|
@areas = areas.freeze
|
|
145
|
+
@ids = Array(ids).map(&:to_s).uniq.freeze
|
|
93
146
|
end
|
|
94
147
|
|
|
95
148
|
# Returns true when any debug area is enabled, or when one named area is enabled.
|
|
@@ -99,6 +152,13 @@ class Configuration
|
|
|
99
152
|
@areas.include?(normalise_runtime_area(area))
|
|
100
153
|
end
|
|
101
154
|
|
|
155
|
+
# Returns true when this setting either has no ID filter or overlaps one.
|
|
156
|
+
def matches_ids?(related_ids)
|
|
157
|
+
return true if @ids.empty?
|
|
158
|
+
|
|
159
|
+
Array(related_ids).map(&:to_s).any? { |related_id| @ids.include?(related_id) }
|
|
160
|
+
end
|
|
161
|
+
|
|
102
162
|
private
|
|
103
163
|
|
|
104
164
|
# Normalises one runtime area name and raises on internal typos.
|
|
@@ -14,7 +14,7 @@ class Configuration
|
|
|
14
14
|
# The resolved helper exposes explicit input and output paths so the tree
|
|
15
15
|
# graph never has to reimplement config merging rules.
|
|
16
16
|
class TreeFrontmatter
|
|
17
|
-
PATH_NAMES = %w[parent child parents children ancestors descendants].freeze
|
|
17
|
+
PATH_NAMES = %w[parent child parents children ancestors descendants depth].freeze
|
|
18
18
|
|
|
19
19
|
# Builds one tree-frontmatter helper from the resolved values.
|
|
20
20
|
def initialize(base:, raw_paths:, raw_output_path:, string_array:)
|
|
@@ -118,6 +118,11 @@ class Configuration
|
|
|
118
118
|
first_path_for('descendants')
|
|
119
119
|
end
|
|
120
120
|
|
|
121
|
+
# Returns the absolute prevailing depth output path.
|
|
122
|
+
def depth_output_path
|
|
123
|
+
first_path_for('depth')
|
|
124
|
+
end
|
|
125
|
+
|
|
121
126
|
# Returns the absolute output container path, if one is configured.
|
|
122
127
|
def output_path
|
|
123
128
|
return nil if blank_path?(@raw_output_path)
|
|
@@ -131,7 +136,7 @@ class Configuration
|
|
|
131
136
|
# The configured relative tree paths become nested keys within the output
|
|
132
137
|
# hash, while the configured output container path becomes the single
|
|
133
138
|
# frontmatter location on which that hash is written.
|
|
134
|
-
def output_payload(parent_value:, parents_value:, child_value:, children_value:, ancestors_value:, descendants_value:, max_parents:, max_children:)
|
|
139
|
+
def output_payload(parent_value:, parents_value:, child_value:, children_value:, ancestors_value:, descendants_value:, depth_value:, max_parents:, max_children:)
|
|
135
140
|
payload = {}
|
|
136
141
|
data_path = Jekyll::Plugins::Relationships::Support::DataPath.new
|
|
137
142
|
|
|
@@ -149,6 +154,7 @@ class Configuration
|
|
|
149
154
|
|
|
150
155
|
data_path.write(payload, first_relative_path_for('ancestors'), ancestors_value)
|
|
151
156
|
data_path.write(payload, first_relative_path_for('descendants'), descendants_value)
|
|
157
|
+
data_path.write(payload, first_relative_path_for('depth'), depth_value)
|
|
152
158
|
payload
|
|
153
159
|
end
|
|
154
160
|
|
|
@@ -11,10 +11,30 @@ module Relationships
|
|
|
11
11
|
# Jekyll logger. Callers should pass only already-resolved runtime state.
|
|
12
12
|
class DebugLogger
|
|
13
13
|
MAX_VALUE_LENGTH = 500
|
|
14
|
+
STRING_ID_PROPERTIES = %w[
|
|
15
|
+
id
|
|
16
|
+
key
|
|
17
|
+
target_key
|
|
18
|
+
reference
|
|
19
|
+
result
|
|
20
|
+
references
|
|
21
|
+
entries
|
|
22
|
+
value
|
|
23
|
+
parent_value
|
|
24
|
+
child_value
|
|
25
|
+
ancestors_value
|
|
26
|
+
descendants_value
|
|
27
|
+
].freeze
|
|
28
|
+
|
|
29
|
+
# Builds one logger with the active reference key property for hash parsing.
|
|
30
|
+
def initialize(reference_key_property:)
|
|
31
|
+
@reference_key_property = reference_key_property.to_s
|
|
32
|
+
@data_path = Jekyll::Plugins::Relationships::Support::DataPath.new
|
|
33
|
+
end
|
|
14
34
|
|
|
15
35
|
# Emits one debug line for one normal relationship state.
|
|
16
36
|
def relationship_event(document:, definition:, area:, event:, details: {})
|
|
17
|
-
return unless
|
|
37
|
+
return unless should_log?(definition: definition, area: area, document: document, details: details)
|
|
18
38
|
|
|
19
39
|
log(
|
|
20
40
|
area: area,
|
|
@@ -25,7 +45,9 @@ class DebugLogger
|
|
|
25
45
|
|
|
26
46
|
# Emits one debug line for a document-level write-back event.
|
|
27
47
|
def document_event(document:, definitions:, area:, event:, details: {})
|
|
28
|
-
debug_definitions = Array(definitions).select
|
|
48
|
+
debug_definitions = Array(definitions).select do |definition|
|
|
49
|
+
should_log?(definition: definition, area: area, document: document, details: details)
|
|
50
|
+
end
|
|
29
51
|
return if debug_definitions.empty?
|
|
30
52
|
|
|
31
53
|
log(
|
|
@@ -41,7 +63,7 @@ class DebugLogger
|
|
|
41
63
|
|
|
42
64
|
# Emits one debug line for one tree relationship event.
|
|
43
65
|
def tree_event(document:, definition:, area:, event:, details: {})
|
|
44
|
-
return unless
|
|
66
|
+
return unless should_log?(definition: definition, area: area, document: document, details: details)
|
|
45
67
|
|
|
46
68
|
log(
|
|
47
69
|
area: area,
|
|
@@ -62,6 +84,21 @@ class DebugLogger
|
|
|
62
84
|
|
|
63
85
|
private
|
|
64
86
|
|
|
87
|
+
# Returns true when one definition wants this area and its ID filter matches.
|
|
88
|
+
def should_log?(definition:, area:, document:, details:)
|
|
89
|
+
return false unless definition.debug?(area)
|
|
90
|
+
|
|
91
|
+
definition.debug_ids_match?(related_ids_for(definition: definition, document: document, details: details))
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Collects every document or reference ID that one event obviously touches.
|
|
95
|
+
def related_ids_for(definition:, document:, details:)
|
|
96
|
+
([document_key(document, primary_path: definition.primary_path)] + extract_related_ids(
|
|
97
|
+
details,
|
|
98
|
+
primary_path: definition.primary_path
|
|
99
|
+
)).compact.uniq
|
|
100
|
+
end
|
|
101
|
+
|
|
65
102
|
# Emits one line through the standard Jekyll logger.
|
|
66
103
|
def log(area:, prefix:, details:)
|
|
67
104
|
detail_text = details.each_with_object([]) do |(key, value), parts|
|
|
@@ -72,6 +109,60 @@ class DebugLogger
|
|
|
72
109
|
Jekyll.logger.info('Relationships:', "[debug:#{area}] #{message}")
|
|
73
110
|
end
|
|
74
111
|
|
|
112
|
+
# Resolves one document back to the primary key used by the active definition.
|
|
113
|
+
def document_key(document, primary_path:)
|
|
114
|
+
return nil unless document.is_a?(Jekyll::Document)
|
|
115
|
+
|
|
116
|
+
if primary_path.nil?
|
|
117
|
+
document.relative_path.sub(/\A_/, '').sub(/#{Regexp.escape(document.extname)}\z/, '')
|
|
118
|
+
else
|
|
119
|
+
value = @data_path.read(document.data, primary_path)
|
|
120
|
+
value.nil? ? nil : value.to_s
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Walks one debug payload and extracts any obvious relationship IDs from it.
|
|
125
|
+
def extract_related_ids(value, primary_path:, property_name: nil)
|
|
126
|
+
case value
|
|
127
|
+
when Jekyll::Document
|
|
128
|
+
[document_key(value, primary_path: primary_path)].compact
|
|
129
|
+
when Array
|
|
130
|
+
value.flat_map do |item|
|
|
131
|
+
extract_related_ids(item, primary_path: primary_path, property_name: property_name)
|
|
132
|
+
end
|
|
133
|
+
when Hash
|
|
134
|
+
extract_related_ids_from_hash(value, primary_path: primary_path)
|
|
135
|
+
when String
|
|
136
|
+
return [] unless property_name && STRING_ID_PROPERTIES.include?(property_name.to_s)
|
|
137
|
+
|
|
138
|
+
trimmed_value = value.strip
|
|
139
|
+
trimmed_value.empty? ? [] : [trimmed_value]
|
|
140
|
+
else
|
|
141
|
+
[]
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Reads likely ID-bearing properties from one debug hash before recurring.
|
|
146
|
+
def extract_related_ids_from_hash(hash, primary_path:)
|
|
147
|
+
ids = []
|
|
148
|
+
hash.each do |key, value|
|
|
149
|
+
string_key = key.to_s
|
|
150
|
+
if string_key == @reference_key_property || string_key == 'id'
|
|
151
|
+
trimmed_value = value.to_s.strip
|
|
152
|
+
ids << trimmed_value unless trimmed_value.empty?
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
ids.concat(
|
|
156
|
+
extract_related_ids(
|
|
157
|
+
value,
|
|
158
|
+
primary_path: primary_path,
|
|
159
|
+
property_name: string_key
|
|
160
|
+
)
|
|
161
|
+
)
|
|
162
|
+
end
|
|
163
|
+
ids.uniq
|
|
164
|
+
end
|
|
165
|
+
|
|
75
166
|
# Normalises values so large document objects stay readable in logs.
|
|
76
167
|
def normalise_value(value)
|
|
77
168
|
case value
|
|
@@ -38,6 +38,11 @@ class NormalRelationship
|
|
|
38
38
|
@debug.enabled?(area)
|
|
39
39
|
end
|
|
40
40
|
|
|
41
|
+
# Returns true when one event's related IDs satisfy this pair's debug filter.
|
|
42
|
+
def debug_ids_match?(ids)
|
|
43
|
+
@debug.matches_ids?(ids)
|
|
44
|
+
end
|
|
45
|
+
|
|
41
46
|
# Registers one resolver class against this relationship pair.
|
|
42
47
|
def add_resolver(resolver_class)
|
|
43
48
|
@resolver_classes << resolver_class
|
|
@@ -35,6 +35,11 @@ class TreeRelationship
|
|
|
35
35
|
@debug.enabled?(area)
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
+
# Returns true when one event's related IDs satisfy this definition's debug filter.
|
|
39
|
+
def debug_ids_match?(ids)
|
|
40
|
+
@debug.matches_ids?(ids)
|
|
41
|
+
end
|
|
42
|
+
|
|
38
43
|
# Returns true when one parent-child direction is permitted.
|
|
39
44
|
def allows_parent_child?(parent_collection:, child_collection:)
|
|
40
45
|
@parent_child_pairs.include?([parent_collection, child_collection])
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jekyll
|
|
4
|
+
module Plugins
|
|
5
|
+
|
|
6
|
+
module Relationships
|
|
7
|
+
|
|
8
|
+
class Engine
|
|
9
|
+
|
|
10
|
+
# Stores the source-derived normal relationship graph used to seed each round.
|
|
11
|
+
#
|
|
12
|
+
# The seed graph is built once from frontmatter, including deterministic
|
|
13
|
+
# bidirectional mirrors, and is later reduced only by document removals.
|
|
14
|
+
# Resolver output never flows back into this structure.
|
|
15
|
+
class NormalSeed
|
|
16
|
+
# Builds one normal seed graph for the current active document set.
|
|
17
|
+
def initialize(engine:, active_document_ids:)
|
|
18
|
+
@engine = engine
|
|
19
|
+
@configuration = engine.configuration
|
|
20
|
+
@registry = engine.registry
|
|
21
|
+
@data_path = engine.data_path
|
|
22
|
+
@debug_logger = engine.debug_logger
|
|
23
|
+
@active_document_ids = active_document_ids.each_with_object({}) do |document_id, active_ids|
|
|
24
|
+
active_ids[document_id] = true
|
|
25
|
+
end
|
|
26
|
+
@string_array = Jekyll::Plugins::Relationships::Support::StringArray.new
|
|
27
|
+
@raw_path_states = {}
|
|
28
|
+
@entries_by_state = {}
|
|
29
|
+
build!
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Returns the seed entries for one concrete document-to-collection pair.
|
|
33
|
+
def entries_for(document, to_collection)
|
|
34
|
+
duplicate_entries(@entries_by_state[[document.object_id, to_collection]])
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Permanently removes documents from the seed graph in both source and target positions.
|
|
38
|
+
def remove_documents!(documents)
|
|
39
|
+
removed_document_ids = Array(documents).each_with_object({}) do |document, lookup|
|
|
40
|
+
lookup[document.object_id] = true
|
|
41
|
+
end
|
|
42
|
+
return if removed_document_ids.empty?
|
|
43
|
+
|
|
44
|
+
@entries_by_state.delete_if do |(source_document_id, _to_collection), _entries|
|
|
45
|
+
removed_document_ids.key?(source_document_id)
|
|
46
|
+
end
|
|
47
|
+
@entries_by_state.each_value do |entries|
|
|
48
|
+
entries.reject! do |entry|
|
|
49
|
+
removed_document_ids.key?(entry.fetch(:document).object_id)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
# Builds the full source-derived seed graph once.
|
|
57
|
+
def build!
|
|
58
|
+
accumulators = {}
|
|
59
|
+
|
|
60
|
+
@configuration.collections.each do |collection|
|
|
61
|
+
@configuration.normal_relationships_for(collection).each do |definition|
|
|
62
|
+
next unless definition.reads_frontmatter
|
|
63
|
+
|
|
64
|
+
documents_for(collection).each do |document|
|
|
65
|
+
seed_definition_from_frontmatter(
|
|
66
|
+
accumulators: accumulators,
|
|
67
|
+
document: document,
|
|
68
|
+
definition: definition
|
|
69
|
+
)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
@entries_by_state = accumulators.each_with_object({}) do |(state_key, accumulator), entries_by_state|
|
|
75
|
+
entries_by_state[state_key] = accumulator.encounter_entries
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Reads and stores one concrete direct relationship definition from frontmatter.
|
|
80
|
+
def seed_definition_from_frontmatter(accumulators:, document:, definition:)
|
|
81
|
+
definition.foreign_paths.each do |path|
|
|
82
|
+
raw_state = raw_path_state(document, path)
|
|
83
|
+
debug_relationship_event(
|
|
84
|
+
document: document,
|
|
85
|
+
definition: definition,
|
|
86
|
+
event: 'raw_path',
|
|
87
|
+
details: {
|
|
88
|
+
path: path,
|
|
89
|
+
present: raw_state.present?,
|
|
90
|
+
value: raw_state.raw_value
|
|
91
|
+
}
|
|
92
|
+
)
|
|
93
|
+
next unless raw_state.present?
|
|
94
|
+
|
|
95
|
+
resolved_entries = raw_state.resolved_entries_for(
|
|
96
|
+
primary_path: definition.primary_path,
|
|
97
|
+
registry: @registry,
|
|
98
|
+
active_document_checker: proc { |resolved_document| active_document?(resolved_document) }
|
|
99
|
+
)
|
|
100
|
+
debug_relationship_event(
|
|
101
|
+
document: document,
|
|
102
|
+
definition: definition,
|
|
103
|
+
event: 'raw_path_resolved',
|
|
104
|
+
details: {
|
|
105
|
+
path: path,
|
|
106
|
+
entries: resolved_entries.compact
|
|
107
|
+
}
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
resolved_entries.each do |entry|
|
|
111
|
+
next unless entry
|
|
112
|
+
if entry.fetch(:document).collection.label != definition.to_collection
|
|
113
|
+
debug_relationship_event(
|
|
114
|
+
document: document,
|
|
115
|
+
definition: definition,
|
|
116
|
+
event: 'raw_path_skipped',
|
|
117
|
+
details: {
|
|
118
|
+
path: path,
|
|
119
|
+
target: entry.fetch(:document),
|
|
120
|
+
target_key: entry.fetch(:key),
|
|
121
|
+
actual_collection: entry.fetch(:document).collection.label,
|
|
122
|
+
expected_collection: definition.to_collection
|
|
123
|
+
}
|
|
124
|
+
)
|
|
125
|
+
next
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
add_direct_entry(
|
|
129
|
+
accumulators: accumulators,
|
|
130
|
+
document: document,
|
|
131
|
+
definition: definition,
|
|
132
|
+
entry: entry
|
|
133
|
+
)
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Adds one direct link and its deterministic bidirectional mirror when configured.
|
|
139
|
+
def add_direct_entry(accumulators:, document:, definition:, entry:)
|
|
140
|
+
accumulator_for(
|
|
141
|
+
accumulators: accumulators,
|
|
142
|
+
document: document,
|
|
143
|
+
to_collection: definition.to_collection
|
|
144
|
+
).add(
|
|
145
|
+
document: entry.fetch(:document),
|
|
146
|
+
key: entry.fetch(:key),
|
|
147
|
+
metadata: entry.fetch(:metadata),
|
|
148
|
+
count: entry.fetch(:count)
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
return unless definition.bidirectional
|
|
152
|
+
|
|
153
|
+
accumulator_for(
|
|
154
|
+
accumulators: accumulators,
|
|
155
|
+
document: entry.fetch(:document),
|
|
156
|
+
to_collection: definition.from_collection
|
|
157
|
+
).add(
|
|
158
|
+
document: document,
|
|
159
|
+
key: @registry.key_for(document, primary_path: definition.primary_path),
|
|
160
|
+
metadata: entry.fetch(:metadata),
|
|
161
|
+
count: entry.fetch(:count)
|
|
162
|
+
)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Returns one reusable accumulator for one concrete source pair.
|
|
166
|
+
def accumulator_for(accumulators:, document:, to_collection:)
|
|
167
|
+
accumulators[[document.object_id, to_collection]] ||= Jekyll::Plugins::Relationships::References::Accumulator.new(
|
|
168
|
+
reference_template: @configuration.reference_template,
|
|
169
|
+
multiple_settings: @configuration.multiple_settings
|
|
170
|
+
)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Returns every active document for one collection.
|
|
174
|
+
def documents_for(collection)
|
|
175
|
+
@registry.documents_for(collection).select do |document|
|
|
176
|
+
active_document?(document)
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Returns true when one document is still active in the seed graph.
|
|
181
|
+
def active_document?(document)
|
|
182
|
+
@active_document_ids.key?(document.object_id)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Returns the cached raw-path parser for one document/path pair.
|
|
186
|
+
def raw_path_state(document, path)
|
|
187
|
+
@raw_path_states[[document.object_id, path]] ||= RawPathState.new(
|
|
188
|
+
document: document,
|
|
189
|
+
path: path,
|
|
190
|
+
data_path: @data_path,
|
|
191
|
+
string_array: @string_array,
|
|
192
|
+
reference_template: @configuration.reference_template
|
|
193
|
+
)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# Duplicates one stored seed-entry array defensively.
|
|
197
|
+
def duplicate_entries(entries)
|
|
198
|
+
Array(entries).map(&:dup)
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# Emits one debug event while the seed graph is being built.
|
|
202
|
+
def debug_relationship_event(document:, definition:, event:, details:)
|
|
203
|
+
@debug_logger.relationship_event(
|
|
204
|
+
document: document,
|
|
205
|
+
definition: definition,
|
|
206
|
+
area: 'upgrading',
|
|
207
|
+
event: event,
|
|
208
|
+
details: details
|
|
209
|
+
)
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
end
|
|
217
|
+
end
|