Class: Puppet::Pops::Visitor::Visitor2

Inherits:
Object
  • Object
show all
Defined in:
lib/puppet/pops/visitor.rb

Overview

This is an alternative implementation that separates the finding of method names (Cached in the Visitor2 class), and bound methods (in an inner Delegator class) that are cached for this receiver instance. This is based on micro benchmarks measuring that a send is slower that directly calling a bound method. Larger benchmark however show that the overhead is fractional. Additional (larger) tests may show otherwise. To use this class instead of the regular Visitor.

@@the_visitor_c = Visitor2.new(...)
@@the_visitor = @@the_visitor_c.instance(self)
then visit with one of the Delegator's visit methods.

Performance Note: there are still issues with this implementation (although cleaner) since it requires holding on to the first instance in order to compute respond_do?. This is required if the class is using method_missing? which cannot be computed by introspection of the class (which would be ideal). Another approach is to pre-scan all the available methods starting with the pattern for the visitor, scan the class, and just check if the class has this method. (This will not work for dispatch to methods that requires method missing. (Maybe that does not matter) Further experiments could try looking up unbound methods via the class, cloning and binding them instead of again looking them up with #method(name) Also note that this implementation does not check min/max args on each call - there was not much gain from skipping this. It is safe to skip, but produces less friendly errors if there is an error in the implementation.

Defined Under Namespace

Classes: Delegator

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(receiver, message, min_args = 0, max_args = nil) ⇒ Visitor2

Returns a new instance of Visitor2.

Raises:

  • (ArgumentError)


115
116
117
118
119
120
121
122
123
124
125
# File 'lib/puppet/pops/visitor.rb', line 115

def initialize(receiver, message, min_args=0, max_args=nil)
  raise ArgumentError.new("receiver can not be nil") if receiver.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

Instance Attribute Details

#cacheObject (readonly)



113
114
115
# File 'lib/puppet/pops/visitor.rb', line 113

def cache
  @cache
end

#max_argsObject (readonly)



113
114
115
# File 'lib/puppet/pops/visitor.rb', line 113

def max_args
  @max_args
end

#messageObject (readonly)



113
114
115
# File 'lib/puppet/pops/visitor.rb', line 113

def message
  @message
end

#min_argsObject (readonly)



113
114
115
# File 'lib/puppet/pops/visitor.rb', line 113

def min_args
  @min_args
end

#receiverObject (readonly)



113
114
115
# File 'lib/puppet/pops/visitor.rb', line 113

def receiver
  @receiver
end

Instance Method Details

#instance(receiver) ⇒ Object



127
128
129
130
# File 'lib/puppet/pops/visitor.rb', line 127

def instance(receiver)
  # Create a visitable instance for the receiver
  Delegator.new(receiver, self)
end

#method_name_for(thing) ⇒ Symbol?

Produce the name of the method to use

Returns:

  • (Symbol, nil)

    the method name symbol, or nil if there is no method to call for thing



135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/puppet/pops/visitor.rb', line 135

def method_name_for(thing)
  if method_name = @cache[thing.class]
    return method_name
  else
    thing.class.ancestors().each do |ancestor|
      method_name = :"#{@message}_#{ancestor.name.split(/::/).last}"
      next unless receiver.respond_to?(method_name, true)
      @cache[thing.class] = method_name
      return method_name
    end
  end
end