Commits

Michael Granger committed dc968f6

Add plugin exclusion patterns

  • Participants
  • Parent commits 1b4bb80

Comments (0)

Files changed (6)

 gem "rdoc", "~>4.0", :group => [:development, :test]
 gem "hoe-deveiate", "~>0.3", :group => [:development, :test]
 gem "hoe-bundler", "~>1.2", :group => [:development, :test]
+gem "rdoc-generator-fivefish", "~>0.1", :group => [:development, :test]
 gem "hoe", "~>3.7", :group => [:development, :test]
 
 # vim: syntax=ruby
 the message will list all the permutations that were tried.
 
 
+=== Preloaded Plugins
+
+Sometimes you don't want to wait for plugins to be loaded on demand. For
+that case, Pluggability provides the load_all[rdoc-ref:Pluggability#load_all]
+method. This will find all possible matches for plugin files and load
+them, returning an Array of all the loaded classes:
+
+    class Template::Tag
+        extend Pluggability
+        plugin_prefixes 'tag'
+    end
+
+    tag_classes = Template::Tag.load_all
+
+
+=== Excluding Some Files
+
+You can also prevent some files from being automatically loaded by
+either create[rdoc-ref:Pluggability#create] or 
+load_all[rdoc-ref:Pluggability#load_all] by setting one or more exclusion
+patterns:
+
+    LogReader.plugin_exclusions 'spec/*', %r{/test/}
+
+The patterns can either be Regexps or glob Strings.
+
+
 === Logging
 
 If you need a little more insight into what's going on, Pluggability
-uses the "Loggability":https://rubygems.org/gems/loggability library.
+uses the Loggability[https://rubygems.org/gems/loggability] library.
 Just set the log level to 'debug' and it'll explain what's going on:
 
     require 'pluggability'
     
     LogReader.create( 'ringbuffer' )
 
-this might generate a log that looks like:
+this might generate a log that looks (something) like:
 
     [...] debug {} -- Loading derivative ringbuffer
     [...] debug {} -- Subdirs are: [""]
-    [...] debug {} -- Path is: ["ringbuffer_logreader", 
-      "ringbuffer_LogReader", "ringbufferlogreader", 
-      "ringbufferLogReader", "ringbuffer"]...
+    [...] debug {} -- Path is: ["ringbuffer_logreader", "ringbuffer_LogReader",
+      "ringbufferlogreader", "ringbufferLogReader", "ringbuffer"]...
     [...] debug {} -- Trying ringbuffer_logreader...
-    [...] debug {} -- No module at 'ringbuffer_logreader', trying the 
-      next alternative: 'cannot load such file -- ringbuffer_logreader'
+    [...] debug {} -- No module at 'ringbuffer_logreader', trying the next alternative:
+      'cannot load such file -- ringbuffer_logreader'
     [...] debug {} -- Trying ringbuffer_LogReader...
-    [...] debug {} -- No module at 'ringbuffer_LogReader', trying the
-      next alternative: 'cannot load such file -- ringbuffer_LogReader'
+    [...] debug {} -- No module at 'ringbuffer_LogReader', trying the next alternative:
+      'cannot load such file -- ringbuffer_LogReader'
     [...] debug {} -- Trying ringbufferlogreader...
-    [...] debug {} -- No module at 'ringbufferlogreader', trying the
-      next alternative: 'cannot load such file -- ringbufferlogreader'
+    [...] debug {} -- No module at 'ringbufferlogreader', trying the next alternative:
+      'cannot load such file -- ringbufferlogreader'
     [...] debug {} -- Trying ringbufferLogReader...
-    [...] debug {} -- No module at 'ringbufferLogReader', trying the
-      next alternative: 'cannot load such file -- ringbufferLogReader'
+    [...] debug {} -- No module at 'ringbufferLogReader', trying the next alternative:
+      'cannot load such file -- ringbufferLogReader'
     [...] debug {} -- Trying ringbuffer...
-    [...] debug {} -- No module at 'ringbuffer', trying the next
-      alternative: 'cannot load such file -- ringbuffer'
+    [...] debug {} -- No module at 'ringbuffer', trying the next alternative:
+      'cannot load such file -- ringbuffer'
     [...] debug {} -- fatals = []
-    [...] error {} -- Couldn't find a LogReader named 'ringbuffer':
-      tried ["ringbuffer_logreader", "ringbuffer_LogReader", 
+    [...] error {} -- Couldn't find a LogReader named 'ringbuffer': tried 
+      ["ringbuffer_logreader", "ringbuffer_LogReader", 
       "ringbufferlogreader", "ringbufferLogReader", "ringbuffer"]
 
 
 	self.extra_rdoc_files = Rake::FileList[ '*.rdoc' ]
 	self.spec_extras[:rdoc_options] = ['-t', 'Pluggability Toolkit']
 
+	# Hoops to avoid adding a formatting to the gem's spec, but still build
+	# with a custom formatter locally
+	self.spec_extras[:rdoc_options] += [ '-f', 'fivefish' ] if
+		File.directory?( '.hg' ) && !(ARGV.include?('gem') || ARGV.include?('release'))
+
 	self.developer 'Martin Chase', 'stillflame@FaerieMUD.org'
 	self.developer 'Michael Granger', 'ged@FaerieMUD.org'
 
 
 	self.dependency 'hoe-deveiate', '~> 0.3', :development
 	self.dependency 'hoe-bundler', '~> 1.2', :development
+	self.dependency 'rdoc-generator-fivefish', '~> 0.1', :development
 
 	self.license "BSD"
 	self.hg_sign_tags = true if self.respond_to?( :hg_sign_tags= )
 	self.rdoc_locations << "deveiate:/usr/local/www/public/code/#{remote_rdoc_dir}"
 end
 
+task :gem do
+	hoespec.spec.rdoc_options.delete( '-f' )
+	hoespec.spec.rdoc_options.delete( 'fivefish' )
+end
+
 ENV['VERSION'] ||= hoespec.spec.version.to_s
 
 task 'hg:precheckin' => [ :check_history, :check_manifest, :spec ]

File experiments/logger_output.rb

 	require 'pathname'
 	basedir = Pathname.new( __FILE__ ).dirname.parent
 	libdir = basedir + 'lib'
-	
+
 	$LOAD_PATH.unshift( libdir.to_s ) unless $LOAD_PATH.include?( libdir.to_s )
 }
 
 require 'pluggability'
 require 'loggability'
 
-class DataDriver
+class LogReader
 	extend Pluggability
 end
 
 # Or just Pluggability's level:
 Pluggability.logger.level = :debug
 
-DataDriver.create( 'ringbuffer' )
+LogReader.create( 'ringbuffer' )
 
 # $ ruby experiments/logger_output.rb 
 # [2012-08-03 12:23:43.680405 89358/main] debug {} -- Loading derivative ringbuffer

File lib/pluggability.rb

 	### Add the @derivatives instance variable to including classes.
 	def self::extend_object( obj )
 		obj.instance_variable_set( :@plugin_prefixes, [] )
+		obj.instance_variable_set( :@plugin_exclusions, [] )
 		obj.instance_variable_set( :@derivatives, {} )
 
 		Pluggability.pluggable_classes << obj
 	end
 
 
+	### Get/set patterns which cause files in a plugin path to not be loaded. Typical
+	### use case is to exclude test/spec directories:
+	###
+	###     MyFactoryType.plugin_exclude( 'spec/**' )
+	###
+	def plugin_exclusions( *exclusions )
+		@plugin_exclusions.replace( exclusions ) if !exclusions.empty?
+		return @plugin_exclusions
+	end
+
+
+	### Set the plugin exclusion patterns which cause files in a plugin path to not
+	### be loaded.
+	def plugin_exclusions=( args )
+		@plugin_exclusions = Array( args )
+	end
+
+
+	### Returns +true+ if any of the #plugin_exclusions match the specified
+	### +path.
+	def is_excluded_path?( path )
+		rval = self.plugin_exclusions.find do |exclusion|
+			case exclusion
+			when Regexp
+				path =~ exclusion
+			when String
+				flags = 0
+				flags &= File::FNM_EXTGLOB if defined?( File::FNM_EXTGLOB )
+				File.fnmatch( exclusion, path, flags )
+			else
+				Pluggability.log.warn "Don't know how to apply exclusion: %p" % [ exclusion ]
+				false
+			end
+		end
+
+		if rval
+			Pluggability.log.debug "load path %p is excluded by %p" % [ path, rval ]
+			return true
+		else
+			return false
+		end
+	end
+
+
 	### Returns the type name used when searching for a derivative.
 	def plugin_type
 		base = Pluggability.plugin_base_class( self ) or
 			Pluggability.log.debug "  found %d matching files" % [ candidates.length ]
 			next if candidates.empty?
 
-			candidates.each {|path| require(path) }
+			candidates.each do |path|
+				next if self.is_excluded_path?( path )
+				require( path )
+			end
 		end
 
 		return self.derivative_classes
 		# module.
 		subdirs.map( &:strip ).each do |subdir|
 			self.make_require_path( mod_name, subdir ).each do |path|
+				next if self.is_excluded_path?( path )
+
 				Pluggability.logger.debug "Trying #{path}..."
 				tries << path
 

File spec/pluggability_spec.rb

 #
 describe Pluggability do
 
-	before( :each ) do
-		setup_logging( :fatal )
+	before( :all ) do
+		setup_logging()
 	end
 
-	after( :each ) do
+	before( :each ) do
+		Plugin.plugin_exclusions = []
+	end
+
+	after( :all ) do
 		reset_logging()
 	end
 
 	context "-extended class" do
 
 		it "knows about all of its derivatives" do
-			expect( Plugin.derivatives.keys ).to include( 'sub' )
-			expect( Plugin.derivatives.keys ).to include( 'subplugin' )
-			expect( Plugin.derivatives.keys ).to include( 'SubPlugin' )
-			expect( Plugin.derivatives.keys ).to include( SubPlugin )
+			expect( Plugin.derivatives.keys ).
+				to include( 'sub', 'subplugin', 'SubPlugin', SubPlugin )
 		end
 
 		it "returns derivatives directly if they're already loaded" do
 
 			Plugin.load_all
 		end
+
+
+		it "doesn't preload derivatives whose path matches a Regexp exclusion" do
+			expect( Gem ).to receive( :find_files ).with( 'plugins/*.rb' ).
+				and_return([ 'plugins/first.rb' ])
+			expect( Gem ).to receive( :find_files ).with( 'plugins/private/*.rb' ).
+				and_return([ 'plugins/private/second.rb', 'plugins/private/third.rb' ])
+
+			expect( Plugin ).to receive( :require ).with( 'plugins/first.rb' ).
+				and_return( true )
+			expect( Plugin ).to_not receive( :require ).with( 'plugins/private/second.rb' )
+			expect( Plugin ).to_not receive( :require ).with( 'plugins/private/third.rb' )
+
+			Plugin.plugin_exclusions( %r{/private} )
+			Plugin.load_all
+		end
+
+
+		it "doesn't preload derivatives whose path matches a glob String exclusion" do
+			expect( Gem ).to receive( :find_files ).with( 'plugins/*.rb' ).
+				and_return([ 'plugins/first.rb' ])
+			expect( Gem ).to receive( :find_files ).with( 'plugins/private/*.rb' ).
+				and_return([ 'plugins/private/second.rb', 'plugins/private/third.rb' ])
+
+			expect( Plugin ).to receive( :require ).with( 'plugins/first.rb' ).
+				and_return( true )
+			expect( Plugin ).to receive( :require ).with( 'plugins/private/second.rb' ).
+				and_return( true )
+			expect( Plugin ).to_not receive( :require ).with( 'plugins/private/third.rb' )
+
+			Plugin.plugin_exclusions( '**/third.rb' )
+			Plugin.load_all
+		end
+
 	end