[Pkg-puppet-devel] [SCM] Puppet packaging for Debian branch, upstream, updated. 0.25.5-639-g8f94f35

James Turnbull james at lovedthanlost.net
Wed Jul 14 10:29:38 UTC 2010


The following commit has been merged in the upstream branch:
commit 8971d8beae2c409f9052f27c3f80ad3bdfff4de2
Author: Luke Kanies <luke at madstop.com>
Date:   Tue Dec 1 16:41:38 2009 -0800

    Fixing #2596 - Node, Class, Definition are not AST
    
    This commit extracts these three classes into a single
    ResourceType class in the Parser heirarchy, now completely
    independent of the AST heirarchy.
    
    Most of the other changes are just changing the interface
    to the new class, which is greatly simplified over the previous
    classes.
    
    This opens up the possibility of drastically simplifying a lot
    of this other code, too -- in particular, replacing the reference
    to the parser with a reference to the (soon to be renamed)
    LoadedCode class.
    
    Signed-off-by: Luke Kanies <luke at madstop.com>

diff --git a/lib/puppet/parser/ast.rb b/lib/puppet/parser/ast.rb
index ad8af74..feceb60 100644
--- a/lib/puppet/parser/ast.rb
+++ b/lib/puppet/parser/ast.rb
@@ -99,15 +99,12 @@ require 'puppet/parser/ast/casestatement'
 require 'puppet/parser/ast/collection'
 require 'puppet/parser/ast/collexpr'
 require 'puppet/parser/ast/comparison_operator'
-require 'puppet/parser/ast/definition'
 require 'puppet/parser/ast/else'
 require 'puppet/parser/ast/function'
-require 'puppet/parser/ast/hostclass'
 require 'puppet/parser/ast/ifstatement'
 require 'puppet/parser/ast/leaf'
 require 'puppet/parser/ast/match_operator'
 require 'puppet/parser/ast/minus'
-require 'puppet/parser/ast/node'
 require 'puppet/parser/ast/nop'
 require 'puppet/parser/ast/not'
 require 'puppet/parser/ast/resource'
diff --git a/lib/puppet/parser/ast/definition.rb b/lib/puppet/parser/ast/definition.rb
deleted file mode 100644
index 00b0416..0000000
--- a/lib/puppet/parser/ast/definition.rb
+++ /dev/null
@@ -1,207 +0,0 @@
-require 'puppet/parser/ast/branch'
-
-require 'puppet/util/warnings'
-
-# The AST class for defined types, which is also the base class
-# nodes and classes.
-class Puppet::Parser::AST::Definition < Puppet::Parser::AST::Branch
-    include Puppet::Util::Warnings
-    class << self
-        attr_accessor :name
-    end
-
-    associates_doc
-
-    # The class name
-    @name = :definition
-
-    attr_accessor :classname, :arguments, :code, :scope, :keyword
-    attr_accessor :exported, :namespace, :parser, :virtual, :name
-
-    attr_reader :parentclass
-
-    def child_of?(klass)
-        false
-    end
-
-    def get_classname(scope)
-        self.classname
-    end
-
-    # Create a resource that knows how to evaluate our actual code.
-    def evaluate(scope)
-        resource = Puppet::Parser::Resource.new(:type => self.class.name, :title => get_classname(scope), :scope => scope, :source => scope.source)
-
-        scope.catalog.tag(*resource.tags)
-
-        scope.compiler.add_resource(scope, resource)
-
-        return resource
-    end
-
-    # Now evaluate the code associated with this class or definition.
-    def evaluate_code(resource)
-        # Create a new scope.
-        scope = subscope(resource.scope, resource)
-
-        set_resource_parameters(scope, resource)
-
-        if self.code
-            return self.code.safeevaluate(scope)
-        else
-            return nil
-        end
-    end
-
-    def initialize(hash = {})
-        @arguments = nil
-        @parentclass = nil
-        super
-
-        # Convert the arguments to a hash for ease of later use.
-        if @arguments
-            unless @arguments.is_a? Array
-                @arguments = [@arguments]
-            end
-            oldargs = @arguments
-            @arguments = {}
-            oldargs.each do |arg, val|
-                @arguments[arg] = val
-            end
-        else
-            @arguments = {}
-        end
-
-        # Deal with metaparams in the argument list.
-        @arguments.each do |arg, defvalue|
-            next unless Puppet::Type.metaparamclass(arg)
-            if defvalue
-                warnonce "%s is a metaparam; this value will inherit to all contained resources" % arg
-            else
-                raise Puppet::ParseError, "%s is a metaparameter; please choose another parameter name in the %s definition" % [arg, self.classname]
-            end
-        end
-    end
-
-    def find_parentclass
-        @parser.find_hostclass(namespace, parentclass)
-    end
-
-    # Set our parent class, with a little check to avoid some potential
-    # weirdness.
-    def parentclass=(name)
-        if name == self.classname
-            parsefail "Parent classes must have dissimilar names"
-        end
-
-        @parentclass = name
-    end
-
-    # Hunt down our class object.
-    def parentobj
-        return nil unless @parentclass
-
-        # Cache our result, since it should never change.
-        unless defined?(@parentobj)
-            unless tmp = find_parentclass
-                parsefail "Could not find %s parent %s" % [self.class.name, @parentclass]
-            end
-
-            if tmp == self
-                parsefail "Parent classes must have dissimilar names"
-            end
-
-            @parentobj = tmp
-        end
-        @parentobj
-    end
-
-    # Create a new subscope in which to evaluate our code.
-    def subscope(scope, resource)
-        args = {
-            :resource => resource,
-            :keyword => self.keyword,
-            :namespace => self.namespace,
-            :source => self
-        }
-
-        oldscope = scope
-        scope = scope.newscope(args)
-        scope.source = self
-
-        return scope
-    end
-
-    def to_s
-        classname
-    end
-
-    # Check whether a given argument is valid.  Searches up through
-    # any parent classes that might exist.
-    def validattr?(param)
-        param = param.to_s
-
-        if @arguments.include?(param)
-            # It's a valid arg for us
-            return true
-        elsif param == "name"
-            return true
-#            elsif defined? @parentclass and @parentclass
-#                # Else, check any existing parent
-#                if parent = @scope.lookuptype(@parentclass) and parent != []
-#                    return parent.validarg?(param)
-#                elsif builtin = Puppet::Type.type(@parentclass)
-#                    return builtin.validattr?(param)
-#                else
-#                    raise Puppet::Error, "Could not find parent class %s" %
-#                        @parentclass
-#                end
-        elsif Puppet::Type.metaparam?(param)
-            return true
-        else
-            # Or just return false
-            return false
-        end
-    end
-
-    private
-
-    # Set any arguments passed by the resource as variables in the scope.
-    def set_resource_parameters(scope, resource)
-        args = symbolize_options(resource.to_hash || {})
-
-        # Verify that all required arguments are either present or
-        # have been provided with defaults.
-        if self.arguments
-            self.arguments.each { |arg, default|
-                arg = arg.to_sym
-                unless args.include?(arg)
-                    if defined? default and ! default.nil?
-                        default = default.safeevaluate scope
-                        args[arg] = default
-                        #Puppet.debug "Got default %s for %s in %s" %
-                        #    [default.inspect, arg.inspect, @name.inspect]
-                    else
-                        parsefail "Must pass %s to %s of type %s" %
-                                [arg, resource.title, @classname]
-                    end
-                end
-            }
-        end
-
-        # Set each of the provided arguments as variables in the
-        # definition's scope.
-        args.each { |arg,value|
-            unless validattr?(arg)
-                parsefail "%s does not accept attribute %s" % [@classname, arg]
-            end
-
-            exceptwrap do
-                scope.setvar(arg.to_s, args[arg])
-            end
-        }
-
-        scope.setvar("title", resource.title) unless args.include? :title
-        scope.setvar("name", resource.name) unless args.include? :name
-    end
-end
diff --git a/lib/puppet/parser/ast/hostclass.rb b/lib/puppet/parser/ast/hostclass.rb
deleted file mode 100644
index 23d9a00..0000000
--- a/lib/puppet/parser/ast/hostclass.rb
+++ /dev/null
@@ -1,95 +0,0 @@
-require 'puppet/parser/ast/definition'
-
-# The code associated with a class.  This is different from definitions
-# in that each class is a singleton -- only one will exist for a given
-# node.
-class Puppet::Parser::AST::HostClass < Puppet::Parser::AST::Definition
-
-    associates_doc
-
-    @name = :class
-
-    # Are we a child of the passed class?  Do a recursive search up our
-    # parentage tree to figure it out.
-    def child_of?(klass)
-        return false unless self.parentclass
-
-        if klass == self.parentobj
-            return true
-        else
-            return self.parentobj.child_of?(klass)
-        end
-    end
-
-    # Make sure our parent class has been evaluated, if we have one.
-    def evaluate(scope)
-        if parentclass and ! scope.catalog.resource(self.class.name, parentclass)
-            parent_resource = parentobj.evaluate(scope)
-        end
-
-        # Do nothing if the resource already exists; this makes sure we don't
-        # get multiple copies of the class resource, which helps provide the
-        # singleton nature of classes.
-        if resource = scope.catalog.resource(self.class.name, self.classname)
-            return resource
-        end
-
-        super
-    end
-
-    # Evaluate the code associated with this class.
-    def evaluate_code(resource)
-        scope = resource.scope
-        # Verify that we haven't already been evaluated.  This is
-        # what provides the singleton aspect.
-        if existing_scope = scope.compiler.class_scope(self)
-            Puppet.debug "Class '%s' already evaluated; not evaluating again" % (classname == "" ? "main" : classname)
-            return nil
-        end
-
-        pnames = nil
-        if pklass = self.parentobj
-            parent_resource = resource.scope.compiler.catalog.resource(self.class.name, pklass.classname)
-            # This shouldn't evaluate if the class has already been evaluated.
-            pklass.evaluate_code(parent_resource)
-
-            scope = parent_scope(scope, pklass)
-            pnames = scope.namespaces
-        end
-
-        # Don't create a subscope for the top-level class, since it already
-        # has its own scope.
-        unless resource.title == :main
-            scope = subscope(scope, resource)
-
-            scope.setvar("title", resource.title)
-            scope.setvar("name", resource.name)
-        end
-
-        # Add the parent scope namespaces to our own.
-        if pnames
-            pnames.each do |ns|
-                scope.add_namespace(ns)
-            end
-        end
-
-        # Set the class before we evaluate the code, so that it's set during
-        # the evaluation and can be inspected.
-        scope.compiler.class_set(self.classname, scope)
-
-        # Now evaluate our code, yo.
-        if self.code
-            return self.code.safeevaluate(scope)
-        else
-            return nil
-        end
-    end
-
-    def parent_scope(scope, klass)
-        if s = scope.compiler.class_scope(klass)
-            return s
-        else
-            raise Puppet::DevError, "Could not find scope for %s" % klass.classname
-        end
-    end
-end
diff --git a/lib/puppet/parser/ast/leaf.rb b/lib/puppet/parser/ast/leaf.rb
index b73c781..07bba1b 100644
--- a/lib/puppet/parser/ast/leaf.rb
+++ b/lib/puppet/parser/ast/leaf.rb
@@ -94,6 +94,7 @@ class Puppet::Parser::AST
         def initialize(hash)
             super
 
+            # Note that this is an AST::Regex, not a Regexp
             @value = @value.to_s.downcase unless @value.is_a?(Regex)
             if @value =~ /[^-\w.]/
                 raise Puppet::DevError,
@@ -101,10 +102,6 @@ class Puppet::Parser::AST
             end
         end
 
-        def to_classname
-            to_s.downcase.gsub(/[^-\w:.]/,'').sub(/^\.+/,'')
-        end
-
         # implementing eql? and hash so that when an HostName is stored
         # in a hash it has the same hashing properties as the underlying value
         def eql?(value)
@@ -116,25 +113,6 @@ class Puppet::Parser::AST
             return @value.hash
         end
 
-        def match(value)
-            return @value.match(value) unless value.is_a?(HostName)
-
-            if value.regex? and self.regex?
-                # Wow this is some sweet design; maybe a touch of refactoring
-                # in order here.
-                return value.value.value == self.value.value
-            elsif value.regex? # we know if the existing name is not a regex, it won't match a regex
-                return false
-            else
-                # else, we could be either a regex or normal and it doesn't matter
-                return @value.match(value.value)
-            end
-        end
-
-        def regex?
-            @value.is_a?(Regex)
-        end
-
         def to_s
             @value.to_s
         end
diff --git a/lib/puppet/parser/ast/node.rb b/lib/puppet/parser/ast/node.rb
deleted file mode 100644
index 4f75201..0000000
--- a/lib/puppet/parser/ast/node.rb
+++ /dev/null
@@ -1,42 +0,0 @@
-require 'puppet/parser/ast/hostclass'
-
-# The specific code associated with a host.  Nodes are annoyingly unlike
-# other objects.  That's just the way it is, at least for now.
-class Puppet::Parser::AST::Node < Puppet::Parser::AST::HostClass
-
-    associates_doc
-
-    @name = :node
-
-    def initialize(options)
-        @parentclass = nil
-        super
-    end
-
-    def namespace
-        ""
-    end
-
-    # in Regex mode, our classname can't be our Regex.
-    # so we use the currently connected client as our
-    # classname, mimicing exactly what would have happened
-    # if there was a specific node definition for this node.
-    def get_classname(scope)
-        return scope.host if name.regex?
-        classname
-    end
-
-    # Make sure node scopes are marked as such.
-    def subscope(*args)
-        scope = super
-        scope.nodescope = true
-        scope
-    end
-
-    private
-
-    # Search for the object matching our parent class.
-    def find_parentclass
-        @parser.node(parentclass)
-    end
-end
diff --git a/lib/puppet/parser/ast/resource_reference.rb b/lib/puppet/parser/ast/resource_reference.rb
index 117bc88..794e505 100644
--- a/lib/puppet/parser/ast/resource_reference.rb
+++ b/lib/puppet/parser/ast/resource_reference.rb
@@ -44,7 +44,7 @@ class Puppet::Parser::AST
         def qualified_class(scope, title)
             # Look up the full path to the class
             if classobj = scope.find_hostclass(title)
-                title = classobj.classname
+                title = classobj.name
             else
                 raise Puppet::ParseError, "Could not find class %s" % title
             end
@@ -57,7 +57,7 @@ class Puppet::Parser::AST
             objtype = @type.downcase
             unless builtintype?(objtype)
                 if dtype = scope.find_definition(objtype)
-                    objtype = dtype.classname
+                    objtype = dtype.name
                 else
                     raise Puppet::ParseError, "Could not find resource type %s" % objtype
                 end
diff --git a/lib/puppet/parser/compiler.rb b/lib/puppet/parser/compiler.rb
index 77b0bb9..6b6cd68 100644
--- a/lib/puppet/parser/compiler.rb
+++ b/lib/puppet/parser/compiler.rb
@@ -71,8 +71,8 @@ class Puppet::Parser::Compiler
     # variables.
     def class_scope(klass)
         # They might pass in either the class or class name
-        if klass.respond_to?(:classname)
-            @class_scopes[klass.classname]
+        if klass.respond_to?(:name)
+            @class_scopes[klass.name]
         else
             @class_scopes[klass]
         end
@@ -140,7 +140,7 @@ class Puppet::Parser::Compiler
             if klass = scope.find_hostclass(name)
                 found << name and next if class_scope(klass)
 
-                resource = klass.evaluate(scope)
+                resource = klass.mk_plain_resource(scope)
 
                 # If they've disabled lazy evaluation (which the :include function does),
                 # then evaluate our resource immediately.
@@ -230,7 +230,7 @@ class Puppet::Parser::Compiler
 
         # Create a resource to model this node, and then add it to the list
         # of resources.
-        resource = astnode.evaluate(topscope)
+        resource = astnode.mk_plain_resource(topscope)
 
         resource.evaluate
 
diff --git a/lib/puppet/parser/loaded_code.rb b/lib/puppet/parser/loaded_code.rb
index 3efd115..d7f179a 100644
--- a/lib/puppet/parser/loaded_code.rb
+++ b/lib/puppet/parser/loaded_code.rb
@@ -8,44 +8,62 @@ class Puppet::Parser::LoadedCode
         @node_list = []
     end
 
-    def add_hostclass(name, code)
-        @hostclasses[munge_name(name)] = code
+    def <<(thing)
+        add(thing)
+        self
+    end
+
+    def add(instance)
+        method = "add_#{instance.type}"
+        send(method, instance)
+        instance.code_collection = self
+        instance
+    end
+
+    def add_hostclass(instance)
+        dupe_check(instance, @hostclasses) { |dupe| "Class #{instance.name} is already defined#{dupe.error_context}; cannot redefine" }
+        dupe_check(instance, @definitions) { |dupe| "Definition #{instance.name} is already defined#{dupe.error_context}; cannot be redefined as a class" }
+
+        @hostclasses[instance.name] = instance
+        instance
     end
 
     def hostclass(name)
         @hostclasses[munge_name(name)]
     end
 
-    def add_node(name, code)
-        name = check_name(name)
-        @node_list << name unless @node_list.include?(name)
-        @nodes[name] = code
+    def add_node(instance)
+        dupe_check(instance, @nodes) { |dupe| "Node #{instance.name} is already defined#{dupe.error_context}; cannot redefine" }
+
+        @node_list << instance
+        @nodes[instance.name] = instance
+        instance
     end
 
     def node(name)
-        name = check_name(name)
+        name = munge_name(name)
 
         if node = @nodes[name]
             return node
         end
 
-        @node_list.each do |nodename|
-            n = @nodes[nodename]
-            return n if nodename.regex? and nodename.match(name)
+        @node_list.each do |node|
+            next unless node.name_is_regex?
+            return node if node.match(name)
         end
         nil
     end
 
     def node_exists?(name)
-        @nodes[check_name(name)]
+        @nodes[munge_name(name)]
     end
 
     def nodes?
         @nodes.length > 0
     end
 
-    def add_definition(name, code)
-        @definitions[munge_name(name)] = code
+    def add_definition(code)
+        @definitions[code.name] = code
     end
 
     def definition(name)
@@ -108,12 +126,9 @@ class Puppet::Parser::LoadedCode
         name.to_s.downcase
     end
 
-    # Check that the given (node) name is an HostName instance
-    # We're doing this so that hashing of node in the @nodes hash
-    # is consistent (see AST::HostName#hash and AST::HostName#eql?)
-    # and that the @nodes hash still keep its O(1) get/put properties.
-    def check_name(name)
-        name = Puppet::Parser::AST::HostName.new(:value => name) unless name.is_a?(Puppet::Parser::AST::HostName)
-        name
+    def dupe_check(instance, hash)
+        return unless dupe = hash[instance.name]
+        message = yield dupe
+        instance.fail Puppet::ParseError, message
     end
 end
diff --git a/lib/puppet/parser/parser_support.rb b/lib/puppet/parser/parser_support.rb
index 7a8fa81..1b961cd 100644
--- a/lib/puppet/parser/parser_support.rb
+++ b/lib/puppet/parser/parser_support.rb
@@ -4,6 +4,7 @@ class Puppet::Parser::Parser
     require 'puppet/parser/functions'
     require 'puppet/parser/files'
     require 'puppet/parser/loaded_code'
+    require 'puppet/parser/resource_type'
     require 'monitor'
 
     AST = Puppet::Parser::AST
@@ -42,17 +43,16 @@ class Puppet::Parser::Parser
     # Create an AST object, and automatically add the file and line information if
     # available.
     def ast(klass, hash = {})
-        hash[:line] = @lexer.line unless hash.include?(:line)
-
-        unless hash.include?(:file)
-            if file = @lexer.file
-                hash[:file] = file
-            end
-        end
+        klass.new ast_context(klass.use_docs).merge(hash)
+    end
 
-        k = klass.new(hash)
-        k.doc = lexer.getcomment(hash[:line]) if !k.nil? and k.use_docs and k.doc.empty?
-        return k
+    def ast_context(include_docs = false)
+        result = {
+            :line => lexer.line,
+            :file => lexer.file
+        }
+        result[:doc] = lexer.getcomment(result[:line]) if include_docs
+        result
     end
 
     # The fully qualifed name, with the full namespace.
@@ -276,126 +276,21 @@ class Puppet::Parser::Parser
 
     # Create a new class, or merge with an existing class.
     def newclass(name, options = {})
-        name = name.downcase
-
-        if @loaded_code.definition(name)
-            raise Puppet::ParseError, "Cannot redefine class %s as a definition" % name
-        end
-        code = options[:code]
-        parent = options[:parent]
-        doc = options[:doc]
-
-        # If the class is already defined, then add code to it.
-        if other = @loaded_code.hostclass(name) || @loaded_code.definition(name)
-            # Make sure the parents match
-            if parent and other.parentclass and (parent != other.parentclass)
-                error("Class %s is already defined at %s:%s; cannot redefine" % [name, other.file, other.line])
-            end
-
-            # This might be dangerous...
-            if parent and ! other.parentclass
-                other.parentclass = parent
-            end
-
-            # This might just be an empty, stub class.
-            if code
-                tmp = name
-                if tmp == ""
-                    tmp = "main"
-                end
-
-                Puppet.debug addcontext("Adding code to %s" % tmp)
-                # Else, add our code to it.
-                if other.code and code
-                    # promote if neededcodes to ASTArray so that we can append code
-                    # ASTArray knows how to evaluate its members.
-                    other.code = ast AST::ASTArray, :children => [other.code] unless other.code.is_a?(AST::ASTArray)
-                    code = ast AST::ASTArray, :children => [code] unless code.is_a?(AST::ASTArray)
-                    other.code.children += code.children
-                else
-                    other.code ||= code
-                end
-            end
-
-            if other.doc and doc
-                other.doc += doc
-            else
-                other.doc ||= doc
-            end
-        else
-            # Define it anew.
-            # Note we're doing something somewhat weird here -- we're setting
-            # the class's namespace to its fully qualified name.  This means
-            # anything inside that class starts looking in that namespace first.
-            args = {:namespace => name, :classname => name, :parser => self}
-            args[:code] = code if code
-            args[:parentclass] = parent if parent
-            args[:doc] = doc
-            args[:line] = options[:line]
-
-            @loaded_code.add_hostclass(name, ast(AST::HostClass, args))
-        end
-
-        return @loaded_code.hostclass(name)
+        @loaded_code.add Puppet::Parser::ResourceType.new(:hostclass, name, ast_context(true).merge(options))
     end
 
     # Create a new definition.
     def newdefine(name, options = {})
-        name = name.downcase
-        if @loaded_code.hostclass(name)
-            raise Puppet::ParseError, "Cannot redefine class %s as a definition" %
-                name
-        end
-        # Make sure our definition doesn't already exist
-        if other = @loaded_code.definition(name)
-            error("%s is already defined at %s:%s; cannot redefine" % [name, other.file, other.line])
-        end
-
-        ns, whatever = namesplit(name)
-        args = {
-            :namespace => ns,
-            :arguments => options[:arguments],
-            :code => options[:code],
-            :parser => self,
-            :classname => name,
-            :doc => options[:doc],
-            :line => options[:line]
-        }
-
-        [:code, :arguments].each do |param|
-            args[param] = options[param] if options[param]
-        end
-
-        @loaded_code.add_definition(name, ast(AST::Definition, args))
+        @loaded_code.add Puppet::Parser::ResourceType.new(:definition, name, ast_context(true).merge(options))
     end
 
     # Create a new node.  Nodes are special, because they're stored in a global
     # table, not according to namespaces.
     def newnode(names, options = {})
         names = [names] unless names.instance_of?(Array)
-        doc = lexer.getcomment
+        context = ast_context(true)
         names.collect do |name|
-            name = AST::HostName.new :value => name unless name.is_a?(AST::HostName)
-            if other = @loaded_code.node_exists?(name)
-                error("Node %s is already defined at %s:%s; cannot redefine" % [other.name, other.file, other.line])
-            end
-            name = name.to_s if name.is_a?(Symbol)
-            args = {
-                :name => name,
-                :parser => self,
-                :doc => doc,
-                :line => options[:line]
-            }
-            if options[:code]
-                args[:code] = options[:code]
-            end
-            if options[:parent]
-                args[:parentclass] = options[:parent]
-            end
-            node = ast(AST::Node, args)
-            node.classname = name.to_classname
-            @loaded_code.add_node(name, node)
-            node
+            @loaded_code.add(Puppet::Parser::ResourceType.new(:node, name, context.merge(options)))
         end
     end
 
diff --git a/lib/puppet/parser/resource_type.rb b/lib/puppet/parser/resource_type.rb
new file mode 100644
index 0000000..a679659
--- /dev/null
+++ b/lib/puppet/parser/resource_type.rb
@@ -0,0 +1,235 @@
+require 'puppet/parser/parser'
+require 'puppet/util/warnings'
+require 'puppet/util/errors'
+require 'puppet/util/inline_docs'
+require 'puppet/parser/ast/leaf'
+
+class Puppet::Parser::ResourceType
+    include Puppet::Util::InlineDocs
+    include Puppet::Util::Warnings
+    include Puppet::Util::Errors
+
+    RESOURCE_SUPERTYPES = [:hostclass, :node, :definition]
+
+    attr_accessor :file, :line, :doc, :code, :parent, :code_collection
+    attr_reader :type, :namespace, :arguments, :behaves_like
+
+    # Are we a child of the passed class?  Do a recursive search up our
+    # parentage tree to figure it out.
+    def child_of?(klass)
+        return false unless parent
+
+        return true if klass == parent_type
+        return parent_type.child_of?(klass)
+    end
+
+    # Now evaluate the code associated with this class or definition.
+    def evaluate_code(resource)
+        # Create a new scope.
+        scope = subscope(resource.scope, resource)
+
+        set_resource_parameters(resource, scope)
+
+        return nil unless c = self.code
+        return c.safeevaluate(scope)
+    end
+
+    def initialize(type, name, options = {})
+        @type = type.to_s.downcase.to_sym
+        raise ArgumentError, "Invalid resource supertype '#{type}'" unless RESOURCE_SUPERTYPES.include?(@type)
+
+        name = convert_from_ast(name) if name.is_a?(Puppet::Parser::AST::HostName)
+
+        set_name_and_namespace(name)
+
+        [:code, :doc, :line, :file, :parent].each do |param|
+            next unless value = options[param]
+            send(param.to_s + "=", value)
+        end
+
+        set_arguments(options[:arguments])
+    end
+
+    # This is only used for node names, and really only when the node name
+    # is a regexp.
+    def match(string)
+        return string.to_s.downcase == name unless name_is_regex?
+
+        return @name =~ string
+    end
+
+    # Add code from a new instance to our code.
+    def merge(other)
+        fail ArgumentError, "#{name} is not a class; cannot add code to it" unless type == :hostclass
+        fail ArgumentError, "#{other.name} is not a class; cannot add code from it" unless other.type == :hostclass
+
+        if parent and other.parent and parent != other.parent
+            fail ArgumentError, "Cannot merge classes with different parent classes"
+        end
+
+        # We know they're either equal or only one is set, so keep whichever parent is specified.
+        self.parent ||= other.parent
+
+        if other.doc
+            self.doc ||= ""
+            self.doc += other.doc
+        end
+
+        # This might just be an empty, stub class.
+        return unless other.code
+
+        unless self.code
+            self.code = other.code
+            return
+        end
+
+        array_class = Puppet::Parser::AST::ASTArray
+        unless self.code.is_a?(array_class)
+            self.code = array_class.new(:children => [self.code])
+        end
+
+        if other.code.is_a?(array_class)
+            code.children += other.code.children
+        else
+            code.children << other.code
+        end
+    end
+
+    # Make an instance of our resource type.  This is only possible
+    # for those classes and nodes that don't have any arguments, and is
+    # only useful for things like the 'include' function.
+    def mk_plain_resource(scope)
+        type == :definition and raise ArgumentError, "Cannot create resources for defined resource types"
+        resource_type = type == :hostclass ? :class : :node
+
+        # Make sure our parent class has been evaluated, if we have one.
+        if parent and ! scope.catalog.resource(resource_type, parent)
+            parent_type.mk_plain_resource(scope)
+        end
+
+        # Do nothing if the resource already exists; this makes sure we don't
+        # get multiple copies of the class resource, which helps provide the
+        # singleton nature of classes.
+        if resource = scope.catalog.resource(resource_type, name)
+            return resource
+        end
+
+        resource = Puppet::Parser::Resource.new(:type => resource_type, :title => name, :scope => scope, :source => self)
+        scope.compiler.add_resource(scope, resource)
+        scope.catalog.tag(*resource.tags)
+        resource
+    end
+
+    def name
+        return @name unless @name.is_a?(Regexp)
+        return @name.source.downcase.gsub(/[^-\w:.]/,'').sub(/^\.+/,'')
+    end
+
+    def name_is_regex?
+        @name.is_a?(Regexp)
+    end
+
+    def parent_type
+        return nil unless parent
+
+        unless @parent_type ||= code_collection.send(type, parent)
+            fail Puppet::ParseError, "Could not find parent resource type '#{parent}'"
+        end
+
+        @parent_type
+    end
+
+    # Set any arguments passed by the resource as variables in the scope.
+    def set_resource_parameters(resource, scope)
+        set = {}
+        resource.to_hash.each do |param, value|
+            param = param.to_sym
+            fail Puppet::ParseError, "#{resource.ref} does not accept attribute #{param}" unless validattr?(param)
+
+            exceptwrap { scope.setvar(param.to_s, value) }
+
+            set[param] = true
+        end
+
+        # Verify that all required arguments are either present or
+        # have been provided with defaults.
+        arguments.each do |param, default|
+            param = param.to_sym
+            next if set.include?(param)
+            
+            # Even if 'default' is a false value, it's an AST value, so this works fine
+            fail Puppet::ParseError, "Must pass #{param} to #{resource.ref}" unless default
+
+            scope.setvar(param.to_s, default.safeevaluate(scope))
+        end
+
+        scope.setvar("title", resource.title) unless set.include? :title
+        scope.setvar("name", resource.name) unless set.include? :name
+    end
+
+    # Create a new subscope in which to evaluate our code.
+    def subscope(scope, resource)
+        scope.newscope :resource => resource, :namespace => self.namespace, :source => self
+    end
+
+    # Check whether a given argument is valid.
+    def validattr?(param)
+        param = param.to_s
+
+        return true if param == "name"
+        return true if Puppet::Type.metaparam?(param)
+        return false unless defined?(@arguments)
+        return true if arguments.include?(param)
+        return false
+    end
+
+    def set_arguments(arguments)
+        @arguments = {}
+        return if arguments.nil?
+
+        arguments.each do |arg, default|
+            arg = arg.to_s
+            warn_if_metaparam(arg, default)
+            @arguments[arg] = default
+        end
+    end
+
+    private
+
+    def convert_from_ast(name)
+        value = name.value
+        if value.is_a?(Puppet::Parser::AST::Regex)
+            name = value.value
+        else
+            name = value
+        end
+    end
+
+    # Split an fq name into a namespace and name
+    def namesplit(fullname)
+        ary = fullname.split("::")
+        n = ary.pop || ""
+        ns = ary.join("::")
+        return ns, n
+    end
+
+    def set_name_and_namespace(name)
+        if name.is_a?(Regexp)
+            @name = name
+            @namespace = ""
+        else
+            @name = name.to_s.downcase
+            @namespace, ignored_shortname = namesplit(@name)
+        end
+    end
+
+    def warn_if_metaparam(param, default)
+        return unless Puppet::Type.metaparamclass(param)
+
+        if default
+            warnonce "#{param} is a metaparam; this value will inherit to all contained resources"
+        else
+            raise Puppet::ParseError, "#{param} is a metaparameter; please choose another parameter name in the #{self.name} definition"
+        end
+    end
+end
diff --git a/lib/puppet/util/rdoc/parser.rb b/lib/puppet/util/rdoc/parser.rb
index c2d3077..3f04fbf 100644
--- a/lib/puppet/util/rdoc/parser.rb
+++ b/lib/puppet/util/rdoc/parser.rb
@@ -323,8 +323,8 @@ class Parser
     # that contains the documentation
     def parse_elements(container)
         Puppet.debug "rdoc: scanning manifest"
-        @ast.hostclasses.values.sort { |a,b| a.classname <=> b.classname }.each do |klass|
-            name = klass.classname
+        @ast.hostclasses.values.sort { |a,b| a.name <=> b.name }.each do |klass|
+            name = klass.name
             if klass.file == @input_file_name
                 unless name.empty?
                     document_class(name,klass,container)
@@ -463,4 +463,4 @@ class Parser
         comment.sub!(/^#--.*/m, '')
     end
 end
-end
\ No newline at end of file
+end
diff --git a/spec/integration/parser/parser.rb b/spec/integration/parser/parser.rb
new file mode 100755
index 0000000..71ae136
--- /dev/null
+++ b/spec/integration/parser/parser.rb
@@ -0,0 +1,21 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+describe Puppet::Parser::Parser do
+    before :each do
+        @loaded_code = Puppet::Parser::LoadedCode.new
+        @parser = Puppet::Parser::Parser.new :environment => "development", :loaded_code => @loaded_code
+    end
+
+    describe "when parsing comments before statement" do
+        it "should associate the documentation to the statement AST node" do
+            ast = @parser.parse("""
+            # comment
+            class test {}
+            """)
+
+            ast.hostclass("test").doc.should == "comment\n"
+        end
+    end
+end
diff --git a/spec/integration/util/rdoc/parser.rb b/spec/integration/util/rdoc/parser.rb
index df8c62d..5426609 100755
--- a/spec/integration/util/rdoc/parser.rb
+++ b/spec/integration/util/rdoc/parser.rb
@@ -36,7 +36,7 @@ describe RDoc::Parser do
     end
 
     it "should parse to RDoc data structure" do
-        @parser.expects(:document_class).with { |n,k,c| n == "::test" and k.is_a?(Puppet::Parser::AST::HostClass) }
+        @parser.expects(:document_class).with { |n,k,c| n == "::test" and k.is_a?(Puppet::Parser::ResourceType) }
         @parser.scan
     end
-end
\ No newline at end of file
+end
diff --git a/spec/unit/parser/ast.rb b/spec/unit/parser/ast.rb
index 35f575b..ca6f808 100644
--- a/spec/unit/parser/ast.rb
+++ b/spec/unit/parser/ast.rb
@@ -20,9 +20,9 @@ describe Puppet::Parser::AST do
         ast.should respond_to(:use_docs)
     end
 
-    [ Puppet::Parser::AST::Collection, Puppet::Parser::AST::Definition, Puppet::Parser::AST::Else,
-      Puppet::Parser::AST::Function, Puppet::Parser::AST::HostClass, Puppet::Parser::AST::IfStatement,
-      Puppet::Parser::AST::Node, Puppet::Parser::AST::Resource, Puppet::Parser::AST::ResourceDefaults,
+    [ Puppet::Parser::AST::Collection, Puppet::Parser::AST::Else,
+      Puppet::Parser::AST::Function, Puppet::Parser::AST::IfStatement,
+      Puppet::Parser::AST::Resource, Puppet::Parser::AST::ResourceDefaults,
       Puppet::Parser::AST::ResourceOverride, Puppet::Parser::AST::VarDef
     ].each do |k|
         it "#{k}.use_docs should return true" do
diff --git a/spec/unit/parser/ast/definition.rb b/spec/unit/parser/ast/definition.rb
deleted file mode 100755
index c267065..0000000
--- a/spec/unit/parser/ast/definition.rb
+++ /dev/null
@@ -1,213 +0,0 @@
-#!/usr/bin/env ruby
-
-require File.dirname(__FILE__) + '/../../../spec_helper'
-
-describe Puppet::Parser::AST::Definition, "when initializing" do
-end
-
-describe Puppet::Parser::AST::Definition, "when evaluating" do
-    before do
-        @type = Puppet::Parser::Resource
-        @parser = Puppet::Parser::Parser.new :Code => ""
-        @source = @parser.newclass ""
-        @definition = @parser.newdefine "mydefine"
-        @node = Puppet::Node.new("yaynode")
-        @compiler = Puppet::Parser::Compiler.new(@node, @parser)
-        @scope = @compiler.topscope
-
-        @resource = Puppet::Parser::Resource.new(:type => "mydefine", :title => "myresource", :scope => @scope, :source => @source)
-    end
-
-    it "should create a new scope" do
-        scope = nil
-        code = mock 'code'
-        code.expects(:safeevaluate).with do |scope|
-            scope.object_id.should_not == @scope.object_id
-            true
-        end
-        @definition.stubs(:code).returns(code)
-        @definition.evaluate_code(@resource)
-    end
-
-    it "should have a get_classname method" do
-        @definition.should respond_to(:get_classname)
-    end
-
-    it "should return the current classname with get_classname" do
-        @definition.expects(:classname)
-
-        @definition.get_classname(@scope)
-    end
-
-    describe "when evaluating" do
-        it "should create a resource whose title comes from get_classname" do
-            @definition.expects(:get_classname).returns("classname")
-
-            @definition.evaluate(@scope)
-        end
-    end
-
-#    it "should copy its namespace to the scope"
-#
-#    it "should mark the scope virtual if the resource is virtual"
-#
-#    it "should mark the scope exported if the resource is exported"
-#
-#    it "should set the resource's parameters as variables in the scope"
-#
-#    it "should set the resource's title as a variable in the scope"
-#
-#    it "should copy the resource's title in a 'name' variable in the scope"
-#
-#    it "should not copy the resource's title as the name if 'name' is one of the resource parameters"
-#
-#    it "should evaluate the associated code with the new scope"
-
-    def old_test_initialize
-        parser = mkparser
-
-        # Create a new definition
-        klass = parser.newdefine "yayness",
-            :arguments => [["owner", stringobj("nobody")], %w{mode}],
-            :code => AST::ASTArray.new(
-                :children => [resourcedef("file", "/tmp/$name",
-                        "owner" => varref("owner"), "mode" => varref("mode"))]
-            )
-
-        # Test validattr? a couple different ways
-        [:owner, "owner", :schedule, "schedule"].each do |var|
-            assert(klass.validattr?(var), "%s was not considered valid" % var.inspect)
-        end
-
-        [:random, "random"].each do |var|
-            assert(! klass.validattr?(var), "%s was considered valid" % var.inspect)
-        end
-
-    end
-
-    def oldtest_evaluate
-        parser = mkparser
-        config = mkcompiler
-        config.send(:evaluate_main)
-        scope = config.topscope
-        klass = parser.newdefine "yayness",
-            :arguments => [["owner", stringobj("nobody")], %w{mode}],
-            :code => AST::ASTArray.new(
-                :children => [resourcedef("file", "/tmp/$name",
-                        "owner" => varref("owner"), "mode" => varref("mode"))]
-            )
-
-        resource = Puppet::Parser::Resource.new(
-            :title => "first",
-            :type => "yayness",
-            :exported => false,
-            :virtual => false,
-            :scope => scope,
-            :source => scope.source
-        )
-        resource.send(:set_parameter, "name", "first")
-        resource.send(:set_parameter, "mode", "755")
-
-        resource.stubs(:title)
-        assert_nothing_raised do
-            klass.evaluate_code(resource)
-        end
-
-        firstobj = config.findresource("File[/tmp/first]")
-        assert(firstobj, "Did not create /tmp/first obj")
-
-        assert_equal("File", firstobj.type)
-        assert_equal("/tmp/first", firstobj.title)
-        assert_equal("nobody", firstobj[:owner])
-        assert_equal("755", firstobj[:mode])
-
-        # Make sure we can't evaluate it with the same args
-        assert_raise(Puppet::ParseError) do
-            klass.evaluate_code(resource)
-        end
-
-        # Now create another with different args
-        resource2 = Puppet::Parser::Resource.new(
-            :title => "second",
-            :type => "yayness",
-            :exported => false,
-            :virtual => false,
-            :scope => scope,
-            :source => scope.source
-        )
-        resource2.send(:set_parameter, "name", "second")
-        resource2.send(:set_parameter, "mode", "755")
-        resource2.send(:set_parameter, "owner", "daemon")
-
-        assert_nothing_raised do
-            klass.evaluate_code(resource2)
-        end
-
-        secondobj = config.findresource("File[/tmp/second]")
-        assert(secondobj, "Did not create /tmp/second obj")
-
-        assert_equal("File", secondobj.type)
-        assert_equal("/tmp/second", secondobj.title)
-        assert_equal("daemon", secondobj[:owner])
-        assert_equal("755", secondobj[:mode])
-    end
-
-    # #539 - definitions should support both names and titles
-    def oldtest_names_and_titles
-        parser = mkparser
-        scope = mkscope :parser => parser
-
-        [
-            {:name => "one", :title => "two"},
-            {:title => "mytitle"}
-        ].each_with_index do |hash, i|
-            # Create a definition that uses both name and title.  Put this
-            # inside the loop so the subscope expectations work.
-            klass = parser.newdefine "yayness%s" % i
-
-            resource = Puppet::Parser::Resource.new(
-                :title => hash[:title],
-                :type => "yayness%s" % i,
-                :exported => false,
-                :virtual => false,
-                :scope => scope,
-                :source => scope.source
-            )
-
-            subscope = klass.subscope(scope, resource)
-
-            klass.expects(:subscope).returns(subscope)
-
-            if hash[:name]
-                resource.stubs(:to_hash).returns({:name => hash[:name]})
-            end
-
-            assert_nothing_raised("Could not evaluate definition with %s" % hash.inspect) do
-                klass.evaluate_code(resource)
-            end
-
-            name = hash[:name] || hash[:title]
-            title = hash[:title]
-
-            assert_equal(name, subscope.lookupvar("name"),
-                "Name did not get set correctly")
-            assert_equal(title, subscope.lookupvar("title"),
-                "title did not get set correctly")
-
-            [:name, :title].each do |param|
-                val = resource.send(param)
-                assert(subscope.tags.include?(val),
-                    "Scope was not tagged with %s '%s'" % [param, val])
-            end
-        end
-    end
-
-    # Testing the root cause of #615.  We should be using the fqname for the type, instead
-    # of just the short name.
-    def oldtest_fully_qualified_types
-        parser = mkparser
-        klass = parser.newclass("one::two")
-
-        assert_equal("one::two", klass.classname, "Class did not get fully qualified class name")
-    end
-end
diff --git a/spec/unit/parser/ast/hostclass.rb b/spec/unit/parser/ast/hostclass.rb
deleted file mode 100755
index 10aa621..0000000
--- a/spec/unit/parser/ast/hostclass.rb
+++ /dev/null
@@ -1,148 +0,0 @@
-#!/usr/bin/env ruby
-
-require File.dirname(__FILE__) + '/../../../spec_helper'
-
-describe Puppet::Parser::AST::HostClass do
-    before :each do
-        @node = Puppet::Node.new "testnode"
-        @parser = Puppet::Parser::Parser.new :environment => "development"
-        @scope_resource = stub 'scope_resource', :builtin? => true
-        @compiler = Puppet::Parser::Compiler.new(@node, @parser)
-
-        @scope = @compiler.topscope
-    end
-
-    describe Puppet::Parser::AST::HostClass, "when evaluating" do
-
-        before do
-            @top = @parser.newclass "top"
-            @middle = @parser.newclass "middle", :parent => "top"
-        end
-
-        it "should create a resource that references itself" do
-            @top.evaluate(@scope)
-
-            @compiler.catalog.resource(:class, "top").should be_instance_of(Puppet::Parser::Resource)
-        end
-
-        it "should evaluate the parent class if one exists" do
-            @middle.evaluate(@scope)
-
-            @compiler.catalog.resource(:class, "top").should be_instance_of(Puppet::Parser::Resource)
-        end
-
-        it "should fail to evaluate if a parent class is defined but cannot be found" do
-            othertop = @parser.newclass "something", :parent => "yay"
-            lambda { othertop.evaluate(@scope) }.should raise_error(Puppet::ParseError)
-        end
-
-        it "should not create a new resource if one already exists" do
-            @compiler.catalog.expects(:resource).with(:class, "top").returns("something")
-            @compiler.catalog.expects(:add_resource).never
-            @top.evaluate(@scope)
-        end
-
-        it "should return the existing resource when not creating a new one" do
-            @compiler.catalog.expects(:resource).with(:class, "top").returns("something")
-            @compiler.catalog.expects(:add_resource).never
-            @top.evaluate(@scope).should == "something"
-        end
-
-        it "should not create a new parent resource if one already exists and it has a parent class" do
-            @top.evaluate(@scope)
-
-            top_resource = @compiler.catalog.resource(:class, "top")
-
-            @middle.evaluate(@scope)
-
-            @compiler.catalog.resource(:class, "top").should equal(top_resource)
-        end
-
-        # #795 - tag before evaluation.
-        it "should tag the catalog with the resource tags when it is evaluated" do
-            @middle.evaluate(@scope)
-
-            @compiler.catalog.should be_tagged("middle")
-        end
-
-        it "should tag the catalog with the parent class tags when it is evaluated" do
-            @middle.evaluate(@scope)
-
-            @compiler.catalog.should be_tagged("top")
-        end
-    end
-
-    describe Puppet::Parser::AST::HostClass, "when evaluating code" do
-
-        before do
-            @top_resource = stub "top_resource"
-            @top = @parser.newclass "top", :code => @top_resource
-
-            @middle_resource = stub "middle_resource"
-            @middle = @parser.newclass "top::middle", :parent => "top", :code => @middle_resource
-        end
-
-        it "should set its namespace to its fully qualified name" do
-            @middle.namespace.should == "top::middle"
-        end
-
-        it "should evaluate the code referred to by the class" do
-            @top_resource.expects(:safeevaluate)
-
-            resource = @top.evaluate(@scope)
-
-            @top.evaluate_code(resource)
-        end
-
-        it "should evaluate the parent class's code if it has a parent" do
-            @top_resource.expects(:safeevaluate)
-            @middle_resource.expects(:safeevaluate)
-
-            resource = @middle.evaluate(@scope)
-
-            @middle.evaluate_code(resource)
-        end
-
-        it "should not evaluate the parent class's code if the parent has already been evaluated" do
-            @top_resource.stubs(:safeevaluate)
-            resource = @top.evaluate(@scope)
-            @top.evaluate_code(resource)
-
-            @top_resource.expects(:safeevaluate).never
-            @middle_resource.stubs(:safeevaluate)
-            resource = @middle.evaluate(@scope)
-            @middle.evaluate_code(resource)
-        end
-
-        it "should use the parent class's scope as its parent scope" do
-            @top_resource.stubs(:safeevaluate)
-            @middle_resource.stubs(:safeevaluate)
-            resource = @middle.evaluate(@scope)
-            @middle.evaluate_code(resource)
-
-            @compiler.class_scope(@middle).parent.should equal(@compiler.class_scope(@top))
-        end
-
-        it "should add the class's name and title to its own scope" do
-            @top_resource.stubs(:safeevaluate)
-            @middle_resource.stubs(:safeevaluate)
-            resource = @middle.evaluate(@scope)
-            scope = stub_everything 'scope', :compiler => @compiler
-            @middle.stubs(:subscope).returns(scope)
-
-            scope.expects(:setvar).with("title","top::middle")
-            scope.expects(:setvar).with("name","top::middle")
-
-            @middle.evaluate_code(resource)
-        end
-
-        it "should add the parent class's namespace to its namespace search path" do
-            @top_resource.stubs(:safeevaluate)
-            @middle_resource.stubs(:safeevaluate)
-            resource = @middle.evaluate(@scope)
-            @middle.evaluate_code(resource)
-
-            @compiler.class_scope(@middle).namespaces.should be_include(@top.namespace)
-        end
-    end
-end
diff --git a/spec/unit/parser/ast/leaf.rb b/spec/unit/parser/ast/leaf.rb
index fecfba3..ee1ee04 100755
--- a/spec/unit/parser/ast/leaf.rb
+++ b/spec/unit/parser/ast/leaf.rb
@@ -195,40 +195,6 @@ describe Puppet::Parser::AST::HostName do
         @host.evaluate(@scope).should == @value
     end
 
-    it "should implement to_classname" do
-        @host.should respond_to(:to_classname)
-    end
-
-    it "should return the downcased nodename as classname" do
-        host = Puppet::Parser::AST::HostName.new( :value => "KLASSNAME" )
-        host.to_classname.should == "klassname"
-    end
-
-    it "should preserve '_' in to_classname with a string nodename" do
-        host = Puppet::Parser::AST::HostName.new( :value => "node_with_underscore")
-        host.to_classname.should == "node_with_underscore"
-    end
-
-    it "should preserve '_' in to_classname with a regex nodename" do
-        host = Puppet::Parser::AST::HostName.new( :value => Puppet::Parser::AST::Regex.new(:value => "/\dnode_with_underscore\.+/") )
-        host.to_classname.should == "dnode_with_underscore."
-    end
-
-    it "should return a string usable as classname when calling to_classname" do
-        host = Puppet::Parser::AST::HostName.new( :value => Puppet::Parser::AST::Regex.new(:value => "/^this-is not at a classname$/") )
-        host.to_classname.should == "this-isnotaclassname"
-    end
-
-    it "should return a string usable as a tag when calling to_classname" do
-        host = Puppet::Parser::AST::HostName.new( :value => Puppet::Parser::AST::Regex.new(:value => "/.+.reductivelabs\.com/") )
-        host.to_classname.should == "reductivelabs.com"
-    end
-
-    it "should delegate 'match' to the underlying value if it is an HostName" do
-        @value.expects(:match).with("value")
-        @host.match("value")
-    end
-
     it "should delegate eql? to the underlying value if it is an HostName" do
         @value.expects(:eql?).with("value")
         @host.eql?("value")
@@ -244,42 +210,4 @@ describe Puppet::Parser::AST::HostName do
         @value.expects(:hash)
         @host.hash
     end
-
-    it "should return true when regex? is called and value is a Regex" do
-        @value.expects(:is_a?).with(Puppet::Parser::AST::Regex).returns(true)
-        @host.regex?.should be_true
-    end
-
-    it "should return the results of comparing the regexes if asked whether a regex matches another regex" do
-        hosts = [1,2].collect do |num|
-            vreg = /vreg#{num}/
-            value = Puppet::Parser::AST::Regex.new(:value => vreg)
-            Puppet::Parser::AST::HostName.new(:value => value)
-        end
-
-        hosts[0].match(hosts[1]).should be_false
-        hosts[0].match(hosts[0]).should be_true
-    end
-
-    it "should return false when comparing a non-regex to a regex" do
-        vreg = /vreg/
-        value = Puppet::Parser::AST::Regex.new(:value => vreg)
-        regex = Puppet::Parser::AST::HostName.new(:value => value)
-
-        value = Puppet::Parser::AST::Regex.new(:value => "foo")
-        normal = Puppet::Parser::AST::HostName.new(:value => value)
-
-        normal.match(regex).should be_false
-    end
-
-    it "should true when a provided string matches a regex" do
-        vreg = /r/
-        value = Puppet::Parser::AST::Regex.new(:value => vreg)
-        regex = Puppet::Parser::AST::HostName.new(:value => value)
-
-        value = Puppet::Parser::AST::Leaf.new(:value => "bar")
-        normal = Puppet::Parser::AST::HostName.new(:value => value)
-
-        regex.match(normal).should be_true
-    end
 end
diff --git a/spec/unit/parser/ast/node.rb b/spec/unit/parser/ast/node.rb
deleted file mode 100755
index 5a4a5ef..0000000
--- a/spec/unit/parser/ast/node.rb
+++ /dev/null
@@ -1,145 +0,0 @@
-#!/usr/bin/env ruby
-
-require File.dirname(__FILE__) + '/../../../spec_helper'
-
-describe Puppet::Parser::AST::Node do
-    before :each do
-        @node = Puppet::Node.new "testnode"
-        @parser = Puppet::Parser::Parser.new :environment => "development"
-        @scope_resource = stub 'scope_resource', :builtin? => true
-        @compiler = Puppet::Parser::Compiler.new(@node, @parser)
-
-        @scope = @compiler.topscope
-    end
-
-    describe "when calling get_classname" do
-        it "should return current node name if name is a Regex" do
-            name = stub 'name', :regex? => true
-            node = @parser.newnode("node").shift
-            node.stubs(:name).returns(name)
-
-            @scope.expects(:host).returns("testnode")
-
-            node.get_classname(@scope).should == "testnode"
-        end
-
-        it "should return the current node classname if name is not a Regex" do
-            name = stub 'name', :regex? => false
-            node = @parser.newnode("node").shift
-            node.stubs(:name).returns(name)
-
-            node.get_classname(@scope).should == "node"
-        end
-    end
-
-    describe Puppet::Parser::AST::Node, "when evaluating" do
-
-        before do
-            @top = @parser.newnode("top").shift
-            @middle = @parser.newnode("middle", :parent => "top").shift
-        end
-
-        it "should create a resource that references itself" do
-            @top.evaluate(@scope)
-
-            @compiler.catalog.resource(:node, "top").should be_an_instance_of(Puppet::Parser::Resource)
-        end
-
-        it "should evaluate the parent class if one exists" do
-            @middle.evaluate(@scope)
-
-            @compiler.catalog.resource(:node, "top").should be_an_instance_of(Puppet::Parser::Resource)
-        end
-
-        it "should fail to evaluate if a parent class is defined but cannot be found" do
-            othertop = @parser.newnode("something", :parent => "yay").shift
-            lambda { othertop.evaluate(@scope) }.should raise_error(Puppet::ParseError)
-        end
-
-        it "should not create a new resource if one already exists" do
-            @compiler.catalog.expects(:resource).with(:node, "top").returns("something")
-            @compiler.catalog.expects(:add_resource).never
-            @top.evaluate(@scope)
-        end
-
-        it "should not create a new parent resource if one already exists and it has a parent class" do
-            @top.evaluate(@scope)
-
-            top_resource = @compiler.catalog.resource(:node, "top")
-
-            @middle.evaluate(@scope)
-
-            @compiler.catalog.resource(:node, "top").should equal(top_resource)
-        end
-
-        # #795 - tag before evaluation.
-        it "should tag the catalog with the resource tags when it is evaluated" do
-            @middle.evaluate(@scope)
-
-            @compiler.catalog.should be_tagged("middle")
-        end
-
-        it "should tag the catalog with the parent class tags when it is evaluated" do
-            @middle.evaluate(@scope)
-
-            @compiler.catalog.should be_tagged("top")
-        end
-    end
-
-    describe Puppet::Parser::AST::Node, "when evaluating code" do
-
-        before do
-            @top_resource = stub "top_resource"
-            @top = @parser.newnode("top", :code => @top_resource).shift
-
-            @middle_resource = stub "middle_resource"
-            @middle = @parser.newnode("middle", :parent => "top", :code => @middle_resource).shift
-        end
-
-        it "should evaluate the code referred to by the class" do
-            @top_resource.expects(:safeevaluate)
-
-            resource = @top.evaluate(@scope)
-
-            @top.evaluate_code(resource)
-        end
-
-        it "should evaluate the parent class's code if it has a parent" do
-            @top_resource.expects(:safeevaluate)
-            @middle_resource.expects(:safeevaluate)
-
-            resource = @middle.evaluate(@scope)
-
-            @middle.evaluate_code(resource)
-        end
-
-        it "should not evaluate the parent class's code if the parent has already been evaluated" do
-            @top_resource.stubs(:safeevaluate)
-            resource = @top.evaluate(@scope)
-            @top.evaluate_code(resource)
-
-            @top_resource.expects(:safeevaluate).never
-            @middle_resource.stubs(:safeevaluate)
-            resource = @middle.evaluate(@scope)
-            @middle.evaluate_code(resource)
-        end
-
-        it "should use the parent class's scope as its parent scope" do
-            @top_resource.stubs(:safeevaluate)
-            @middle_resource.stubs(:safeevaluate)
-            resource = @middle.evaluate(@scope)
-            @middle.evaluate_code(resource)
-
-            @compiler.class_scope(@middle).parent.should equal(@compiler.class_scope(@top))
-        end
-
-        it "should add the parent class's namespace to its namespace search path" do
-            @top_resource.stubs(:safeevaluate)
-            @middle_resource.stubs(:safeevaluate)
-            resource = @middle.evaluate(@scope)
-            @middle.evaluate_code(resource)
-
-            @compiler.class_scope(@middle).namespaces.should be_include(@top.namespace)
-        end
-    end
-end
diff --git a/spec/unit/parser/ast/resource_reference.rb b/spec/unit/parser/ast/resource_reference.rb
index 24865e8..10d9678 100755
--- a/spec/unit/parser/ast/resource_reference.rb
+++ b/spec/unit/parser/ast/resource_reference.rb
@@ -21,7 +21,7 @@ describe Puppet::Parser::AST::ResourceReference do
 
     %{ "one::two" "one-two"}.each do |type|
         it "should evaluate correctly reference to define" do
-            klass = stub 'klass', :title => "three", :classname => type
+            klass = stub 'klass', :title => "three", :name => type
             @scope.stubs(:find_definition).returns(klass)
         
             newref("three", type).evaluate(@scope).to_ref.should == Puppet::Parser::Resource::Reference.new( :type => type, :title => "three" ).to_ref
@@ -29,13 +29,13 @@ describe Puppet::Parser::AST::ResourceReference do
     end
 
     it "should be able to call qualified_class" do
-        klass = stub 'klass', :title => "three", :classname => "one"
+        klass = stub 'klass', :title => "three", :name => "one"
         @scope.expects(:find_hostclass).with("one").returns(klass)
         newref("three","class").qualified_class(@scope,"one").should == "one" 
     end
 
     it "should be able to find qualified classes when evaluating" do
-        klass = stub 'klass', :title => "one", :classname => "one"
+        klass = stub 'klass', :title => "one", :name => "one"
         @scope.stubs(:find_hostclass).returns(klass)
         
         evaled = newref("one", "class").evaluate(@scope)
diff --git a/spec/unit/parser/compiler.rb b/spec/unit/parser/compiler.rb
index adc9732..8a41242 100755
--- a/spec/unit/parser/compiler.rb
+++ b/spec/unit/parser/compiler.rb
@@ -51,7 +51,7 @@ describe Puppet::Parser::Compiler do
 
     it "should be able to retrieve class scopes by object" do
         klass = mock 'ast_class'
-        klass.expects(:classname).returns("myname")
+        klass.expects(:name).returns("myname")
         @compiler.class_set "myname", "myscope"
         @compiler.class_scope(klass).should == "myscope"
     end
@@ -153,8 +153,8 @@ describe Puppet::Parser::Compiler do
         it "should evaluate any existing classes named in the node" do
             classes = %w{one two three four}
             main = stub 'main'
-            one = stub 'one', :classname => "one"
-            three = stub 'three', :classname => "three"
+            one = stub 'one', :name => "one"
+            three = stub 'three', :name => "three"
             @node.stubs(:name).returns("whatever")
             @node.stubs(:classes).returns(classes)
 
@@ -396,7 +396,7 @@ describe Puppet::Parser::Compiler do
     describe "when evaluating found classes" do
 
         before do
-            @class = stub 'class', :classname => "my::class"
+            @class = stub 'class', :name => "my::class"
             @scope.stubs(:find_hostclass).with("myclass").returns(@class)
 
             @resource = stub 'resource', :ref => "Class[myclass]", :type => "file"
@@ -405,7 +405,7 @@ describe Puppet::Parser::Compiler do
         it "should evaluate each class" do
             @compiler.catalog.stubs(:tag)
 
-            @class.expects(:evaluate).with(@scope)
+            @class.expects(:mk_plain_resource).with(@scope)
 
             @compiler.evaluate_classes(%w{myclass}, @scope)
         end
@@ -415,7 +415,7 @@ describe Puppet::Parser::Compiler do
 
             @resource.expects(:evaluate).never
 
-            @class.expects(:evaluate).returns(@resource)
+            @class.expects(:mk_plain_resource).returns(@resource)
 
             @compiler.evaluate_classes(%w{myclass}, @scope)
         end
@@ -424,7 +424,7 @@ describe Puppet::Parser::Compiler do
             @compiler.catalog.stubs(:tag)
 
             @resource.expects(:evaluate)
-            @class.expects(:evaluate).returns(@resource)
+            @class.expects(:mk_plain_resource).returns(@resource)
 
             @compiler.evaluate_classes(%w{myclass}, @scope, false)
         end
@@ -459,7 +459,7 @@ describe Puppet::Parser::Compiler do
             @scope.stubs(:find_hostclass).with("notfound").returns(nil)
 
             Puppet::Parser::Resource.stubs(:new).returns(@resource)
-            @class.stubs :evaluate
+            @class.stubs :mk_plain_resource
             @compiler.evaluate_classes(%w{myclass notfound}, @scope).should == %w{myclass}
         end
     end
@@ -495,31 +495,31 @@ describe Puppet::Parser::Compiler do
         end
 
         it "should evaluate the first node class matching the node name" do
-            node_class = stub 'node', :classname => "c", :evaluate_code => nil
+            node_class = stub 'node', :name => "c", :evaluate_code => nil
             @compiler.parser.stubs(:node).with("c").returns(node_class)
 
             node_resource = stub 'node resource', :ref => "Node[c]", :evaluate => nil, :type => "node"
-            node_class.expects(:evaluate).returns(node_resource)
+            node_class.expects(:mk_plain_resource).returns(node_resource)
 
             @compiler.compile
         end
 
         it "should match the default node if no matching node can be found" do
-            node_class = stub 'node', :classname => "default", :evaluate_code => nil
+            node_class = stub 'node', :name => "default", :evaluate_code => nil
             @compiler.parser.stubs(:node).with("default").returns(node_class)
 
             node_resource = stub 'node resource', :ref => "Node[default]", :evaluate => nil, :type => "node"
-            node_class.expects(:evaluate).returns(node_resource)
+            node_class.expects(:mk_plain_resource).returns(node_resource)
 
             @compiler.compile
         end
 
         it "should evaluate the node resource immediately rather than using lazy evaluation" do
-            node_class = stub 'node', :classname => "c"
+            node_class = stub 'node', :name => "c"
             @compiler.parser.stubs(:node).with("c").returns(node_class)
 
             node_resource = stub 'node resource', :ref => "Node[c]", :type => "node"
-            node_class.expects(:evaluate).returns(node_resource)
+            node_class.expects(:mk_plain_resource).returns(node_resource)
 
             node_resource.expects(:evaluate)
 
@@ -528,13 +528,13 @@ describe Puppet::Parser::Compiler do
 
         it "should set the node's scope as the top scope" do
             node_resource = stub 'node resource', :ref => "Node[c]", :evaluate => nil, :type => "node"
-            node_class = stub 'node', :classname => "c", :evaluate => node_resource
+            node_class = stub 'node', :name => "c", :mk_plain_resource => node_resource
 
             @compiler.parser.stubs(:node).with("c").returns(node_class)
 
             # The #evaluate method normally does this.
             scope = stub 'scope', :source => "mysource"
-            @compiler.class_set(node_class.classname, scope)
+            @compiler.class_set(node_class.name, scope)
             node_resource.stubs(:evaluate)
 
             @compiler.compile
diff --git a/spec/unit/parser/loaded_code.rb b/spec/unit/parser/loaded_code.rb
index 75f2bc7..9b72dab 100644
--- a/spec/unit/parser/loaded_code.rb
+++ b/spec/unit/parser/loaded_code.rb
@@ -3,23 +3,92 @@
 require File.dirname(__FILE__) + '/../../spec_helper'
 
 require 'puppet/parser/loaded_code'
+require 'puppet/parser/resource_type'
 
 describe Puppet::Parser::LoadedCode do
+    before do
+        @instance = Puppet::Parser::ResourceType.new(:hostclass, "foo")
+        @code = Puppet::Parser::LoadedCode.new
+    end
+
+    it "should be able to add a resource type" do
+        Puppet::Parser::LoadedCode.new.should respond_to(:add)
+    end
+
+    it "should consider '<<' to be an alias to 'add' but should return self" do
+        loader = Puppet::Parser::LoadedCode.new
+        loader.expects(:add).with "foo"
+        loader.expects(:add).with "bar"
+        loader << "foo" << "bar"
+    end
+
+    it "should set itself as the code collection for added resource types" do
+        loader = Puppet::Parser::LoadedCode.new
+
+        node = Puppet::Parser::ResourceType.new(:node, "foo")
+
+        @code.add(node)
+        @code.node("foo").should equal(node)
+
+        node.code_collection.should equal(@code)
+    end
+
+    it "should store node resource types as nodes" do
+        node = Puppet::Parser::ResourceType.new(:node, "foo")
+
+        @code.add(node)
+        @code.node("foo").should equal(node)
+    end
+
+    it "should store hostclasses as hostclasses" do
+        klass = Puppet::Parser::ResourceType.new(:hostclass, "foo")
+
+        @code.add(klass)
+        @code.hostclass("foo").should equal(klass)
+    end
+
+    it "should store definitions as definitions" do
+        define = Puppet::Parser::ResourceType.new(:definition, "foo")
+
+        @code.add(define)
+        @code.definition("foo").should equal(define)
+    end
+
     %w{hostclass node definition}.each do |data|
         it "should have a method for adding a #{data}" do
             Puppet::Parser::LoadedCode.new.should respond_to("add_" + data)
         end
 
+        it "should use the name of the instance to add it" do
+            loader = Puppet::Parser::LoadedCode.new
+            loader.send("add_#{data}", @instance)
+            loader.send(data, @instance.name).should equal(@instance)
+        end
+
+        it "should fail to add a #{data} when one already exists" do
+            loader = Puppet::Parser::LoadedCode.new
+            loader.add @instance
+            lambda { loader.add(@instance) }.should raise_error(Puppet::ParseError)
+        end
+
+        it "should return the added #{data}" do
+            loader = Puppet::Parser::LoadedCode.new
+
+            loader.add(@instance).should equal(@instance)
+        end
+
         it "should be able to retrieve #{data} by name" do
             loader = Puppet::Parser::LoadedCode.new
-            loader.send("add_" + data, "foo", "bar")
-            loader.send(data, "foo").should == "bar"
+            instance = Puppet::Parser::ResourceType.new(data, "bar")
+            loader.add instance
+            loader.send(data, "bar").should equal(instance)
         end
 
         it "should retrieve #{data} insensitive to case" do
             loader = Puppet::Parser::LoadedCode.new
-            loader.send("add_" + data, "Foo", "bar")
-            loader.send(data, "fOo").should == "bar"
+            instance = Puppet::Parser::ResourceType.new(data, "Bar")
+            loader.add instance
+            loader.send(data, "bAr").should equal(instance)
         end
 
         it "should return nil when asked for a #{data} that has not been added" do
@@ -29,16 +98,18 @@ describe Puppet::Parser::LoadedCode do
         it "should be able to retrieve all #{data}s" do
             plurals = { "hostclass" => "hostclasses", "node" => "nodes", "definition" => "definitions" }
             loader = Puppet::Parser::LoadedCode.new
-            loader.send("add_" + data , "foo", "bar")
-            loader.send(plurals[data]).should == { "foo" => "bar" }
+            instance = Puppet::Parser::ResourceType.new(data, "foo")
+            loader.add instance
+            loader.send(plurals[data]).should == { "foo" => instance }
         end
     end
 
     describe "when finding a qualified instance" do
         it "should return any found instance if the instance name is fully qualified" do
             loader = Puppet::Parser::LoadedCode.new
-            loader.add_hostclass "foo::bar", "yay"
-            loader.find("namespace", "::foo::bar", :hostclass).should == "yay"
+            instance = Puppet::Parser::ResourceType.new(:hostclass, "foo::bar")
+            loader.add instance
+            loader.find("namespace", "::foo::bar", :hostclass).should equal(instance)
         end
 
         it "should return nil if the instance name is fully qualified and no such instance exists" do
@@ -48,37 +119,43 @@ describe Puppet::Parser::LoadedCode do
 
         it "should return the partially qualified object if it exists in the provided namespace" do
             loader = Puppet::Parser::LoadedCode.new
-            loader.add_hostclass "foo::bar::baz", "yay"
-            loader.find("foo", "bar::baz", :hostclass).should == "yay"
+            instance = Puppet::Parser::ResourceType.new(:hostclass, "foo::bar::baz")
+            loader.add instance
+            loader.find("foo", "bar::baz", :hostclass).should equal(instance)
         end
 
         it "should return the unqualified object if it exists in the provided namespace" do
             loader = Puppet::Parser::LoadedCode.new
-            loader.add_hostclass "foo::bar", "yay"
-            loader.find("foo", "bar", :hostclass).should == "yay"
+            instance = Puppet::Parser::ResourceType.new(:hostclass, "foo::bar")
+            loader.add instance
+            loader.find("foo", "bar", :hostclass).should equal(instance)
         end
 
         it "should return the unqualified object if it exists in the parent namespace" do
             loader = Puppet::Parser::LoadedCode.new
-            loader.add_hostclass "foo::bar", "yay"
-            loader.find("foo::bar::baz", "bar", :hostclass).should == "yay"
+            instance = Puppet::Parser::ResourceType.new(:hostclass, "foo::bar")
+            loader.add instance
+            loader.find("foo::bar::baz", "bar", :hostclass).should equal(instance)
         end
 
         it "should should return the partially qualified object if it exists in the parent namespace" do
             loader = Puppet::Parser::LoadedCode.new
-            loader.add_hostclass "foo::bar::baz", "yay"
-            loader.find("foo::bar", "bar::baz", :hostclass).should == "yay"
+            instance = Puppet::Parser::ResourceType.new(:hostclass, "foo::bar::baz")
+            loader.add instance
+            loader.find("foo::bar", "bar::baz", :hostclass).should equal(instance)
         end
 
         it "should return the qualified object if it exists in the root namespace" do
             loader = Puppet::Parser::LoadedCode.new
-            loader.add_hostclass "foo::bar::baz", "yay"
-            loader.find("foo::bar", "foo::bar::baz", :hostclass).should == "yay"
+            instance = Puppet::Parser::ResourceType.new(:hostclass, "foo::bar::baz")
+            loader.add instance
+            loader.find("foo::bar", "foo::bar::baz", :hostclass).should equal(instance)
         end
 
         it "should return nil if the object cannot be found" do
             loader = Puppet::Parser::LoadedCode.new
-            loader.add_hostclass "foo::bar::baz", "yay"
+            instance = Puppet::Parser::ResourceType.new(:hostclass, "foo::bar::baz")
+            loader.add instance
             loader.find("foo::bar", "eh", :hostclass).should be_nil
         end
     end
@@ -103,7 +180,7 @@ describe Puppet::Parser::LoadedCode do
 
     it "should indicate whether any nodes are defined" do
         loader = Puppet::Parser::LoadedCode.new
-        loader.add_node("foo", "bar")
+        loader.add_node(Puppet::Parser::ResourceType.new(:node, "foo"))
         loader.should be_nodes
     end
 
@@ -111,105 +188,32 @@ describe Puppet::Parser::LoadedCode do
         Puppet::Parser::LoadedCode.new.should_not be_nodes
     end
 
-    describe "when adding nodes" do
-        it "should create an HostName if nodename is a string" do
-            Puppet::Parser::AST::HostName.expects(:new).with(:value => "foo")
-            loader = Puppet::Parser::LoadedCode.new
-            loader.add_node("foo", "bar")
-        end
-
-        it "should not create an HostName if nodename is an HostName" do
-            name = Puppet::Parser::AST::HostName.new(:value => "foo")
-
-            Puppet::Parser::AST::HostName.expects(:new).with(:value => "foo").never
-
-            loader = Puppet::Parser::LoadedCode.new
-            loader.add_node(name, "bar")
-        end
-    end
-
     describe "when finding nodes" do
         before :each do
             @loader = Puppet::Parser::LoadedCode.new
-
-            @nodename1 = stub 'nodename1', :is_a? => true
-            @node1 = stub 'node1'
-            @nodename2 = stub 'nodename2', :is_a? => true
-            @node2 = stub 'node2'
-        end
-        it "should create an HostName if nodename is a string" do
-            Puppet::Parser::AST::HostName.expects(:new).with(:value => "foo")
-
-            @loader.node("foo")
-        end
-
-        it "should not create an HostName if nodename is an HostName" do
-            name = Puppet::Parser::AST::HostName.new(:value => "foo")
-
-            Puppet::Parser::AST::HostName.expects(:new).with(:value => "foo").never
-
-            @loader.node(name)
-        end
-
-        it "should be able to find node by HostName" do
-            namein = Puppet::Parser::AST::HostName.new(:value => "foo")
-            nameout = Puppet::Parser::AST::HostName.new(:value => "foo")
-
-            @loader.add_node(namein, "bar")
-            @loader.node(nameout).should == "bar"
         end
 
-        it "should be able to find node by HostName strict equality" do
-            namein = Puppet::Parser::AST::HostName.new(:value => "foo")
-            nameout = Puppet::Parser::AST::HostName.new(:value => "foo")
+        it "should return any node whose name exactly matches the provided node name" do
+            node = Puppet::Parser::ResourceType.new(:node, "foo")
+            @loader << node
 
-            @loader.add_node(namein, "bar")
-            @loader.node_exists?(nameout).should == "bar"
+            @loader.node("foo").should equal(node)
         end
 
-        it "should not use node name matching when finding with strict node HostName" do
-            name1 = Puppet::Parser::AST::HostName.new(:value => "foo")
-            name2 = Puppet::Parser::AST::HostName.new(:value => Puppet::Parser::AST::Regex.new(:value => /foo/))
+        it "should return the first regex node whose regex matches the provided node name" do
+            node1 = Puppet::Parser::ResourceType.new(:node, /\w/)
+            node2 = Puppet::Parser::ResourceType.new(:node, /\d/)
+            @loader << node1 << node2
 
-            @loader.add_node(name1, "bar")
-            @loader.add_node(name2, "baz")
-            @loader.node_exists?(name1).should == "bar"
+            @loader.node("foo10").should equal(node1)
         end
 
-        it "should return the first matching regex nodename" do
-            @nodename1.stubs(:regex?).returns(true)
-            @nodename1.stubs(:match).returns(true)
-            @nodename2.stubs(:regex?).returns(true)
-            @nodename2.stubs(:match).returns(true)
-
-            @loader.add_node(@nodename1, @node1)
-            @loader.add_node(@nodename2, @node2)
-
-            @loader.node("test").should == @node1
-        end
-
-        it "should not scan non-regex node" do
-            @nodename1.stubs(:regex?).returns(true)
-            @nodename1.stubs(:match).returns(false)
-            @nodename2.stubs(:regex?).returns(false)
-            @nodename2.expects(:match).never
-
-            @loader.add_node(@nodename1, at node1)
-            @loader.add_node(@nodename2, at node2)
-
-            @loader.node("test")
-        end
-
-        it "should prefer non-regex nodes to regex nodes" do
-            @nodename1.stubs(:regex?).returns(false)
-            @nodename1.expects(:match).never
-            @nodename2.stubs(:regex?).returns(true)
-            @nodename2.expects(:match).never
-
-            @loader.add_node(@nodename1, at node1)
-            @loader.add_node(@nodename2, at node2)
+        it "should preferentially return a node whose name is string-equal over returning a node whose regex matches a provided name" do
+            node1 = Puppet::Parser::ResourceType.new(:node, /\w/)
+            node2 = Puppet::Parser::ResourceType.new(:node, "foo")
+            @loader << node1 << node2
 
-            @loader.node(@nodename1)
+            @loader.node("foo").should equal(node2)
         end
     end
 end
diff --git a/spec/unit/parser/parser.rb b/spec/unit/parser/parser.rb
index 78caf18..a044446 100755
--- a/spec/unit/parser/parser.rb
+++ b/spec/unit/parser/parser.rb
@@ -174,56 +174,62 @@ describe Puppet::Parser do
         end
     end
 
-    describe "when instantiating class of same name" do
-
-        before :each do
-            @one = stub 'one', :is_a? => true
-            @one.stubs(:is_a?).with(ast::ASTArray).returns(false)
-            @one.stubs(:is_a?).with(ast).returns(true)
-
-            @two = stub 'two'
-            @two.stubs(:is_a?).with(ast::ASTArray).returns(false)
-            @two.stubs(:is_a?).with(ast).returns(true)
+    describe "when providing AST context" do
+        before do
+            @lexer = stub 'lexer', :line => 50, :file => "/foo/bar", :getcomment => "whev"
+            @parser.stubs(:lexer).returns @lexer
         end
 
-        it "should return the first class" do
-
-            klass1 = @parser.newclass("one", { :code => @one })
+        it "should include the lexer's line" do
+            @parser.ast_context[:line].should == 50
+        end
 
-            @parser.newclass("one", { :code => @two }).should == klass1
+        it "should include the lexer's file" do
+            @parser.ast_context[:file].should == "/foo/bar"
         end
 
-        it "should concatenate code" do
-            klass1 = @parser.newclass("one", { :code => @one })
+        it "should include the docs if directed to do so" do
+            @parser.ast_context(true)[:doc].should == "whev"
+        end
 
-            @parser.newclass("one", { :code => @two })
+        it "should not include the docs when told not to" do
+            @parser.ast_context(false)[:doc].should be_nil
+        end
 
-            klass1.code.children.should == [@one, at two]
+        it "should not include the docs by default" do
+            @parser.ast_context()[:doc].should be_nil
         end
     end
 
-    describe "when parsing comments before statement" do
-        it "should associate the documentation to the statement AST node" do
-            ast = @parser.parse("""
-            # comment
-            class test {}
-            """)
+    describe "when building ast nodes" do
+        before do
+            @lexer = stub 'lexer', :line => 50, :file => "/foo/bar", :getcomment => "whev"
+            @parser.stubs(:lexer).returns @lexer
+            @class = stub 'class', :use_docs => false
+        end
 
-            ast.hostclass("test").doc.should == "comment\n"
+        it "should return a new instance of the provided class created with the provided options" do
+            @class.expects(:new).with { |opts| opts[:foo] == "bar" }
+            @parser.ast(@class, :foo => "bar")
         end
-    end
 
-    describe "when building ast nodes" do
-        it "should get lexer comments if ast node declares use_docs" do
-            lexer = stub 'lexer'
-            ast = mock 'ast', :nil? => false, :use_docs => true, :doc => ""
-            @parser.stubs(:lexer).returns(lexer)
+        it "should merge the ast context into the provided options" do
+            @class.expects(:new).with { |opts| opts[:file] == "/foo" }
+            @parser.expects(:ast_context).returns :file => "/foo"
+            @parser.ast(@class, :foo => "bar")
+        end
 
-            Puppet::Parser::AST::Definition.expects(:new).returns(ast)
-            lexer.expects(:getcomment).returns("comment")
-            ast.expects(:doc=).with("comment")
+        it "should prefer provided options over AST context" do
+            @class.expects(:new).with { |opts| opts[:file] == "/bar" }
+            @parser.expects(:ast_context).returns :file => "/foo"
+            @parser.ast(@class, :file => "/bar")
+        end
 
-            @parser.ast(Puppet::Parser::AST::Definition)
+        it "should include docs when the AST class uses them" do
+            @class.expects(:use_docs).returns true
+            @class.stubs(:new)
+            @parser.expects(:ast_context).with(true).returns({})
+            @parser.ast(@class, :file => "/bar")
         end
     end
 
@@ -233,59 +239,15 @@ describe Puppet::Parser do
             @lexer.stubs(:getcomment)
             @parser.stubs(:lexer).returns(@lexer)
             @node = stub_everything 'node'
-            @parser.stubs(:ast).returns(@node)
+            @parser.stubs(:ast_context).returns({})
             @parser.stubs(:node).returns(nil)
 
-            @nodename = stub 'nodename', :is_a? => false, :to_classname => "node"
+            @nodename = stub 'nodename', :is_a? => false, :value => "foo"
             @nodename.stubs(:is_a?).with(Puppet::Parser::AST::HostName).returns(true)
         end
 
-        it "should get the lexer stacked comments" do
-            @lexer.expects(:getcomment)
-
-            @parser.newnode(@nodename)
-        end
-
-        it "should create an HostName if needed" do
-            Puppet::Parser::AST::HostName.expects(:new).with(:value => "node").returns(@nodename)
-
-            @parser.newnode("node")
-        end
-
-        it "should raise an error if the node already exists" do
-            @loaded_code.stubs(:node_exists?).with(@nodename).returns(@node)
-
-            lambda { @parser.newnode(@nodename) }.should raise_error
-        end
-
-        it "should store the created node in the loaded code" do
-            @loaded_code.expects(:add_node).with(@nodename, @node)
-
-            @parser.newnode(@nodename)
-        end
-
-        it "should create the node with code if provided" do
-            @parser.stubs(:ast).with { |*args| args[1][:code] == :code }.returns(@node)
-
-            @parser.newnode(@nodename, :code => :code)
-        end
-
-        it "should create the node with a parentclass if provided" do
-            @parser.stubs(:ast).with { |*args| args[1][:parent] == :parent }.returns(@node)
-
-            @parser.newnode(@nodename, :parent => :parent)
-        end
-
-        it "should set the node classname from the HostName" do
-            @nodename.stubs(:to_classname).returns(:classname)
-
-            @node.expects(:classname=).with(:classname)
-
-            @parser.newnode(@nodename)
-        end
-
         it "should return an array of nodes" do
-            @parser.newnode(@nodename).should == [@node]
+            @parser.newnode(@nodename).should be_instance_of(Array)
         end
     end
 
diff --git a/spec/unit/parser/resource/reference.rb b/spec/unit/parser/resource/reference.rb
index 6284e67..e082136 100755
--- a/spec/unit/parser/resource/reference.rb
+++ b/spec/unit/parser/resource/reference.rb
@@ -84,7 +84,7 @@ describe Puppet::Parser::Resource::Reference, " when modeling defined types" do
         scope = @compiler.topscope.class.new(:parent => @compiler.topscope, :namespace => "other", :parser => @parser)
 
         ref = @type.new(:type => "class", :title => "top", :scope => scope)
-        ref.definedtype.classname.should equal(top.classname)
+        ref.definedtype.name.should equal(top.name)
     end
 
     it "should only look for fully qualified definitions" do
@@ -94,6 +94,6 @@ describe Puppet::Parser::Resource::Reference, " when modeling defined types" do
         scope = @compiler.topscope.class.new(:parent => @compiler.topscope, :namespace => "other", :parser => @parser)
 
         ref = @type.new(:type => "top", :title => "foo", :scope => scope)
-        ref.definedtype.classname.should equal(top.classname)
+        ref.definedtype.name.should equal(top.name)
     end
 end
diff --git a/spec/unit/parser/resource_type.rb b/spec/unit/parser/resource_type.rb
new file mode 100755
index 0000000..ddf8ce9
--- /dev/null
+++ b/spec/unit/parser/resource_type.rb
@@ -0,0 +1,522 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+require 'puppet/parser/resource_type'
+
+describe Puppet::Parser::ResourceType do
+    it "should have a 'name' attribute" do
+        Puppet::Parser::ResourceType.new(:hostclass, "foo").name.should == "foo"
+    end
+
+    [:code, :doc, :line, :file, :code_collection].each do |attr|
+        it "should have a '#{attr}' attribute" do
+            type = Puppet::Parser::ResourceType.new(:hostclass, "foo")
+            type.send(attr.to_s + "=", "yay")
+            type.send(attr).should == "yay"
+        end
+    end
+
+    describe "when a node"  do
+        it "should allow a regex as its name" do
+            lambda { Puppet::Parser::ResourceType.new(:node, /foo/) }.should_not raise_error
+        end
+
+        it "should allow a AST::HostName instance as its name" do
+            regex = Puppet::Parser::AST::Regex.new(:value => /foo/)
+            name = Puppet::Parser::AST::HostName.new(:value => regex)
+            lambda { Puppet::Parser::ResourceType.new(:node, name) }.should_not raise_error
+        end
+
+        it "should match against the regexp in the AST::HostName when a HostName instance is provided" do
+            regex = Puppet::Parser::AST::Regex.new(:value => /\w/)
+            name = Puppet::Parser::AST::HostName.new(:value => regex)
+            node = Puppet::Parser::ResourceType.new(:node, name)
+
+            node.match("foo").should be_true
+        end
+
+        it "should return the value of the hostname if provided a string-form AST::HostName instance as the name" do
+            name = Puppet::Parser::AST::HostName.new(:value => "foo")
+            node = Puppet::Parser::ResourceType.new(:node, name)
+
+            node.name.should == "foo"
+        end
+
+        describe "and the name is a regex" do
+            it "should have a method that indicates that this is the case" do
+                Puppet::Parser::ResourceType.new(:node, /w/).should be_name_is_regex
+            end
+
+            it "should set its namespace to ''" do
+                Puppet::Parser::ResourceType.new(:node, /w/).namespace.should == ""
+            end
+
+            it "should return the regex converted to a string when asked for its name" do
+                Puppet::Parser::ResourceType.new(:node, /ww/).name.should == "ww"
+            end
+
+            it "should downcase the regex when returning the name as a string" do
+                Puppet::Parser::ResourceType.new(:node, /W/).name.should == "w"
+            end
+
+            it "should remove non-alpha characters when returning the name as a string" do
+                Puppet::Parser::ResourceType.new(:node, /w*w/).name.should_not include("*")
+            end
+
+            it "should remove leading dots when returning the name as a string" do
+                Puppet::Parser::ResourceType.new(:node, /.ww/).name.should_not =~ /^\./
+            end
+
+            it "should have a method for matching its regex name against a provided name" do
+                Puppet::Parser::ResourceType.new(:node, /.ww/).should respond_to(:match)
+            end
+
+            it "should return true when its regex matches the provided name" do
+                Puppet::Parser::ResourceType.new(:node, /\w/).match("foo").should be_true
+            end
+
+            it "should return false when its regex does not match the provided name" do
+                (!!Puppet::Parser::ResourceType.new(:node, /\d/).match("foo")).should be_false
+            end
+
+            it "should return true when its name, as a string, is matched against an equal string" do
+                Puppet::Parser::ResourceType.new(:node, "foo").match("foo").should be_true
+            end
+
+            it "should return false when its name is matched against an unequal string" do
+                Puppet::Parser::ResourceType.new(:node, "foo").match("bar").should be_false
+            end
+
+            it "should match names insensitive to case" do
+                Puppet::Parser::ResourceType.new(:node, "fOo").match("foO").should be_true
+            end
+        end
+
+        it "should return the name converted to a string when the name is not a regex" do
+            pending "Need to define LoadedCode behaviour first"
+            name = Puppet::Parser::AST::HostName.new(:value => "foo")
+            Puppet::Parser::ResourceType.new(:node, name).name.should == "foo"
+        end
+
+        it "should return the name converted to a string when the name is a regex" do
+            pending "Need to define LoadedCode behaviour first"
+            name = Puppet::Parser::AST::HostName.new(:value => /regex/)
+            Puppet::Parser::ResourceType.new(:node, name).name.should == /regex/.to_s
+        end
+
+        it "should mark any created scopes as a node scope" do
+            pending "Need to define LoadedCode behaviour first"
+            name = Puppet::Parser::AST::HostName.new(:value => /regex/)
+            Puppet::Parser::ResourceType.new(:node, name).name.should == /regex/.to_s
+        end
+    end
+
+    describe "when initializing" do
+        it "should require a resource super type" do
+            Puppet::Parser::ResourceType.new(:hostclass, "foo").type.should == :hostclass
+        end
+
+        it "should fail if provided an invalid resource super type" do
+            lambda { Puppet::Parser::ResourceType.new(:nope, "foo") }.should raise_error(ArgumentError)
+        end
+
+        it "should set its name to the downcased, stringified provided name" do
+            Puppet::Parser::ResourceType.new(:hostclass, "Foo::Bar".intern).name.should == "foo::bar"
+        end
+
+        it "should set its namespace to the downcased, stringified qualified portion of the name" do
+            Puppet::Parser::ResourceType.new(:hostclass, "Foo::Bar::Baz".intern).namespace.should == "foo::bar"
+        end
+
+        %w{code line file doc}.each do |arg|
+            it "should set #{arg} if provided" do
+                type = Puppet::Parser::ResourceType.new(:hostclass, "foo", arg.to_sym => "something")
+                type.send(arg).should == "something"
+            end
+        end
+
+        it "should set any provided arguments with the keys as symbols" do
+            type = Puppet::Parser::ResourceType.new(:hostclass, "foo", :arguments => {:foo => "bar", :baz => "biz"})
+            type.should be_validattr("foo")
+            type.should be_validattr("baz")
+        end
+
+        it "should set any provided arguments with they keys as strings" do
+            type = Puppet::Parser::ResourceType.new(:hostclass, "foo", :arguments => {"foo" => "bar", "baz" => "biz"})
+            type.should be_validattr(:foo)
+            type.should be_validattr(:baz)
+        end
+
+        it "should function if provided no arguments" do
+            type = Puppet::Parser::ResourceType.new(:hostclass, "foo")
+            type.should_not be_validattr(:foo)
+        end
+    end
+
+    describe "when testing the validity of an attribute" do
+        it "should return true if the parameter was typed at initialization" do
+            Puppet::Parser::ResourceType.new(:hostclass, "foo", :arguments => {"foo" => "bar"}).should be_validattr("foo")
+        end
+
+        it "should return true if it is a metaparam" do
+            Puppet::Parser::ResourceType.new(:hostclass, "foo").should be_validattr("require")
+        end
+
+        it "should return true if the parameter is named 'name'" do
+            Puppet::Parser::ResourceType.new(:hostclass, "foo").should be_validattr("name")
+        end
+
+        it "should return false if it is not a metaparam and was not provided at initialization" do
+            Puppet::Parser::ResourceType.new(:hostclass, "foo").should_not be_validattr("yayness")
+        end
+    end
+
+    describe "when creating a subscope" do
+        before do
+            @scope = stub 'scope', :newscope => nil
+            @resource = stub 'resource'
+            @type = Puppet::Parser::ResourceType.new(:hostclass, "foo")
+        end
+
+        it "should return a new scope created with the provided scope as the parent" do
+            @scope.expects(:newscope).returns "foo"
+            @type.subscope(@scope, @resource).should == "foo"
+        end
+
+        it "should set the source as itself" do
+            @scope.expects(:newscope).with { |args| args[:source] == @type }
+            @type.subscope(@scope, @resource)
+        end
+
+        it "should set the scope's namespace to its namespace" do
+            @type.expects(:namespace).returns "yayness"
+            @scope.expects(:newscope).with { |args| args[:namespace] == "yayness" }
+            @type.subscope(@scope, @resource)
+        end
+
+        it "should set the scope's resource to the provided resource" do
+            @scope.expects(:newscope).with { |args| args[:resource] == @resource }
+            @type.subscope(@scope, @resource)
+        end
+    end
+
+    describe "when setting its parameters in the scope" do
+        before do
+            @scope = stub 'scope', :newscope => nil, :setvar => nil
+            @resource = stub 'resource', :title => "yay", :name => "yea", :ref => "Foo[bar]"
+            @type = Puppet::Parser::ResourceType.new(:hostclass, "foo")
+        end
+
+        it "should set each of the resource's parameters as variables in the scope" do
+            @type.set_arguments :foo => nil, :boo => nil
+            @resource.expects(:to_hash).returns(:foo => "bar", :boo => "baz")
+
+            @scope.expects(:setvar).with("foo", "bar")
+            @scope.expects(:setvar).with("boo", "baz")
+
+            @type.set_resource_parameters(@resource, @scope)
+        end
+
+        it "should set the variables as strings" do
+            @type.set_arguments :foo => nil
+            @resource.expects(:to_hash).returns(:foo => "bar")
+            @scope.expects(:setvar).with("foo", "bar")
+
+            @type.set_resource_parameters(@resource, @scope)
+        end
+
+        it "should fail if any of the resource's parameters are not valid attributes" do
+            @type.set_arguments :foo => nil
+            @resource.expects(:to_hash).returns(:boo => "baz")
+
+            lambda { @type.set_resource_parameters(@resource, @scope) }.should raise_error(Puppet::ParseError)
+        end
+
+        it "should evaluate and set its default values as variables for parameters not provided by the resource" do
+            @type.set_arguments :foo => stub("value", :safeevaluate => "something")
+            @resource.expects(:to_hash).returns({})
+
+            @scope.expects(:setvar).with("foo", "something")
+
+            @type.set_resource_parameters(@resource, @scope)
+        end
+
+        it "should fail if the resource does not provide a value for a required argument" do
+            @type.set_arguments :foo => nil
+            @resource.expects(:to_hash).returns({})
+
+            lambda { @type.set_resource_parameters(@resource, @scope) }.should raise_error(Puppet::ParseError)
+        end
+
+        it "should set the resource's title as a variable if not otherwise provided" do
+            @resource.expects(:to_hash).returns({})
+
+            @resource.expects(:title).returns 'teetle'
+            @scope.expects(:setvar).with("title", "teetle")
+
+            @type.set_resource_parameters(@resource, @scope)
+        end
+
+        it "should set the resource's name as a variable if not otherwise provided" do
+            @resource.expects(:to_hash).returns({})
+
+            @resource.expects(:name).returns 'nombre'
+            @scope.expects(:setvar).with("name", "nombre")
+
+            @type.set_resource_parameters(@resource, @scope)
+        end
+    end
+
+    describe "when describing and managing parent classes" do
+        before do
+            @code = Puppet::Parser::LoadedCode.new
+            @parent = Puppet::Parser::ResourceType.new(:hostclass, "bar")
+            @code.add @parent
+
+            @child = Puppet::Parser::ResourceType.new(:hostclass, "foo", :parent => "bar")
+            @code.add @child
+        end
+
+        it "should be able to define a parent" do
+            Puppet::Parser::ResourceType.new(:hostclass, "foo", :parent => "bar")
+        end
+
+        it "should use the code collection to find the parent resource type" do
+            @child.parent_type.should equal(@parent)
+        end
+
+        it "should be able to find parent nodes" do
+            parent = Puppet::Parser::ResourceType.new(:node, "bar")
+            @code.add parent
+            child = Puppet::Parser::ResourceType.new(:node, "foo", :parent => "bar")
+            @code.add child
+
+            child.parent_type.should equal(parent)
+        end
+
+        it "should cache a reference to the parent type" do
+            @code.expects(:hostclass).once.with("bar").returns @parent
+            @child.parent_type
+            @child.parent_type
+        end
+
+        it "should correctly state when it is another type's child" do
+            @child.should be_child_of(@parent)
+        end
+
+        it "should be considered the child of a parent's parent" do
+            @grandchild = Puppet::Parser::ResourceType.new(:hostclass, "baz", :parent => "foo")
+            @code.add @grandchild
+
+            @grandchild.should be_child_of(@parent)
+        end
+
+        it "should correctly state when it is not another type's child" do
+            @notchild = Puppet::Parser::ResourceType.new(:hostclass, "baz")
+            @code.add @notchild
+
+            @notchild.should_not be_child_of(@parent)
+        end
+    end
+
+    describe "when evaluating its code" do
+        before do
+            @scope = stub 'scope', :newscope => nil, :setvar => nil
+            @resource = stub 'resource', :title => "yay", :name => "yea", :ref => "Foo[bar]", :scope => @scope
+            @type = Puppet::Parser::ResourceType.new(:hostclass, "foo")
+            @type.stubs(:set_resource_parameters)
+        end
+
+        it "should set all of its parameters in a subscope" do
+            subscope = stub 'subscope'
+            @type.expects(:subscope).with(@scope, @resource).returns subscope
+            @type.expects(:set_resource_parameters).with(@resource, subscope)
+
+            @type.evaluate_code(@resource)
+        end
+
+        it "should evaluate the code if any is provided" do
+            code = stub 'code'
+            @type.expects(:code).returns code
+            @type.stubs(:subscope).returns stub("subscope")
+            code.expects(:safeevaluate).with @type.subscope
+
+            @type.evaluate_code(@resource)
+        end
+
+        it "should noop if there is no code" do
+            @type.expects(:code).returns nil
+            @type.stubs(:subscope).returns stub("subscope")
+
+            @type.evaluate_code(@resource)
+        end
+    end
+
+    describe "when creating a resource" do
+        before do
+            @catalog = Puppet::Resource::Catalog.new
+            @node = stub 'node', :name => "foo", :classes => []
+            @compiler = Puppet::Parser::Compiler.new(@node, @catalog)
+            @scope = Puppet::Parser::Scope.new
+            @scope.stubs(:compiler).returns @compiler
+
+            @top = Puppet::Parser::ResourceType.new :hostclass, "top"
+            @middle = Puppet::Parser::ResourceType.new :hostclass, "middle", :parent => "top"
+
+            @code = Puppet::Parser::LoadedCode.new
+            @code.add @top
+            @code.add @middle
+        end
+
+        it "should create a resource instance" do
+            @top.mk_plain_resource(@scope).should be_instance_of(Puppet::Parser::Resource)
+        end
+
+        it "should set its resource type to 'class' when it is a hostclass" do
+            Puppet::Parser::ResourceType.new(:hostclass, "top").mk_plain_resource(@scope).type.should == "Class"
+        end
+
+        it "should set its resource type to 'node' when it is a node" do
+            Puppet::Parser::ResourceType.new(:node, "top").mk_plain_resource(@scope).type.should == "Node"
+        end
+
+        it "should fail when it is a definition" do
+            lambda { Puppet::Parser::ResourceType.new(:definition, "top").mk_plain_resource(@scope) }.should raise_error(ArgumentError)
+        end
+
+        it "should add the created resource to the scope's catalog" do
+            @top.mk_plain_resource(@scope)
+
+            @compiler.catalog.resource(:class, "top").should be_instance_of(Puppet::Parser::Resource)
+        end
+
+        it "should evaluate the parent class if one exists" do
+            @middle.mk_plain_resource(@scope)
+
+            @compiler.catalog.resource(:class, "top").should be_instance_of(Puppet::Parser::Resource)
+        end
+
+        it "should fail to evaluate if a parent class is defined but cannot be found" do
+            othertop = Puppet::Parser::ResourceType.new :hostclass, "something", :parent => "yay"
+            @code.add othertop
+            lambda { othertop.mk_plain_resource(@scope) }.should raise_error(Puppet::ParseError)
+        end
+
+        it "should not create a new resource if one already exists" do
+            @compiler.catalog.expects(:resource).with(:class, "top").returns("something")
+            @compiler.catalog.expects(:add_resource).never
+            @top.mk_plain_resource(@scope)
+        end
+
+        it "should return the existing resource when not creating a new one" do
+            @compiler.catalog.expects(:resource).with(:class, "top").returns("something")
+            @compiler.catalog.expects(:add_resource).never
+            @top.mk_plain_resource(@scope).should == "something"
+        end
+
+        it "should not create a new parent resource if one already exists and it has a parent class" do
+            @top.mk_plain_resource(@scope)
+
+            top_resource = @compiler.catalog.resource(:class, "top")
+
+            @middle.mk_plain_resource(@scope)
+
+            @compiler.catalog.resource(:class, "top").should equal(top_resource)
+        end
+
+        # #795 - tag before evaluation.
+        it "should tag the catalog with the resource tags when it is evaluated" do
+            @middle.mk_plain_resource(@scope)
+
+            @compiler.catalog.should be_tagged("middle")
+        end
+
+        it "should tag the catalog with the parent class tags when it is evaluated" do
+            @middle.mk_plain_resource(@scope)
+
+            @compiler.catalog.should be_tagged("top")
+        end
+    end
+
+    describe "when merging code from another instance" do
+        def code(str)
+            Puppet::Parser::AST::Leaf.new :value => str
+        end
+
+        it "should fail unless it is a class" do
+            lambda { Puppet::Parser::ResourceType.new(:node, "bar").merge("foo") }.should raise_error(ArgumentError)
+        end
+
+        it "should fail unless the source instance is a class" do
+            dest = Puppet::Parser::ResourceType.new(:hostclass, "bar")
+            source = Puppet::Parser::ResourceType.new(:node, "foo")
+            lambda { dest.merge(source) }.should raise_error(ArgumentError)
+        end
+
+        it "should fail if both classes have different parent classes" do
+            code = Puppet::Parser::LoadedCode.new
+            {"a" => "b", "c" => "d"}.each do |parent, child|
+                code.add Puppet::Parser::ResourceType.new(:hostclass, parent)
+                code.add Puppet::Parser::ResourceType.new(:hostclass, child, :parent => parent)
+            end
+            lambda { code.hostclass("b").merge(code.hostclass("d")) }.should raise_error(ArgumentError)
+        end
+
+        it "should copy the other class's parent if it has not parent" do
+            dest = Puppet::Parser::ResourceType.new(:hostclass, "bar")
+
+            parent = Puppet::Parser::ResourceType.new(:hostclass, "parent")
+            source = Puppet::Parser::ResourceType.new(:hostclass, "foo", :parent => "parent")
+            dest.merge(source)
+
+            dest.parent.should == "parent"
+        end
+
+        it "should copy the other class's documentation as its docs if it has no docs" do
+            dest = Puppet::Parser::ResourceType.new(:hostclass, "bar")
+            source = Puppet::Parser::ResourceType.new(:hostclass, "foo", :doc => "yayness")
+            dest.merge(source)
+
+            dest.doc.should == "yayness"
+        end
+
+        it "should append the other class's docs to its docs if it has any" do
+            dest = Puppet::Parser::ResourceType.new(:hostclass, "bar", :doc => "fooness")
+            source = Puppet::Parser::ResourceType.new(:hostclass, "foo", :doc => "yayness")
+            dest.merge(source)
+
+            dest.doc.should == "foonessyayness"
+        end
+
+        it "should turn its code into an ASTArray if necessary" do
+            dest = Puppet::Parser::ResourceType.new(:hostclass, "bar", :code => code("foo"))
+            source = Puppet::Parser::ResourceType.new(:hostclass, "foo", :code => code("bar"))
+
+            dest.merge(source)
+
+            dest.code.should be_instance_of(Puppet::Parser::AST::ASTArray)
+        end
+
+        it "should set the other class's code as its code if it has none" do
+            dest = Puppet::Parser::ResourceType.new(:hostclass, "bar")
+            source = Puppet::Parser::ResourceType.new(:hostclass, "foo", :code => code("bar"))
+
+            dest.merge(source)
+
+            dest.code.value.should == "bar"
+        end
+
+        it "should append the other class's code to its code if it has any" do
+            dcode = Puppet::Parser::AST::ASTArray.new :children => [code("dest")]
+            dest = Puppet::Parser::ResourceType.new(:hostclass, "bar", :code => dcode)
+
+            scode = Puppet::Parser::AST::ASTArray.new :children => [code("source")]
+            source = Puppet::Parser::ResourceType.new(:hostclass, "foo", :code => scode)
+
+            dest.merge(source)
+
+            dest.code.children.collect { |l| l.value }.should == %w{dest source}
+        end
+    end
+end
diff --git a/spec/unit/util/rdoc/parser.rb b/spec/unit/util/rdoc/parser.rb
index 593e6ac..19c91bb 100755
--- a/spec/unit/util/rdoc/parser.rb
+++ b/spec/unit/util/rdoc/parser.rb
@@ -137,9 +137,9 @@ describe RDoc::Parser do
 
     describe "when parsing AST elements" do
         before :each do
-            @klass = stub_everything 'klass', :file => "module/manifests/init.pp", :classname => "myclass"
-            @definition = stub_everything 'definition', :file => "module/manifests/init.pp"
-            @node = stub_everything 'node', :file => "module/manifests/init.pp"
+            @klass = stub_everything 'klass', :file => "module/manifests/init.pp", :name => "myclass", :type => :hostclass
+            @definition = stub_everything 'definition', :file => "module/manifests/init.pp", :type => :definition, :name => "mydef"
+            @node = stub_everything 'node', :file => "module/manifests/init.pp", :type => :node, :name => "mynode"
 
             @loadedcode = Puppet::Parser::LoadedCode.new
             @parser.ast = @loadedcode
@@ -148,7 +148,7 @@ describe RDoc::Parser do
         end
 
         it "should document classes in the parsed file" do
-            @loadedcode.add_hostclass("myclass", @klass)
+            @loadedcode.add_hostclass(@klass)
 
             @parser.expects(:document_class).with("myclass", @klass, @container)
 
@@ -157,7 +157,7 @@ describe RDoc::Parser do
 
         it "should not document class parsed in an other file" do
             @klass.stubs(:file).returns("/not/same/path/file.pp")
-            @loadedcode.add_hostclass("myclass", @klass)
+            @loadedcode.add_hostclass(@klass)
 
             @parser.expects(:document_class).with("myclass", @klass, @container).never
 
@@ -165,10 +165,11 @@ describe RDoc::Parser do
         end
 
         it "should document vardefs for the main class" do
-            @loadedcode.add_hostclass(:main, @klass)
+            @klass.stubs(:name).returns :main
+            @loadedcode.add_hostclass(@klass)
 
             code = stub 'code', :is_a? => false
-            @klass.stubs(:classname).returns("")
+            @klass.stubs(:name).returns("")
             @klass.stubs(:code).returns(code)
 
             @parser.expects(:scan_for_vardef).with(@container, code)
@@ -177,7 +178,7 @@ describe RDoc::Parser do
         end
 
         it "should document definitions in the parsed file" do
-            @loadedcode.add_definition("mydef", @definition)
+            @loadedcode.add_definition(@definition)
 
             @parser.expects(:document_define).with("mydef", @definition, @container)
 
@@ -186,7 +187,7 @@ describe RDoc::Parser do
 
         it "should not document definitions parsed in an other file" do
             @definition.stubs(:file).returns("/not/same/path/file.pp")
-            @loadedcode.add_definition("mydef", @definition)
+            @loadedcode.add_definition(@definition)
 
             @parser.expects(:document_define).with("mydef", @definition, @container).never
 
@@ -194,7 +195,7 @@ describe RDoc::Parser do
         end
 
         it "should document nodes in the parsed file" do
-            @loadedcode.add_node("mynode", @node)
+            @loadedcode.add_node(@node)
 
             @parser.expects(:document_node).with("mynode", @node, @container)
 
@@ -203,7 +204,7 @@ describe RDoc::Parser do
 
         it "should not document node parsed in an other file" do
             @node.stubs(:file).returns("/not/same/path/file.pp")
-            @loadedcode.add_node("mynode", @node)
+            @loadedcode.add_node(@node)
 
             @parser.expects(:document_node).with("mynode", @node, @container).never
 

-- 
Puppet packaging for Debian



More information about the Pkg-puppet-devel mailing list