# A module for loading subclasses into an array and retrieving # them by name. Also sets up a method for each class so # that you can just do Klass.subclass, rather than Klass.subclass(:subclass). # # This module is currently used by network handlers and clients. module Puppet::Util::SubclassLoader attr_accessor :loader, :classloader # Iterate over each of the subclasses. def each @subclasses ||= [] @subclasses.each { |c| yield c } end # The hook method that sets up subclass loading. We need the name # of the method to create and the path in which to look for them. def handle_subclasses(name, path) unless self.is_a?(Class) raise ArgumentError, "Must be a class to use SubclassLoader" end @subclasses = [] @loader = Puppet::Util::Autoload.new(self, path, :wrap => false ) @subclassname = name @classloader = self # Now create a method for retrieving these subclasses by name. Note # that we're defining a class method here, not an instance. meta_def(name) do |subname| subname = subname.to_s.downcase unless c = @subclasses.find { |c| c.name.to_s.downcase == subname } loader.load(subname) c = @subclasses.find { |c| c.name.to_s.downcase == subname } # Now make the method that returns this subclass. This way we # normally avoid the method_missing method. if c and ! respond_to?(subname) define_method(subname) { c } end end return c end end # Add a new class to our list. Note that this has to handle subclasses of # subclasses, thus the reason we're keeping track of the @@classloader. def inherited(sub) @subclasses ||= [] sub.classloader = self.classloader if self.classloader == self @subclasses << sub else @classloader.inherited(sub) end end # See if we can load a class. def method_missing(method, *args) unless self == self.classloader super end return nil unless defined? @subclassname if c = self.send(@subclassname, method) return c else return nil end end # Retrieve or calculate a name. def name unless defined? @name @name = self.to_s.sub(/.+::/, '').intern end return @name end # Provide a list of all subclasses. def subclasses @loader.loadall @subclasses.collect { |klass| klass.name } end end