summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/puppet/parser/ast/node.rb4
-rw-r--r--lib/puppet/parser/interpreter.rb95
-rw-r--r--lib/puppet/parser/scope.rb1736
3 files changed, 880 insertions, 955 deletions
diff --git a/lib/puppet/parser/ast/node.rb b/lib/puppet/parser/ast/node.rb
index 32bed7a2b..2e68cbdb3 100644
--- a/lib/puppet/parser/ast/node.rb
+++ b/lib/puppet/parser/ast/node.rb
@@ -19,10 +19,6 @@ class Puppet::Parser::AST
)
scope.context = self.object_id
- # Mark this scope as a nodescope, so that classes will be
- # singletons within it
- scope.isnodescope
-
# Now set all of the facts inside this scope
facts.each { |var, value|
scope.setvar(var, value)
diff --git a/lib/puppet/parser/interpreter.rb b/lib/puppet/parser/interpreter.rb
index 730b59e15..35c122c43 100644
--- a/lib/puppet/parser/interpreter.rb
+++ b/lib/puppet/parser/interpreter.rb
@@ -85,6 +85,7 @@ module Puppet
require 'ldap'
rescue LoadError
@ldap = nil
+ return
end
begin
@ldap = LDAP::Conn.new(Puppet[:ldapserver], Puppet[:ldapport])
@@ -95,6 +96,25 @@ module Puppet
end
end
+ # Search for our node in the various locations.
+ def nodesearch(node)
+ # At this point, stop at the first source that defines
+ # the node
+ @nodesources.each do |source|
+ method = "nodesearch_%s" % source
+ if self.respond_to? method
+ parent, nodeclasses = self.send(method, node)
+ end
+
+ if nodeclasses
+ Puppet.info "Found %s in %s" % [client, source]
+ return parent, nodeclasses
+ end
+ end
+
+ return nil, nil
+ end
+
# Find the ldap node and extra the info, returning just
# the critical data.
def nodesearch_ldap(node)
@@ -171,63 +191,42 @@ module Puppet
names << "#{client}.#{facts['domain']}"
end
- begin
- if @usenodes
- unless client
- raise Puppet::Error,
- "Cannot evaluate nodes with a nil client"
- end
+ scope = Puppet::Parser::Scope.new() # no parent scope
+ scope.name = "top"
+ scope.type = "puppet"
+ scope.interp = self
- classes = nil
- parent = nil
- # At this point, stop at the first source that defines
- # the node
- @nodesources.each do |source|
- method = "nodesearch_%s" % source
- if self.respond_to? method
- parent, classes = self.send(method, client)
- end
+ classes = @classes.dup
- if classes
- Puppet.info "Found %s in %s" % [client, source]
- break
- end
- end
+ args = {:ast => @ast, :facts => facts, :classes => classes}
- # We've already evaluated the AST, in this case
- #return @scope.evalnode(names, facts, classes, parent)
- return @scope.evalnode(
- :name => names,
- :facts => facts,
- :classes => classes,
- :parent => parent
- )
- else
- # We've already evaluated the AST, in this case
- @scope = Puppet::Parser::Scope.new() # no parent scope
- @scope.interp = self
- #return @scope.evaluate(@ast, facts, @classes)
- return @scope.evaluate(
- :ast => @ast,
- :facts => facts,
- :classes => @classes
- )
+ if @usenodes
+ unless client
+ raise Puppet::Error,
+ "Cannot evaluate nodes with a nil client"
end
- #@ast.evaluate(@scope)
+
+ Puppet.debug "Nodes defined"
+ args[:names] = names
+
+ parent, nodeclasses = nodesearch(client)
+
+ classes += nodeclasses if nodeclasses
+
+ args[:parentnode] = parent if parent
+ end
+
+ begin
+ return scope.evaluate(args)
rescue Puppet::DevError, Puppet::Error, Puppet::ParseError => except
- #Puppet.err "File %s, line %s: %s" %
- # [except.file, except.line, except.message]
- if Puppet[:debug]
- puts except.backtrace
- end
- #exit(1)
raise
rescue => except
error = Puppet::DevError.new("%s: %s" %
[except.class, except.message])
- if Puppet[:debug]
- puts except.backtrace
- end
+ error.backtrace = except.backtrace
+ #if Puppet[:debug]
+ # puts except.backtrace
+ #end
raise error
end
end
diff --git a/lib/puppet/parser/scope.rb b/lib/puppet/parser/scope.rb
index 6e945adb4..35137fda7 100644
--- a/lib/puppet/parser/scope.rb
+++ b/lib/puppet/parser/scope.rb
@@ -1,1061 +1,991 @@
# The scope class, which handles storing and retrieving variables and types and
# such.
+require 'puppet/parser/parser'
require 'puppet/transportable'
-module Puppet
- module Parser
- class Scope
- class ScopeObj < Hash
- attr_accessor :file, :line, :type, :name
- end
+module Puppet::Parser
+ class Scope
+ class ScopeObj < Hash
+ attr_accessor :file, :line, :type, :name
+ end
- Puppet::Util.logmethods(self)
+ Puppet::Util.logmethods(self)
- include Enumerable
- attr_accessor :parent, :level, :interp
- attr_accessor :name, :type, :topscope, :base, :keyword
+ include Enumerable
+ attr_accessor :parent, :level, :interp
+ attr_accessor :name, :type, :topscope, :base, :keyword
- attr_accessor :top, :context
+ attr_accessor :top, :context
- # This is probably not all that good of an idea, but...
- # This way a parent can share its tables with all of its children.
- attr_writer :nodetable, :classtable, :definedtable
+ # This is probably not all that good of an idea, but...
+ # This way a parent can share its tables with all of its children.
+ attr_writer :nodetable, :classtable, :definedtable
- # Whether we behave declaratively. Note that it's a class variable,
- # so all scopes behave the same.
- @@declarative = true
+ # Whether we behave declaratively. Note that it's a class variable,
+ # so all scopes behave the same.
+ @@declarative = true
- # Retrieve and set the declarative setting.
- def Scope.declarative
- return @@declarative
- end
-
- def Scope.declarative=(val)
- @@declarative = val
- end
+ # Retrieve and set the declarative setting.
+ def self.declarative
+ return @@declarative
+ end
- # Add all of the defaults for a given object to that object.
- def adddefaults(obj)
- defaults = self.lookupdefaults(obj.type)
+ def self.declarative=(val)
+ @@declarative = val
+ end
- defaults.each do |var, value|
- unless obj[var]
- self.debug "Adding default %s for %s" %
- [var, obj.type]
+ # Add all of the defaults for a given object to that object.
+ def adddefaults(obj)
+ defaults = lookupdefaults(obj.type)
- obj[var] = value
- end
- end
- end
+ defaults.each do |var, value|
+ unless obj[var]
+ self.debug "Adding default %s for %s" %
+ [var, obj.type]
- # Add a single object's tags to the global list of tags for
- # that object.
- def addtags(obj)
- unless defined? @tagtable
- raise Puppet::DevError, "Told to add tags, but no tag table"
+ obj[var] = value
end
- list = @tagtable[obj.type][obj.name]
-
- obj.tags.each { |tag|
- unless list.include?(tag)
- if tag.nil? or tag == ""
- Puppet.debug "Got tag %s from %s(%s)" %
- [tag.inspect, obj.type, obj.name]
- else
- list << tag
- end
- end
- }
end
+ end
- # Is the type a builtin type?
- def builtintype?(type)
- if typeklass = Puppet::Type.type(type)
- return typeklass
- else
- return false
- end
+ # Add a single object's tags to the global list of tags for
+ # that object.
+ def addtags(obj)
+ unless defined? @tagtable
+ raise Puppet::DevError, "Told to add tags, but no tag table"
end
+ list = @tagtable[obj.type][obj.name]
- # Verify that the given object isn't defined elsewhere.
- def chkobjectclosure(hash)
- type = hash[:type]
- name = hash[:name]
- unless name
- return true
- end
- if @definedtable[type].include?(name)
- typeklass = Puppet::Type.type(type)
- if typeklass and ! typeklass.isomorphic?
- Puppet.info "Allowing duplicate %s" % type
+ obj.tags.each { |tag|
+ unless list.include?(tag)
+ if tag.nil? or tag == ""
+ Puppet.debug "Got tag %s from %s(%s)" %
+ [tag.inspect, obj.type, obj.name]
else
- # Either it's a defined type, which are never
- # isomorphic, or it's a non-isomorphic type.
- msg = "Duplicate definition: %s[%s] is already defined" %
- [type, name]
- error = Puppet::ParseError.new(msg)
- if hash[:line]
- error.line = hash[:line]
- end
- if hash[:file]
- error.file = hash[:file]
- end
- raise error
+ list << tag
end
end
+ }
+ end
- return true
- end
-
- def declarative=(val)
- self.class.declarative = val
- end
-
- def declarative
- self.class.declarative
+ # Is the type a builtin type?
+ def builtintype?(type)
+ if typeklass = Puppet::Type.type(type)
+ return typeklass
+ else
+ return false
end
+ end
- # Log the existing tags. At some point this should be in a better
- # place, but eh.
- def logtags
- @tagtable.sort { |a, b|
- a[0] <=> b[0]
- }.each { |type, names|
- names.sort { |a, b|
- a[0] <=> b[0]
- }.each { |name, tags|
- Puppet.info "%s(%s): '%s'" % [type, name, tags.join("' '")]
- }
- }
+ # Verify that the given object isn't defined elsewhere.
+ def chkobjectclosure(hash)
+ type = hash[:type]
+ name = hash[:name]
+ unless name
+ return true
end
-
- # Create a new child scope.
- def child=(scope)
- @children.push(scope)
-
- if defined? @nodetable
- scope.nodetable = @nodetable
+ if @definedtable[type].include?(name)
+ typeklass = Puppet::Type.type(type)
+ if typeklass and ! typeklass.isomorphic?
+ Puppet.info "Allowing duplicate %s" % type
else
- raise Puppet::DevError, "No nodetable has been defined"
- end
-
- if defined? @classtable
- scope.classtable = @classtable
- else
- raise Puppet::DevError, "No classtable has been defined"
- end
-
- if defined? @definedtable
- scope.definedtable = @definedtable
- else
- raise Puppet::DevError, "No definedtable has been defined"
- end
- end
-
- # Test whether a given scope is declarative. Even though it's
- # a global value, the calling objects don't need to know that.
- def declarative?
- @@declarative
- end
-
- # Remove a specific child.
- def delete(child)
- @children.delete(child)
- end
-
- # Verify that no nodescopes are hanging around.
- def nodeclean
- @children.find_all { |child|
- if child.is_a?(Scope)
- child.nodescope?
- else
- false
+ # Either it's a defined type, which are never
+ # isomorphic, or it's a non-isomorphic type.
+ msg = "Duplicate definition: %s[%s] is already defined" %
+ [type, name]
+ error = Puppet::ParseError.new(msg)
+ if hash[:line]
+ error.line = hash[:line]
end
- }.each { |child|
- @children.delete(child)
- }
-
- @children.each { |child|
- if child.is_a?(Scope)
- child.nodeclean
+ if hash[:file]
+ error.file = hash[:file]
end
- }
+ raise error
+ end
end
- # Is this scope associated with being a node? The answer determines
- # whether we store class instances here
- def nodescope?
- @nodescope
- end
+ return true
+ end
- # Mark that we are a nodescope.
- def isnodescope
- @nodescope = true
+ def declarative=(val)
+ self.class.declarative = val
+ end
- # Also, create the extra tables associated with being a node
- # scope.
- # The table for storing class singletons.
- @classtable = Hash.new(nil)
+ def declarative
+ self.class.declarative
+ end
- # Also, create the object checking map
- @definedtable = Hash.new { |types, type|
- types[type] = {}
+ # Log the existing tags. At some point this should be in a better
+ # place, but eh.
+ def logtags
+ @tagtable.sort { |a, b|
+ a[0] <=> b[0]
+ }.each { |type, names|
+ names.sort { |a, b|
+ a[0] <=> b[0]
+ }.each { |name, tags|
+ Puppet.info "%s(%s): '%s'" % [type, name, tags.join("' '")]
}
- end
+ }
+ end
- # Are we the top scope?
- def topscope?
- @level == 1
- end
+ # Create a new child scope.
+ def child=(scope)
+ @children.push(scope)
- # Return a list of all of the defined classes.
- def classlist
- unless defined? @classtable
- raise Puppet::DevError, "Scope did not receive class table"
- end
- return @classtable.values
+ if defined? @nodetable
+ scope.nodetable = @nodetable
+ else
+ raise Puppet::DevError, "No nodetable has been defined"
end
- # Yield each child scope in turn
- def each
- @children.reject { |child|
- yield child
- }
+ if defined? @classtable
+ scope.classtable = @classtable
+ else
+ raise Puppet::DevError, "No classtable has been defined"
end
- # Evaluate a specific node's code. This method will normally be called
- # on the top-level scope, but it actually evaluates the node at the
- # appropriate scope.
- #def evalnode(names, facts, classes = nil, parent = nil)
- def evalnode(hash)
- names = hash[:name]
- facts = hash[:facts]
- classes = hash[:classes]
- parent = hash[:parent]
- # First make sure there aren't any other node scopes lying around
- self.nodeclean
-
- # If they've passed classes in, then just generate from there.
- if classes
- return self.gennode(
- :names => names,
- :facts => facts,
- :classes => classes,
- :parent => parent
- )
- end
-
- scope = code = nil
- # Find a node that matches one of our names
- names.each { |node|
- if hash = @nodetable[node]
- code = hash[:node]
- scope = hash[:scope]
- break
- end
- }
-
- # And fail if we don't find one.
- unless scope and code
- raise Puppet::Error, "Could not find configuration for %s" %
- names.join(" or ")
- end
-
- # We need to do a little skullduggery here. We want a
- # temporary scope, because we don't want this scope to
- # show up permanently in the scope tree -- otherwise we could
- # not evaluate the node multiple times. We could conceivably
- # cache the results, but it's not worth it at this stage.
-
- # Note that we evaluate the node code with its containing
- # scope, not with the top scope. We also retrieve the created
- # nodescope so that we can get any classes set within it
- nodescope = code.safeevaluate(:scope => scope, :facts => facts)
-
- # We don't need to worry about removing the Node code because
- # it will be removed during translation.
-
- # convert the whole thing
- objects = self.to_trans
-
- # Add any evaluated classes to our top-level object
- unless nodescope.classlist.empty?
- objects.classes = nodescope.classlist
- end
-
- if objects.is_a?(Puppet::TransBucket)
- objects.top = true
- end
- # I should do something to add the node as an object with tags
- # but that will possibly end up with far too many tags.
- #self.logtags
- return objects
+ if defined? @definedtable
+ scope.definedtable = @definedtable
+ else
+ raise Puppet::DevError, "No definedtable has been defined"
end
+ end
- # Pull in all of the appropriate classes and evaluate them. It'd
- # be nice if this didn't know quite so much about how AST::Node
- # operated internally.
- #def gennode(names, facts, classes, parent)
- def gennode(hash)
- names = hash[:names]
- facts = hash[:facts]
- classes = hash[:classes]
- parent = hash[:parent]
- name = names.shift
- arghash = {
- :type => name,
- :code => AST::ASTArray.new(:pin => "[]")
- }
-
- if parent
- arghash[:parentclass] = parent
- end
+ # Test whether a given scope is declarative. Even though it's
+ # a global value, the calling objects don't need to know that.
+ def declarative?
+ @@declarative
+ end
- # Create the node
- node = AST::Node.new(arghash)
- node.keyword = "node"
-
- # Now evaluate it, which evaluates the parent but doesn't really
- # do anything else but does return the nodescope
- scope = node.safeevaluate(:scope => self)
-
- # And now evaluate each set klass within the nodescope.
- classes.each { |klass|
- if code = scope.lookuptype(klass)
- #code.safeevaluate(scope, {}, klass, klass)
- code.safeevaluate(
- :scope => scope,
- :facts => {},
- :type => klass
- )
- end
- }
+ # Remove a specific child.
+ def delete(child)
+ @children.delete(child)
+ end
- return scope.to_trans
- end
+ # Are we the top scope?
+ def topscope?
+ @level == 1
+ end
- # Retrieve a specific node. This is used in ast.rb to find a
- # parent node and in findnode to retrieve and evaluate a node.
- def node(name)
- @nodetable[name]
+ # Return a list of all of the defined classes.
+ def classlist
+ unless defined? @classtable
+ raise Puppet::DevError, "Scope did not receive class table"
end
+ return @classtable.values
+ end
- # Store a host in the site node table.
- def setnode(name,code)
- unless defined? @nodetable
- raise Puppet::DevError, "No node table defined"
- end
- if @nodetable.include?(name)
- raise Puppet::ParseError, "Host %s is already defined" % name
- else
- #Puppet.warning "Setting node %s at level %s" % [name, @level]
+ # Yield each child scope in turn
+ def each
+ @children.reject { |child|
+ yield child
+ }
+ end
- # We have to store both the scope that's setting the node and
- # the node itself, so that the node gets evaluated in the correct
- # scope.
- @nodetable[name] = {
+ # Evaluate a list of classes.
+ def evalclasses(classes)
+ return unless classes
+ classes.each do |klass|
+ if code = lookuptype(klass)
+ code.safeevaluate(
:scope => self,
- :node => code
- }
+ :facts => {},
+ :type => klass
+ )
end
end
+ end
- # Evaluate normally, with no node definitions. This is a bit of a
- # silly method, in that it just calls evaluate on the passed-in
- # objects, and then calls to_trans on itself. It just conceals
- # a paltry amount of info from whomever's using the scope object.
- def evaluate(hash)
- objects = hash[:ast]
- facts = hash[:facts] || {}
- classes = hash[:classes] || []
- facts.each { |var, value|
- self.setvar(var, value)
- }
+ # Evaluate a specific node's code. This method will normally be called
+ # on the top-level scope, but it actually evaluates the node at the
+ # appropriate scope.
+ #def evalnode(names, facts, classes = nil, parent = nil)
+ def evalnode(hash)
+ objects = hash[:ast]
+ names = hash[:names] or
+ raise Puppet::DevError, "Node names must be provided to evalnode"
+ facts = hash[:facts]
+ classes = hash[:classes]
+ parent = hash[:parent]
+
+ scope = code = nil
+ # Find a node that matches one of our names
+ names.each { |node|
+ if nodehash = @nodetable[node]
+ code = nodehash[:node]
+ scope = nodehash[:scope]
+ break
+ end
+ }
+
+ # And fail if we don't find one.
+ unless scope and code
+ raise Puppet::Error, "Could not find configuration for %s" %
+ names.join(" or ")
+ end
+
+ # We need to do a little skullduggery here. We want a
+ # temporary scope, because we don't want this scope to
+ # show up permanently in the scope tree -- otherwise we could
+ # not evaluate the node multiple times. We could conceivably
+ # cache the results, but it's not worth it at this stage.
+
+ # Note that we evaluate the node code with its containing
+ # scope, not with the top scope. We also retrieve the created
+ # scope so that we can get any classes set within it
+ nodescope = code.safeevaluate(:scope => scope, :facts => facts)
+
+ scope.evalclasses(classes)
+ end
- objects.safeevaluate(:scope => self)
+ # Evaluate normally, with no node definitions. This is a bit of a
+ # silly method, in that it just calls evaluate on the passed-in
+ # objects, and then calls to_trans on itself. It just conceals
+ # a paltry amount of info from whomever's using the scope object.
+ def evaluate(hash)
+ objects = hash[:ast]
+ facts = hash[:facts] || {}
+
+ unless objects
+ raise Puppet::DevError, "Evaluation requires an AST tree"
+ end
+ # Set all of our facts in the top-level scope.
+ facts.each { |var, value|
+ self.setvar(var, value)
+ }
+
+ # Evaluate all of our configuration. This does not evaluate any
+ # node definitions.
+ objects.safeevaluate(:scope => self)
+
+ # If they've provided a name or a parent, we assume they're looking for nodes.
+ if hash.include? :parentnode
+ # Specifying a parent node takes precedence, because it is assumed
+ # that this node was found in a remote repository like ldap.
+ gennode(hash)
+ elsif hash.include? :names # else, look for it in the config
+ evalnode(hash)
+ else
+ # Else we're not using nodes at all, so just evaluate any passed-in
+ # classes.
+ classes = hash[:classes] || []
+ evalclasses(classes)
# These classes would be passed in manually, via something like
# a cfengine module
- classes.each { |klass|
- if code = self.lookuptype(klass)
- code.safeevaluate(
- :scope => self,
- :facts => {},
- :type => klass
- )
- end
- }
-
- objects = self.to_trans
- objects.top = true
-
- # Add our class list
- unless self.classlist.empty?
- objects.classes = self.classlist
- end
-
- return objects
end
- # Take all of our objects and evaluate them.
- def finish
- self.info "finishing"
- @objectlist.each { |object|
- if object.is_a? ScopeObj
- self.info "finishing %s" % object.name
- if obj = finishobject(object)
- @children << obj
- end
- end
- }
-
- @finished = true
-
- self.info "finished"
- end
-
- # If the object is defined in an upper scope, then add our
- # params to that upper scope; else, create a transobject
- # or evaluate the definition.
- def finishobject(object)
- type = object.type
- name = object.name
-
- # It should be a defined type.
- definedtype = self.lookuptype(type)
-
- unless definedtype
- error = Puppet::ParseError.new("No such type %s" % type)
- error.line = object.line
- error.file = object.file
- raise error
- end
-
- return definedtype.safeevaluate(
- :scope => self,
- :arguments => object,
- :type => type,
- :name => name
- )
- end
+ objects = self.to_trans
+ objects.top = true
- def finished?
- @finished
+ # Add our class list
+ unless self.classlist.empty?
+ objects.classes = self.classlist
end
- # Initialize our new scope. Defaults to having no parent and to
- # being declarative.
- def initialize(hash = {})
- @parent = nil
- @type = nil
- @name = nil
- @finished = false
- hash.each { |name, val|
- method = name.to_s + "="
- if self.respond_to? method
- self.send(method, val)
- else
- raise Puppet::DevError, "Invalid scope argument %s" % name
- end
- }
- #@parent = hash[:parent]
- @nodescope = false
+ return objects
+ end
- @tags = []
+ # Pull in all of the appropriate classes and evaluate them. It'd
+ # be nice if this didn't know quite so much about how AST::Node
+ # operated internally. This is used when a list of classes is passed in,
+ # instead of a node definition, such as from the cfengine module.
+ def gennode(hash)
+ names = hash[:names] or
+ raise Puppet::DevError, "Node names must be provided to gennode"
+ facts = hash[:facts]
+ classes = hash[:classes]
+ parent = hash[:parent]
+ name = names.shift
+ arghash = {
+ :type => name,
+ :code => AST::ASTArray.new(:pin => "[]")
+ }
+
+ if parent
+ arghash[:parentclass] = parent
+ end
+
+ # Create the node
+ node = AST::Node.new(arghash)
+ node.keyword = "node"
+
+ # Now evaluate it, which evaluates the parent and nothing else
+ # but does return the nodescope.
+ scope = node.safeevaluate(:scope => self)
+
+ # Finally evaluate our list of classes in this new scope.
+ scope.evalclasses(classes)
+ end
- if @parent.nil?
- unless hash.include?(:declarative)
- hash[:declarative] = true
- end
- self.istop(hash[:declarative])
+ # Take all of our objects and evaluate them.
+# def finish
+# self.info "finishing"
+# @objectlist.each { |object|
+# if object.is_a? ScopeObj
+# self.info "finishing %s" % object.name
+# if obj = finishobject(object)
+# @children << obj
+# end
+# end
+# }
+#
+# @finished = true
+#
+# self.info "finished"
+# end
+#
+# # If the object is defined in an upper scope, then add our
+# # params to that upper scope; else, create a transobject
+# # or evaluate the definition.
+# def finishobject(object)
+# type = object.type
+# name = object.name
+#
+# # It should be a defined type.
+# definedtype = lookuptype(type)
+#
+# unless definedtype
+# error = Puppet::ParseError.new("No such type %s" % type)
+# error.line = object.line
+# error.file = object.file
+# raise error
+# end
+#
+# return definedtype.safeevaluate(
+# :scope => self,
+# :arguments => object,
+# :type => type,
+# :name => name
+# )
+# end
+#
+# def finished?
+# @finished
+# end
+
+ # Initialize our new scope. Defaults to having no parent and to
+ # being declarative.
+ def initialize(hash = {})
+ @parent = nil
+ @type = nil
+ @name = nil
+ @finished = false
+ hash.each { |name, val|
+ method = name.to_s + "="
+ if self.respond_to? method
+ self.send(method, val)
else
- @parent.child = self
- @level = @parent.level + 1
- @interp = @parent.interp
- @topscope = @parent.topscope
- @context = @parent.context
- end
-
- # Our child scopes and objects
- @children = []
+ raise Puppet::DevError, "Invalid scope argument %s" % name
+ end
+ }
+
+ @tags = []
+
+ if @parent.nil?
+ unless hash.include?(:declarative)
+ hash[:declarative] = true
+ end
+ self.istop(hash[:declarative])
+ else
+ @parent.child = self
+ @level = @parent.level + 1
+ @interp = @parent.interp
+ @topscope = @parent.topscope
+ @context = @parent.context
+ end
+
+ # Our child scopes and objects
+ @children = []
+
+ # The symbol table for this scope
+ @symtable = Hash.new(nil)
+
+ # The type table for this scope
+ @typetable = Hash.new(nil)
+
+ # 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.
+ @defaultstable = Hash.new { |dhash,type|
+ dhash[type] = Hash.new(nil)
+ }
+
+ # The object table is similar, but it is actually a hash of hashes
+ # where the innermost objects are TransObject instances.
+ @objectable = Hash.new { |typehash,typekey|
+ # See #newobject for how to create the actual objects
+ typehash[typekey] = Hash.new(nil)
+ }
+
+ # The list of simpler hash objects.
+ @objectlist = []
+
+ # This is just for collecting statements locally, so we can
+ # verify that there is no overlap within this specific scope
+ @localobjectable = Hash.new { |typehash,typekey|
+ typehash[typekey] = Hash.new(nil)
+ }
+
+ # Map the names to the tables.
+ @map = {
+ "variable" => @symtable,
+ "type" => @typetable,
+ "node" => @nodetable,
+ "object" => @objectable,
+ "defaults" => @defaultstable
+ }
+ end
- # The symbol table for this scope
- @symtable = Hash.new(nil)
+ # Mark that we're the top scope, and set some hard-coded info.
+ def istop(declarative = true)
+ # the level is mostly used for debugging
+ @level = 1
- # The type table for this scope
- @typetable = Hash.new(nil)
+ # The table for storing class singletons. This will only actually
+ # be used by top scopes and node scopes.
+ @classtable = Hash.new(nil)
- # 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.
- @defaultstable = Hash.new { |dhash,type|
- dhash[type] = Hash.new(nil)
- }
+ self.class.declarative = declarative
- # The object table is similar, but it is actually a hash of hashes
- # where the innermost objects are TransObject instances.
- @objectable = Hash.new { |typehash,typekey|
- # See #newobject for how to create the actual objects
- typehash[typekey] = Hash.new(nil)
- }
+ # The table for all defined objects.
+ @definedtable = Hash.new { |types, type|
+ types[type] = {}
+ }
- # The list of simpler hash objects.
- @objectlist = []
+ # A table for storing nodes.
+ @nodetable = Hash.new(nil)
- # This is just for collecting statements locally, so we can
- # verify that there is no overlap within this specific scope
- @localobjectable = Hash.new { |typehash,typekey|
- typehash[typekey] = Hash.new(nil)
- }
+ # Eventually, if we support sites, this will allow definitions
+ # of nodes with the same name in different sites. For now
+ # the top-level scope is always the only site scope.
+ @sitescope = true
- # Map the names to the tables.
- @map = {
- "variable" => @symtable,
- "type" => @typetable,
- "node" => @nodetable,
- "object" => @objectable,
- "defaults" => @defaultstable
+ # And create a tag table, so we can collect all of the tags
+ # associated with any objects created in this scope tree
+ @tagtable = Hash.new { |types, type|
+ types[type] = Hash.new { |names, name|
+ names[name] = []
}
- end
+ }
- # Mark that we're the top scope, and set some hard-coded info.
- def istop(declarative = true)
- # the level is mostly used for debugging
- @level = 1
-
- # The table for storing class singletons. This will only actually
- # be used by top scopes and node scopes.
- @classtable = Hash.new(nil)
+ @context = nil
+ @topscope = self
+ @type = "puppet"
+ @name = "top"
+ end
- self.class.declarative = declarative
+ # Look up a given class. This enables us to make sure classes are
+ # singletons
+ def lookupclass(klassid)
+ unless defined? @classtable
+ raise Puppet::DevError, "Scope did not receive class table"
+ end
+ return @classtable[klassid]
+ end
- # The table for all defined objects. This will only be
- # used in the top scope if we don't have any nodescopes.
- @definedtable = Hash.new { |types, type|
- types[type] = {}
+ # 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.
+ def lookupdefaults(type)
+ values = {}
+
+ # first collect the values from the parents
+ unless @parent.nil?
+ @parent.lookupdefaults(type).each { |var,value|
+ values[var] = value
}
+ end
- # A table for storing nodes.
- @nodetable = Hash.new(nil)
-
- # Eventually, if we support sites, this will allow definitions
- # of nodes with the same name in different sites. For now
- # the top-level scope is always the only site scope.
- @sitescope = true
-
- # And create a tag table, so we can collect all of the tags
- # associated with any objects created in this scope tree
- @tagtable = Hash.new { |types, type|
- types[type] = Hash.new { |names, name|
- names[name] = []
- }
+ # then override them with any current values
+ # this should probably be done differently
+ if @defaultstable.include?(type)
+ @defaultstable[type].each { |var,value|
+ values[var] = value
}
+ end
+ #Puppet.debug "Got defaults for %s: %s" %
+ # [type,values.inspect]
+ return values
+ end
- @context = nil
- @topscope = self
- @type = "puppet"
- @name = "top"
+ # Look up a node by name
+ def lookupnode(name)
+ #Puppet.debug "Looking up type %s" % name
+ value = lookup("type",name)
+ if value == :undefined
+ return nil
+ else
+ #Puppet.debug "Found node %s" % name
+ return value
end
+ end
- # This method abstracts recursive searching. It accepts the type
- # of search being done and then either a literal key to search for or
- # a Proc instance to do the searching.
- def lookup(type,sub, usecontext = false)
- table = @map[type]
- if table.nil?
- error = Puppet::ParseError.new(
- "Could not retrieve %s table at level %s" %
- [type,self.level]
- )
- raise error
- end
+ # Look up a defined type.
+ def lookuptype(name)
+ #Puppet.debug "Looking up type %s" % name
+ value = lookup("type",name)
+ if value == :undefined
+ return nil
+ else
+ #Puppet.debug "Found type %s" % name
+ return value
+ end
+ end
- if sub.is_a?(Proc) and obj = sub.call(table)
- return obj
- elsif table.include?(sub)
- return table[sub]
- elsif ! @parent.nil?
- #self.notice "Context is %s, parent %s is %s" %
- # [self.context, @parent.type, @parent.context]
- if usecontext and self.context != @parent.context
- return :undefined
- else
- return @parent.lookup(type,sub, usecontext)
+ # Look up an object by name and type. This should only look up objects
+ # within a class structure, not within the entire scope structure.
+ def lookupobject(hash)
+ type = hash[:type]
+ name = hash[:name]
+ #Puppet.debug "Looking up object %s of type %s in level %s" %
+ # [name, type, @level]
+ sub = proc { |table|
+ if table.include?(type)
+ if table[type].include?(name)
+ table[type][name]
end
else
- return :undefined
+ nil
end
+ }
+ value = lookup("object",sub, true)
+ if value == :undefined
+ return nil
+ else
+ return value
end
+ end
- # Look up a given class. This enables us to make sure classes are
- # singletons
- def lookupclass(klassid)
- unless defined? @classtable
- raise Puppet::DevError, "Scope did not receive class table"
- end
- return @classtable[klassid]
+ # Look up a variable. The simplest value search we do.
+ def lookupvar(name)
+ #Puppet.debug "Looking up variable %s" % name
+ value = lookup("variable", name)
+ if value == :undefined
+ return ""
+ #error = Puppet::ParseError.new(
+ # "Undefined variable '%s'" % name
+ #)
+ #raise error
+ else
+ return value
end
+ end
- # 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.
- 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 @defaultstable.include?(type)
- @defaultstable[type].each { |var,value|
- values[var] = value
- }
- end
- #Puppet.debug "Got defaults for %s: %s" %
- # [type,values.inspect]
- return values
+ # Add a new object to our object table.
+ def newobject(hash)
+ if @objectable[hash[:type]].include?(hash[:name])
+ raise Puppet::DevError, "Object %s[%s] is already defined" %
+ [hash[:type], hash[:name]]
end
- # Look up a node by name
- def lookupnode(name)
- #Puppet.debug "Looking up type %s" % name
- value = self.lookup("type",name)
- if value == :undefined
- return nil
- else
- #Puppet.debug "Found node %s" % name
- return value
- end
- end
+ self.chkobjectclosure(hash)
- # Look up a defined type.
- def lookuptype(name)
- #Puppet.debug "Looking up type %s" % name
- value = self.lookup("type",name)
- if value == :undefined
- return nil
- else
- #Puppet.debug "Found type %s" % name
- return value
- end
- end
+ obj = nil
- # Look up an object by name and type. This should only look up objects
- # within a class structure, not within the entire scope structure.
- def lookupobject(hash)
- type = hash[:type]
- name = hash[:name]
- #Puppet.debug "Looking up object %s of type %s in level %s" %
- # [name, type, @level]
- sub = proc { |table|
- if table.include?(type)
- if table[type].include?(name)
- table[type][name]
- end
- else
- nil
- end
- }
- value = self.lookup("object",sub, true)
- if value == :undefined
- return nil
- else
- return value
- end
- end
+ # If it's a builtin type, then use a transobject, else use
+ # a ScopeObj, which will get replaced later.
+ if self.builtintype?(hash[:type])
+ obj = Puppet::TransObject.new(hash[:name], hash[:type])
- # Look up a variable. The simplest value search we do.
- def lookupvar(name)
- #Puppet.debug "Looking up variable %s" % name
- value = self.lookup("variable", name)
- if value == :undefined
- return ""
- #error = Puppet::ParseError.new(
- # "Undefined variable '%s'" % name
- #)
- #raise error
- else
- return value
- end
+ @children << obj
+ else
+ obj = ScopeObj.new(nil)
+ obj.name = hash[:name]
+ obj.type = hash[:type]
end
- # Add a new object to our object table.
- def newobject(hash)
- if @objectable[hash[:type]].include?(hash[:name])
- raise Puppet::DevError, "Object %s[%s] is already defined" %
- [hash[:type], hash[:name]]
- end
-
- self.chkobjectclosure(hash)
+ @objectable[hash[:type]][hash[:name]] = obj
- obj = nil
+ @definedtable[hash[:type]][hash[:name]] = obj
- # If it's a builtin type, then use a transobject, else use
- # a ScopeObj, which will get replaced later.
- if self.builtintype?(hash[:type])
- obj = TransObject.new(hash[:name], hash[:type])
+ # Keep them in order, just for kicks
+ @objectlist << obj
- @children << obj
- else
- obj = ScopeObj.new(nil)
- obj.name = hash[:name]
- obj.type = hash[:type]
- end
-
- @objectable[hash[:type]][hash[:name]] = obj
+ return obj
+ end
- @definedtable[hash[:type]][hash[:name]] = obj
+ # Create a new scope.
+ def newscope(hash = {})
+ hash[:parent] = self
+ #debug "Creating new scope, level %s" % [self.level + 1]
+ return Puppet::Parser::Scope.new(hash)
+ end
- # Keep them in order, just for kicks
- @objectlist << obj
+ # Retrieve a specific node. This is used in ast.rb to find a
+ # parent node and in findnode to retrieve and evaluate a node.
+ def node(name)
+ @nodetable[name]
+ end
- return obj
+ # Store the fact that we've evaluated a given class. We use a hash
+ # that gets inherited from the top scope down, rather than a global
+ # hash. We store the object ID, not class name, so that we
+ # can support multiple unrelated classes with the same name.
+ def setclass(id, name)
+ if self.topscope?
+ @classtable[id] = name
+ else
+ @parent.setclass(id, name)
end
+ end
- # Create a new scope.
- def newscope(hash = {})
- hash[:parent] = self
- #Puppet.debug "Creating new scope, level %s" % [self.level + 1]
- return Puppet::Parser::Scope.new(hash)
+ # Set defaults for a type. The typename should already be downcased,
+ # so that the syntax is isolated.
+ def setdefaults(type,params)
+ table = @defaultstable[type]
+
+ # if we got a single param, it'll be in its own array
+ unless params[0].is_a?(Array)
+ params = [params]
end
- # Store the fact that we've evaluated a given class. We use a hash
- # that gets inherited from the nodescope down, rather than a global
- # hash. We store the object ID, not class name, so that we
- # can support multiple unrelated classes with the same name.
- def setclass(id, name)
- if self.nodescope? or self.topscope?
- @classtable[id] = name
+ params.each { |ary|
+ #Puppet.debug "Default for %s is %s => %s" %
+ # [type,ary[0].inspect,ary[1].inspect]
+ if @@declarative
+ if table.include?(ary[0])
+ error = Puppet::ParseError.new(
+ "Default already defined for %s { %s }" %
+ [type,ary[0]]
+ )
+ raise error
+ end
else
- @parent.setclass(id, name)
+ if table.include?(ary[0])
+ # we should maybe allow this warning to be turned off...
+ Puppet.warning "Replacing default for %s { %s }" %
+ [type,ary[0]]
+ end
end
- end
-
- # Set defaults for a type. The typename should already be downcased,
- # so that the syntax is isolated.
- def setdefaults(type,params)
- table = @defaultstable[type]
+ table[ary[0]] = ary[1]
+ }
+ end
- # if we got a single param, it'll be in its own array
- unless params[0].is_a?(Array)
- params = [params]
- end
+ # Store a host in the site node table.
+ def setnode(name,code)
+ unless defined? @nodetable
+ raise Puppet::DevError, "No node table defined"
+ end
+ if @nodetable.include?(name)
+ raise Puppet::ParseError, "Host %s is already defined" % name
+ else
+ #Puppet.warning "Setting node %s at level %s" % [name, @level]
- params.each { |ary|
- #Puppet.debug "Default for %s is %s => %s" %
- # [type,ary[0].inspect,ary[1].inspect]
- if @@declarative
- if table.include?(ary[0])
- error = Puppet::ParseError.new(
- "Default already defined for %s { %s }" %
- [type,ary[0]]
- )
- raise error
- end
- else
- if table.include?(ary[0])
- # we should maybe allow this warning to be turned off...
- Puppet.warning "Replacing default for %s { %s }" %
- [type,ary[0]]
- end
- end
- table[ary[0]] = ary[1]
+ # We have to store both the scope that's setting the node and
+ # the node itself, so that the node gets evaluated in the correct
+ # scope.
+ @nodetable[name] = {
+ :scope => self,
+ :node => code
}
end
+ end
- # Define our type.
- def settype(name,ltype)
- # Don't let them redefine the class in this scope.
- if @typetable.include?(name)
- raise Puppet::ParseError,
- "%s is already defined" % name
- else
- @typetable[name] = ltype
- end
+ # Define our type.
+ def settype(name,ltype)
+ # Don't let them redefine the class in this scope.
+ if @typetable.include?(name)
+ raise Puppet::ParseError,
+ "%s is already defined" % name
+ else
+ @typetable[name] = ltype
end
+ end
- # Return an interpolated string.
- def strinterp(string)
- newstring = string.dup
- regex = Regexp.new('\$\{(\w+)\}|\$(\w+)')
- #Puppet.debug("interpreting '%s'" % string)
- while match = regex.match(newstring) do
- if match[1]
- newstring.sub!(regex,self.lookupvar(match[1]).to_s)
- elsif match[2]
- newstring.sub!(regex,self.lookupvar(match[2]).to_s)
- else
- raise Puppet::DevError, "Could not match variable in %s" %
- newstring
+ # This method will fail if the named object is already defined anywhere
+ # in the scope tree, which is what provides some minimal closure-like
+ # behaviour.
+ def setobject(hash)
+ # FIXME This objectlookup stuff should be looking up using both
+ # the name and the namevar.
+
+ # First see if we can look the object up using normal scope
+ # rules, i.e., one of our parent classes has defined the
+ # object or something
+
+ name = hash[:name]
+ type = hash[:type]
+ params = hash[:arguments]
+ file = hash[:file]
+ line = hash[:line]
+
+ # Verify that we're not overriding any already-set parameters.
+ if localobj = @localobjectable[type][name]
+ params.each { |var, value|
+ if localobj.include?(var)
+ msg = "Cannot reassign attribute %s on %s[%s]" %
+ [var, type, name]
+
+ error = Puppet::ParseError.new(msg)
+ error.line = line
+ error.file = file
+ raise error
end
- end
- #Puppet.debug("result is '%s'" % newstring)
- return newstring.gsub(/\\t/, "\t").gsub(/\\n/, "\n").gsub(/\\s/, "\s")
+ }
end
- # This method will fail if the named object is already defined anywhere
- # in the scope tree, which is what provides some minimal closure-like
- # behaviour.
- def setobject(hash)
- # FIXME This objectlookup stuff should be looking up using both
- # the name and the namevar.
-
- # First see if we can look the object up using normal scope
- # rules, i.e., one of our parent classes has defined the
- # object or something
-
- name = hash[:name]
- type = hash[:type]
- params = hash[:arguments]
- file = hash[:file]
- line = hash[:line]
-
- # Verify that we're not overriding any already-set parameters.
- if localobj = @localobjectable[type][name]
- params.each { |var, value|
- if localobj.include?(var)
- msg = "Cannot reassign attribute %s on %s[%s]" %
- [var, type, name]
-
- error = Puppet::ParseError.new(msg)
- error.line = line
- error.file = file
- raise error
- end
- }
- end
-
- if objecttype = self.lookuptype(type)
- # It's a defined type
- objecttype.safeevaluate(
- :name => name,
- :type => type,
- :arguments => params,
- :scope => self
- )
- else
- # First look for it in a parent scope
- obj = self.lookupobject(:name => name, :type => type)
-
- unless obj and obj != :undefined
- unless obj = @objectable[type][name]
- obj = self.newobject(
- :type => type,
- :name => name,
- :line => line,
- :file => file
- )
-
- # only set these if we've created the object,
- # which is the most common case
- # FIXME we eventually need to store the file
- # and line with each param, not the object
- # itself.
- obj.file = file
- obj.line = line
- end
+ if objecttype = lookuptype(type)
+ # It's a defined type
+ objecttype.safeevaluate(
+ :name => name,
+ :type => type,
+ :arguments => params,
+ :scope => self
+ )
+ else
+ # First look for it in a parent scope
+ obj = lookupobject(:name => name, :type => type)
+
+ unless obj and obj != :undefined
+ unless obj = @objectable[type][name]
+ obj = self.newobject(
+ :type => type,
+ :name => name,
+ :line => line,
+ :file => file
+ )
- # Now add our parameters. This has the function of
- # overriding existing values, which might have been
- # defined in a higher scope.
+ # only set these if we've created the object,
+ # which is the most common case
+ # FIXME we eventually need to store the file
+ # and line with each param, not the object
+ # itself.
+ obj.file = file
+ obj.line = line
end
- params.each { |var,value|
- # Add it to our found object
- obj[var] = value
- }
end
- @localobjectable[type][name] ||= {}
-
+ # Now add our parameters. This has the function of overriding
+ # existing values, which might have been defined in a higher
+ # scope.
params.each { |var,value|
- # And add it to the local table; mmm, hack
- @localobjectable[type][name][var] = value
+ # Add it to our found object
+ obj[var] = value
}
+ end
- return obj
+ @localobjectable[type][name] ||= {}
+
+ params.each { |var,value|
+ # And add it to the local table; mmm, hack
+ @localobjectable[type][name][var] = value
+ }
+
+ return obj
+ end
+
+ # 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 if we're declarative (which is the default).
+ def setvar(name,value)
+ #Puppet.debug "Setting %s to '%s' at level %s" %
+ # [name.inspect,value,self.level]
+ if @@declarative and @symtable.include?(name)
+ raise Puppet::ParseError, "Cannot reassign variable %s" % name
+ else
+ if @symtable.include?(name)
+ Puppet.warning "Reassigning %s to %s" % [name,value]
+ end
+ @symtable[name] = value
end
+ end
- # 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 if we're declarative (which is the default).
- def setvar(name,value)
- #Puppet.debug "Setting %s to '%s' at level %s" %
- # [name.inspect,value,self.level]
- if @@declarative and @symtable.include?(name)
- raise Puppet::ParseError, "Cannot reassign variable %s" % name
+ # Return an interpolated string.
+ def strinterp(string)
+ newstring = string.dup
+ regex = Regexp.new('\$\{(\w+)\}|\$(\w+)')
+ #Puppet.debug("interpreting '%s'" % string)
+ while match = regex.match(newstring) do
+ if match[1]
+ newstring.sub!(regex,lookupvar(match[1]).to_s)
+ elsif match[2]
+ newstring.sub!(regex,lookupvar(match[2]).to_s)
else
- if @symtable.include?(name)
- Puppet.warning "Reassigning %s to %s" % [name,value]
- end
- @symtable[name] = value
+ raise Puppet::DevError, "Could not match variable in %s" %
+ newstring
end
end
+ #Puppet.debug("result is '%s'" % newstring)
+ return newstring.gsub(/\\t/, "\t").gsub(/\\n/, "\n").gsub(/\\s/, "\s")
+ end
+
+ # Add a tag to our current list. These tags will be added to all
+ # of the objects contained in this scope.
+ def tag(*ary)
+ ary.each { |tag|
+ if tag.nil? or tag == ""
+ Puppet.debug "got told to tag with %s" % tag.inspect
+ next
+ end
+ unless @tags.include?(tag)
+ #Puppet.info "Tagging scope %s with %s" % [self.object_id, tag]
+ @tags << tag.to_s
+ end
+ }
+ end
- # Add a tag to our current list. These tags will be added to all
- # of the objects contained in this scope.
- def tag(*ary)
- ary.each { |tag|
+ # Return the tags associated with this scope. It's basically
+ # just our parents' tags, plus our type.
+ def tags
+ tmp = [] + @tags
+ unless ! defined? @type or @type.nil? or @type == ""
+ tmp << @type.to_s
+ end
+ if @parent
+ @parent.tags.each { |tag|
if tag.nil? or tag == ""
- Puppet.debug "got told to tag with %s" % tag.inspect
+ Puppet.debug "parent returned tag %s" % tag.inspect
next
end
- unless @tags.include?(tag)
- #Puppet.info "Tagging scope %s with %s" % [self.object_id, tag]
- @tags << tag.to_s
+ unless tmp.include?(tag)
+ tmp << tag
end
}
end
+ return tmp
+ end
- # Return the tags associated with this scope. It's basically
- # just our parents' tags, plus our type.
- def tags
- tmp = [] + @tags
- unless ! defined? @type or @type.nil? or @type == ""
- tmp << @type.to_s
- end
- if @parent
- @parent.tags.each { |tag|
- if tag.nil? or tag == ""
- Puppet.debug "parent returned tag %s" % tag.inspect
- next
- end
- unless tmp.include?(tag)
- tmp << tag
- end
- }
- end
- return tmp
+ # Used mainly for logging
+ def to_s
+ if @name
+ return "%s[%s]" % [@type, @name]
+ else
+ return @type.to_s
end
+ end
- # Used mainly for logging
- def to_s
- if @name
- return "%s[%s]" % [@type, @name]
+ # Convert our scope to a list of Transportable objects.
+ def to_trans
+
+ results = []
+
+ # Iterate across our child scopes and call to_trans on them
+ @children.each { |child|
+ if child.is_a?(Scope)
+ cresult = child.to_trans
+
+ # Scopes normally result in a TransBucket, but they could
+ # also result in a normal array; if that happens, get rid
+ # of the array.
+ unless cresult.is_a?(Puppet::TransBucket)
+ cresult.each { |result|
+ results.push(result)
+ }
+ else
+ unless cresult.empty?
+ # Otherwise, just add it to our list of results.
+ results.push(cresult)
+ end
+ end
+ elsif child.is_a?(Puppet::TransObject)
+ if child.empty?
+ next
+ end
+ # Wait until the last minute to set tags, although this
+ # probably should not matter
+ child.tags = self.tags
+
+ # Add any defaults.
+ self.adddefaults(child)
+
+ # Then make sure this child's tags are stored in the
+ # central table. This should maybe be in the evaluate
+ # methods, but, eh.
+ @topscope.addtags(child)
+ results.push(child)
else
- return @type.to_s
+ raise Puppet::DevError,
+ "Puppet::Parse::Scope cannot handle objects of type %s" %
+ child.class
end
- end
+ }
- # Convert our scope to a list of Transportable objects.
- def to_trans
- #unless self.finished?
- # raise Puppet::DevError, "%s not finished" % self.type
- # self.err "Not finished"
- # self.finish
- #end
- #Puppet.debug "Translating scope %s at level %s" %
- # [self.object_id,self.level]
-
- results = []
-
- # Iterate across our child scopes and call to_trans on them
- @children.each { |child|
- if child.is_a?(Scope)
- cresult = child.to_trans
- #Puppet.debug "Got %s from scope %s" %
- # [cresult.class,child.object_id]
-
- # Scopes normally result in a TransBucket, but they could
- # also result in a normal array; if that happens, get rid
- # of the array.
- unless cresult.is_a?(TransBucket)
- cresult.each { |result|
- results.push(result)
- }
- else
- unless cresult.empty?
- # Otherwise, just add it to our list of results.
- results.push(cresult)
- end
- end
+ # Get rid of any nil objects.
+ results = results.reject { |child|
+ child.nil?
+ }
- # Nodescopes are one-time; once they've been evaluated
- # I need to destroy them. Nodeclean makes sure this is
- # done correctly, but this should catch most of them.
- if child.nodescope?
- @children.delete(child)
- end
- elsif child.is_a?(TransObject)
- if child.empty?
- next
- end
- # Wait until the last minute to set tags, although this
- # probably should not matter
- child.tags = self.tags
-
- # Add any defaults.
- self.adddefaults(child)
-
- # Then make sure this child's tags are stored in the
- # central table. This should maybe be in the evaluate
- # methods, but, eh.
- @topscope.addtags(child)
- results.push(child)
- else
- raise Puppet::DevError,
- "Puppet::Parse::Scope cannot handle objects of type %s" %
- child.class
- end
- }
+ # If we have a name and type, then make a TransBucket, which
+ # becomes a component.
+ # Else, just stack all of the objects into the current bucket.
+ if @type
+ bucket = Puppet::TransBucket.new
- # Get rid of any nil objects.
- results = results.reject { |child|
- child.nil?
- }
+ if defined? @name and @name
+ bucket.name = @name
+ end
- # If we have a name and type, then make a TransBucket, which
- # becomes a component.
- # Else, just stack all of the objects into the current bucket.
- if @type
- bucket = TransBucket.new
+ # it'd be nice not to have to do this...
+ results.each { |result|
+ #Puppet.warning "Result type is %s" % result.class
+ bucket.push(result)
+ }
+ if defined? @type
+ bucket.type = @type
+ else
+ raise Puppet::ParseError,
+ "No type for scope %s" % @name
+ end
- if defined? @name and @name
- bucket.name = @name
- end
+ if defined? @keyword
+ bucket.keyword = @keyword
+ end
+ #Puppet.debug(
+ # "TransBucket with name %s and type %s in scope %s" %
+ # [@name,@type,self.object_id]
+ #)
- # it'd be nice not to have to do this...
- results.each { |result|
- #Puppet.warning "Result type is %s" % result.class
- bucket.push(result)
- }
- if defined? @type
- bucket.type = @type
+ # now find metaparams
+ @symtable.each { |var,value|
+ if Puppet::Type.metaparam?(var.intern)
+ #Puppet.debug("Adding metaparam %s" % var)
+ bucket.param(var,value)
else
- raise Puppet::ParseError,
- "No type for scope %s" % @name
+ #Puppet.debug("%s is not a metaparam" % var)
end
+ }
+ #Puppet.debug "Returning bucket %s from scope %s" %
+ # [bucket.name,self.object_id]
+ return bucket
+ else
+ Puppet.debug "nameless scope; just returning a list"
+ return results
+ end
+ end
- if defined? @keyword
- bucket.keyword = @keyword
- end
- #Puppet.debug(
- # "TransBucket with name %s and type %s in scope %s" %
- # [@name,@type,self.object_id]
- #)
-
- # now find metaparams
- @symtable.each { |var,value|
- if Puppet::Type.metaparam?(var.intern)
- #Puppet.debug("Adding metaparam %s" % var)
- bucket.param(var,value)
- else
- #Puppet.debug("%s is not a metaparam" % var)
- end
- }
- #Puppet.debug "Returning bucket %s from scope %s" %
- # [bucket.name,self.object_id]
- return bucket
+ protected
+
+ # This method abstracts recursive searching. It accepts the type
+ # of search being done and then either a literal key to search for or
+ # a Proc instance to do the searching.
+ def lookup(type,sub, usecontext = false)
+ table = @map[type]
+ if table.nil?
+ error = Puppet::ParseError.new(
+ "Could not retrieve %s table at level %s" %
+ [type,self.level]
+ )
+ raise error
+ end
+
+ if sub.is_a?(Proc) and obj = sub.call(table)
+ return obj
+ elsif table.include?(sub)
+ return table[sub]
+ elsif ! @parent.nil?
+ #self.notice "Context is %s, parent %s is %s" %
+ # [self.context, @parent.type, @parent.context]
+ if usecontext and self.context != @parent.context
+ return :undefined
else
- #Puppet.debug "nameless scope; just returning a list"
- return results
+ return @parent.lookup(type,sub, usecontext)
end
+ else
+ return :undefined
end
end
end