json-ld 2.0.0.1 → 2.1.0
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/README.md +12 -1
- data/VERSION +1 -1
- data/lib/json/ld.rb +3 -1
- data/lib/json/ld/api.rb +23 -20
- data/lib/json/ld/compact.rb +27 -26
- data/lib/json/ld/context.rb +499 -428
- data/lib/json/ld/expand.rb +284 -281
- data/lib/json/ld/extensions.rb +2 -0
- data/lib/json/ld/flatten.rb +84 -88
- data/lib/json/ld/format.rb +3 -1
- data/lib/json/ld/frame.rb +154 -163
- data/lib/json/ld/from_rdf.rb +10 -6
- data/lib/json/ld/reader.rb +2 -0
- data/lib/json/ld/resource.rb +2 -0
- data/lib/json/ld/streaming_writer.rb +8 -6
- data/lib/json/ld/to_rdf.rb +11 -9
- data/lib/json/ld/utils.rb +2 -0
- data/lib/json/ld/version.rb +2 -0
- data/lib/json/ld/writer.rb +2 -0
- data/spec/context_spec.rb +73 -3
- data/spec/frame_spec.rb +97 -1
- metadata +6 -7
data/lib/json/ld/extensions.rb
CHANGED
data/lib/json/ld/flatten.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
# frozen_string_literal: true
|
1
3
|
module JSON::LD
|
2
4
|
module Flatten
|
3
5
|
include Utils
|
@@ -14,109 +16,103 @@ module JSON::LD
|
|
14
16
|
# The name assigned to the current input if it is a bnode
|
15
17
|
# @param [Array] list
|
16
18
|
# List to append to, nil for none
|
17
|
-
def create_node_map(input,
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
#
|
27
|
-
input
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
# Rename blanknode @type
|
32
|
-
input['@type'] = namer.get_name(type) if type && blank_node?(type)
|
33
|
-
list << input if list
|
34
|
-
else
|
35
|
-
# Input is a node definition
|
19
|
+
def create_node_map(input, graphs, graph: '@default', name: nil, list: nil)
|
20
|
+
#log_debug("node_map") {"graph: #{graph}, input: #{input.inspect}, name: #{name}"}
|
21
|
+
case input
|
22
|
+
when Array
|
23
|
+
# If input is an array, process each entry in input recursively by passing item for input, node map, active graph, active subject, active property, and list.
|
24
|
+
input.map {|o| create_node_map(o, graphs, graph: graph, list: list)}
|
25
|
+
when Hash
|
26
|
+
type = input['@type']
|
27
|
+
if value?(input)
|
28
|
+
# Rename blanknode @type
|
29
|
+
input['@type'] = namer.get_name(type) if type && blank_node?(type)
|
30
|
+
list << input if list
|
31
|
+
else
|
32
|
+
# Input is a node definition
|
36
33
|
|
37
|
-
|
38
|
-
|
34
|
+
# spec requires @type to be named first, so assign names early
|
35
|
+
Array(type).each {|t| namer.get_name(t) if blank_node?(t)}
|
39
36
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
37
|
+
# get name for subject
|
38
|
+
if name.nil?
|
39
|
+
name ||= input['@id']
|
40
|
+
name = namer.get_name(name) if blank_node?(name)
|
41
|
+
end
|
45
42
|
|
46
|
-
|
47
|
-
|
43
|
+
# add subject reference to list
|
44
|
+
list << {'@id' => name} if list
|
48
45
|
|
49
|
-
|
50
|
-
|
46
|
+
# create new subject or merge into existing one
|
47
|
+
subject = (graphs[graph] ||= {})[name] ||= {'@id' => name}
|
51
48
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
end
|
71
|
-
end
|
72
|
-
when '@graph'
|
73
|
-
graphs[name] ||= {}
|
74
|
-
g = graph == '@merged' ? graph : name
|
75
|
-
create_node_map(objects, graphs, g)
|
76
|
-
when /^@(?!type)/
|
77
|
-
# copy non-@type keywords
|
78
|
-
if property == '@index' && subject['@index']
|
79
|
-
raise JsonLdError::ConflictingIndexes,
|
80
|
-
"Element already has index #{subject['@index']} dfferent from #{input['@index']}" if
|
81
|
-
subject['@index'] != input['@index']
|
82
|
-
subject['@index'] = input.delete('@index')
|
49
|
+
input.keys.kw_sort.each do |property|
|
50
|
+
objects = input[property]
|
51
|
+
case property
|
52
|
+
when '@id'
|
53
|
+
# Skip
|
54
|
+
when '@reverse'
|
55
|
+
# handle reverse properties
|
56
|
+
referenced_node, reverse_map = {'@id' => name}, objects
|
57
|
+
reverse_map.each do |reverse_property, items|
|
58
|
+
items.each do |item|
|
59
|
+
item_name = item['@id']
|
60
|
+
item_name = namer.get_name(item_name) if blank_node?(item_name)
|
61
|
+
create_node_map(item, graphs, graph: graph, name: item_name)
|
62
|
+
add_value(graphs[graph][item_name],
|
63
|
+
reverse_property,
|
64
|
+
referenced_node,
|
65
|
+
property_is_array: true,
|
66
|
+
allow_duplicate: false)
|
83
67
|
end
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
68
|
+
end
|
69
|
+
when '@graph'
|
70
|
+
graphs[name] ||= {}
|
71
|
+
g = graph == '@merged' ? graph : name
|
72
|
+
create_node_map(objects, graphs, graph: g)
|
73
|
+
when /^@(?!type)/
|
74
|
+
# copy non-@type keywords
|
75
|
+
if property == '@index' && subject['@index']
|
76
|
+
raise JsonLdError::ConflictingIndexes,
|
77
|
+
"Element already has index #{subject['@index']} dfferent from #{input['@index']}" if
|
78
|
+
subject['@index'] != input['@index']
|
79
|
+
subject['@index'] = input.delete('@index')
|
80
|
+
end
|
81
|
+
subject[property] = objects
|
82
|
+
else
|
83
|
+
# if property is a bnode, assign it a new id
|
84
|
+
property = namer.get_name(property) if blank_node?(property)
|
88
85
|
|
89
|
-
|
86
|
+
add_value(subject, property, [], property_is_array: true) if objects.empty?
|
90
87
|
|
91
|
-
|
92
|
-
|
88
|
+
objects.each do |o|
|
89
|
+
o = namer.get_name(o) if property == '@type' && blank_node?(o)
|
93
90
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
91
|
+
case
|
92
|
+
when node?(o) || node_reference?(o)
|
93
|
+
id = o['@id']
|
94
|
+
id = namer.get_name(id) if blank_node?(id)
|
98
95
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
end
|
96
|
+
# add reference and recurse
|
97
|
+
add_value(subject, property, {'@id' => id}, property_is_array: true, allow_duplicate: false)
|
98
|
+
create_node_map(o, graphs, graph: graph, name: id)
|
99
|
+
when list?(o)
|
100
|
+
olist = []
|
101
|
+
create_node_map(o['@list'], graphs, graph: graph, name: name, list: olist)
|
102
|
+
o = {'@list' => olist}
|
103
|
+
add_value(subject, property, o, property_is_array: true, allow_duplicate: true)
|
104
|
+
else
|
105
|
+
# handle @value
|
106
|
+
create_node_map(o, graphs, graph: graph, name: name)
|
107
|
+
add_value(subject, property, o, property_is_array: true, allow_duplicate: false)
|
112
108
|
end
|
113
109
|
end
|
114
110
|
end
|
115
111
|
end
|
116
|
-
else
|
117
|
-
# add non-object to list
|
118
|
-
list << input if list
|
119
112
|
end
|
113
|
+
else
|
114
|
+
# add non-object to list
|
115
|
+
list << input if list
|
120
116
|
end
|
121
117
|
end
|
122
118
|
end
|
data/lib/json/ld/format.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
# frozen_string_literal: true
|
1
3
|
module JSON::LD
|
2
4
|
##
|
3
5
|
# JSON-LD format specification.
|
@@ -44,7 +46,7 @@ module JSON::LD
|
|
44
46
|
|
45
47
|
##
|
46
48
|
# Hash of CLI commands appropriate for this format
|
47
|
-
# @return [Hash{Symbol =>
|
49
|
+
# @return [Hash{Symbol => Hash}]
|
48
50
|
def self.cli_commands
|
49
51
|
{
|
50
52
|
expand: {
|
data/lib/json/ld/frame.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
# frozen_string_literal: true
|
1
3
|
module JSON::LD
|
2
4
|
module Frame
|
3
5
|
include Utils
|
@@ -17,131 +19,133 @@ module JSON::LD
|
|
17
19
|
# The parent property.
|
18
20
|
# @raise [JSON::LD::InvalidFrame]
|
19
21
|
def frame(state, subjects, frame, options = {})
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
end
|
22
|
+
parent, property = options[:parent], options[:property]
|
23
|
+
# Validate the frame
|
24
|
+
validate_frame(state, frame)
|
25
|
+
frame = frame.first if frame.is_a?(Array)
|
26
|
+
|
27
|
+
# Get values for embedOn and explicitOn
|
28
|
+
flags = {
|
29
|
+
embed: get_frame_flag(frame, options, :embed),
|
30
|
+
explicit: get_frame_flag(frame, options, :explicit),
|
31
|
+
requireAll: get_frame_flag(frame, options, :requireAll),
|
32
|
+
}
|
33
|
+
|
34
|
+
# Create a set of matched subjects by filtering subjects by checking the map of flattened subjects against frame
|
35
|
+
# This gives us a hash of objects indexed by @id
|
36
|
+
matches = filter_subjects(state, subjects, frame, flags)
|
37
|
+
|
38
|
+
# For each id and node from the set of matched subjects ordered by id
|
39
|
+
matches.keys.kw_sort.each do |id|
|
40
|
+
subject = matches[id]
|
41
|
+
|
42
|
+
if flags[:embed] == '@link' && state[:link].has_key?(id)
|
43
|
+
# TODO: may want to also match an existing linked subject
|
44
|
+
# against the current frame ... so different frames could
|
45
|
+
# produce different subjects that are only shared in-memory
|
46
|
+
# when the frames are the same
|
47
|
+
|
48
|
+
# add existing linked subject
|
49
|
+
add_frame_output(parent, property, state[:link][id])
|
50
|
+
next
|
51
|
+
end
|
51
52
|
|
52
|
-
|
53
|
-
|
54
|
-
# when the property is None, which only occurs at the top-level.
|
55
|
-
state = state.merge(uniqueEmbeds: {}) if property.nil?
|
56
|
-
|
57
|
-
output = {'@id' => id}
|
58
|
-
state[:link][id] = output
|
59
|
-
|
60
|
-
# if embed is @never or if a circular reference would be created
|
61
|
-
# by an embed, the subject cannot be embedded, just add the
|
62
|
-
# reference; note that a circular reference won't occur when the
|
63
|
-
# embed flag is `@link` as the above check will short-circuit
|
64
|
-
# before reaching this point
|
65
|
-
if flags[:embed] == '@never' || creates_circular_reference(subject, state[:subjectStack])
|
66
|
-
add_frame_output(parent, property, output)
|
67
|
-
next
|
68
|
-
end
|
53
|
+
# Note: In order to treat each top-level match as a compartmentalized result, clear the unique embedded subjects map when the property is None, which only occurs at the top-level.
|
54
|
+
state = state.merge(uniqueEmbeds: {}) if property.nil?
|
69
55
|
|
70
|
-
|
71
|
-
|
72
|
-
# remove any existing embed
|
73
|
-
remove_embed(state, id) if state[:uniqueEmbeds].include?(id)
|
74
|
-
state[:uniqueEmbeds][id] = {
|
75
|
-
parent: parent,
|
76
|
-
property: property
|
77
|
-
}
|
78
|
-
end
|
56
|
+
output = {'@id' => id}
|
57
|
+
state[:link][id] = output
|
79
58
|
|
80
|
-
|
81
|
-
|
59
|
+
# if embed is @never or if a circular reference would be created by an embed, the subject cannot be embedded, just add the reference; note that a circular reference won't occur when the embed flag is `@link` as the above check will short-circuit before reaching this point
|
60
|
+
if flags[:embed] == '@never' || creates_circular_reference(subject, state[:subjectStack])
|
61
|
+
add_frame_output(parent, property, output)
|
62
|
+
next
|
63
|
+
end
|
82
64
|
|
83
|
-
|
84
|
-
|
85
|
-
|
65
|
+
# if only the last match should be embedded
|
66
|
+
if flags[:embed] == '@last'
|
67
|
+
# remove any existing embed
|
68
|
+
remove_embed(state, id) if state[:uniqueEmbeds].include?(id)
|
69
|
+
state[:uniqueEmbeds][id] = {
|
70
|
+
parent: parent,
|
71
|
+
property: property
|
72
|
+
}
|
73
|
+
end
|
86
74
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
75
|
+
# push matching subject onto stack to enable circular embed checks
|
76
|
+
state[:subjectStack] << subject
|
77
|
+
|
78
|
+
# iterate over subject properties in order
|
79
|
+
subject.keys.kw_sort.each do |prop|
|
80
|
+
objects = subject[prop]
|
81
|
+
|
82
|
+
# copy keywords to output
|
83
|
+
if prop.start_with?('@')
|
84
|
+
output[prop] = objects.dup
|
85
|
+
next
|
86
|
+
end
|
92
87
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
end
|
88
|
+
# explicit is on and property isn't in frame, skip processing
|
89
|
+
next if flags[:explicit] && !frame.has_key?(prop)
|
90
|
+
|
91
|
+
# add objects
|
92
|
+
objects.each do |o|
|
93
|
+
case
|
94
|
+
when list?(o)
|
95
|
+
# add empty list
|
96
|
+
list = {'@list' => []}
|
97
|
+
add_frame_output(output, prop, list)
|
98
|
+
|
99
|
+
src = o['@list']
|
100
|
+
src.each do |oo|
|
101
|
+
if node_reference?(oo)
|
102
|
+
subframe = frame[prop].first['@list'] if frame[prop].is_a?(Array) && frame[prop].first.is_a?(Hash)
|
103
|
+
subframe ||= create_implicit_frame(flags)
|
104
|
+
frame(state, [oo['@id']], subframe, options.merge(parent: list, property: '@list'))
|
105
|
+
else
|
106
|
+
add_frame_output(list, '@list', oo.dup)
|
113
107
|
end
|
114
|
-
when node_reference?(o)
|
115
|
-
# recurse into subject reference
|
116
|
-
subframe = frame[prop] || create_implicit_frame(flags)
|
117
|
-
frame(state, [o['@id']], subframe, options.merge(parent: output, property: prop))
|
118
|
-
else
|
119
|
-
# include other values automatically
|
120
|
-
add_frame_output(output, prop, o.dup)
|
121
108
|
end
|
109
|
+
when node_reference?(o)
|
110
|
+
# recurse into subject reference
|
111
|
+
subframe = frame[prop] || create_implicit_frame(flags)
|
112
|
+
frame(state, [o['@id']], subframe, options.merge(parent: output, property: prop))
|
113
|
+
else
|
114
|
+
# include other values automatically
|
115
|
+
add_frame_output(output, prop, o.dup)
|
122
116
|
end
|
123
117
|
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# handle defaults in order
|
121
|
+
frame.keys.kw_sort.reject {|p| p.start_with?('@')}.each do |prop|
|
122
|
+
# if omit default is off, then include default values for properties that appear in the next frame but are not in the matching subject
|
123
|
+
n = frame[prop].first || {}
|
124
|
+
omit_default_on = get_frame_flag(n, options, :omitDefault)
|
125
|
+
if !omit_default_on && !output[prop]
|
126
|
+
preserve = n.fetch('@default', '@null').dup
|
127
|
+
preserve = [preserve] unless preserve.is_a?(Array)
|
128
|
+
output[prop] = [{'@preserve' => preserve}]
|
129
|
+
end
|
130
|
+
end
|
124
131
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
preserve = n.fetch('@default', '@null').dup
|
134
|
-
preserve = [preserve] unless preserve.is_a?(Array)
|
135
|
-
output[prop] = [{'@preserve' => preserve}]
|
132
|
+
# If frame has @reverse, embed identified nodes having this subject as a value of the associated property.
|
133
|
+
frame.fetch('@reverse', {}).each do |reverse_prop, subframe|
|
134
|
+
state[:subjects].each do |r_id, node|
|
135
|
+
if Array(node[reverse_prop]).any? {|v| v['@id'] == id}
|
136
|
+
# Node has property referencing this subject
|
137
|
+
# recurse into reference
|
138
|
+
(output['@reverse'] ||= {})[reverse_prop] ||= []
|
139
|
+
frame(state, [r_id], subframe, options.merge(parent: output['@reverse'][reverse_prop]))
|
136
140
|
end
|
137
141
|
end
|
142
|
+
end
|
138
143
|
|
139
|
-
|
140
|
-
|
144
|
+
# add output to parent
|
145
|
+
add_frame_output(parent, property, output)
|
141
146
|
|
142
|
-
|
143
|
-
|
144
|
-
end
|
147
|
+
# pop matching subject from circular ref-checking stack
|
148
|
+
state[:subjectStack].pop()
|
145
149
|
end
|
146
150
|
end
|
147
151
|
|
@@ -151,35 +155,33 @@ module JSON::LD
|
|
151
155
|
# @param [Array, Hash] input
|
152
156
|
# @return [Array, Hash]
|
153
157
|
def cleanup_preserve(input)
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
v = cleanup_preserve(value)
|
158
|
+
result = case input
|
159
|
+
when Array
|
160
|
+
# If, after replacement, an array contains only the value null remove the value, leaving an empty array.
|
161
|
+
input.map {|o| cleanup_preserve(o)}.compact
|
162
|
+
when Hash
|
163
|
+
output = Hash.new
|
164
|
+
input.each do |key, value|
|
165
|
+
if key == '@preserve'
|
166
|
+
# replace all key-value pairs where the key is @preserve with the value from the key-pair
|
167
|
+
output = cleanup_preserve(value)
|
168
|
+
else
|
169
|
+
v = cleanup_preserve(value)
|
167
170
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
end
|
171
|
+
# Because we may have added a null value to an array, we need to clean that up, if we possible
|
172
|
+
v = v.first if v.is_a?(Array) && v.length == 1 &&
|
173
|
+
context.expand_iri(key) != "@graph" && context.container(key).nil?
|
174
|
+
output[key] = v
|
173
175
|
end
|
174
|
-
output
|
175
|
-
when '@null'
|
176
|
-
# If the value from the key-pair is @null, replace the value with nul
|
177
|
-
nil
|
178
|
-
else
|
179
|
-
input
|
180
176
|
end
|
181
|
-
|
177
|
+
output
|
178
|
+
when '@null'
|
179
|
+
# If the value from the key-pair is @null, replace the value with nul
|
180
|
+
nil
|
181
|
+
else
|
182
|
+
input
|
182
183
|
end
|
184
|
+
result
|
183
185
|
end
|
184
186
|
|
185
187
|
private
|
@@ -206,13 +208,9 @@ module JSON::LD
|
|
206
208
|
##
|
207
209
|
# Returns true if the given node matches the given frame.
|
208
210
|
#
|
209
|
-
# Matches either based on explicit type inclusion where the node
|
210
|
-
# has any type listed in the frame. If the frame has empty types defined
|
211
|
-
# matches nodes not having a @type. If the frame has a type of {} defined
|
212
|
-
# matches nodes having any type defined.
|
211
|
+
# Matches either based on explicit type inclusion where the node has any type listed in the frame. If the frame has empty types defined matches nodes not having a @type. If the frame has a type of {} defined matches nodes having any type defined.
|
213
212
|
#
|
214
|
-
# Otherwise, does duck typing, where the node must have all of the properties
|
215
|
-
# defined in the frame.
|
213
|
+
# Otherwise, does duck typing, where the node must have all of the properties defined in the frame.
|
216
214
|
#
|
217
215
|
# @param [Hash{String => Object}] subject the subject to check.
|
218
216
|
# @param [Hash{String => Object}] frame the frame to check.
|
@@ -238,22 +236,21 @@ module JSON::LD
|
|
238
236
|
case k
|
239
237
|
when '@id'
|
240
238
|
return false if v.is_a?(String) && subject['@id'] != v
|
241
|
-
wildcard, matches_some =
|
239
|
+
wildcard, matches_some = false, true
|
242
240
|
when '@type'
|
243
|
-
wildcard, matches_some =
|
241
|
+
wildcard, matches_some = false, false
|
244
242
|
when /^@/
|
245
243
|
else
|
246
244
|
wildcard = false
|
247
245
|
|
248
246
|
# v == [] means do not match if property is present
|
249
247
|
if subject.has_key?(k)
|
250
|
-
return false if v == []
|
248
|
+
return false if v == [] && !subject[k].nil?
|
251
249
|
matches_some = true
|
252
250
|
next
|
253
251
|
end
|
254
252
|
|
255
|
-
# all properties must match to be a duck unless a @default is
|
256
|
-
# specified
|
253
|
+
# all properties must match to be a duck unless a @default is specified
|
257
254
|
has_default = v.is_a?(Array) && v.length == 1 && v.first.is_a?(Hash) && v.first.has_key?('@default')
|
258
255
|
return false if flags[:requireAll] && !has_default
|
259
256
|
end
|
@@ -270,8 +267,7 @@ module JSON::LD
|
|
270
267
|
frame.is_a?(Hash) || (frame.is_a?(Array) && frame.first.is_a?(Hash) && frame.length == 1)
|
271
268
|
end
|
272
269
|
|
273
|
-
# Checks the current subject stack to see if embedding the given subject
|
274
|
-
# would cause a circular reference.
|
270
|
+
# Checks the current subject stack to see if embedding the given subject would cause a circular reference.
|
275
271
|
#
|
276
272
|
# @param subject_to_embed the subject to embed.
|
277
273
|
# @param subject_stack the current stack of subjects.
|
@@ -283,6 +279,7 @@ module JSON::LD
|
|
283
279
|
end
|
284
280
|
end
|
285
281
|
|
282
|
+
##
|
286
283
|
# Gets the frame flag value for the given flag name.
|
287
284
|
#
|
288
285
|
# @param frame the frame.
|
@@ -336,17 +333,14 @@ module JSON::LD
|
|
336
333
|
|
337
334
|
# recursively remove dependent dangling embeds
|
338
335
|
def remove_dependents(id, embeds)
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
embeds.delete(id_dep)
|
348
|
-
remove_dependents(id_dep, embeds)
|
349
|
-
end
|
336
|
+
# get embed keys as a separate array to enable deleting keys in map
|
337
|
+
embeds.each do |id_dep, e|
|
338
|
+
p = e.fetch(:parent, {}) if e.is_a?(Hash)
|
339
|
+
next unless p.is_a?(Hash)
|
340
|
+
pid = p.fetch('@id', nil)
|
341
|
+
if pid == id
|
342
|
+
embeds.delete(id_dep)
|
343
|
+
remove_dependents(id_dep, embeds)
|
350
344
|
end
|
351
345
|
end
|
352
346
|
end
|
@@ -369,10 +363,7 @@ module JSON::LD
|
|
369
363
|
end
|
370
364
|
end
|
371
365
|
|
372
|
-
# Creates an implicit frame when recursing through subject matches. If
|
373
|
-
# a frame doesn't have an explicit frame for a particular property, then
|
374
|
-
# a wildcard child frame will be created that uses the same flags that
|
375
|
-
# the parent frame used.
|
366
|
+
# Creates an implicit frame when recursing through subject matches. If a frame doesn't have an explicit frame for a particular property, then a wildcard child frame will be created that uses the same flags that the parent frame used.
|
376
367
|
#
|
377
368
|
# @param [Hash] flags the current framing flags.
|
378
369
|
# @return [Array<Hash>] the implicit frame.
|