Class: Puppet::Parser::Scope

Inherits:
Object
  • Object
show all
Includes:
Enumerable, Resource::TypeCollectionHelper, Util::Errors
Defined in:
lib/vendor/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/vendor/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



457
458
459
460
461
462
463
464
465
466
467
468
# File 'lib/vendor/puppet/parser/scope.rb', line 457

def method_missing(method, *args, &block)
  method.to_s =~ /^function_(.*)$/
  super unless $1
  super unless Puppet::Parser::Functions.function($1)
  # In odd circumstances, this might not end up defined by the previous
  # method, so we might as well be certain.
  if respond_to? method
    send(method, *args)
  else
    raise Puppet::DevError, "Function #{$1} not defined despite being loaded!"
  end
end

Instance Attribute Details

#baseObject

Returns the value of attribute base.



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

def base
  @base
end

#compilerObject

Returns the value of attribute compiler.



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

def compiler
  @compiler
end

#keywordObject

Returns the value of attribute keyword.



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

def keyword
  @keyword
end

#namespacesObject (readonly)

Returns the value of attribute namespaces.



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

def namespaces
  @namespaces
end

#parentObject

Returns the value of attribute parent.



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

def parent
  @parent
end

#resourceObject

Returns the value of attribute resource.



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

def resource
  @resource
end

#sourceObject

Returns the value of attribute source.



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

def source
  @source
end

#topObject

Returns the value of attribute top.



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

def top
  @top
end

#translatedObject

Returns the value of attribute translated.



23
24
25
# File 'lib/vendor/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/vendor/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/vendor/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/vendor/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/vendor/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/vendor/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/vendor/puppet/parser/scope.rb', line 166

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

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

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



266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
# File 'lib/vendor/puppet/parser/scope.rb', line 266

def dynamic_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).dynamic_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
    table[name]
  elsif parent
    parent.dynamic_lookupvar(name,options)
  else
    :undefined
  end
end

#environmentObject

Remove this when rebasing



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

def environment
  compiler.environment
end

#ephemeral?(name) ⇒ Boolean

is name an ephemeral variable?

Returns:

  • (Boolean)


420
421
422
# File 'lib/vendor/puppet/parser/scope.rb', line 420

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

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

Raises:

  • (ArgumentError)


432
433
434
435
436
437
438
439
440
441
# File 'lib/vendor/puppet/parser/scope.rb', line 432

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)


412
413
414
415
416
417
# File 'lib/vendor/puppet/parser/scope.rb', line 412

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

#ephemeral_levelObject



424
425
426
# File 'lib/vendor/puppet/parser/scope.rb', line 424

def ephemeral_level
  @ephemeral.size
end

#find_builtin_resource_type(type) ⇒ Object



449
450
451
# File 'lib/vendor/puppet/parser/scope.rb', line 449

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

#find_defined_resource_type(type) ⇒ Object



453
454
455
# File 'lib/vendor/puppet/parser/scope.rb', line 453

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/vendor/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/vendor/puppet/parser/scope.rb', line 107

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

#find_resource_type(type) ⇒ Object



443
444
445
446
447
# File 'lib/vendor/puppet/parser/scope.rb', line 443

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/vendor/puppet/parser/scope.rb', line 115

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

#hostObject

Proxy accessors



61
62
63
# File 'lib/vendor/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/vendor/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/vendor/puppet/parser/scope.rb', line 209

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

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

Look up a variable with traditional scoping and then with new scoping. If the answers differ then print a deprecation warning.



227
228
229
230
231
232
233
234
235
236
237
# File 'lib/vendor/puppet/parser/scope.rb', line 227

def lookupvar(name, options = {})
  dynamic_value = dynamic_lookupvar(name,options)
  twoscope_value = twoscope_lookupvar(name,options)
  if dynamic_value != twoscope_value
    location = (options[:file] && options[:line]) ? " at #{options[:file]}:#{options[:line]}" : ''
    Puppet.deprecation_warning("Dynamic lookup of $#{name}#{location} is deprecated. For more information, see http://docs.puppetlabs.com/guides/scope_and_puppet.html. To see the change in behavior, use the --debug flag.")
    Puppet.debug("Currently $#{name} is #{dynamic_value.inspect}")
    Puppet.debug("In the future $#{name} will be #{twoscope_value == :undefined ? "undefined" : twoscope_value.inspect}")
  end
  dynamic_value
end

#new_ephemeralObject



428
429
430
# File 'lib/vendor/puppet/parser/scope.rb', line 428

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

#newscope(options = {}) ⇒ Object

Create a new scope and set these options.



309
310
311
# File 'lib/vendor/puppet/parser/scope.rb', line 309

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

#parent_module_nameObject



313
314
315
316
317
# File 'lib/vendor/puppet/parser/scope.rb', line 313

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)


470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
# File 'lib/vendor/puppet/parser/scope.rb', line 470

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.



321
322
323
324
325
326
327
# File 'lib/vendor/puppet/parser/scope.rb', line 321

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.



332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
# File 'lib/vendor/puppet/parser/scope.rb', line 332

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.



351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
# File 'lib/vendor/puppet/parser/scope.rb', line 351

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.



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

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.



290
291
292
293
294
295
296
297
298
299
300
301
302
# File 'lib/vendor/puppet/parser/scope.rb', line 290

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



390
391
392
# File 'lib/vendor/puppet/parser/scope.rb', line 390

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

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

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



240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
# File 'lib/vendor/puppet/parser/scope.rb', line 240

def twoscope_lookupvar(name, options = {})
  # Save the originating scope for the request
  options[:origin] = self unless options[:origin]
  table = ephemeral?(name) ? @ephemeral.last : @symtable

  if name =~ /^(.*)::(.+)$/
    begin
      qualified_scope($1).twoscope_lookupvar($2, options.merge({:origin => nil}))
    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
  # If the value is present and either we are top/node scope or originating scope...
  elsif (ephemeral_include?(name) or table.include?(name)) and (compiler and self == compiler.topscope or (resource and resource.type == "Node") or self == options[:origin])
    table[name]
  elsif resource and resource.type == "Class" and parent_type = resource.resource_type.parent
    qualified_scope(parent_type).twoscope_lookupvar(name,options.merge({:origin => nil}))
  elsif parent
    parent.twoscope_lookupvar(name, options)
  else
    :undefined
  end
end

#undef_as(x, v) ⇒ Object



213
214
215
# File 'lib/vendor/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



401
402
403
404
405
406
407
408
409
# File 'lib/vendor/puppet/parser/scope.rb', line 401

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.



395
396
397
398
# File 'lib/vendor/puppet/parser/scope.rb', line 395

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