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.
@@ -1,3 +1,5 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # frozen_string_literal: true
1
3
  module RDF
2
4
  class Node
3
5
  # Odd case of appending to a BNode identifier
@@ -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
- graphs,
19
- graph = '@default',
20
- name = nil,
21
- list = nil)
22
- log_depth do
23
- log_debug("node_map") {"graph: #{graph}, input: #{input.inspect}, name: #{name}"}
24
- case input
25
- when Array
26
- # 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.
27
- input.map {|o| create_node_map(o, graphs, graph, nil, list)}
28
- when Hash
29
- type = input['@type']
30
- if value?(input)
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
- # spec requires @type to be named first, so assign names early
38
- Array(type).each {|t| namer.get_name(t) if blank_node?(t)}
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
- # get name for subject
41
- if name.nil?
42
- name ||= input['@id']
43
- name = namer.get_name(name) if blank_node?(name)
44
- end
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
- # add subject reference to list
47
- list << {'@id' => name} if list
43
+ # add subject reference to list
44
+ list << {'@id' => name} if list
48
45
 
49
- # create new subject or merge into existing one
50
- subject = (graphs[graph] ||= {})[name] ||= {'@id' => name}
46
+ # create new subject or merge into existing one
47
+ subject = (graphs[graph] ||= {})[name] ||= {'@id' => name}
51
48
 
52
- input.keys.kw_sort.each do |property|
53
- objects = input[property]
54
- case property
55
- when '@id'
56
- # Skip
57
- when '@reverse'
58
- # handle reverse properties
59
- referenced_node, reverse_map = {'@id' => name}, objects
60
- reverse_map.each do |reverse_property, items|
61
- items.each do |item|
62
- item_name = item['@id']
63
- item_name = namer.get_name(item_name) if blank_node?(item_name)
64
- create_node_map(item, graphs, graph, item_name)
65
- add_value(graphs[graph][item_name],
66
- reverse_property,
67
- referenced_node,
68
- property_is_array: true,
69
- allow_duplicate: false)
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
- subject[property] = objects
85
- else
86
- # if property is a bnode, assign it a new id
87
- property = namer.get_name(property) if blank_node?(property)
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
- add_value(subject, property, [], property_is_array: true) if objects.empty?
86
+ add_value(subject, property, [], property_is_array: true) if objects.empty?
90
87
 
91
- objects.each do |o|
92
- o = namer.get_name(o) if property == '@type' && blank_node?(o)
88
+ objects.each do |o|
89
+ o = namer.get_name(o) if property == '@type' && blank_node?(o)
93
90
 
94
- case
95
- when node?(o) || node_reference?(o)
96
- id = o['@id']
97
- id = namer.get_name(id) if blank_node?(id)
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
- # add reference and recurse
100
- add_value(subject, property, {'@id' => id}, property_is_array: true, allow_duplicate: false)
101
- create_node_map(o, graphs, graph, id)
102
- when list?(o)
103
- olist = []
104
- create_node_map(o['@list'], graphs, graph, name, olist)
105
- o = {'@list' => olist}
106
- add_value(subject, property, o, property_is_array: true, allow_duplicate: true)
107
- else
108
- # handle @value
109
- create_node_map(o, graphs, graph, name)
110
- add_value(subject, property, o, property_is_array: true, allow_duplicate: false)
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
@@ -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 => Lambda(Array, Hash)}]
49
+ # @return [Hash{Symbol => Hash}]
48
50
  def self.cli_commands
49
51
  {
50
52
  expand: {
@@ -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
- log_depth do
21
- parent, property = options[:parent], options[:property]
22
- # Validate the frame
23
- validate_frame(state, frame)
24
- frame = frame.first if frame.is_a?(Array)
25
-
26
- # Get values for embedOn and explicitOn
27
- flags = {
28
- embed: get_frame_flag(frame, options, :embed),
29
- explicit: get_frame_flag(frame, options, :explicit),
30
- requireAll: get_frame_flag(frame, options, :requireAll),
31
- }
32
-
33
- # Create a set of matched subjects by filtering subjects by checking the map of flattened subjects against frame
34
- # This gives us a hash of objects indexed by @id
35
- matches = filter_subjects(state, subjects, frame, flags)
36
-
37
- # For each id and node from the set of matched subjects ordered by id
38
- matches.keys.kw_sort.each do |id|
39
- subject = matches[id]
40
-
41
- if flags[:embed] == '@link' && state[:link].has_key?(id)
42
- # TODO: may want to also match an existing linked subject
43
- # against the current frame ... so different frames could
44
- # produce different subjects that are only shared in-memory
45
- # when the frames are the same
46
-
47
- # add existing linked subject
48
- add_frame_output(parent, property, state[:link][id])
49
- next
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
- # Note: In order to treat each top-level match as a
53
- # compartmentalized result, clear the unique embedded subjects map
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
- # if only the last match should be embedded
71
- if flags[:embed] == '@last'
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
- # push matching subject onto stack to enable circular embed checks
81
- state[:subjectStack] << subject
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
- # iterate over subject properties in order
84
- subject.keys.kw_sort.each do |prop|
85
- objects = subject[prop]
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
- # copy keywords to output
88
- if prop.start_with?('@')
89
- output[prop] = objects.dup
90
- next
91
- end
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
- # explicit is on and property isn't in frame, skip processing
94
- next if flags[:explicit] && !frame.has_key?(prop)
95
-
96
- # add objects
97
- objects.each do |o|
98
- case
99
- when list?(o)
100
- # add empty list
101
- list = {'@list' => []}
102
- add_frame_output(output, prop, list)
103
-
104
- src = o['@list']
105
- src.each do |oo|
106
- if node_reference?(oo)
107
- subframe = frame[prop].first['@list'] if frame[prop].is_a?(Array) && frame[prop].first.is_a?(Hash)
108
- subframe ||= create_implicit_frame(flags)
109
- frame(state, [oo['@id']], subframe, options.merge(parent: list, property: '@list'))
110
- else
111
- add_frame_output(list, '@list', oo.dup)
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
- # handle defaults in order
126
- frame.keys.kw_sort.reject {|p| p.start_with?('@')}.each do |prop|
127
- # if omit default is off, then include default values for
128
- # properties that appear in the next frame but are not in
129
- # the matching subject
130
- n = frame[prop].first || {}
131
- omit_default_on = get_frame_flag(n, options, :omitDefault)
132
- if !omit_default_on && !output[prop]
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
- # add output to parent
140
- add_frame_output(parent, property, output)
144
+ # add output to parent
145
+ add_frame_output(parent, property, output)
141
146
 
142
- # pop matching subject from circular ref-checking stack
143
- state[:subjectStack].pop()
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
- log_depth do
155
- result = case input
156
- when Array
157
- # If, after replacement, an array contains only the value null remove the value, leaving an empty array.
158
- input.map {|o| cleanup_preserve(o)}.compact
159
- when Hash
160
- output = Hash.new
161
- input.each do |key, value|
162
- if key == '@preserve'
163
- # replace all key-value pairs where the key is @preserve with the value from the key-pair
164
- output = cleanup_preserve(value)
165
- else
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
- # Because we may have added a null value to an array, we need to clean that up, if we possible
169
- v = v.first if v.is_a?(Array) && v.length == 1 &&
170
- context.expand_iri(key) != "@graph" && context.container(key).nil?
171
- output[key] = v
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
- result
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 = true, true
239
+ wildcard, matches_some = false, true
242
240
  when '@type'
243
- wildcard, matches_some = true, true
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
- log_depth do
341
- # get embed keys as a separate array to enable deleting keys in map
342
- embeds.each do |id_dep, e|
343
- p = e.fetch(:parent, {}) if e.is_a?(Hash)
344
- next unless p.is_a?(Hash)
345
- pid = p.fetch('@id', nil)
346
- if pid == id
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.