module DeepCover::Node::Mixin::HasChild::ClassMethods
Public Instance Methods
check_children_types(nodes)
click to toggle source
# File lib/deep_cover/node/mixin/has_child.rb, line 51 def check_children_types(nodes) types = expected_types(nodes) nodes_mismatches(nodes, types) end
child_index_to_name(index, nb_children)
click to toggle source
# File lib/deep_cover/node/mixin/has_child.rb, line 43 def child_index_to_name(index, nb_children) self::CHILDREN.each do |name, i| return name if i == index || (i == index - nb_children) || (i.is_a?(Range) && i.begin <= index && i.end + nb_children >= index) end raise IndexError, "index #{index} does not correspond to any child of #{self}" end
has_child(rest_: false, **args)
click to toggle source
# File lib/deep_cover/node/mixin/has_child.rb, line 29 def has_child(rest_: false, **args) raise "Needs exactly one custom named argument, got #{args.size}" if args.size != 1 name, types = args.first raise TypeError, "Expect a Symbol for name, got a #{name.class} (#{name.inspect})" unless name.is_a?(Symbol) update_children_const(name, rest: rest_) define_accessor(name) add_runtime_check(name, types) self end
has_extra_children(**args)
click to toggle source
# File lib/deep_cover/node/mixin/has_child.rb, line 39 def has_extra_children(**args) has_child(**args, rest_: true) end
min_children()
click to toggle source
# File lib/deep_cover/node/mixin/has_child.rb, line 56 def min_children self::CHILDREN.values.grep(Integer).size end
Private Instance Methods
add_runtime_check(name, type)
click to toggle source
# File lib/deep_cover/node/mixin/has_child.rb, line 130 def add_runtime_check(name, type) self::CHILDREN_TYPES[name] = type end
define_accessor(name)
click to toggle source
# File lib/deep_cover/node/mixin/has_child.rb, line 121 def define_accessor(name) warn "child name '#{name}' conflicts with existing method for #{self}" if method_defined? name class_eval <<-EVAL, __FILE__, __LINE__ + 1 def #{name} children[CHILDREN.fetch(#{name.inspect})] end EVAL end
expected_types(nodes)
click to toggle source
# File lib/deep_cover/node/mixin/has_child.rb, line 62 def expected_types(nodes) self::CHILDREN.flat_map do |name, i| type = self::CHILDREN_TYPES[name] Array.new(nodes.values_at(i).size, type) end end
inherited(subclass)
click to toggle source
Calls superclass method
# File lib/deep_cover/node/mixin/has_child.rb, line 92 def inherited(subclass) subclass.const_set :CHILDREN, self::CHILDREN.dup subclass.const_set :CHILDREN_TYPES, self::CHILDREN_TYPES.dup super end
node_matches_type?(node, expected)
click to toggle source
# File lib/deep_cover/node/mixin/has_child.rb, line 77 def node_matches_type?(node, expected) case expected when :any true when nil node.nil? when Array expected.any? { |exp| node_matches_type?(node, exp) } when Class node.is_a?(expected) else raise "Unrecognized expected type #{expected}" end end
nodes_mismatches(nodes, types)
click to toggle source
# File lib/deep_cover/node/mixin/has_child.rb, line 69 def nodes_mismatches(nodes, types) nodes = nodes.dup nodes[nodes.size...types.size] = nil nodes.zip(types).reject do |node, type| node_matches_type?(node, type) end end
update_children_const(name, rest: false)
click to toggle source
# File lib/deep_cover/node/mixin/has_child.rb, line 98 def update_children_const(name, rest: false) children_map = self::CHILDREN already_has_rest = false children_map.each do |key, value| if value.is_a? Range children_map[key] = children_map[key].begin..(children_map[key].end - 1) already_has_rest = key elsif value < 0 children_map[key] -= 1 end end children_map[name] = if rest if already_has_rest raise "Class #{self} can't have extra children '#{name}' because it already has '#{name}' (#{children_map})" end children_map.size..-1 elsif already_has_rest -1 else children_map.size end end