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