jekyll-relationships 0.1.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 +7 -0
- data/lib/jekyll-relationships/configuration/debug_setting.rb +117 -0
- data/lib/jekyll-relationships/configuration/defaults.rb +54 -0
- data/lib/jekyll-relationships/configuration/frontmatter.rb +102 -0
- data/lib/jekyll-relationships/configuration/hash_utilities.rb +55 -0
- data/lib/jekyll-relationships/configuration/multiple_settings.rb +101 -0
- data/lib/jekyll-relationships/configuration/parser.rb +551 -0
- data/lib/jekyll-relationships/configuration/prune_rule_settings.rb +101 -0
- data/lib/jekyll-relationships/configuration/prune_settings.rb +82 -0
- data/lib/jekyll-relationships/configuration/tree_frontmatter.rb +244 -0
- data/lib/jekyll-relationships/configuration/tree_settings.rb +74 -0
- data/lib/jekyll-relationships/configuration.rb +250 -0
- data/lib/jekyll-relationships/debug_logger.rb +104 -0
- data/lib/jekyll-relationships/definitions/configured_relationship.rb +61 -0
- data/lib/jekyll-relationships/definitions/normal_relationship.rb +51 -0
- data/lib/jekyll-relationships/definitions/prune_rule.rb +67 -0
- data/lib/jekyll-relationships/definitions/tree_relationship.rb +48 -0
- data/lib/jekyll-relationships/documents/registry.rb +136 -0
- data/lib/jekyll-relationships/engine/raw_path_state.rb +217 -0
- data/lib/jekyll-relationships/engine/relationship_state.rb +251 -0
- data/lib/jekyll-relationships/engine/session.rb +187 -0
- data/lib/jekyll-relationships/engine/write_back.rb +154 -0
- data/lib/jekyll-relationships/engine.rb +228 -0
- data/lib/jekyll-relationships/errors.rb +30 -0
- data/lib/jekyll-relationships/generators/relationships.rb +27 -0
- data/lib/jekyll-relationships/pruning/normal_graph.rb +119 -0
- data/lib/jekyll-relationships/pruning/rule_pruner.rb +67 -0
- data/lib/jekyll-relationships/pruning/tree_phase.rb +351 -0
- data/lib/jekyll-relationships/pruning/tree_provenance.rb +32 -0
- data/lib/jekyll-relationships/references/accumulator.rb +185 -0
- data/lib/jekyll-relationships/references/template.rb +222 -0
- data/lib/jekyll-relationships/resolvers/base.rb +207 -0
- data/lib/jekyll-relationships/run_logger.rb +101 -0
- data/lib/jekyll-relationships/support/data_path.rb +194 -0
- data/lib/jekyll-relationships/support/frontmatter_path.rb +217 -0
- data/lib/jekyll-relationships/support/hash_deep_merge.rb +63 -0
- data/lib/jekyll-relationships/support/placeholders.rb +21 -0
- data/lib/jekyll-relationships/support/string_array.rb +157 -0
- data/lib/jekyll-relationships/trees/edge_builder.rb +196 -0
- data/lib/jekyll-relationships/trees/graph.rb +454 -0
- data/lib/jekyll-relationships/version.rb +11 -0
- data/lib/jekyll-relationships.rb +34 -0
- data/readme.md +509 -0
- metadata +189 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 9656850968f173e997fe9e9a0b5500f12682c94d613f84444827bc5e52aa955a
|
|
4
|
+
data.tar.gz: e56a5ec1a9a44b58d2aa03e2066e99f7a4b39df1335c449e5325f6a5599b6871
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 010f090f1083c9f8ede2f8a99bd23e05a68b474f6c90b1519a4002c2446bf97eee6256b8cb7858cbd9a7e9aa9def901885fed07290e8a83c995656f71e47afd4
|
|
7
|
+
data.tar.gz: 1f0c0c85af8fd938f7d6d66a15e4dfb2941615057656867702a01b146e10426901969a2498e10d9fe08427d2226fe9d0f3968e50e262560223fff439ebfcda0f
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jekyll
|
|
4
|
+
module Plugins
|
|
5
|
+
|
|
6
|
+
module Relationships
|
|
7
|
+
|
|
8
|
+
class Configuration
|
|
9
|
+
|
|
10
|
+
# Normalises the `debug` config into one explicit set of enabled log areas.
|
|
11
|
+
#
|
|
12
|
+
# Supported areas:
|
|
13
|
+
# * `resolution`: relationship-resolution start and finish
|
|
14
|
+
# * `upgrading`: raw reference reading, resolution, and write-back
|
|
15
|
+
# * `mutations`: link and unlink operations
|
|
16
|
+
# * `resolvers`: resolver execution and helper calls
|
|
17
|
+
# * `trees`: tree discovery, edge handling, and tree write-back
|
|
18
|
+
class DebugSetting
|
|
19
|
+
AREAS = {
|
|
20
|
+
'resolution' => 'relationship-resolution lifecycle',
|
|
21
|
+
'upgrading' => 'reference upgrading and write-back',
|
|
22
|
+
'mutations' => 'link and unlink operations',
|
|
23
|
+
'resolvers' => 'resolver execution and helper calls',
|
|
24
|
+
'trees' => 'tree discovery, edge handling, and tree write-back'
|
|
25
|
+
}.freeze
|
|
26
|
+
|
|
27
|
+
ALL_AREAS = AREAS.keys.freeze
|
|
28
|
+
|
|
29
|
+
class << self
|
|
30
|
+
# Builds one explicit debug setting from a loose config value.
|
|
31
|
+
def build(value:, string_array:, context:)
|
|
32
|
+
return disabled if value.nil? || false_value?(value)
|
|
33
|
+
return all if true_value?(value)
|
|
34
|
+
|
|
35
|
+
raw_areas = string_array.interpret(value, split: true, flatten: true)
|
|
36
|
+
raise_invalid_value!(context: context) if raw_areas.empty?
|
|
37
|
+
|
|
38
|
+
areas = raw_areas.each_with_object([]) do |raw_area, interpreted_areas|
|
|
39
|
+
normalised_area = normalise_area(raw_area, context: context)
|
|
40
|
+
return all if normalised_area == 'all'
|
|
41
|
+
|
|
42
|
+
interpreted_areas << normalised_area
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
new(areas.uniq)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Returns one disabled debug setting.
|
|
49
|
+
def disabled
|
|
50
|
+
new([])
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Returns one debug setting that enables every area.
|
|
54
|
+
def all
|
|
55
|
+
new(ALL_AREAS)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
# Returns true when a loose value means "debug everything".
|
|
61
|
+
def true_value?(value)
|
|
62
|
+
value == true || value.to_s.strip.casecmp('true').zero?
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Returns true when a loose value means "debug nothing".
|
|
66
|
+
def false_value?(value)
|
|
67
|
+
value == false || value.to_s.strip.casecmp('false').zero?
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Normalises one configured area name and validates it.
|
|
71
|
+
def normalise_area(raw_area, context:)
|
|
72
|
+
area = raw_area.to_s.strip.downcase
|
|
73
|
+
raise_invalid_value!(context: context) if area.empty?
|
|
74
|
+
return 'all' if area == 'all'
|
|
75
|
+
return area if AREAS.key?(area)
|
|
76
|
+
|
|
77
|
+
raise ConfigurationError,
|
|
78
|
+
"`#{context}` contains unknown debug area `#{raw_area}`. Supported areas: #{ALL_AREAS.join(', ')}."
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Raises one shared error for invalid debug values.
|
|
82
|
+
def raise_invalid_value!(context:)
|
|
83
|
+
raise ConfigurationError,
|
|
84
|
+
"`#{context}` must be true, false, `all`, or a comma-delimited string/array of debug areas: #{ALL_AREAS.join(', ')}."
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
attr_reader :areas
|
|
89
|
+
|
|
90
|
+
# Captures one immutable set of enabled debug areas.
|
|
91
|
+
def initialize(areas)
|
|
92
|
+
@areas = areas.freeze
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Returns true when any debug area is enabled, or when one named area is enabled.
|
|
96
|
+
def enabled?(area = nil)
|
|
97
|
+
return !@areas.empty? if area.nil?
|
|
98
|
+
|
|
99
|
+
@areas.include?(normalise_runtime_area(area))
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
private
|
|
103
|
+
|
|
104
|
+
# Normalises one runtime area name and raises on internal typos.
|
|
105
|
+
def normalise_runtime_area(area)
|
|
106
|
+
string_area = area.to_s.strip.downcase
|
|
107
|
+
return string_area if self.class::ALL_AREAS.include?(string_area)
|
|
108
|
+
|
|
109
|
+
raise ArgumentError, "Unknown runtime debug area `#{area}`."
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
end
|
|
117
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jekyll
|
|
4
|
+
module Plugins
|
|
5
|
+
|
|
6
|
+
module Relationships
|
|
7
|
+
|
|
8
|
+
class Configuration
|
|
9
|
+
|
|
10
|
+
# Holds the built-in configuration defaults used when the site omits them.
|
|
11
|
+
module Defaults
|
|
12
|
+
ENABLED = true
|
|
13
|
+
DEBUG = false
|
|
14
|
+
|
|
15
|
+
FRONTMATTER = {
|
|
16
|
+
'base' => 'relationships',
|
|
17
|
+
'primary' => nil,
|
|
18
|
+
'foreign' => '<collection>',
|
|
19
|
+
'output' => nil
|
|
20
|
+
}.freeze
|
|
21
|
+
|
|
22
|
+
TREE = {
|
|
23
|
+
'frontmatter' => {
|
|
24
|
+
'parent' => 'parent',
|
|
25
|
+
'child' => 'child',
|
|
26
|
+
'parents' => 'parents',
|
|
27
|
+
'children' => 'children',
|
|
28
|
+
'ancestors' => 'ancestors',
|
|
29
|
+
'descendants' => 'descendants'
|
|
30
|
+
}.freeze,
|
|
31
|
+
'max' => {
|
|
32
|
+
'parents' => -1,
|
|
33
|
+
'children' => -1
|
|
34
|
+
}.freeze,
|
|
35
|
+
'url' => false
|
|
36
|
+
}.freeze
|
|
37
|
+
|
|
38
|
+
MULTIPLE = {
|
|
39
|
+
'mode' => 'drop',
|
|
40
|
+
'sort' => nil
|
|
41
|
+
}.freeze
|
|
42
|
+
|
|
43
|
+
KEYWORDS = {
|
|
44
|
+
'self' => 'self',
|
|
45
|
+
'others' => 'others',
|
|
46
|
+
'all' => 'all'
|
|
47
|
+
}.freeze
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jekyll
|
|
4
|
+
module Plugins
|
|
5
|
+
|
|
6
|
+
module Relationships
|
|
7
|
+
|
|
8
|
+
class Configuration
|
|
9
|
+
|
|
10
|
+
# Encapsulates one merged frontmatter configuration block.
|
|
11
|
+
#
|
|
12
|
+
# Instances resolve `base`, `primary`, `foreign`, and `output` paths so the
|
|
13
|
+
# rest of the engine can work with explicit values only.
|
|
14
|
+
class Frontmatter
|
|
15
|
+
# Builds one frontmatter configuration helper.
|
|
16
|
+
def initialize(raw_config:, string_array:)
|
|
17
|
+
@raw_config = raw_config.is_a?(Hash) ? raw_config : {}
|
|
18
|
+
@string_array = string_array
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Returns the configured base path.
|
|
22
|
+
def base
|
|
23
|
+
raw_base = fetch_value('base')
|
|
24
|
+
return '' if raw_base.nil?
|
|
25
|
+
|
|
26
|
+
raw_base.to_s
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Returns the resolved primary-key path, or nil for the default key rule.
|
|
30
|
+
def primary_path
|
|
31
|
+
raw_primary = fetch_value('primary')
|
|
32
|
+
return nil if raw_primary.nil?
|
|
33
|
+
|
|
34
|
+
apply_base(raw_primary.to_s)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Returns the resolved foreign input paths for one target collection.
|
|
38
|
+
def foreign_paths_for(to_collection:)
|
|
39
|
+
raw_foreign = fetch_value('foreign')
|
|
40
|
+
paths = @string_array.interpret(raw_foreign, split: true, flatten: true)
|
|
41
|
+
raise ConfigurationError, 'Each normal relationship must define at least one foreign path.' if paths.empty?
|
|
42
|
+
|
|
43
|
+
paths.map do |path|
|
|
44
|
+
string_path = path.to_s.strip
|
|
45
|
+
raise ConfigurationError, 'Foreign paths cannot be blank.' if string_path.empty?
|
|
46
|
+
|
|
47
|
+
resolve_collection_path(path: string_path, to_collection: to_collection)
|
|
48
|
+
end.uniq
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Returns the resolved output path, if one is configured.
|
|
52
|
+
def output_path_for(to_collection:)
|
|
53
|
+
raw_output = fetch_value('output')
|
|
54
|
+
return nil if raw_output.nil? || raw_output.to_s.strip.empty?
|
|
55
|
+
|
|
56
|
+
resolve_collection_path(path: raw_output.to_s.strip, to_collection: to_collection)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Returns one arbitrary path with the active base applied.
|
|
60
|
+
def path_with_base(path)
|
|
61
|
+
apply_base(path.to_s)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Returns a cloned frontmatter configuration with one override merged in.
|
|
65
|
+
def merge(raw_override)
|
|
66
|
+
self.class.new(
|
|
67
|
+
raw_config: Configuration::HashUtilities.merge_hash(@raw_config, raw_override),
|
|
68
|
+
string_array: @string_array
|
|
69
|
+
)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
# Reads one configured property from the raw hash.
|
|
75
|
+
def fetch_value(key)
|
|
76
|
+
Configuration::HashUtilities.fetch_hash_value(@raw_config, key)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Applies the active base path to one relative path.
|
|
80
|
+
def apply_base(path)
|
|
81
|
+
return path if base.empty?
|
|
82
|
+
return base if path.to_s.empty?
|
|
83
|
+
|
|
84
|
+
"#{base}.#{path}"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Resolves one path that may include the active collection placeholder.
|
|
88
|
+
def resolve_collection_path(path:, to_collection:)
|
|
89
|
+
apply_base(path.gsub(collection_placeholder, to_collection))
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Returns the active `<collection>` placeholder token.
|
|
93
|
+
def collection_placeholder
|
|
94
|
+
Jekyll::Plugins::Relationships::Support::Placeholders::COLLECTION
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
end
|
|
102
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jekyll
|
|
4
|
+
module Plugins
|
|
5
|
+
|
|
6
|
+
module Relationships
|
|
7
|
+
|
|
8
|
+
class Configuration
|
|
9
|
+
|
|
10
|
+
# Provides shared hash-reading helpers for configuration components.
|
|
11
|
+
module HashUtilities
|
|
12
|
+
module_function
|
|
13
|
+
|
|
14
|
+
# Returns true when a string- or symbol-keyed hash includes one key.
|
|
15
|
+
def hash_key?(hash, key)
|
|
16
|
+
return false unless hash.is_a?(Hash)
|
|
17
|
+
|
|
18
|
+
hash.key?(key) || hash.key?(key.to_s) || hash.key?(key.to_sym)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Reads one string- or symbol-keyed hash value.
|
|
22
|
+
def fetch_hash_value(hash, key)
|
|
23
|
+
return nil unless hash.is_a?(Hash)
|
|
24
|
+
|
|
25
|
+
Jekyll::Plugins::Relationships::Support::FrontmatterPath.read_hash(hash, key)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Deep-merges two hashes without mutating either.
|
|
29
|
+
def merge_hash(base_hash, override_hash)
|
|
30
|
+
Jekyll::Plugins::Relationships::Support.hash_deep_merge(base_hash, override_hash)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Converts one config value to an integer with a helpful error.
|
|
34
|
+
def integer_value(value, context)
|
|
35
|
+
return value if value.is_a?(Integer)
|
|
36
|
+
return value.to_i if value.is_a?(String) && value.strip.match?(/\A-?\d+\z/)
|
|
37
|
+
|
|
38
|
+
raise ConfigurationError, "`#{context}` must be an integer."
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Converts one config value to a boolean with a helpful error.
|
|
42
|
+
def boolean_value(value, context)
|
|
43
|
+
return value if value == true || value == false
|
|
44
|
+
return true if value.is_a?(String) && value.strip.casecmp('true').zero?
|
|
45
|
+
return false if value.is_a?(String) && value.strip.casecmp('false').zero?
|
|
46
|
+
|
|
47
|
+
raise ConfigurationError, "`#{context}` must be true or false."
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jekyll
|
|
4
|
+
module Plugins
|
|
5
|
+
|
|
6
|
+
module Relationships
|
|
7
|
+
|
|
8
|
+
class Configuration
|
|
9
|
+
|
|
10
|
+
# Encapsulates the configured handling for duplicate normal relationships.
|
|
11
|
+
#
|
|
12
|
+
# The selected mode determines whether duplicates are counted, dropped, or
|
|
13
|
+
# preserved, while the optional sort controls the final output order in count
|
|
14
|
+
# mode only.
|
|
15
|
+
class MultipleSettings
|
|
16
|
+
attr_reader :mode, :sort
|
|
17
|
+
|
|
18
|
+
# Builds one multiple-settings helper from raw configuration.
|
|
19
|
+
def initialize(raw_config:)
|
|
20
|
+
config = normalise_config(raw_config)
|
|
21
|
+
@mode = normalise_mode(config['mode'])
|
|
22
|
+
@sort = normalise_sort(config['sort'])
|
|
23
|
+
validate!
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Returns true when duplicate links should aggregate into counts.
|
|
27
|
+
def count?
|
|
28
|
+
@mode == 'count'
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Returns true when duplicate links should be dropped.
|
|
32
|
+
def drop?
|
|
33
|
+
@mode == 'drop'
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Returns true when duplicate links should be preserved individually.
|
|
37
|
+
def keep?
|
|
38
|
+
@mode == 'keep'
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Returns true when counted output should be sorted in ascending order.
|
|
42
|
+
def sort_ascending?
|
|
43
|
+
@sort == 'asc'
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Returns true when counted output should be sorted in descending order.
|
|
47
|
+
def sort_descending?
|
|
48
|
+
@sort == 'desc'
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
# Converts the loose raw value into a normalised hash.
|
|
54
|
+
def normalise_config(raw_config)
|
|
55
|
+
case raw_config
|
|
56
|
+
when nil
|
|
57
|
+
Defaults::MULTIPLE
|
|
58
|
+
when String, Symbol
|
|
59
|
+
{ 'mode' => raw_config.to_s }
|
|
60
|
+
when Hash
|
|
61
|
+
Configuration::HashUtilities.merge_hash(Defaults::MULTIPLE, raw_config)
|
|
62
|
+
else
|
|
63
|
+
raise ConfigurationError, '`relationships.multiple` must be a string or hash.'
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Resolves one configured duplicate-handling mode.
|
|
68
|
+
def normalise_mode(raw_mode)
|
|
69
|
+
string_mode = raw_mode.to_s.strip.downcase
|
|
70
|
+
string_mode = Defaults::MULTIPLE.fetch('mode') if string_mode.empty?
|
|
71
|
+
|
|
72
|
+
return string_mode if %w[count drop keep].include?(string_mode)
|
|
73
|
+
|
|
74
|
+
raise ConfigurationError, "Unsupported relationships.multiple mode `#{raw_mode}`."
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Resolves one configured count-sort direction.
|
|
78
|
+
def normalise_sort(raw_sort)
|
|
79
|
+
return nil if raw_sort.nil?
|
|
80
|
+
|
|
81
|
+
string_sort = raw_sort.to_s.strip.downcase
|
|
82
|
+
return nil if string_sort.empty?
|
|
83
|
+
return 'asc' if %w[asc ascending].include?(string_sort)
|
|
84
|
+
return 'desc' if %w[desc descending].include?(string_sort)
|
|
85
|
+
|
|
86
|
+
raise ConfigurationError, "Unsupported relationships.multiple.sort value `#{raw_sort}`."
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Raises for mode combinations that are not meaningful.
|
|
90
|
+
def validate!
|
|
91
|
+
return if @sort.nil? || count?
|
|
92
|
+
|
|
93
|
+
raise ConfigurationError, '`relationships.multiple.sort` is only supported when `relationships.multiple` is `count`.'
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
end
|
|
101
|
+
end
|