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
17 def interpolate(value, context, allow_methods)
18   case value
19   when String
20     value.index('%{').nil? ? value : interpolate_string(value, context, allow_methods)
21   when Array
22     value.map { |element| interpolate(element, context, allow_methods) }
23   when Hash
24     result = {}
25     value.each_pair { |k, v| result[interpolate(k, context, allow_methods)] = interpolate(v, context, allow_methods) }
26     result
27   else
28     value
29   end
30 end

Private Instance Methods

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