Skip to content

Commit

Permalink
Base#jsi_select_children_node_first, #jsi_select_children_leaf_first
Browse files Browse the repository at this point in the history
  • Loading branch information
notEthan committed Oct 9, 2021
1 parent 0baa9fe commit 3166e60
Show file tree
Hide file tree
Showing 2 changed files with 143 additions and 0 deletions.
72 changes: 72 additions & 0 deletions lib/jsi/base.rb
Expand Up @@ -223,6 +223,78 @@ def jsi_each_child_node(&block)
nil
end

# recursively selects child nodes of this JSI, returning a modified copy of self containing only
# child nodes for which the given block had a true-ish result.
#
# this method yields a node before recursively descending to its child nodes, so leaf nodes are yielded
# last, after their parents. if a node is not selected, its children are never recursed.
#
# @yield [JSI::Base] each child node below self
# @return [JSI::Base] modified copy of self containing only the selected nodes
def jsi_select_children_node_first(&block)
return to_enum(__method__) unless block

jsi_modified_copy do |instance|
if respond_to?(:to_hash)
res = instance.class.new
each_key do |k|
v = self[k, as_jsi: true]
if yield(v)
res[k] = v.jsi_select_children_node_first(&block).jsi_node_content
end
end
res
elsif respond_to?(:to_ary)
res = instance.class.new
each_index do |i|
e = self[i, as_jsi: true]
if yield(e)
res << e.jsi_select_children_node_first(&block).jsi_node_content
end
end
res
else
instance
end
end
end

# recursively selects child nodes of this JSI, returning a modified copy of self containing only
# child nodes for which the given block had a true-ish result.
#
# this method recursively descends child nodes before yielding each node, so leaf nodes are yielded
# before their parents.
#
# @yield [JSI::Base] each child node below self
# @return [JSI::Base] modified copy of self containing only the selected nodes
def jsi_select_children_leaf_first(&block)
return to_enum(__method__) unless block

jsi_modified_copy do |instance|
if respond_to?(:to_hash)
res = instance.class.new
each_key do |k|
v = self[k, as_jsi: true].jsi_select_children_leaf_first(&block)
if yield(v)
res[k] = v.jsi_node_content
end
end
res
elsif respond_to?(:to_ary)
res = instance.class.new
each_index do |i|
e = self[i, as_jsi: true].jsi_select_children_leaf_first(&block)
if yield(e)
res << e.jsi_node_content
end
end
res
else
instance
end
end
end

# an array of JSI instances above this one in the document.
#
# @return [Array<JSI::Base>]
Expand Down
71 changes: 71 additions & 0 deletions test/base_test.rb
Expand Up @@ -245,6 +245,77 @@
end
end
end
describe 'selecting child nodes' do
let(:schema_content) do
YAML.safe_load(<<~YAML
patternProperties:
...:
$ref: "#"
items:
- $ref: "#"
- $ref: "#"
pattern: "..."
YAML
)
end
describe '#jsi_select_children_node_first: selecting in a complex structure those elements described by a schema or subschema' do
# note that 'described by a schema' does not imply the instance or subinstance validates against
# its schema(s). string subinstances ('y') are described but fail validation against `pattern`.
let(:instance) do
YAML.safe_load(<<~YAML
n:
n: []
yyy:
- y
- n: [y, {yyy: y}, n, {nnn: n}] # the 'y's in the value here are irrelevant as they are below a 'n'
yyy: [y, {yyy: y}, n, {nnn: n}]
- n
YAML
)
end
it "selects the nodes" do
exp = schema.new_jsi(
'yyy' => [
'y',
{'yyy' => ['y', {'yyy' => 'y'}]},
]
)
act = subject.jsi_select_children_node_first do |node|
node.jsi_schemas.any?
end
assert_equal(exp, act)
end
end
describe 'jsi_select_children_leaf_first: selecting in a complex structure by validity' do
# here we select valid leaf nodes and thereby end up with a result consisting of valid child nodes
let(:instance) do
YAML.safe_load(<<~YAML
y: # valid because no schema applies to this or its children
y: [y]
yyy: # will be valid when its invalid children are rejected
- n # fails pattern
- y: [y] # valid; no schemas apply
yyy: [[yyy, n], {nnn: n}, yyy]
- yyy
YAML
)
end
it "selects the nodes" do
exp = schema.new_jsi(
'y' => {'y' => ['y']},
'yyy' => [
{
'y' => ['y'],
'yyy' => [['yyy'], {}, 'yyy']
},
'yyy',
]
)
act = subject.jsi_select_children_leaf_first(&:jsi_valid?)
assert_equal(exp, act)
end
end
end
describe '#jsi_modified_copy' do
describe 'with an instance that does not have #jsi_modified_copy' do
let(:instance) { Object.new }
Expand Down

0 comments on commit 3166e60

Please sign in to comment.