class Puppet::Pops::Evaluator::RelationshipOperator

The RelationshipOperator implements the semantics of the -> <- ~> <~ operators creating relationships or notification relationships between the left and right hand side's references to resources.

This is separate class since a second level of evaluation is required that transforms string in left or right hand to type references. The task of “making a relationship” is delegated to the “runtime support” class that is included. This is done to separate the concerns of the new evaluator from the 3x runtime; messy logic goes into the runtime support module. Later when more is cleaned up this can be simplified further.

Constants

RELATIONSHIP_OPERATORS
RELATION_TYPE
REVERSE_OPERATORS

Public Class Methods

new() click to toggle source
   # File lib/puppet/pops/evaluator/relationship_operator.rb
32 def initialize
33   @type_transformer_visitor = Visitor.new(self, "transform", 1, 1)
34   @type_calculator = Types::TypeCalculator.new()
35 
36   tf = Types::TypeFactory
37   @catalog_type = tf.variant(tf.catalog_entry, tf.type_type(tf.catalog_entry))
38 end

Public Instance Methods

assert_catalog_type(o, scope) click to toggle source

Asserts (and returns) the type if it is a PCatalogEntryType (A PCatalogEntryType is the base class of PClassType, and PResourceType).

    # File lib/puppet/pops/evaluator/relationship_operator.rb
 96 def assert_catalog_type(o, scope)
 97   unless @type_calculator.assignable?(@catalog_type, o)
 98     raise NotCatalogTypeError.new(o)
 99   end
100   # TODO must check if this is an abstract PResourceType (i.e. without a type_name) - which should fail ?
101   # e.g. File -> File (and other similar constructs) - maybe the catalog protects against this since references
102   # may be to future objects...
103   o
104 end
evaluate(left_right_evaluated, relationship_expression, scope) click to toggle source

Evaluate a relationship. TODO: The error reporting is not fine grained since evaluation has already taken place There is no references to the original source expressions at this point, only the overall relationship expression. (e.g.. the expression may be ['string', func_call(), etc.] -> func_call()) To implement this, the general evaluator needs to be able to track each evaluation result and associate it with a corresponding expression. This structure should then be passed to the relationship operator.

    # File lib/puppet/pops/evaluator/relationship_operator.rb
122 def evaluate (left_right_evaluated, relationship_expression, scope)
123   # assert operator (should have been validated, but this logic makes assumptions which would
124   # screw things up royally). Better safe than sorry.
125   unless RELATIONSHIP_OPERATORS.include?(relationship_expression.operator)
126     fail(Issues::UNSUPPORTED_OPERATOR, relationship_expression, {:operator => relationship_expression.operator})
127   end
128 
129   begin
130     # Turn each side into an array of types (this also asserts their type)
131     # (note wrap in array first if value is not already an array)
132     #
133     # TODO: Later when objects are Puppet Runtime Objects and know their type, it will be more efficient to check/infer
134     # the type first since a chained operation then does not have to visit each element again. This is not meaningful now
135     # since inference needs to visit each object each time, and this is what the transformation does anyway).
136     #
137     # real is [left, right], and both the left and right may be a single value or an array. In each case all content
138     # should be flattened, and then transformed to a type. left or right may also be a value that is transformed
139     # into an array, and thus the resulting left and right must be flattened individually
140     # Once flattened, the operands should be sets (to remove duplicate entries)
141     #
142     real = left_right_evaluated.collect {|x| [x].flatten.collect {|y| transform(y, scope) }}
143     real[0].flatten!
144     real[1].flatten!
145     real[0].uniq!
146     real[1].uniq!
147 
148     # reverse order if operator is Right to Left
149     source, target = reverse_operator?(relationship_expression) ? real.reverse : real
150 
151     # Add the relationships to the catalog
152     source.each {|s| target.each {|t| add_relationship(s, t, RELATION_TYPE[relationship_expression.operator], scope) }}
153 
154     # The result is the transformed source RHS unless it is empty, in which case the transformed LHS is returned.
155     # This closes the gap created by an empty set of references in a chain of relationship
156     # such that X -> [ ] -> Y results in  X -> Y.
157     #result = real[1].empty? ? real[0] : real[1]
158     if real[1].empty?
159       # right side empty, simply use the left (whatever it may be)
160       result = real[0]
161     else
162       right = real[1]
163       if right.size == 1 && right[0].is_a?(Puppet::Pops::Evaluator::Collectors::AbstractCollector)
164         # the collector when evaluated later may result in an empty set, if so, the
165         # lazy relationship forming logic needs to have access to the left value.
166         adapter = Puppet::Pops::Adapters::EmptyAlternativeAdapter.adapt(right[0])
167         adapter.empty_alternative = real[0]
168       end
169       result = right
170     end
171     result
172 
173   rescue NotCatalogTypeError => e
174     fail(Issues::NOT_CATALOG_TYPE, relationship_expression, {:type => @type_calculator.string(e.type)})
175   rescue IllegalRelationshipOperandError => e
176     fail(Issues::ILLEGAL_RELATIONSHIP_OPERAND_TYPE, relationship_expression, {:operand => e.operand})
177   end
178 end
reverse_operator?(o) click to toggle source
    # File lib/puppet/pops/evaluator/relationship_operator.rb
180 def reverse_operator?(o)
181   REVERSE_OPERATORS.include?(o.operator)
182 end
transform(o, scope) click to toggle source
   # File lib/puppet/pops/evaluator/relationship_operator.rb
40 def transform(o, scope)
41   @type_transformer_visitor.visit_this_1(self, o, scope)
42 end
transform_AbstractCollector(o, scope) click to toggle source
   # File lib/puppet/pops/evaluator/relationship_operator.rb
84 def transform_AbstractCollector(o, scope)
85   o
86 end
transform_Array(o, scope) click to toggle source

Array content needs to be transformed

   # File lib/puppet/pops/evaluator/relationship_operator.rb
89 def transform_Array(o, scope)
90   o.map{|x| transform(x, scope) }
91 end
transform_Collector(o, scope) click to toggle source

This transforms a 3x Collector (the result of evaluating a 3x AST::Collection). It is passed through verbatim since it is evaluated late by the compiler. At the point where the relationship is evaluated, it is simply recorded with the compiler for later evaluation. If one of the sides of the relationship is a Collector it is evaluated before the actual relationship is formed. (All of this happens at a later point in time.

   # File lib/puppet/pops/evaluator/relationship_operator.rb
80 def transform_Collector(o, scope)
81   o
82 end
transform_Object(o, scope) click to toggle source

Catch all non transformable objects @api private

   # File lib/puppet/pops/evaluator/relationship_operator.rb
46 def transform_Object(o, scope)
47   raise IllegalRelationshipOperandError.new(o)
48 end
transform_PAnyType(o, scope) click to toggle source

Types are what they are, just check the type @api private

   # File lib/puppet/pops/evaluator/relationship_operator.rb
70 def transform_PAnyType(o, scope)
71   assert_catalog_type(o, scope)
72 end
transform_QualifiedName(o, scope) click to toggle source

A qualified name is short hand for a class with this name @api private

   # File lib/puppet/pops/evaluator/relationship_operator.rb
64 def transform_QualifiedName(o, scope)
65   Types::TypeFactory.host_class(o.value)
66 end
transform_Resource(o, scope) click to toggle source

A Resource is by definition a Catalog type, but of 3.x type @api private

   # File lib/puppet/pops/evaluator/relationship_operator.rb
52 def transform_Resource(o, scope)
53   Types::TypeFactory.resource(o.type, o.title)
54 end
transform_String(o, scope) click to toggle source

A string must be a type reference in string format @api private

   # File lib/puppet/pops/evaluator/relationship_operator.rb
58 def transform_String(o, scope)
59   assert_catalog_type(Types::TypeParser.singleton.parse(o), scope)
60 end