This code is impressively difficult, because
sometimes resource types act like resources (classes
and nodes are singletons) and sometimes like resource
types (defined and builtin resources).

So, to get nodes to show as Node[foo] and classes as
Class[Foo::Bar], but defined resources to show up as
Foo::Bar[baz], we have to do some silliness.

Signed-off-by: Luke Kanies <[email protected]>
---
 lib/puppet/resource.rb                  |  156 +++++++++++++++++-----
 spec/unit/resource.rb                   |  214 +++++++++++++++++++------------
 test/language/ast/resource_reference.rb |   77 -----------
 3 files changed, 252 insertions(+), 195 deletions(-)
 delete mode 100755 test/language/ast/resource_reference.rb

diff --git a/lib/puppet/resource.rb b/lib/puppet/resource.rb
index 91a2610..fa6fea1 100644
--- a/lib/puppet/resource.rb
+++ b/lib/puppet/resource.rb
@@ -13,8 +13,7 @@ class Puppet::Resource
     extend Puppet::Util::Pson
     include Enumerable
     attr_accessor :file, :line, :catalog, :exported, :virtual, 
:validate_parameters, :strict
-    attr_reader :title, :namespaces
-    attr_writer :relative_type
+    attr_reader :namespaces
 
     ATTRIBUTES = [:file, :line, :exported]
 
@@ -158,6 +157,8 @@ class Puppet::Resource
             extract_parameters(params)
         end
 
+        resolve_type_and_title()
+
         tag(self.type)
         tag(self.title) if valid_tag?(self.title)
 
@@ -177,23 +178,38 @@ class Puppet::Resource
     end
 
     def title=(value)
-        if klass = resource_type and klass.respond_to?(:canonicalize_ref)
-            value = klass.canonicalize_ref(value)
-        end
-        @title = value
+        @unresolved_title = value
+        @title = nil
     end
 
-    # Canonize the type so we know it's always consistent.
-    def relative_type
-        munge_type_name(@relative_type)
+    def old_title
+        if type == "Class" and value == ""
+            @title = :main
+            return
+        end
+
+        if klass = resource_type
+            p klass
+            if type == "Class"
+                value = munge_type_name(resource_type.name)
+            end
+
+            if klass.respond_to?(:canonicalize_ref)
+                value = klass.canonicalize_ref(value)
+            end
+        elsif type == "Class"
+            value = munge_type_name(value)
+        end
+
+        @title = value
     end
 
     def resource_type
-        case relative_type.to_s.downcase
-        when "class"; find_hostclass
-        when "node"; find_node
+        case type
+        when "Class"; find_hostclass(title)
+        when "Node"; find_node(title)
         else
-            find_builtin_resource_type || find_defined_resource_type
+            find_resource_type(type)
         end
     end
 
@@ -287,18 +303,26 @@ class Puppet::Resource
         return result
     end
 
-    def type
-        munge_type_name(if r = resource_type
-            resource_type.name
-        else
-            relative_type
-        end)
+    # We have to lazy-evaluate this.
+    def title=(value)
+        @title = nil
+        @unresolved_title = value
     end
 
-    # Only allow people to set the relative type,
-    # so we force it to be looked up each time.
+    # We have to lazy-evaluate this.
     def type=(value)
-        @relative_type = value
+        @type = nil
+        @unresolved_type = value || "Class"
+    end
+
+    def title
+        resolve_type_and_title unless @title
+        @title
+    end
+
+    def type
+        resolve_type_and_title unless @type
+        @type
     end
 
     def valid_parameter?(name)
@@ -311,21 +335,25 @@ class Puppet::Resource
 
     private
 
-    def find_node
-        known_resource_types.node(title)
+    def find_node(name)
+        known_resource_types.node(name)
     end
 
-    def find_hostclass
+    def find_hostclass(title)
         name = title == :main ? "" : title
         known_resource_types.find_hostclass(namespaces, name)
     end
 
-    def find_builtin_resource_type
-        Puppet::Type.type(relative_type.to_s.downcase.to_sym)
+    def find_resource_type(type)
+        find_builtin_resource_type(type) || find_defined_resource_type(type)
+    end
+
+    def find_builtin_resource_type(type)
+        Puppet::Type.type(type.to_s.downcase.to_sym)
     end
 
-    def find_defined_resource_type
-        known_resource_types.find_definition(namespaces, 
relative_type.to_s.downcase)
+    def find_defined_resource_type(type)
+        known_resource_types.find_definition(namespaces, type.to_s.downcase)
     end
 
     # Produce a canonical method name.
@@ -362,8 +390,6 @@ class Puppet::Resource
         return bucket
     end
 
-    private
-
     def extract_parameters(params)
         params.each do |param, value|
             validate_parameter(param) if strict?
@@ -380,12 +406,72 @@ class Puppet::Resource
     end
 
     def munge_type_name(value)
-        return :main if value == ""
+        return :main if value == :main
+        return "Class" if value == "" or value.nil? or value.to_s.downcase == 
"component"
+
+        value.to_s.split("::").collect { |s| s.capitalize }.join("::")
+    end
+
+    # This is an annoyingly complicated method for resolving qualified
+    # types as necessary, and putting them in type or title attributes.
+    def resolve_type_and_title
+        if @unresolved_type
+            @type = resolve_type
+            @unresolved_type = nil
+        end
+        if @unresolved_title
+            @title = resolve_title
+            @unresolved_title = nil
+        end
+    end
+
+    def resolve_type
+        type = munge_type_name(@unresolved_type)
+
+        case type
+        when "Class", "Node";
+            return type
+        else
+            # Otherwise, some kind of builtin or defined resource type
+            return munge_type_name(if r = find_resource_type(type)
+                r.name
+            else
+                type
+            end)
+        end
+    end
+
+    # This method only works if resolve_type was called first
+    def resolve_title
+        case @type
+        when "Node"; return @unresolved_title
+        when "Class";
+            resolve_title_for_class(@unresolved_title)
+        else
+            resolve_title_for_resource(@unresolved_title)
+        end
+    end
+
+    def resolve_title_for_class(title)
+        if title == "" or title == :main
+            return :main
+        end
+
+        if klass = find_hostclass(title)
+            result = klass.name
+
+            if klass.respond_to?(:canonicalize_ref)
+                result = klass.canonicalize_ref(result)
+            end
+        end
+        return munge_type_name(result || title)
+    end
 
-        if value.nil? or value.to_s.downcase == "component"
-            "Class"
+    def resolve_title_for_resource(title)
+        if type = find_resource_type(@type) and 
type.respond_to?(:canonicalize_ref)
+            return type.canonicalize_ref(title)
         else
-            value.to_s.split("::").collect { |s| s.capitalize }.join("::")
+            return title
         end
     end
 end
diff --git a/spec/unit/resource.rb b/spec/unit/resource.rb
index a4952c0..f516c59 100755
--- a/spec/unit/resource.rb
+++ b/spec/unit/resource.rb
@@ -48,7 +48,7 @@ describe Puppet::Resource do
     it "should set its type to 'Class' and its title to the passed title if 
the passed type is :component and the title has no square brackets in it" do
         ref = Puppet::Resource.new(:component, "foo")
         ref.type.should == "Class"
-        ref.title.should == "foo"
+        ref.title.should == "Foo"
     end
 
     it "should interpret the title as a reference and assign appropriately if 
the type is :component and the title contains square brackets" do
@@ -60,7 +60,7 @@ describe Puppet::Resource do
     it "should set the type to 'Class' if it is nil and the title contains no 
square brackets" do
         ref = Puppet::Resource.new(nil, "yay")
         ref.type.should == "Class"
-        ref.title.should == "yay"
+        ref.title.should == "Yay"
     end
 
     it "should interpret the title as a reference and assign appropriately if 
the type is nil and the title contains square brackets" do
@@ -113,108 +113,159 @@ describe Puppet::Resource do
     end
 
     it "should support specifying namespaces" do
-        Puppet::Resource.new("file", "/my/file", :namespaces => 
[:foo]).namespaces.should == [:foo]
+        Puppet::Resource.new("file", "/my/file", :namespaces => 
["foo"]).namespaces.should == ["foo"]
     end
 
     it "should convert namespaces to an array if not specified as one" do
-        Puppet::Resource.new("file", "/my/file", :namespaces => 
:foo).namespaces.should == [:foo]
+        Puppet::Resource.new("file", "/my/file", :namespaces => 
"foo").namespaces.should == ["foo"]
     end
 
     it "should default to a single amespace of an empty string" do
         Puppet::Resource.new("file", "/my/file").namespaces.should == [""]
     end
 
-    it "should be able to look up its resource type when the type is a builtin 
resource" do
-        Puppet::Resource.new("file", "/my/file").resource_type.should 
equal(Puppet::Type.type(:file))
-    end
+    describe "and munging its type and title" do
+        describe "when modeling a builtin resource" do
+            it "should be able to find the resource type" do
+                Puppet::Resource.new("file", "/my/file").resource_type.should 
equal(Puppet::Type.type(:file))
+            end
 
-    it "should be able to look up its resource type via its environment when 
the type is a defined resource type" do
-        resource = Puppet::Resource.new("foobar", "/my/file")
-        type = Puppet::Resource::Type.new(:definition, "foobar")
-        resource.environment.known_resource_types.add type
+            it "should set its type to the capitalized type name" do
+                Puppet::Resource.new("file", "/my/file").type.should == "File"
+            end
+        end
 
-        resource.resource_type.should equal(type)
-    end
+        describe "when modeling a defined resource" do
+            describe "that exists" do
+                before do
+                    @type = Puppet::Resource::Type.new(:definition, "foo::bar")
+                    Puppet::Node::Environment.new.known_resource_types.add 
@type
+                end
 
-    it "should be able to look up its resource type via its environment when 
the type is a node" do
-        resource = Puppet::Resource.new("node", "foobar")
-        node = Puppet::Resource::Type.new(:node, "foobar")
-        resource.environment.known_resource_types.add node
+                it "should set its type to the capitalized type name" do
+                    Puppet::Resource.new("foo::bar", "/my/file").type.should 
== "Foo::Bar"
+                end
 
-        resource.resource_type.should equal(node)
-    end
+                it "should be able to find the resource type" do
+                    Puppet::Resource.new("foo::bar", 
"/my/file").resource_type.should equal(@type)
+                end
 
-    it "should be able to look up its resource type via its environment when 
the type is a class" do
-        resource = Puppet::Resource.new("class", "foobar")
-        klass = Puppet::Resource::Type.new(:hostclass, "foobar")
-        resource.environment.known_resource_types.add klass
+                it "should set its title to the provided title" do
+                    Puppet::Resource.new("foo::bar", "/my/file").title.should 
== "/my/file"
+                end
 
-        resource.resource_type.should equal(klass)
-    end
+                describe "and the resource is unqualified and models a 
qualified resource type" do
+                    it "should set its type to the fully qualified resource 
type" do
+                        Puppet::Resource.new("bar", "/my/file", :namespaces => 
%w{foo}).type.should == "Foo::Bar"
+                    end
 
-    it "should use its namespaces when looking up defined resource types" do
-        resource = Puppet::Resource.new("bar", "/my/file", :namespaces => 
["foo"])
-        type = Puppet::Resource::Type.new(:definition, "foo::bar")
-        resource.environment.known_resource_types.add type
+                    it "should be able to find the resource type" do
+                        Puppet::Resource.new("bar", "/my/file", :namespaces => 
%w{foo}).resource_type.should equal(@type)
+                    end
+                end
+            end
 
-        resource.resource_type.should equal(type)
-    end
+            describe "that does not exist" do
+                it "should set its resource type to the capitalized resource 
type name" do
+                    Puppet::Resource.new("foo::bar", "/my/file").type.should 
== "Foo::Bar"
+                end
+            end
+        end
 
-    it "should use its namespaces to set its type name when looking up defined 
resource types" do
-        type = Puppet::Resource::Type.new(:definition, "foo::bar")
-        Puppet::Node::Environment.new.known_resource_types.add type
-        resource = Puppet::Resource.new("bar", "/my/file", :namespaces => 
["foo"])
-        resource.type.should == "Foo::Bar"
-    end
+        describe "when modeling a node" do
+            # Life's easier with nodes, because they can't be qualified.
+            it "should set its type to 'Node' and its title to the provided 
title" do
+                node = Puppet::Resource.new("node", "foo")
+                node.type.should == "Node"
+                node.title.should == "foo"
+            end
+        end
 
-    it "should look up its resource type when set manually" do
-        type = Puppet::Resource::Type.new(:definition, "foo::bar")
-        Puppet::Node::Environment.new.known_resource_types.add type
-        resource = Puppet::Resource.new("foo", "/my/file", :namespaces => 
["foo"])
-        resource.type = "bar"
-        resource.type.should == "Foo::Bar"
-    end
+        describe "when modeling a class" do
+            it "should set its type to 'Class'" do
+                Puppet::Resource.new("class", "foo").type.should == "Class"
+            end
 
-    it "should use its namespaces when looking up host classes" do
-        resource = Puppet::Resource.new("class", "bar", :namespaces => ["foo"])
-        type = Puppet::Resource::Type.new(:hostclass, "foo::bar")
-        resource.environment.known_resource_types.add type
+            describe "that exists" do
+                before do
+                    @type = Puppet::Resource::Type.new(:hostclass, "foo::bar")
+                    Puppet::Node::Environment.new.known_resource_types.add 
@type
+                end
+
+                it "should set its title to the capitalized, fully qualified 
resource type" do
+                    Puppet::Resource.new("class", "foo::bar").title.should == 
"Foo::Bar"
+                end
+
+                it "should be able to find the resource type" do
+                    Puppet::Resource.new("class", 
"foo::bar").resource_type.should equal(@type)
+                end
+
+                describe "and the resource is unqualified and models a 
qualified class" do
+                    it "should set its title to the fully qualified resource 
type" do
+                        Puppet::Resource.new("class", "bar", :namespaces => 
%w{foo}).title.should == "Foo::Bar"
+                    end
+
+                    it "should be able to find the resource type" do
+                        Puppet::Resource.new("class", "bar", :namespaces => 
%w{foo}).resource_type.should equal(@type)
+                    end
+
+                    it "should set its type to 'Class'" do
+                        Puppet::Resource.new("class", "bar", :namespaces => 
%w{foo}).type.should == "Class"
+                    end
+                end
+            end
 
-        resource.resource_type.should equal(type)
-    end
+            describe "that does not exist" do
+                it "should set its type to 'Class' and its title to the 
capitalized provided name" do
+                    klass = Puppet::Resource.new("class", "foo::bar")
+                    klass.type.should == "Class"
+                    klass.title.should == "Foo::Bar"
+                end
+            end
 
-    it "should consider a class whose name is an empty string to be the main 
class" do
-        type = Puppet::Resource::Type.new(:hostclass, "")
-        Puppet::Node::Environment.new.known_resource_types.add type
+            describe "and its name is set to the empty string" do
+                it "should set its title to :main" do
+                    Puppet::Resource.new("class", "").title.should == :main
+                end
+
+                describe "and a class exists whose name is the empty string" 
do # this was a bit tough to track down
+                    it "should set its title to :main" do
+                        @type = Puppet::Resource::Type.new(:hostclass, "")
+                        Puppet::Node::Environment.new.known_resource_types.add 
@type
+
+                        Puppet::Resource.new("class", "").title.should == :main
+                    end
+                end
+            end
 
-        resource = Puppet::Resource.new("class", "").type.should == :main
+            describe "and its name is set to :main" do
+                it "should set its title to :main" do
+                    Puppet::Resource.new("class", :main).title.should == :main
+                end
+
+                describe "and a class exists whose name is the empty string" 
do # this was a bit tough to track down
+                    it "should set its title to :main" do
+                        @type = Puppet::Resource::Type.new(:hostclass, "")
+                        Puppet::Node::Environment.new.known_resource_types.add 
@type
+
+                        Puppet::Resource.new("class", :main).title.should == 
:main
+                    end
+                end
+            end
+        end
     end
 
     it "should return nil when looking up resource types that don't exist" do
         Puppet::Resource.new("foobar", "bar").resource_type.should be_nil
     end
 
-    it "should fail when an invalid parameter is used and parameter validation 
is enabled" do
-        type = Puppet::Resource::Type.new(:definition, "foobar")
-        Puppet::Node::Environment.new.known_resource_types.add type
-        resource = Puppet::Resource.new("foobar", "/my/file", 
:validate_parameters => true)
-        lambda { resource[:yay] = true }.should raise_error(ArgumentError)
-    end
-
-    it "should not fail when an invalid parameter is used and parameter 
validation is disabled" do
+    it "should not fail when an invalid parameter is used and strict mode is 
disabled" do
         type = Puppet::Resource::Type.new(:definition, "foobar")
         Puppet::Node::Environment.new.known_resource_types.add type
         resource = Puppet::Resource.new("foobar", "/my/file")
         resource[:yay] = true
     end
 
-    it "should not fail when a valid parameter is used and parameter 
validation is enabled" do
-        type = Puppet::Resource::Type.new(:definition, "foobar", :arguments => 
{"yay" => nil})
-        Puppet::Node::Environment.new.known_resource_types.add type
-        resource = Puppet::Resource.new("foobar", "/my/file", 
:validate_parameters => true)
-        resource[:yay] = true
-    end
-
     it "should be considered equivalent to another resource if their type and 
title match and no parameters are set" do
         Puppet::Resource.new("file", "/f").should == 
Puppet::Resource.new("file", "/f")
     end
@@ -239,10 +290,7 @@ describe Puppet::Resource do
         Puppet::Resource.new("file", "/foo").should_not == 
Puppet::Resource.new("file", "/f")
     end
 
-    describe "when refering to a resource with name canonicalization" do
-        before do
-        end
-
+    describe "when referring to a resource with name canonicalization" do
         it "should canonicalize its own name" do
             res = Puppet::Resource.new("file", "/path/")
             res.title.should == "/path"
diff --git a/test/language/ast/resource_reference.rb 
b/test/language/ast/resource_reference.rb
deleted file mode 100755
index 5abb0d6..0000000
--- a/test/language/ast/resource_reference.rb
+++ /dev/null
@@ -1,77 +0,0 @@
-#!/usr/bin/env ruby
-#
-#  Created by Luke A. Kanies on 2007-07-8.
-#  Copyright (c) 2007. All rights reserved.
-
-require File.dirname(__FILE__) + '/../../lib/puppettest'
-
-require 'puppettest'
-require 'puppettest/parsertesting'
-
-class TestASTResourceReference < Test::Unit::TestCase
-    include PuppetTest
-    include PuppetTest::ParserTesting
-    AST = Puppet::Parser::AST
-
-    def newref(type, title)
-        AST::ResourceReference.new(:type => type, :title => 
AST::String.new(:value => title))
-    end
-
-    def setup
-        super
-        @scope = mkscope
-        @parser = Puppet::Parser::Parser.new(Puppet::Node::Environment.new)
-    end
-
-    # Related to #706, make sure resource references correctly translate to 
qualified types.
-    def test_scoped_references
-        @parser.newdefine "one"
-        @parser.newdefine "one::two"
-        @parser.newdefine "three"
-        twoscope = @scope.newscope(:namespace => "one")
-        assert(twoscope.find_definition("two"), "Could not find 'two' 
definition")
-        title = "title"
-
-        # First try a qualified type
-        assert_equal("One::Two", newref("two", title).evaluate(twoscope).type,
-            "Defined type was not made fully qualified")
-
-        # Then try a type that does not need to be qualified
-        assert_equal("One", newref("one", title).evaluate(twoscope).type,
-            "Unqualified defined type was not handled correctly")
-
-        # Then an unqualified type from within the one namespace
-        assert_equal("Three", newref("three", title).evaluate(twoscope).type,
-            "Defined type was not made fully qualified")
-
-        # Then a builtin type
-        assert_equal("File", newref("file", title).evaluate(twoscope).type,
-            "Builtin type was not handled correctly")
-
-        # Now try a type that does not exist, which should throw an error.
-        assert_raise(Puppet::ParseError, "Did not fail on a missing type in a 
resource reference") do
-            newref("nosuchtype", title).evaluate(twoscope)
-        end
-
-        # Now run the same tests, but with the classes
-        @parser.newclass "four"
-        @parser.newclass "one::five"
-
-        # First try an unqualified type
-        assert_equal("four", newref("class", "four").evaluate(twoscope).title,
-            "Unqualified class was not found")
-
-        # Then a qualified class
-        assert_equal("one::five", newref("class", 
"five").evaluate(twoscope).title,
-            "Class was not made fully qualified")
-
-        # Then try a type that does not need to be qualified
-        assert_equal("four", newref("class", "four").evaluate(twoscope).title,
-            "Unqualified class was not handled correctly")
-
-        # Now try a type that does not exist, which should throw an error.
-        assert_raise(Puppet::ParseError, "Did not fail on a missing type in a 
resource reference") do
-            newref("class", "nosuchclass").evaluate(twoscope)
-        end
-    end
-end
-- 
1.6.1

-- 
You received this message because you are subscribed to the Google Groups 
"Puppet Developers" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/puppet-dev?hl=en.

Reply via email to