class Puppet::Parser::Scope

This class is part of the internal parser/evaluator/compiler functionality of Puppet. It is passed between the various classes that participate in evaluation. None of its methods are API except those that are clearly marked as such.

@api public

Constants

AST

#<RDoc::Comment:0x22b4b424>


#<RDoc::Comment:0x22f41e70>


#<RDoc::Comment:0x22fd9c70>


#<RDoc::Comment:0x23097d24>


#<RDoc::Comment:0x231582b8>


#<RDoc::Comment:0x231fa388>


#<RDoc::Comment:0x2327cd9c>


#<RDoc::Comment:0x23309134>


#<RDoc::Comment:0x2339b174>


#<RDoc::Comment:0x234319e4>


#<RDoc::Comment:0x23501694>


#<RDoc::Comment:0x2359bed8>


#<RDoc::Comment:0x2360e94c>


#<RDoc::Comment:0x21fdfdb0>


#<RDoc::Comment:0x21611dd8>


#<RDoc::Comment:0x2182ab60>


#<RDoc::Comment:0x2277a5e8>


#<RDoc::Comment:0x2280970c>


#<RDoc::Comment:0x2298ac34>


#<RDoc::Comment:0x229cb964>


#<RDoc::Comment:0x22b2e57c>


#<RDoc::Comment:0x22e98960>


#<RDoc::Comment:0x22efb63c>


#<RDoc::Comment:0x22f2cea8>


#<RDoc::Comment:0x2309da44>


#<RDoc::Comment:0x2313d01c>


#<RDoc::Comment:0x23218f68>


#<RDoc::Comment:0x2329e320>

Attributes

base[RW]
compiler[RW]
keyword[RW]
namespaces[R]
parent[RW]
resource[RW]
source[RW]
top[RW]
translated[RW]

Public Class Methods

new(compiler, options = {}) click to toggle source

Initialize our new scope. Defaults to having no parent.

# File lib/puppet/parser/scope.rb, line 146
def initialize(compiler, options = {})
  if compiler.is_a? Puppet::Parser::Compiler
    self.compiler = compiler
  else
    raise Puppet::DevError, "you must pass a compiler instance to a new scope object"
  end

  if n = options.delete(:namespace)
    @namespaces = [n]
  else
    @namespaces = [""]
  end

  raise Puppet::DevError, "compiler passed in options" if options.include? :compiler
  set_options(options)

  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
new_for_test_harness(node_name) click to toggle source

Initialize a new scope suitable for parser function testing. This method should be considered a public API for external modules. A shared spec helper should consume this API method.

@api protected

# File lib/puppet/parser/scope.rb, line 67
def self.new_for_test_harness(node_name)
  node = Puppet::Node.new(node_name)
  compiler = Puppet::Parser::Compiler.new(node)
  scope = new(compiler)
  scope.source = Puppet::Resource::Type.new(:node, node_name)
  scope.parent = compiler.topscope
  scope
end
number?(value) click to toggle source

Coerce value to a number, or return `nil` if it isn’t one.

# File lib/puppet/parser/scope.rb, line 107
def self.number?(value)
  case value
  when Numeric
    value
  when /^-?\d+(:?\.\d+|(:?\.\d+)?e\d+)$/
    value.to_f
  when /^0x[0-9a-f]+$/
    value.to_i(16)
  when /^0[0-7]+$/
    value.to_i(8)
  when /^-?\d+$/
    value.to_i
  else
    nil
  end
end
true?(value) click to toggle source

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

# File lib/puppet/parser/scope.rb, line 95
def self.true?(value)
  case value
  when ''
    false
  when :undef
    false
  else
    !!value
  end
end

Public Instance Methods

[](varname, options={}) click to toggle source

Retrieves the variable value assigned to the name given as an argument. The name must be a String, and namespace can be qualified with ‘::’. The value is looked up in this scope, its parent scopes, or in a specific visible named scope.

@param varname [String] the name of the variable (may be a qualified name using `(ns’::‘)*varname` @param options [Hash] Additional options, not part of api. @return [Object] the value assigned to the given varname @see #[]= @api public

# File lib/puppet/parser/scope.rb, line 294
def [](varname, options={})
  lookupvar(varname, options)
end
[]=(varname, value, options = {}) click to toggle source

Sets the variable value of the name given as an argument to the given value. The value is set in the current scope and may shadow a variable with the same name in a visible outer scope. It is illegal to re-assign a variable in the same scope. It is illegal to set a variable in some other scope/namespace than the scope passed to a method.

@param varname [String] The variable name to which the value is assigned. Must not contain `::` @param value [String] The value to assign to the given variable name. @param options [Hash] Additional options, not part of api.

@api public

# File lib/puppet/parser/scope.rb, line 398
def []=(varname, value, options = {})
  setvar(varname, value, options = {})
end
add_namespace(ns) click to toggle source

Add to our list of namespaces.

# File lib/puppet/parser/scope.rb, line 125
def add_namespace(ns)
  return false if @namespaces.include?(ns)
  if @namespaces == [""]
    @namespaces = [ns]
  else
    @namespaces << ns
  end
end
class_scope(klass) click to toggle source

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.

# File lib/puppet/parser/scope.rb, line 202
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) click to toggle source

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.

# File lib/puppet/parser/scope.rb, line 190
def class_set(name, scope)
  if parent
    parent.class_set(name, scope)
  else
    @class_scopes[name] = scope
  end
end
define_settings(type, params) click to toggle source

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.

# File lib/puppet/parser/scope.rb, line 344
def define_settings(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|
    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
each() { |name, value| ... } click to toggle source
# File lib/puppet/parser/scope.rb, line 76
def each
  to_hash.each { |name, value| yield(name, value) }
end
ephemeral?(name) click to toggle source

is name an ephemeral variable?

# File lib/puppet/parser/scope.rb, line 445
def ephemeral?(name)
  name =~ /^\d+$/
end
ephemeral_from(match, file = nil, line = nil) click to toggle source
# File lib/puppet/parser/scope.rb, line 457
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) click to toggle source

check if name exists in one of the ephemeral scope.

# File lib/puppet/parser/scope.rb, line 440
def ephemeral_include?(name)
  @ephemeral.any? {|eph| eph.include?(name) }
end
ephemeral_level() click to toggle source
# File lib/puppet/parser/scope.rb, line 449
def ephemeral_level
  @ephemeral.size
end
facts() click to toggle source
# File lib/puppet/parser/scope.rb, line 85
def facts
  compiler.node.facts
end
find_builtin_resource_type(type) click to toggle source
# File lib/puppet/parser/scope.rb, line 474
def find_builtin_resource_type(type)
  Puppet::Type.type(type.to_s.downcase.to_sym)
end
find_defined_resource_type(type) click to toggle source
# File lib/puppet/parser/scope.rb, line 478
def find_defined_resource_type(type)
  environment.known_resource_types.find_definition(namespaces, type.to_s.downcase)
end
find_definition(name) click to toggle source
# File lib/puppet/parser/scope.rb, line 138
def find_definition(name)
  known_resource_types.find_definition(namespaces, name)
end
find_hostclass(name, options = {}) click to toggle source
# File lib/puppet/parser/scope.rb, line 134
def find_hostclass(name, options = {})
  known_resource_types.find_hostclass(namespaces, name, options)
end
find_resource_type(type) click to toggle source
# File lib/puppet/parser/scope.rb, line 468
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
host() click to toggle source

Proxy accessors

# File lib/puppet/parser/scope.rb, line 81
def host
  compiler.node.name
end
include?(name) click to toggle source
# File lib/puppet/parser/scope.rb, line 89
def include?(name)
  ! self[name].nil?
end
lookupdefaults(type) click to toggle source

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.

# File lib/puppet/parser/scope.rb, line 212
def lookupdefaults(type)
  values = {}

  # first collect the values from the parents
  if parent
    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

  values
end
lookuptype(name) click to toggle source

Look up a defined type.

# File lib/puppet/parser/scope.rb, line 234
def lookuptype(name)
  find_definition(name) || find_hostclass(name)
end
lookupvar(name, options = {}) click to toggle source

Lookup a variable within this scope using the Puppet language’s scoping rules. Variables can be qualified using just as in a manifest.

@param [String] name the variable name to lookup

@return Object the value of the variable, or nil if it’s not found

@api public

# File lib/puppet/parser/scope.rb, line 255
def lookupvar(name, options = {})
  unless name.is_a? String
    raise Puppet::DevError, "Scope variable name is a #{name.class}, not a string"
  end

  # 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).lookupvar($2, options.merge({:origin => nil}))
    rescue RuntimeError => e
      location = (options[:file] && (options[:line] || options[:lineproc])) ? " at #{options[:file]}:#{options[:line]|| options[:lineproc].call}" : ''
      warning "Could not look up qualified variable '#{name}'; #{e.message}#{location}"
      nil
    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).lookupvar(name, options.merge({:origin => nil}))
  elsif parent
    parent.lookupvar(name, options)
  else
    nil
  end
end
method_missing(method, *args, &block) click to toggle source
Calls superclass method
# File lib/puppet/parser/scope.rb, line 482
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
new_ephemeral() click to toggle source
# File lib/puppet/parser/scope.rb, line 453
def new_ephemeral
  @ephemeral.push(Ephemeral.new(@ephemeral.last))
end
newscope(options = {}) click to toggle source

Create a new scope and set these options.

# File lib/puppet/parser/scope.rb, line 331
def newscope(options = {})
  compiler.newscope(self, options)
end
parent_module_name() click to toggle source
# File lib/puppet/parser/scope.rb, line 335
def parent_module_name
  return nil unless @parent
  return nil unless @parent.source
  @parent.source.module_name
end
resolve_type_and_titles(type, titles) click to toggle source
# File lib/puppet/parser/scope.rb, line 495
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
setvar(name, value, options = {}) click to toggle source

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.

It's preferred that you use self[]= instead of this; only use this

when you need to set options.

# File lib/puppet/parser/scope.rb, line 363
def setvar(name, value, options = {})
  unless name.is_a? String
    raise Puppet::DevError, "Scope variable name is a #{name.class}, not a string"
  end

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

  if options[:append]
    table[name] = append_value(undef_as('', self[name]), value)
  else 
    table[name] = value
  end
end
to_hash(recursive = true) click to toggle source

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.

# File lib/puppet/parser/scope.rb, line 308
def to_hash(recursive = true)
  if recursive and parent
    target = parent.to_hash(recursive)
  else
    target = Hash.new
  end

  @symtable.each do |name, value|
    if value == :undef
      target.delete(name)
    else
      target[name] = value
    end
  end

  target
end
to_s() click to toggle source

Used mainly for logging

# File lib/puppet/parser/scope.rb, line 421
def to_s
  "Scope(#{@resource})"
end
undef_as(x,v) click to toggle source
# File lib/puppet/parser/scope.rb, line 238
def undef_as(x,v)
  if v.nil? or v == :undef
    x
  else
    v
  end
end
unset_ephemeral_var(level=:all) click to toggle source

remove ephemeral scope up to level

# File lib/puppet/parser/scope.rb, line 426
def unset_ephemeral_var(level=:all)
  if level == :all
    @ephemeral = [ Ephemeral.new ]
  else
    # If we ever drop 1.8.6 and lower, this should be replaced by a single
    # pop-with-a-count - or if someone more ambitious wants to monkey-patch
    # that feature into older rubies. --daniel 2012-07-16
    (@ephemeral.size - level).times do
      @ephemeral.pop
    end
  end
end