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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
|
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]
objname = hash[:name]
args = 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 myscope = scope.lookupclass(self.object_id)
Puppet.debug "%s class already evaluated" % @type
# Not used, but will eventually be used to fix #140.
if myscope.is_a? Puppet::Parser::Scope
unless scope.object_id == myscope.object_id
#scope.parent = myscope
end
end
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)
origscope = scope
# Default to creating a new context
newcontext = true
# If we've got a parent, then we pass it the original scope we
# received. It will get passed all the way up to the top class,
# which will create a subscope and pass that subscope to its
# subclass.
if @parentscope = self.evalparent(
:scope => scope, :arguments => args, :name => objname
)
if @parentscope.is_a? Puppet::TransBucket
raise Puppet::DevError, "Got a bucket instead of a scope"
end
# Override our scope binding with the parent scope
# binding.
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.
result = super(
:scope => scope,
:arguments => args,
:type => @type,
:name => objname, # might be nil
:newcontext => newcontext,
:asparent => hash[:asparent] || false # might be nil
)
# Now set the class again, this time using the scope. This way
# we can look up the parent scope of this class later, so we
# can hook the children together.
scope.setscope(self.object_id, result)
# This is important but painfully difficult. If we're the top-level
# class, that is, we have no parent classes, then the transscope
# is our own scope, but if there are parent classes, then the topmost
# parent's scope is the transscope, since it contains its code and
# all of the subclass's code.
transscope ||= result
if hash[:asparent]
# If we're a parent class, then return the scope object itself.
return result
else
transscope = nil
if @parentscope
transscope = @parentscope
until transscope.parent.object_id == origscope.object_id
transscope = transscope.parent
end
else
transscope = result
end
# But if we're the final subclass, translate the whole scope tree
# into TransObjects and TransBuckets.
return transscope.to_trans
end
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. Specify that it's being evaluated as a parent,
# so that it returns the scope, not a transbucket.
return parentobj.safeevaluate(
:scope => scope,
:arguments => args,
:name => name,
:asparent => true,
:collectable => self.collectable
)
else
return false
end
end
def initialize(hash)
@parentclass = nil
super
end
end
end
|