module Puppet::Pops
# A Visitor performs delegation to a given receiver based on the configuration of the Visitor.
# A new visitor is created with a given receiver, a method prefix, min, and max argument counts.
# e.g.
# visitor = Visitor.new(self, "visit_from", 1, 1)
# will make the visitor call "self.visit_from_CLASS(x)" where CLASS is resolved to the given
# objects class, or one of is ancestors, the first class for which there is an implementation of
# a method will be selected.
#
# Raises RuntimeError if there are too few or too many arguments, or if the receiver is not
# configured to handle a given visiting object.
#
class Visitor
attr_reader :receiver, :message, :min_args, :max_args, :cache
def initialize(receiver, message, min_args=0, max_args=nil)
raise ArgumentError.new("min_args must be >= 0") if min_args < 0
raise ArgumentError.new("max_args must be >= min_args or nil") if max_args && max_args < min_args
@receiver = receiver
@message = message
@min_args = min_args
@max_args = max_args
@cache = Hash.new
end
# Visit the configured receiver
def visit(thing, *args)
visit_this(@receiver, thing, args)
end
NO_ARGS = EMPTY_ARRAY
# Visit an explicit receiver
def visit_this(receiver, thing, args)
raise "Visitor Error: Too few arguments passed. min = #{@min_args}" unless args.length >= @min_args
if @max_args
raise "Visitor Error: Too many arguments passed. max = #{@max_args}" unless args.length <= @max_args
end
method_name = @cache[thing.class]
if method_name
return receiver.send(method_name, thing, *args)
else
thing.class.ancestors().each do |ancestor|
name = ancestor.name
next if name.nil?
method_name = :"#{@message}_#{name.split(DOUBLE_COLON).last}"
next unless receiver.respond_to?(method_name, true)
@cache[thing.class] = method_name
return receiver.send(method_name, thing, *args)
end
end
raise "Visitor Error: the configured receiver (#{receiver.class}) can't handle instance of: #{thing.class}"
end
# Visit an explicit receiver
def visit_this_class(receiver, clazz, args)
raise "Visitor Error: Too few arguments passed. min = #{@min_args}" unless args.length >= @min_args
if @max_args
raise "Visitor Error: Too many arguments passed. max = #{@max_args}" unless args.length <= @max_args
end
method_name = @cache[clazz]
if method_name
return receiver.send(method_name, clazz, *args)
else
clazz.ancestors().each do |ancestor|
name = ancestor.name
next if name.nil?
method_name = :"#{@message}_#{name.split(DOUBLE_COLON).last}"
next unless receiver.respond_to?(method_name, true)
@cache[clazz] = method_name
return receiver.send(method_name, clazz, *args)
end
end
raise "Visitor Error: the configured receiver (#{receiver.class}) can't handle instance of: #{clazz}"
end
# Visit an explicit receiver with 0 args
# (This is ~30% faster than calling the general method)
#
def visit_this_0(receiver, thing)
method_name = @cache[thing.class]
if method_name
return receiver.send(method_name, thing)
end
visit_this(receiver, thing, NO_ARGS)
end
# Visit an explicit receiver with 1 args
# (This is ~30% faster than calling the general method)
#
def visit_this_1(receiver, thing, arg)
method_name = @cache[thing.class]
if method_name
return receiver.send(method_name, thing, arg)
end
visit_this(receiver, thing, [arg])
end
# Visit an explicit receiver with 2 args
# (This is ~30% faster than calling the general method)
#
def visit_this_2(receiver, thing, arg1, arg2)
method_name = @cache[thing.class]
if method_name
return receiver.send(method_name, thing, arg1, arg2)
end
visit_this(receiver, thing, [arg1, arg2])
end
# Visit an explicit receiver with 3 args
# (This is ~30% faster than calling the general method)
#
def visit_this_3(receiver, thing, arg1, arg2, arg3)
method_name = @cache[thing.class]
if method_name
return receiver.send(method_name, thing, arg1, arg2, arg3)
end
visit_this(receiver, thing, [arg1, arg2, arg3])
end
end
end
Copyright 2K16 - 2K18 Indonesian Hacker Rulez