Commits

Michael Granger committed 93a6045

Fix loading of grandchildren of plugins

Comments (0)

Files changed (3)

     'datadriver/apache'
     'datadriver/Apache'
 
-If the plugin is not found, a Pluggability::FactoryError is raised, and
+If the plugin is not found, a Pluggability::PluginError is raised, and
 the message will list all the permutations that were tried.
 
 
 
 == License
 
-Copyright (c) 2008-2012, Michael Granger and Martin Chase
+Copyright (c) 2008-2013, Michael Granger and Martin Chase
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without

lib/pluggability.rb

 	VERSION = '0.1.0'
 
 
-	### An exception class for Pluggability specific errors.
-	class FactoryError < RuntimeError; end
+	# An exception class for Pluggability specific errors.
+	class PluginError < RuntimeError; end
+	FactoryError = PluginError
 
 
 	### Add the @derivatives instance variable to including classes.
 	def self::extend_object( obj )
 		obj.instance_variable_set( :@plugin_prefixes, [] )
 		obj.instance_variable_set( :@derivatives, {} )
+
+		Pluggability.pluggable_classes << obj
+
 		super
 	end
 
 
+	##
+	# The Array of all classes that have added Pluggability
+	@pluggable_classes = []
+	class << self; attr_reader :pluggable_classes; end
+
+
+	### Return the ancestor of +subclass+ that has Pluggability.
+	def self::plugin_base_class( subclass )
+		return subclass.ancestors.find do |klass|
+			Pluggability.pluggable_classes.include?( klass )
+		end
+	end
+
+
 	#############################################################
 	###	M I X I N   M E T H O D S
 	#############################################################
 
+	### Return the Hash of derivative classes, keyed by various versions of
+	### the class name.
+	attr_reader :derivatives
+
+
 	### Get/set the prefixes that will be used when searching for particular plugins
 	### for the calling Class.
 	def plugin_prefixes( *args )
 	end
 
 
-	### Return the Hash of derivative classes, keyed by various versions of
-	### the class name.
-	def derivatives
-		return super unless defined?( @derivatives )
-		return @derivatives
-	end
-
-
 	### Returns the type name used when searching for a derivative.
 	def plugin_type
-		base = nil
-		self.ancestors.each do |klass|
-			if klass.instance_variables.include?( :@derivatives ) ||
-				klass.instance_variables.include?( "@derivatives" )
-				base = klass
-				break
-			end
-		end
-
-		raise FactoryError, "Couldn't find plugin base for #{self.name}" if
-			base.nil?
+		base = Pluggability.plugin_base_class( self ) or
+			raise PluginError, "Couldn't find plugin base for #{self.name}"
 
 		if base.name =~ /^.*::(.*)/
 			return $1
 	### Inheritance callback -- Register subclasses in the derivatives hash
 	### so that ::create knows about them.
 	def inherited( subclass )
-		Pluggability.logger.debug "%p inherited by %p" % [ self, subclass ]
+		plugin_class = Pluggability.plugin_base_class( subclass )
+
+		Pluggability.logger.debug "%p inherited by %p" % [ plugin_class, subclass ]
 		keys = [ subclass ]
 
 		# If it's not an anonymous class, make some keys out of variants of its name
 		if subclass.name
-			keys += self.make_derivative_names( subclass )
+			keys += plugin_class.make_derivative_names( subclass )
 		else
 			Pluggability.log.debug "  no name-based variants for anonymous subclass %p" % [ subclass ]
 		end
 
 		keys.compact.uniq.each do |key|
 			Pluggability.log.info "Registering %s derivative of %s as %p" %
-				[ subclass.name, self.name, key ]
-			self.derivatives[ key ] = subclass
+				[ subclass.name, plugin_class.name, key ]
+			plugin_class.derivatives[ key ] = subclass
 		end
 
 		super
 
 			subclass = self.derivatives[ class_name.downcase ]
 			unless subclass.is_a?( Class )
-				raise FactoryError,
+				raise PluginError,
 					"load_derivative(%s) added something other than a class "\
 					"to the registry for %s: %p" %
 					[ class_name, self.name, subclass ]
 				class_name.downcase,
 			]
 			Pluggability.log.error( errmsg )
-			raise FactoryError, errmsg, caller(3)
+			raise PluginError, errmsg, caller(3)
 		end
 	end
 
 				tries
 			  ]
 			Pluggability.log.error( errmsg )
-			raise FactoryError, errmsg
+			raise PluginError, errmsg
 		else
 			Pluggability.log.debug "Re-raising first fatal error"
 			Kernel.raise( fatals.first )
 
 
 # Backward-compatibility alias
-FactoryError = Pluggability::FactoryError unless defined?( FactoryError )
+FactoryError = Pluggability::PluginError unless defined?( FactoryError )
 

spec/pluggability_spec.rb

 	class LoadablePlugin < Plugin; end
 end
 
+class SubSubPlugin < SubPlugin; end
+
 
 #
 # Examples
 
 			expect {
 				Plugin.create('scintillating')
-			}.to raise_error( Pluggability::FactoryError, /couldn't find a \S+ named \S+.*tried \[/i )
+			}.to raise_error( Pluggability::PluginError, /couldn't find a \S+ named \S+.*tried \[/i )
 		end
 
 
 
 			expect {
 				Plugin.create('corruscating')
-			}.to raise_error( Pluggability::FactoryError, /Require of '\S+' succeeded, but didn't load a Plugin/i )
+			}.to raise_error( Pluggability::PluginError, /Require of '\S+' succeeded, but didn't load a Plugin/i )
 		end
 
 
 			TestingPlugin.plugin_type.should == 'Plugin'
 		end
 
-		it "raises a FactoryError if it can't figure out what type of factory loads it" do
+		it "raises a PluginError if it can't figure out what type of factory loads it" do
 			TestingPlugin.stub!( :ancestors ).and_return( [] )
 			expect {
 				TestingPlugin.plugin_type
-			}.to raise_error( Pluggability::FactoryError, /couldn't find plugin base/i )
+			}.to raise_error( Pluggability::PluginError, /couldn't find plugin base/i )
 		end
 	end
 
 
 	end
 
+
+	context "subclass of a derivative" do
+
+		it "is still registered with the base class" do
+			Plugin.derivatives[ 'subsub' ].should == SubSubPlugin
+		end
+
+	end
+
 end
 
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.