require 'puppet'
require 'yaml'
module Puppet
# The transportable objects themselves. Basically just a hash with some
# metadata and a few extra methods. I used to have the object actually
# be a subclass of Hash, but I could never correctly dump them using
# YAML.
class TransObject
include Enumerable
attr_accessor :type, :name, :file, :line, :collectable, :collected
attr_writer :tags
%w{has_key? include? length delete empty? << [] []=}.each { |method|
define_method(method) do |*args|
@params.send(method, *args)
end
}
def each
@params.each { |p,v| yield p, v }
end
def initialize(name,type)
@type = type
@name = name
@collectable = false
@params = {}
@tags = []
end
def longname
return [@type,@name].join('--')
end
def tags
return @tags
end
def to_hash
@params.dup
end
def to_s
return "%s(%s) => %s" % [@type,@name,super]
end
def to_manifest
"#{self.type.to_s} { \'#{self.name}\':\n%s\n}" % @params.collect { |p, v|
if v.is_a? Array
" #{p} => [\'#{v.join("','")}\']"
else
" #{p} => \'#{v}\'"
end
}.join(",\n")
end
def to_yaml_properties
instance_variables
end
def to_type(parent = nil)
retobj = nil
if typeklass = Puppet::Type.type(self.type)
# FIXME This should really be done differently, but...
if retobj = typeklass[self.name]
self.each do |param, val|
retobj[param] = val
end
else
unless retobj = typeklass.create(self)
return nil
end
end
else
raise Puppet::Error.new("Could not find object type %s" % self.type)
end
if parent
parent.push retobj
end
return retobj
end
end
# Just a linear container for objects. Behaves mostly like an array, except
# that YAML will correctly dump them even with their instance variables.
class TransBucket
include Enumerable
attr_accessor :name, :type, :file, :line, :classes, :keyword, :top
%w{delete shift include? length empty? << []}.each { |method|
define_method(method) do |*args|
#Puppet.warning "Calling %s with %s" % [method, args.inspect]
@children.send(method, *args)
#Puppet.warning @params.inspect
end
}
# Remove all collectable objects from our tree, since the client
# should not see them.
def collectstrip!
@children.dup.each do |child|
if child.is_a? self.class
child.collectstrip!
else
if child.collectable and ! child.collected
@children.delete(child)
end
end
end
end
# Recursively yield everything.
def delve(&block)
@children.each do |obj|
block.call(obj)
if obj.is_a? self.class
obj.delve(&block)
else
obj
end
end
end
def each
@children.each { |c| yield c }
end
# Turn our heirarchy into a flat list
def flatten
@children.collect do |obj|
if obj.is_a? Puppet::TransBucket
obj.flatten
else
obj
end
end.flatten
end
def initialize(children = [])
@children = children
end
def push(*args)
args.each { |arg|
case arg
when Puppet::TransBucket, Puppet::TransObject
# nada
else
raise Puppet::DevError,
"TransBuckets cannot handle objects of type %s" %
arg.class
end
}
@children += args
end
# Convert to a parseable manifest
def to_manifest
unless self.top
unless defined? @keyword and @keyword
raise Puppet::DevError, "No keyword; cannot convert to manifest"
end
end
str = nil
if self.top
str = "%s"
else
str = "#{@keyword} #{@type} {\n%s\n}"
end
str % @children.collect { |child|
child.to_manifest
}.collect { |str|
if self.top
str
else
str.gsub(/^/, " ") # indent everything once
end
}.join("\n\n") # and throw in a blank line
end
def to_yaml_properties
instance_variables
end
def to_type(parent = nil)
# this container will contain the equivalent of all objects at
# this level
#container = Puppet::Component.new(:name => @name, :type => @type)
#unless defined? @name
# raise Puppet::DevError, "TransBuckets must have names"
#end
unless defined? @type
Puppet.debug "TransBucket '%s' has no type" % @name
end
usetrans = true
if usetrans
tmpname = nil
# Nodes have the same name and type
if self.name
tmpname = "%s[%s]" % [@type, self.name]
else
tmpname = @type
end
trans = TransObject.new(tmpname, :component)
if defined? @parameters
@parameters.each { |param,value|
Puppet.debug "Defining %s on %s of type %s" %
[param,@name,@type]
trans[param] = value
}
else
#Puppet.debug "%s[%s] has no parameters" % [@type, @name]
end
container = Puppet::Type::Component.create(trans)
else
hash = {
:name => self.name,
:type => @type
}
if defined? @parameters
@parameters.each { |param,value|
Puppet.debug "Defining %s on %s of type %s" %
[param,@name,@type]
hash[param] = value
}
else
#Puppet.debug "%s[%s] has no parameters" % [@type, @name]
end
#if parent
# hash[:parent] = parent
#end
container = Puppet::Type::Component.create(hash)
end
#Puppet.info container.inspect
if parent
parent.push container
end
# unless we successfully created the container, return an error
unless container
Puppet.warning "Got no container back"
return nil
end
self.each { |child|
# the fact that we descend here means that we are
# always going to execute depth-first
# which is _probably_ a good thing, but one never knows...
unless child.is_a?(Puppet::TransBucket) or
child.is_a?(Puppet::TransObject)
raise Puppet::DevError,
"TransBucket#to_type cannot handle objects of type %s" %
child.class
end
# Now just call to_type on them with the container as a parent
begin
child.to_type(container)
rescue => detail
if Puppet[:trace] and ! detail.is_a?(Puppet::Error)
puts detail.backtrace
end
Puppet.err detail.to_s
end
}
# at this point, no objects at are level are still Transportable
# objects
return container
end
def param(param,value)
unless defined? @parameters
@parameters = {}
end
@parameters[param] = value
end
end
#------------------------------------------------------------
end
# $Id: transportable.rb 2068 2007-01-10 03:25:25Z lutter $
syntax highlighted by Code2HTML, v. 0.9.1