# 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:0x9814fbc>

  #<RDoc::Comment:0x9c02048>

  #<RDoc::Comment:0x9828af8>

  #<RDoc::Comment:0x97d9b10>

  #<RDoc::Comment:0x9c18104>

  #<RDoc::Comment:0x99a4b48>

  #<RDoc::Comment:0x90a88dc>

  #<RDoc::Comment:0x891933c>

  #<RDoc::Comment:0x95788bc>

  #<RDoc::Comment:0x8fba2cc>

  #<RDoc::Comment:0xa00d3b8>

  #<RDoc::Comment:0xac4a874>

  #<RDoc::Comment:0xaca1138>

  #<RDoc::Comment:0xae6a758>

  #<RDoc::Comment:0xaf161d4>

  #<RDoc::Comment:0xafaa6a4>

  #<RDoc::Comment:0xaad6ce0>

  #<RDoc::Comment:0x9354374>

  #<RDoc::Comment:0x99c1860>

  #<RDoc::Comment:0x9ac818c>

  #<RDoc::Comment:0xb7817d8>

  #<RDoc::Comment:0x9810890>

  #<RDoc::Comment:0x8f104ac>

  #<RDoc::Comment:0x9971144>

  #<RDoc::Comment:0x9ae78e8>

  #<RDoc::Comment:0x9b375c8>

  #<RDoc::Comment:0x9899398>

  #<RDoc::Comment:0x9bbea3c>

### 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]+$/i
        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