Module: PluginFactory
- Included in:
- Mongrel::Command::Command
- Defined in:
- lib/pluginfactory.rb
Overview
A mixin that adds PluginFactory class methods to a base class, so that subclasses may be instantiated by name.
Class Attribute Summary collapse
-
.logger_callback ⇒ Object
Returns the value of attribute logger_callback.
Class Method Summary collapse
-
.extend_object(obj) ⇒ Object
Raise an exception if the object being extended is anything but a class.
-
.included(klass) ⇒ Object
Inclusion callback – extends the including class.
-
.log(level, *msg) ⇒ Object
If the logger callback is set, use it to pass on a log entry.
Instance Method Summary collapse
-
#create(subType, *args, &block) ⇒ Object
Given the
className
of the class to instantiate, and other arguments bound for the constructor of the new object, this method loads the derivative class if it is not loaded already (raising a LoadError if an appropriately-named file cannot be found), and instantiates it with the givenargs
. -
#derivativeClasses ⇒ Object
Returns an Array of registered derivatives.
-
#derivatives ⇒ Object
Return the Hash of derivative classes, keyed by various versions of the class name.
-
#factoryType ⇒ Object
Returns the type name used when searching for a derivative.
-
#getModuleName(className) ⇒ Object
Build and return the unique part of the given
className
either by stripping leading namespaces if the name already has the name of the factory type in it (eg., ‘My::FooService’ for Service, or by appending the factory type if it doesn’t.. -
#getSubclass(className) ⇒ Object
Given a
className
like that of the first argument to #create, attempt to load the corresponding class if it is not already loaded and return the class object. -
#inherited(subclass) ⇒ Object
Inheritance callback – Register subclasses in the derivatives hash so that ::create knows about them.
-
#loadDerivative(className) ⇒ Object
Calculates an appropriate filename for the derived class using the name of the base class and tries to load it via
require
. -
#makeRequirePath(modname, subdir) ⇒ Object
Make a list of permutations of the given
modname
for the givensubdir
. -
#requireDerivative(modName) ⇒ Object
If the factory responds to the #derivativeDirs method, call it and use the returned array as a list of directories to search for the module with the specified
modName
.
Class Attribute Details
.logger_callback ⇒ Object
Returns the value of attribute logger_callback.
107 108 109 |
# File 'lib/pluginfactory.rb', line 107 def logger_callback @logger_callback end |
Class Method Details
.extend_object(obj) ⇒ Object
Raise an exception if the object being extended is anything but a class.
124 125 126 127 128 129 130 |
# File 'lib/pluginfactory.rb', line 124 def self::extend_object( obj ) unless obj.is_a?( Class ) raise TypeError, "Cannot extend a #{obj.class.name}", caller(1) end obj.instance_variable_set( :@derivatives, {} ) super end |
.included(klass) ⇒ Object
Inclusion callback – extends the including class.
117 118 119 |
# File 'lib/pluginfactory.rb', line 117 def self::included( klass ) klass.extend( self ) end |
.log(level, *msg) ⇒ Object
If the logger callback is set, use it to pass on a log entry. First argument is
111 112 113 |
# File 'lib/pluginfactory.rb', line 111 def self::log(level, *msg) @logger_callback.call(level, msg.join) if @logger_callback end |
Instance Method Details
#create(subType, *args, &block) ⇒ Object
Given the className
of the class to instantiate, and other arguments bound for the constructor of the new object, this method loads the derivative class if it is not loaded already (raising a LoadError if an appropriately-named file cannot be found), and instantiates it with the given args
. The className
may be the the fully qualified name of the class, the class object itself, or the unique part of the class name. The following examples would all try to load and instantiate a class called “FooListener” if Listener included Factory
obj = Listener::create( 'FooListener' )
obj = Listener::create( FooListener )
obj = Listener::create( 'Foo' )
208 209 210 211 212 213 214 215 216 |
# File 'lib/pluginfactory.rb', line 208 def create( subType, *args, &block ) subclass = getSubclass( subType ) return subclass.new( *args, &block ) rescue => err nicetrace = err.backtrace.reject {|frame| /#{__FILE__}/ =~ frame} msg = "When creating '#{subType}': " + err. Kernel::raise( err.class, msg, nicetrace ) end |
#derivativeClasses ⇒ Object
Returns an Array of registered derivatives
191 192 193 |
# File 'lib/pluginfactory.rb', line 191 def derivativeClasses self.derivatives.values.uniq end |
#derivatives ⇒ Object
Return the Hash of derivative classes, keyed by various versions of the class name.
139 140 141 142 143 144 145 |
# File 'lib/pluginfactory.rb', line 139 def derivatives ancestors.each {|klass| if klass.instance_variables.include?( "@derivatives" ) break klass.instance_variable_get( :@derivatives ) end } end |
#factoryType ⇒ Object
Returns the type name used when searching for a derivative.
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
# File 'lib/pluginfactory.rb', line 149 def factoryType base = nil self.ancestors.each {|klass| if klass.instance_variables.include?( "@derivatives" ) base = klass break end } raise FactoryError, "Couldn't find factory base for #{self.name}" if base.nil? if base.name =~ /^.*::(.*)/ return $1 else return base.name end end |
#getModuleName(className) ⇒ Object
Build and return the unique part of the given className
either by stripping leading namespaces if the name already has the name of the factory type in it (eg., ‘My::FooService’ for Service, or by appending the factory type if it doesn’t.
288 289 290 291 292 293 294 295 296 |
# File 'lib/pluginfactory.rb', line 288 def getModuleName( className ) if className =~ /\w+#{self.factoryType}/ modName = className.sub( /(?:.*::)?(\w+)(?:#{self.factoryType})/, "\\1" ) else modName = className end return modName end |
#getSubclass(className) ⇒ Object
Given a className
like that of the first argument to #create, attempt to load the corresponding class if it is not already loaded and return the class object.
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 |
# File 'lib/pluginfactory.rb', line 222 def getSubclass( className ) return self if ( self.name == className || className == '' ) return className if className.is_a?( Class ) && className >= self unless self.derivatives.has_key?( className.downcase ) self.loadDerivative( className ) unless self.derivatives.has_key?( className.downcase ) raise FactoryError, "loadDerivative(%s) didn't add a '%s' key to the "\ "registry for %s" % [ className, className.downcase, self.name ] end subclass = self.derivatives[ className.downcase ] unless subclass.is_a?( Class ) raise FactoryError, "loadDerivative(%s) added something other than a class "\ "to the registry for %s: %p" % [ className, self.name, subclass ] end end return self.derivatives[ className.downcase ] end |
#inherited(subclass) ⇒ Object
Inheritance callback – Register subclasses in the derivatives hash so that ::create knows about them.
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
# File 'lib/pluginfactory.rb', line 171 def inherited( subclass ) keys = [ subclass.name, subclass.name.downcase, subclass ] # Handle class names like 'FooBar' for 'Bar' factories. if subclass.name.match( /(?:.*::)?(\w+)(?:#{self.factoryType})/i ) keys << Regexp.last_match[1].downcase else keys << subclass.name.sub( /.*::/, '' ).downcase end keys.uniq.each {|key| #PluginFactory::log :info, "Registering %s derivative of %s as %p" % # [ subclass.name, self.name, key ] self.derivatives[ key ] = subclass } super end |
#loadDerivative(className) ⇒ Object
Calculates an appropriate filename for the derived class using the name of the base class and tries to load it via require
. If the including class responds to a method named derivativeDirs
, its return value (either a String, or an array of Strings) is added to the list of prefix directories to try when attempting to require a modules. Eg., if class.derivativeDirs
returns ['foo','bar']
the require line is tried with both 'foo/'
and 'bar/'
prepended to it.
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 |
# File 'lib/pluginfactory.rb', line 258 def loadDerivative( className ) className = className.to_s #PluginFactory::log :debug, "Loading derivative #{className}" # Get the unique part of the derived class name and try to # load it from one of the derivative subdirs, if there are # any. modName = self.getModuleName( className ) self.requireDerivative( modName ) # Check to see if the specified listener is now loaded. If it # is not, raise an error to that effect. unless self.derivatives[ className.downcase ] raise FactoryError, "Couldn't find a %s named '%s'. Loaded derivatives are: %p" % [ self.factoryType, className.downcase, self.derivatives.keys, ], caller(3) end return true end |
#makeRequirePath(modname, subdir) ⇒ Object
Make a list of permutations of the given modname
for the given subdir
. Called on a DataDriver
class with the arguments ‘Socket’ and ‘drivers’, returns:
["drivers/socketdatadriver", "drivers/socketDataDriver",
"drivers/SocketDataDriver", "drivers/socket", "drivers/Socket"]
364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 |
# File 'lib/pluginfactory.rb', line 364 def makeRequirePath( modname, subdir ) path = [] myname = self.factoryType # Make permutations of the two parts path << modname path << modname.downcase path << modname + myname path << modname.downcase + myname path << modname.downcase + myname.downcase # If a non-empty subdir was given, prepend it to all the items in the # path unless subdir.nil? or subdir.empty? path.collect! {|m| File::join(subdir, m)} end return path.uniq.reverse end |
#requireDerivative(modName) ⇒ Object
If the factory responds to the #derivativeDirs method, call it and use the returned array as a list of directories to search for the module with the specified modName
.
302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 |
# File 'lib/pluginfactory.rb', line 302 def requireDerivative( modName ) # See if we have a list of special subdirs that derivatives # live in if ( self.respond_to?(:derivativeDirs) ) subdirs = self.derivativeDirs subdirs = [ subdirs ] unless subdirs.is_a?( Array ) # If not, just try requiring it from $LOAD_PATH else subdirs = [''] end fatals = [] # Iterate over the subdirs until we successfully require a # module. catch( :found ) { subdirs.collect {|dir| dir.strip}.each do |subdir| self.makeRequirePath( modName, subdir ).each {|path| #PluginFactory::log :debug, "Trying #{path}..." # Try to require the module, saving errors and jumping # out of the catch block on success. begin require( path.untaint ) rescue LoadError => err PluginFactory::log :debug, "No module at '%s', trying the next alternative: '%s'" % [ path, err. ] rescue ScriptError,StandardError => err fatals << err PluginFactory::log :error, "Found '#{path}', but encountered an error: %s\n\t%s" % [ err., err.backtrace.join("\n\t") ] else #PluginFactory::log :debug, # "Found '#{path}'. Throwing :found" throw :found end } end #PluginFactory::log :debug, "fatals = %p" % [ fatals ] # Re-raise is there was a file found, but it didn't load for # some reason. if ! fatals.empty? #PluginFactory::log :debug, "Re-raising first fatal error" Kernel::raise( fatals.first ) end nil } end |