summaryrefslogtreecommitdiffstats
path: root/lib/puppet/parser/ast/hostclass.rb
blob: 8290f4ef0393987d6f4b35bc8be10a86cea8efe4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
class Puppet::Parser::AST
    # The code associated with a class.  This is different from components
    # in that each class is a singleton -- only one will exist for a given
    # node.
    class HostClass < AST::Component
        @name = :class
        attr_accessor :parentclass

        #def evaluate(scope,hash,objtype,objname)
        def evaluate(hash)
            scope = hash[:scope]
            objtype = hash[:type]
            objname = hash[:name]
            hash = hash[:arguments]
            # Verify that we haven't already been evaluated
            # FIXME The second subclass won't evaluate the parent class
            # code at all, and any overrides will throw an error.
            if scope.lookupclass(self.object_id)
                Puppet.debug "%s class already evaluated" % @type
                return nil
            end

            # Set the class before we do anything else, so that it's set
            # during the evaluation and can be inspected.
            scope.setclass(self.object_id, @type)

            # Default to creating a new context
            newcontext = true
            if parentscope = self.evalparent(
                :scope => scope, :arguments => hash, :name => objname
            )
                # Override our scope binding with the parent scope
                # binding. This is quite hackish, but I can't think
                # of another way to make sure our scopes end up under
                # our parent scopes.
                scope = parentscope

                # But don't create a new context if our parent created one
                newcontext = false
            end

            # just use the Component evaluate method, but change the type
            # to our own type
            #retval = super(scope,hash,@name,objname)
            retval = super(
                :scope => scope,
                :arguments => hash,
                :type => @type,
                :name => objname,
                :newcontext => newcontext
            )
            return retval
        end

        # Evaluate our parent class.  Parent classes are evaluated in the
        # exact same scope as the children.  This is maybe not a good idea
        # but, eh.
        #def evalparent(scope, args, name)
        def evalparent(hash)
            scope = hash[:scope]
            args = hash[:arguments]
            name = hash[:name]
            if @parentclass
                #scope.warning "parent class of %s is %s" %
                #    [@type, @parentclass.inspect]
                parentobj = nil

                begin
                    parentobj = scope.lookuptype(@parentclass)
                rescue Puppet::ParseError => except
                    except.line = self.line
                    except.file = self.file
                    raise except
                rescue => detail
                    error = Puppet::ParseError.new(detail)
                    error.line = self.line
                    error.file = self.file
                    raise error
                end
                unless parentobj
                    error = Puppet::ParseError.new( 
                        "Could not find parent '%s' of '%s'" %
                            [@parentclass,@name])
                    error.line = self.line
                    error.file = self.file
                    raise error
                end

                # Verify that the parent and child are of the same type
                unless parentobj.class == self.class
                    error = Puppet::ParseError.new(
                        "Class %s has incompatible parent type, %s vs %s" %
                        [@type, parentobj.class, self.class]
                    )
                    error.file = self.file
                    error.line = self.line
                    raise error
                end
                # We don't need to pass the type, because the parent will just
                # use its own type
                return parentobj.safeevaluate(
                    :scope => scope,
                    :arguments => args,
                    :name => name
                )
            else
                return false
            end
        end

        def initialize(hash)
            @parentclass = nil
            super
        end

    end

end