module Puppet::Pops::Lookup::Interpolation

Adds support for interpolation expressions. The expressions may contain keys that uses dot-notation to further navigate into hashes and arrays

@api public

Constants

EMPTY_INTERPOLATIONS
QUOTED_KEY

Matches a key that is quoted using a matching pair of either single or double quotes.

Public Instance Methods

interpolate(value, context, allow_methods) click to toggle source

@param value [Object] The value to interpolate @param context [Context] The current lookup context @param allow_methods [Boolean] `true` if interpolation expression that contains lookup methods are allowed @return [Object] the result of resolving all interpolations in the given value @api public

   # File lib/puppet/pops/lookup/interpolation.rb
18 def interpolate(value, context, allow_methods)
19   case value
20   when String
21     value.index('%{').nil? ? value : interpolate_string(value, context, allow_methods)
22   when Array
23     value.map { |element| interpolate(element, context, allow_methods) }
24   when Hash
25     result = {}
26     value.each_pair { |k, v| result[interpolate(k, context, allow_methods)] = interpolate(v, context, allow_methods) }
27     result
28   else
29     value
30   end
31 end

Private Instance Methods

fail(issue, args = EMPTY_HASH) click to toggle source
    # File lib/puppet/pops/lookup/interpolation.rb
155 def fail(issue, args = EMPTY_HASH)
156   raise Puppet::DataBinding::LookupError.new(
157     issue.format(args), nil, nil, nil, nil, issue.issue_code)
158 end
get_method_and_data(data, allow_methods) click to toggle source
    # File lib/puppet/pops/lookup/interpolation.rb
143 def get_method_and_data(data, allow_methods)
144   match = data.match(/^(\w+)\((?:["]([^"]+)["]|[']([^']+)['])\)$/)
145   if match
146     fail(Issues::HIERA_INTERPOLATION_METHOD_SYNTAX_NOT_ALLOWED) unless allow_methods
147     key = match[1].to_sym
148     data = match[2] || match[3] # double or single qouted
149   else
150     key = :scope
151   end
152   [key, data]
153 end
interpolate_method(method_key) click to toggle source
    # File lib/puppet/pops/lookup/interpolation.rb
 73 def interpolate_method(method_key)
 74   @@interpolate_methods ||= begin
 75     global_lookup = lambda do |key, lookup_invocation, _|
 76       scope = lookup_invocation.scope
 77       if scope.is_a?(Hiera::Scope) && !lookup_invocation.global_only?
 78         # "unwrap" the Hiera::Scope
 79         scope = scope.real
 80       end
 81       lookup_invocation.with_scope(scope) do |sub_invocation|
 82         sub_invocation.lookup(key) {  Lookup.lookup(key, nil, '', true, nil, sub_invocation) }
 83       end
 84     end
 85     scope_lookup = lambda do |key, lookup_invocation, subject|
 86       segments = split_key(key) { |problem| Puppet::DataBinding::LookupError.new("#{problem} in string: #{subject}") }
 87       root_key = segments.shift
 88       value = lookup_invocation.with(:scope, 'Global Scope') do
 89         ovr = lookup_invocation.override_values
 90         if ovr.include?(root_key)
 91           lookup_invocation.report_found_in_overrides(root_key, ovr[root_key])
 92         else
 93           scope = lookup_invocation.scope
 94           val = nil
 95           if (default_key_exists = lookup_invocation.default_values.include?(root_key) )
 96             catch(:undefined_variable) { val = scope[root_key] }
 97           else
 98             val = scope[root_key]
 99           end
100           if val.nil? && !nil_in_scope?(scope, root_key)
101             if default_key_exists
102               lookup_invocation.report_found_in_defaults(root_key,
103                                                          lookup_invocation.default_values[root_key])
104             else
105               nil
106             end
107           else
108             lookup_invocation.report_found(root_key, val)
109           end
110         end
111       end
112       unless value.nil? || segments.empty?
113         found = nil;
114         catch(:no_such_key) { found = sub_lookup(key, lookup_invocation, segments, value) }
115         value = found;
116       end
117       lookup_invocation.remember_scope_lookup(key, root_key, segments, value)
118       value
119     end
120 
121     {
122       :lookup => global_lookup,
123       :hiera => global_lookup, # this is just an alias for 'lookup'
124       :alias => global_lookup, # same as 'lookup' but expression must be entire string and result is not subject to string substitution
125       :scope => scope_lookup,
126       :literal => lambda { |key, _, _| key }
127     }.freeze
128   end
129   interpolate_method = @@interpolate_methods[method_key]
130   fail(Issues::HIERA_INTERPOLATION_UNKNOWN_INTERPOLATION_METHOD, :name => method_key) unless interpolate_method
131   interpolate_method
132 end
interpolate_string(subject, context, allow_methods) click to toggle source
   # File lib/puppet/pops/lookup/interpolation.rb
47 def interpolate_string(subject, context, allow_methods)
48   lookup_invocation = context.is_a?(Invocation) ? context : context.invocation
49   lookup_invocation.with(:interpolate, subject) do
50     subject.gsub(/%\{([^\}]*)\}/) do |match|
51       expr = $1
52       # Leading and trailing spaces inside an interpolation expression are insignificant
53       expr.strip!
54       value = nil
55       unless EMPTY_INTERPOLATIONS[expr]
56         method_key, key = get_method_and_data(expr, allow_methods)
57         is_alias = method_key == :alias
58 
59         # Alias is only permitted if the entire string is equal to the interpolate expression
60         fail(Issues::HIERA_INTERPOLATION_ALIAS_NOT_ENTIRE_STRING) if is_alias && subject != match
61         value = interpolate_method(method_key).call(key, lookup_invocation, subject)
62 
63         # break gsub and return value immediately if this was an alias substitution. The value might be something other than a String
64         return value if is_alias
65 
66         value = lookup_invocation.check(method_key == :scope ? "scope:#{key}" : key) { interpolate(value, lookup_invocation, allow_methods) }
67       end
68       value.nil? ? '' : value
69     end
70   end
71 end
nil_in_scope?(scope, key) click to toggle source

Because the semantics of Puppet::Parser::Scope#include? differs from Hash#include?

    # File lib/puppet/pops/lookup/interpolation.rb
135 def nil_in_scope?(scope, key)
136   if scope.is_a?(Hash)
137     scope.include?(key)
138   else
139     scope.exist?(key)
140   end
141 end