Class: Puppet::Parser::Scope
- Extended by:
- Forwardable
- Includes:
- Enumerable, Resource::TypeCollectionHelper, Util::Errors, Util::MethodHelper
- Defined in:
- lib/puppet/parser/scope.rb
Overview
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.
Defined Under Namespace
Classes: Ephemeral
Constant Summary collapse
Instance Attribute Summary collapse
- #base ⇒ Object
- #compiler ⇒ Object
- #keyword ⇒ Object
- #namespaces ⇒ Object readonly
- #parent ⇒ Object
- #resource ⇒ Object
- #source ⇒ Object
- #top ⇒ Object
- #translated ⇒ Object
Class Method Summary collapse
-
.new_for_test_harness(node_name) ⇒ Object
Initialize a new scope suitable for parser function testing.
-
.number?(value) ⇒ Boolean
Coerce value to a number, or return
nilif it isn’t one. -
.true?(value) ⇒ Boolean
Is the value true? This allows us to control the definition of truth in one place.
Instance Method Summary collapse
-
#[](varname, options = {}) ⇒ Object
Retrieves the variable value assigned to the name given as an argument.
-
#[]=(varname, value, options = {}) ⇒ Object
Sets the variable value of the name given as an argument to the given value.
-
#add_namespace(ns) ⇒ Object
Add to our list of namespaces.
-
#class_scope(klass) ⇒ Object
Return the scope associated with a class.
-
#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.
-
#define_settings(type, params) ⇒ Object
Set defaults for a type.
- #each ⇒ Object
-
#effective_symtable(use_ephemeral) ⇒ Object
Return the effective “table” for setting variables.
-
#enclosing_scope ⇒ Puppet::Parser::Scope
The enclosing scope (topscope or nodescope) of this scope.
-
#ephemeral?(name) ⇒ Boolean
Checks whether the variable should be processed in the ephemeral scope or not.
- #ephemeral_from(match, file = nil, line = nil) ⇒ Object
-
#ephemeral_include?(name) ⇒ Boolean
check if name exists in one of the ephemeral scopes.
- #ephemeral_level ⇒ Object
-
#facts ⇒ Object
TODO: 19514 - this is smelly; who uses this? functions? templates? What about trusted facts ? Should untrusted facts be removed from facts?.
- #find_builtin_resource_type(type) ⇒ Object
- #find_defined_resource_type(type) ⇒ Object
- #find_definition(name) ⇒ Object
- #find_hostclass(name, options = {}) ⇒ Object
- #find_resource_type(type) ⇒ Object
-
#host ⇒ Object
Proxy accessors.
- #include?(name) ⇒ Boolean
-
#inherited_scope ⇒ Puppet::Parser::Scope
The scope of the inherited thing of this scope’s resource.
-
#initialize(compiler, options = {}) ⇒ Scope
constructor
Initialize our new scope.
- #is_classscope? ⇒ Boolean
- #is_nodescope? ⇒ Boolean
- #is_topscope? ⇒ Boolean
-
#lookup_as_local_name?(class_name, variable_name) ⇒ Boolean
Handles the special case of looking up fully qualified variable in not yet evaluated top scope This is ok if the lookup request originated in topscope (this happens when evaluating bindings; using the top scope to provide the values for facts..
- #lookup_qualified_variable(class_name, variable_name, position) ⇒ Object
-
#lookupdefaults(type) ⇒ Object
Collect all of the defaults set at any higher scopes.
-
#lookuptype(name) ⇒ Object
Look up a defined type.
-
#lookupvar(name, options = {}) ⇒ Object
Lookup a variable within this scope using the Puppet language’s scoping rules.
- #method_missing(method, *args, &block) ⇒ Object
- #new_ephemeral(local_scope = false) ⇒ Object
-
#newscope(options = {}) ⇒ Object
Create a new scope and set these options.
- #parent_module_name ⇒ Object
- #resolve_type_and_titles(type, titles) ⇒ Object
- #set_trusted(hash) ⇒ Object
-
#setvar(name, value, options = {}) ⇒ Object
Set a variable in the current scope.
-
#to_hash(recursive = true) ⇒ Object
Returns a Hash containing all variables and their values, optionally (and by default) including the values defined in parent.
-
#to_s ⇒ Object
Used mainly for logging.
- #undef_as(x, v) ⇒ Object
-
#unset_ephemeral_var(level = :all) ⇒ Object
remove ephemeral scope up to level.
Methods included from Util::Errors
#adderrorcontext, #devfail, #error_context, #exceptwrap, #fail
Methods included from Resource::TypeCollectionHelper
Methods included from Util::MethodHelper
#requiredopts, #set_options, #symbolize_options
Constructor Details
#initialize(compiler, options = {}) ⇒ Scope
Initialize our new scope. Defaults to having no parent.
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 |
# File 'lib/puppet/parser/scope.rb', line 183 def initialize(compiler, = {}) 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 = .delete(:namespace) @namespaces = [n] else @namespaces = [""] end raise Puppet::DevError, "compiler passed in options" if .include? :compiler () extend_with_functions_module # The symbol table for this scope. This is where we store variables. @symtable = Ephemeral.new(nil, true) @ephemeral = [ Ephemeral.new(@symtable) ] # 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
643 644 645 646 647 648 649 650 651 652 653 654 655 |
# File 'lib/puppet/parser/scope.rb', line 643 def method_missing(method, *args, &block) method.to_s =~ /^function_(.*)$/ name = $1 super unless name super unless Puppet::Parser::Functions.function(name) # 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 #{name} not defined despite being loaded!" end end |
Instance Attribute Details
#namespaces ⇒ Object (readonly)
33 34 35 |
# File 'lib/puppet/parser/scope.rb', line 33 def namespaces @namespaces end |
#translated ⇒ Object
31 32 33 |
# File 'lib/puppet/parser/scope.rb', line 31 def translated @translated end |
Class Method Details
.new_for_test_harness(node_name) ⇒ Object
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.
101 102 103 104 105 106 107 108 |
# File 'lib/puppet/parser/scope.rb', line 101 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) ⇒ Boolean
Coerce value to a number, or return nil if it isn’t one.
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
# File 'lib/puppet/parser/scope.rb', line 144 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) ⇒ Boolean
Is the value true? This allows us to control the definition of truth in one place.
132 133 134 135 136 137 138 139 140 141 |
# File 'lib/puppet/parser/scope.rb', line 132 def self.true?(value) case value when '' false when :undef false else !!value end end |
Instance Method Details
#[](varname, options = {}) ⇒ Object
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.
318 319 320 |
# File 'lib/puppet/parser/scope.rb', line 318 def [](varname, ={}) lookupvar(varname, ) end |
#[]=(varname, value, options = {}) ⇒ Object
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.
555 556 557 |
# File 'lib/puppet/parser/scope.rb', line 555 def []=(varname, value, = {}) setvar(varname, value, = {}) end |
#add_namespace(ns) ⇒ Object
Add to our list of namespaces.
162 163 164 165 166 167 168 169 |
# File 'lib/puppet/parser/scope.rb', line 162 def add_namespace(ns) return false if @namespaces.include?(ns) if @namespaces == [""] @namespaces = [ns] else @namespaces << ns end 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.
232 233 234 235 236 |
# File 'lib/puppet/parser/scope.rb', line 232 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.
220 221 222 223 224 225 226 |
# File 'lib/puppet/parser/scope.rb', line 220 def class_set(name, scope) if parent parent.class_set(name, scope) else @class_scopes[name] = scope end end |
#define_settings(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.
451 452 453 454 455 456 457 458 459 460 461 462 463 |
# File 'lib/puppet/parser/scope.rb', line 451 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 ⇒ Object
110 111 112 |
# File 'lib/puppet/parser/scope.rb', line 110 def each to_hash.each { |name, value| yield(name, value) } end |
#effective_symtable(use_ephemeral) ⇒ Object
Return the effective “table” for setting variables. This method returns the first ephemeral “table” that acts as a local scope, or this scope’s symtable. If the parameter use_ephemeral is true, the “top most” ephemeral “table” will be returned (irrespective of it being a match scope or a local scope).
534 535 536 537 538 539 540 541 542 |
# File 'lib/puppet/parser/scope.rb', line 534 def effective_symtable use_ephemeral s = @ephemeral.last return s if use_ephemeral while s && !(s.is_a?(Hash) || s.is_local_scope?()) s = s.parent end s ? s : @symtable end |
#enclosing_scope ⇒ Puppet::Parser::Scope
The enclosing scope (topscope or nodescope) of this scope. The enclosing scopes are produced when a class or define is included at some point. The parent scope of the included class or define becomes the scope in which it was included. The chain of parent scopes is followed until a node scope or the topscope is found
341 342 343 344 345 346 347 348 349 350 351 |
# File 'lib/puppet/parser/scope.rb', line 341 def enclosing_scope if has_enclosing_scope? if parent.is_topscope? or parent.is_nodescope? parent else parent.enclosing_scope end else nil end end |
#ephemeral?(name) ⇒ Boolean
Checks whether the variable should be processed in the ephemeral scope or not. All numerical variables are processed in ephemeral scope at all times, and all other variables when the ephemeral scope is a local scope.
600 601 602 |
# File 'lib/puppet/parser/scope.rb', line 600 def ephemeral?(name) @ephemeral.last.is_local_scope? || name =~ /^\d+$/ end |
#ephemeral_from(match, file = nil, line = nil) ⇒ Object
612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 |
# File 'lib/puppet/parser/scope.rb', line 612 def ephemeral_from(match, file = nil, line = nil) case match when Hash # Create local scope ephemeral and set all values from hash new_ephemeral true match.each {|k,v| setvar(k, v, :file => file, :line => line, :ephemeral => true) } else raise(ArgumentError,"Invalid regex match data. Got a #{match.class}") unless match.is_a?(MatchData) # Create a match ephemeral and set values from match data new_ephemeral false 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 end |
#ephemeral_include?(name) ⇒ Boolean
check if name exists in one of the ephemeral scopes.
592 593 594 |
# File 'lib/puppet/parser/scope.rb', line 592 def ephemeral_include?(name) @ephemeral.any? {|eph| eph.include?(name) } end |
#ephemeral_level ⇒ Object
604 605 606 |
# File 'lib/puppet/parser/scope.rb', line 604 def ephemeral_level @ephemeral.size end |
#facts ⇒ Object
TODO: 19514 - this is smelly; who uses this? functions? templates? What about trusted facts ? Should untrusted facts be removed from facts?
122 123 124 |
# File 'lib/puppet/parser/scope.rb', line 122 def facts compiler.node.facts end |
#find_builtin_resource_type(type) ⇒ Object
635 636 637 |
# File 'lib/puppet/parser/scope.rb', line 635 def find_builtin_resource_type(type) Puppet::Type.type(type.to_s.downcase.to_sym) end |
#find_defined_resource_type(type) ⇒ Object
639 640 641 |
# File 'lib/puppet/parser/scope.rb', line 639 def find_defined_resource_type(type) environment.known_resource_types.find_definition(namespaces, type.to_s.downcase) end |
#find_definition(name) ⇒ Object
175 176 177 |
# File 'lib/puppet/parser/scope.rb', line 175 def find_definition(name) known_resource_types.find_definition(namespaces, name) end |
#find_hostclass(name, options = {}) ⇒ Object
171 172 173 |
# File 'lib/puppet/parser/scope.rb', line 171 def find_hostclass(name, = {}) known_resource_types.find_hostclass(namespaces, name, ) end |
#find_resource_type(type) ⇒ Object
629 630 631 632 633 |
# File 'lib/puppet/parser/scope.rb', line 629 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 ⇒ Object
Proxy accessors
115 116 117 |
# File 'lib/puppet/parser/scope.rb', line 115 def host compiler.node.name end |
#include?(name) ⇒ Boolean
126 127 128 |
# File 'lib/puppet/parser/scope.rb', line 126 def include?(name) ! self[name].nil? end |
#inherited_scope ⇒ Puppet::Parser::Scope
The scope of the inherited thing of this scope’s resource. This could either be a node that was inherited or the class.
326 327 328 329 330 331 332 |
# File 'lib/puppet/parser/scope.rb', line 326 def inherited_scope if has_inherited_class? qualified_scope(resource.resource_type.parent) else nil end end |
#is_classscope? ⇒ Boolean
353 354 355 |
# File 'lib/puppet/parser/scope.rb', line 353 def is_classscope? resource and resource.type == "Class" end |
#is_nodescope? ⇒ Boolean
357 358 359 |
# File 'lib/puppet/parser/scope.rb', line 357 def is_nodescope? resource and resource.type == "Node" end |
#is_topscope? ⇒ Boolean
361 362 363 |
# File 'lib/puppet/parser/scope.rb', line 361 def is_topscope? compiler and self == compiler.topscope end |
#lookup_as_local_name?(class_name, variable_name) ⇒ Boolean
Handles the special case of looking up fully qualified variable in not yet evaluated top scope This is ok if the lookup request originated in topscope (this happens when evaluating bindings; using the top scope to provide the values for facts.
392 393 394 395 396 397 398 |
# File 'lib/puppet/parser/scope.rb', line 392 def lookup_as_local_name?(class_name, variable_name) # not a local if name has more than one segment return nil if variable_name =~ /::/ # partial only if the class for "" cannot be found return nil unless class_name == "" && klass = find_hostclass(class_name) && class_scope(klass).nil? is_topscope? end |
#lookup_qualified_variable(class_name, variable_name, position) ⇒ Object
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 |
# File 'lib/puppet/parser/scope.rb', line 365 def lookup_qualified_variable(class_name, variable_name, position) begin if lookup_as_local_name?(class_name, variable_name) self[variable_name] else qualified_scope(class_name).lookupvar(variable_name, position) end rescue RuntimeError => e location = if position[:lineproc] " at #{position[:lineproc].call}" elsif position[:file] && position[:line] " at #{position[:file]}:#{position[:line]}" else "" end warning "Could not look up qualified variable '#{class_name}::#{variable_name}'; #{e.}#{location}" nil end 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.
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 |
# File 'lib/puppet/parser/scope.rb', line 242 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) ⇒ Object
Look up a defined type.
264 265 266 |
# File 'lib/puppet/parser/scope.rb', line 264 def lookuptype(name) find_definition(name) || find_hostclass(name) end |
#lookupvar(name, options = {}) ⇒ Object
Lookup a variable within this scope using the Puppet language’s scoping rules. Variables can be qualified using just as in a manifest.
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 |
# File 'lib/puppet/parser/scope.rb', line 285 def lookupvar(name, = {}) unless name.is_a? String raise Puppet::ParseError, "Scope variable name #{name.inspect} is a #{name.class}, not a string" end table = @ephemeral.last if name =~ /^(.*)::(.+)$/ class_name = $1 variable_name = $2 lookup_qualified_variable(class_name, variable_name, ) elsif table.include?(name) table[name] else next_scope = inherited_scope || enclosing_scope if next_scope next_scope.lookupvar(name, ) else nil end end end |
#new_ephemeral(local_scope = false) ⇒ Object
608 609 610 |
# File 'lib/puppet/parser/scope.rb', line 608 def new_ephemeral(local_scope = false) @ephemeral.push(Ephemeral.new(@ephemeral.last, local_scope)) end |
#newscope(options = {}) ⇒ Object
Create a new scope and set these options.
438 439 440 |
# File 'lib/puppet/parser/scope.rb', line 438 def newscope( = {}) compiler.newscope(self, ) end |
#parent_module_name ⇒ Object
442 443 444 445 446 |
# File 'lib/puppet/parser/scope.rb', line 442 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
657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 |
# File 'lib/puppet/parser/scope.rb', line 657 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 |
#set_trusted(hash) ⇒ Object
505 506 507 |
# File 'lib/puppet/parser/scope.rb', line 505 def set_trusted(hash) setvar('trusted', deep_freeze(hash), :privileged => true) 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.
It's preferred that you use self[]= instead of this; only use this
when you need to set options.
472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 |
# File 'lib/puppet/parser/scope.rb', line 472 def setvar(name, value, = {}) if name =~ /^[0-9]+$/ raise Puppet::ParseError.new("Cannot assign to a numeric match result variable '$#{name}'") unless [:ephemeral] end unless name.is_a? String raise Puppet::ParseError, "Scope variable name #{name.inspect} is a #{name.class}, not a string" end # Check for reserved variable names if Puppet[:trusted_node_data] && ![:privileged] && RESERVED_VARIABLE_NAMES.include?(name) raise Puppet::ParseError, "Attempt to assign to a reserved variable name: '#{name}'" end table = effective_symtable [:ephemeral] if table.bound?(name) if [: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 = [:file] if [:file] error.line = [:line] if [:line] raise error end if [:append] table[name] = append_value(undef_as('', self[name]), value) else table[name] = value end table[name] end |
#to_hash(recursive = true) ⇒ Object
Returns a Hash containing all variables and their values, optionally (and by default) including the values defined in parent. Local values shadow parent values. Ephemeral scopes for match results ($0 - $n) are not included.
421 422 423 424 425 426 427 428 429 430 431 |
# File 'lib/puppet/parser/scope.rb', line 421 def to_hash(recursive = true) if recursive and parent target = parent.to_hash(recursive) else target = Hash.new end # add all local scopes @ephemeral.last.add_entries_to(target) target end |
#to_s ⇒ Object
Used mainly for logging
578 579 580 |
# File 'lib/puppet/parser/scope.rb', line 578 def to_s "Scope(#{@resource})" end |
#undef_as(x, v) ⇒ Object
268 269 270 271 272 273 274 |
# File 'lib/puppet/parser/scope.rb', line 268 def undef_as(x,v) if v.nil? or v == :undef x else v end end |