Class: Puppet::Parser::Scope

Inherits:
Object
  • Object
show all
Includes:
Enumerable, Resource::TypeCollectionHelper, Util::Errors
Defined in:
lib/puppet/parser/scope.rb

Defined Under Namespace

Classes: Ephemeral

Constant Summary collapse

AST =
Puppet::Parser::AST

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Util::Errors

#adderrorcontext, #devfail, #error_context, #exceptwrap, #fail

Methods included from Resource::TypeCollectionHelper

#known_resource_types

Constructor Details

#initialize(hash = {}) ⇒ Scope

Initialize our new scope. Defaults to having no parent.



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/puppet/parser/scope.rb', line 120

def initialize(hash = {})
  if hash.include?(:namespace)
    if n = hash[:namespace]
      @namespaces = [n]
    end
    hash.delete(:namespace)
  else
    @namespaces = [""]
  end
  hash.each { |name, val|
    method = name.to_s + "="
    if self.respond_to? method
      self.send(method, val)
    else
      raise Puppet::DevError, "Invalid scope argument #{name}"
    end
  }

  extend_with_functions_module

  @tags = []

  # The symbol table for this scope.  This is where we store variables.
  @symtable = {}

  # the ephemeral symbol tables
  # those should not persist long, and are used for the moment only
  # for $0..$xy capture variables of regexes
  # this is actually implemented as a stack, with each ephemeral scope
  # shadowing the previous one
  @ephemeral = [ Ephemeral.new ]

  # All of the defaults set for types.  It's a hash of hashes,
  # with the first key being the type, then the second key being
  # the parameter.
  @defaults = Hash.new { |dhash,type|
    dhash[type] = {}
  }

  # The table for storing class singletons.  This will only actually
  # be used by top scopes and node scopes.
  @class_scopes = {}
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *args, &block) ⇒ Object



421
422
423
424
425
426
427
428
429
# File 'lib/puppet/parser/scope.rb', line 421

def method_missing(method, *args, &block)
  method.to_s =~ /^function_(.*)$/
  super unless $1
  super unless Puppet::Parser::Functions.function($1)

  # Calling .function(name) adds "function_#{name}" as a callable method on
  # self if it's found, so now we can just send it
  send(method, *args)
end

Instance Attribute Details

#baseObject

Returns the value of attribute base.



22
23
24
# File 'lib/puppet/parser/scope.rb', line 22

def base
  @base
end

#compilerObject

Returns the value of attribute compiler.



23
24
25
# File 'lib/puppet/parser/scope.rb', line 23

def compiler
  @compiler
end

#dynamicObject

Returns the value of attribute dynamic.



24
25
26
# File 'lib/puppet/parser/scope.rb', line 24

def dynamic
  @dynamic
end

#keywordObject

Returns the value of attribute keyword.



22
23
24
# File 'lib/puppet/parser/scope.rb', line 22

def keyword
  @keyword
end

#namespacesObject (readonly)

Returns the value of attribute namespaces.



25
26
27
# File 'lib/puppet/parser/scope.rb', line 25

def namespaces
  @namespaces
end

#parentObject

Returns the value of attribute parent.



24
25
26
# File 'lib/puppet/parser/scope.rb', line 24

def parent
  @parent
end

#resourceObject

Returns the value of attribute resource.



21
22
23
# File 'lib/puppet/parser/scope.rb', line 21

def resource
  @resource
end

#sourceObject

Returns the value of attribute source.



21
22
23
# File 'lib/puppet/parser/scope.rb', line 21

def source
  @source
end

#topObject

Returns the value of attribute top.



23
24
25
# File 'lib/puppet/parser/scope.rb', line 23

def top
  @top
end

#translatedObject

Returns the value of attribute translated.



23
24
25
# File 'lib/puppet/parser/scope.rb', line 23

def translated
  @translated
end

Class Method Details

.number?(value) ⇒ Boolean

Is the value a number?, return the correct object or nil if not a number

Returns:

  • (Boolean)


72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/puppet/parser/scope.rb', line 72

def self.number?(value)
  return nil unless value.is_a?(Fixnum) or value.is_a?(Bignum) or value.is_a?(Float) or value.is_a?(String)

  if value.is_a?(String)
    if value =~ /^-?\d+(:?\.\d+|(:?\.\d+)?e\d+)$/
      return value.to_f
    elsif value =~ /^0x[0-9a-f]+$/i
      return value.to_i(16)
    elsif value =~ /^0[0-7]+$/
      return value.to_i(8)
    elsif value =~ /^-?\d+$/
      return value.to_i
    else
      return nil
    end
  end
  # it is one of Fixnum,Bignum or Float
  value
end

.true?(value) ⇒ Boolean

Is the value true? This allows us to control the definition of truth in one place.

Returns:

  • (Boolean)


67
68
69
# File 'lib/puppet/parser/scope.rb', line 67

def self.true?(value)
  (value != false and value != "" and value != :undef)
end

Instance Method Details

#add_namespace(ns) ⇒ Object

Add to our list of namespaces.



93
94
95
96
97
98
99
100
# File 'lib/puppet/parser/scope.rb', line 93

def add_namespace(ns)
  return false if @namespaces.include?(ns)
  if @namespaces == [""]
    @namespaces = [ns]
  else
    @namespaces << ns
  end
end

#catalogObject

A demeterific shortcut to the catalog.



52
53
54
# File 'lib/puppet/parser/scope.rb', line 52

def catalog
  compiler.catalog
end

#class_scope(klass) ⇒ Object

Return the scope associated with a class. This is just here so that subclasses can set their parent scopes to be the scope of their parent class, and it’s also used when looking up qualified variables.



175
176
177
178
179
# File 'lib/puppet/parser/scope.rb', line 175

def class_scope(klass)
  # They might pass in either the class or class name
  k = klass.respond_to?(:name) ? klass.name : klass
  @class_scopes[k] || (parent && parent.class_scope(k))
end

#class_set(name, scope) ⇒ Object

Store the fact that we’ve evaluated a class, and store a reference to the scope in which it was evaluated, so that we can look it up later.



166
167
168
169
# File 'lib/puppet/parser/scope.rb', line 166

def class_set(name, scope)
  return parent.class_set(name,scope) if parent
  @class_scopes[name] = scope
end

#environmentObject

Remove this when rebasing



103
104
105
# File 'lib/puppet/parser/scope.rb', line 103

def environment
  compiler.environment
end

#ephemeral?(name) ⇒ Boolean

is name an ephemeral variable?

Returns:

  • (Boolean)


384
385
386
# File 'lib/puppet/parser/scope.rb', line 384

def ephemeral?(name)
  name =~ /^\d+$/
end

#ephemeral_from(match, file = nil, line = nil) ⇒ Object

Raises:

  • (ArgumentError)


396
397
398
399
400
401
402
403
404
405
# File 'lib/puppet/parser/scope.rb', line 396

def ephemeral_from(match, file = nil, line = nil)
  raise(ArgumentError,"Invalid regex match data") unless match.is_a?(MatchData)

  new_ephemeral

  setvar("0", match[0], :file => file, :line => line, :ephemeral => true)
  match.captures.each_with_index do |m,i|
    setvar("#{i+1}", m, :file => file, :line => line, :ephemeral => true)
  end
end

#ephemeral_include?(name) ⇒ Boolean

check if name exists in one of the ephemeral scope.

Returns:

  • (Boolean)


376
377
378
379
380
381
# File 'lib/puppet/parser/scope.rb', line 376

def ephemeral_include?(name)
  @ephemeral.reverse.each do |eph|
    return true if eph.include?(name)
  end
  false
end

#ephemeral_levelObject



388
389
390
# File 'lib/puppet/parser/scope.rb', line 388

def ephemeral_level
  @ephemeral.size
end

#find_builtin_resource_type(type) ⇒ Object



413
414
415
# File 'lib/puppet/parser/scope.rb', line 413

def find_builtin_resource_type(type)
  Puppet::Type.type(type.to_s.downcase.to_sym)
end

#find_defined_resource_type(type) ⇒ Object



417
418
419
# File 'lib/puppet/parser/scope.rb', line 417

def find_defined_resource_type(type)
  environment.known_resource_types.find_definition(namespaces, type.to_s.downcase)
end

#find_definition(name) ⇒ Object



111
112
113
# File 'lib/puppet/parser/scope.rb', line 111

def find_definition(name)
  known_resource_types.find_definition(namespaces, name)
end

#find_hostclass(name) ⇒ Object



107
108
109
# File 'lib/puppet/parser/scope.rb', line 107

def find_hostclass(name)
  known_resource_types.find_hostclass(namespaces, name)
end

#find_resource_type(type) ⇒ Object



407
408
409
410
411
# File 'lib/puppet/parser/scope.rb', line 407

def find_resource_type(type)
  # It still works fine without the type == 'class' short-cut, but it is a lot slower.
  return nil if ["class", "node"].include? type.to_s.downcase
  find_builtin_resource_type(type) || find_defined_resource_type(type)
end

#findresource(string, name = nil) ⇒ Object



115
116
117
# File 'lib/puppet/parser/scope.rb', line 115

def findresource(string, name = nil)
  compiler.findresource(string, name)
end

#hostObject

Proxy accessors



61
62
63
# File 'lib/puppet/parser/scope.rb', line 61

def host
  @compiler.node.name
end

#lookupdefaults(type) ⇒ Object

Collect all of the defaults set at any higher scopes. This is a different type of lookup because it’s additive – it collects all of the defaults, with defaults in closer scopes overriding those in later scopes.



185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/puppet/parser/scope.rb', line 185

def lookupdefaults(type)
  values = {}

  # first collect the values from the parents
  unless parent.nil?
    parent.lookupdefaults(type).each { |var,value|
      values[var] = value
    }
  end

  # then override them with any current values
  # this should probably be done differently
  if @defaults.include?(type)
    @defaults[type].each { |var,value|
      values[var] = value
    }
  end

  #Puppet.debug "Got defaults for %s: %s" %
  #    [type,values.inspect]
  values
end

#lookuptype(name) ⇒ Object

Look up a defined type.



209
210
211
# File 'lib/puppet/parser/scope.rb', line 209

def lookuptype(name)
  find_definition(name) || find_hostclass(name)
end

#lookupvar(name, options = {}) ⇒ Object

Look up a variable. The simplest value search we do.



226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# File 'lib/puppet/parser/scope.rb', line 226

def lookupvar(name, options = {})
  table = ephemeral?(name) ? @ephemeral.last : @symtable
  # If the variable is qualified, then find the specified scope and look the variable up there instead.
  if name =~ /^(.*)::(.+)$/
    begin
      qualified_scope($1).lookupvar($2,options)
    rescue RuntimeError => e
      location = (options[:file] && options[:line]) ? " at #{options[:file]}:#{options[:line]}" : ''
      warning "Could not look up qualified variable '#{name}'; #{e.message}#{location}"
      :undefined
    end
  elsif ephemeral_include?(name) or table.include?(name)
    # We can't use "if table[name]" here because the value might be false
    if options[:dynamic] and self != compiler.topscope
      location = (options[:file] && options[:line]) ? " at #{options[:file]}:#{options[:line]}" : ''
      Puppet.deprecation_warning "Dynamic lookup of $#{name}#{location} is deprecated.  Support will be removed in Puppet 2.8.  Use a fully-qualified variable name (e.g., $classname::variable) or parameterized classes."
    end
    table[name]
  elsif parent
    parent.lookupvar(name,options.merge(:dynamic => (dynamic || options[:dynamic])))
  else
    :undefined
  end
end

#new_ephemeralObject



392
393
394
# File 'lib/puppet/parser/scope.rb', line 392

def new_ephemeral
  @ephemeral.push(Ephemeral.new(@ephemeral.last))
end

#newscope(options = {}) ⇒ Object

Create a new scope and set these options.



273
274
275
# File 'lib/puppet/parser/scope.rb', line 273

def newscope(options = {})
  compiler.newscope(self, options)
end

#parent_module_nameObject



277
278
279
280
281
# File 'lib/puppet/parser/scope.rb', line 277

def parent_module_name
  return nil unless @parent
  return nil unless @parent.source
  @parent.source.module_name
end

#resolve_type_and_titles(type, titles) ⇒ Object

Raises:

  • (ArgumentError)


431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
# File 'lib/puppet/parser/scope.rb', line 431

def resolve_type_and_titles(type, titles)
  raise ArgumentError, "titles must be an array" unless titles.is_a?(Array)

  case type.downcase
  when "class"
    # resolve the titles
    titles = titles.collect do |a_title|
      hostclass = find_hostclass(a_title)
      hostclass ?  hostclass.name : a_title
    end
  when "node"
    # no-op
  else
    # resolve the type
    resource_type = find_resource_type(type)
    type = resource_type.name if resource_type
  end

  return [type, titles]
end

#scope_pathObject

Return the list of scopes up to the top scope, ordered with our own first. This is used for looking up variables and defaults.



285
286
287
288
289
290
291
# File 'lib/puppet/parser/scope.rb', line 285

def scope_path
  if parent
    [self, parent.scope_path].flatten.compact
  else
    [self]
  end
end

#setdefaults(type, params) ⇒ Object

Set defaults for a type. The typename should already be downcased, so that the syntax is isolated. We don’t do any kind of type-checking here; instead we let the resource do it when the defaults are used.



296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
# File 'lib/puppet/parser/scope.rb', line 296

def setdefaults(type, params)
  table = @defaults[type]

  # if we got a single param, it'll be in its own array
  params = [params] unless params.is_a?(Array)

  params.each { |param|
    #Puppet.debug "Default for %s is %s => %s" %
    #    [type,ary[0].inspect,ary[1].inspect]
    if table.include?(param.name)
      raise Puppet::ParseError.new("Default already defined for #{type} { #{param.name} }; cannot redefine", param.line, param.file)
    end
    table[param.name] = param
  }
end

#setvar(name, value, options = {}) ⇒ Object

Set a variable in the current scope. This will override settings in scopes above, but will not allow variables in the current scope to be reassigned.



315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
# File 'lib/puppet/parser/scope.rb', line 315

def setvar(name,value, options = {})
  table = options[:ephemeral] ? @ephemeral.last : @symtable
  if table.include?(name)
    unless options[:append]
      error = Puppet::ParseError.new("Cannot reassign variable #{name}")
    else
      error = Puppet::ParseError.new("Cannot append, variable #{name} is defined in this scope")
    end
    error.file = options[:file] if options[:file]
    error.line = options[:line] if options[:line]
    raise error
  end

  unless options[:append]
    table[name] = value
  else # append case
    # lookup the value in the scope if it exists and insert the var
    table[name] = undef_as('',lookupvar(name))
    # concatenate if string, append if array, nothing for other types
    case value
    when Array
      table[name] += value
    when Hash
      raise ArgumentError, "Trying to append to a hash with something which is not a hash is unsupported" unless value.is_a?(Hash)
      table[name].merge!(value)
    else
      table[name] << value
    end
  end
end

#tagsObject

Return the tags associated with this scope. It’s basically just our parents’ tags, plus our type. We don’t cache this value because our parent tags might change between calls.



349
350
351
# File 'lib/puppet/parser/scope.rb', line 349

def tags
  resource.tags
end

#to_hash(recursive = true) ⇒ Object

Return a hash containing our variables and their values, optionally (and by default) including the values defined in our parent. Local values shadow parent values.



254
255
256
257
258
259
260
261
262
263
264
265
266
# File 'lib/puppet/parser/scope.rb', line 254

def to_hash(recursive = true)
  target = parent.to_hash(recursive) if recursive and parent
  target ||= Hash.new
  @symtable.keys.each { |name|
    value = @symtable[name]
    if value == :undef
      target.delete(name)
    else
      target[name] = value
    end
  }
  target
end

#to_sObject

Used mainly for logging



354
355
356
# File 'lib/puppet/parser/scope.rb', line 354

def to_s
  "Scope(#{@resource})"
end

#undef_as(x, v) ⇒ Object



213
214
215
# File 'lib/puppet/parser/scope.rb', line 213

def undef_as(x,v)
  (v == :undefined) ? x : (v == :undef) ? x : v
end

#unset_ephemeral_var(level = :all) ⇒ Object

remove ephemeral scope up to level



365
366
367
368
369
370
371
372
373
# File 'lib/puppet/parser/scope.rb', line 365

def unset_ephemeral_var(level=:all)
  if level == :all
    @ephemeral = [ Ephemeral.new ]
  else
    (@ephemeral.size - level).times do
      @ephemeral.pop
    end
  end
end

#unsetvar(var) ⇒ Object

Undefine a variable; only used for testing.



359
360
361
362
# File 'lib/puppet/parser/scope.rb', line 359

def unsetvar(var)
  table = ephemeral?(var) ? @ephemeral.last : @symtable
  table.delete(var) if table.include?(var)
end