# = Plugin Handler # # Find plugins across various library managers. # # All plugins are expected to be within a library's designated # loadpath(s) under a toplevel plugins/ subdirectory. # By using this assigned space plugins are kept isolated from # normal library scripts, which helps prevent inadvertent # name clashes. # # For example, lets say we want to create a pluggable template # system for our "luckyweb" project. Our lib/ # directory would have the usual luckyweb directory, # but also now a plugins/luckyweb/ path in which the # plugin templates would be stored. # # == How To Use # # Usage is very simple. Just supply a glob to the +Plugin.find+ # function. # # Plugin.find('syckle/*') # # A shortcut is provided with []. # # Plugin['syckle/*'] # # == Alternate Plugin Location # # By default plugins/ is hardcoded into the system # as a reliable convention. This is intentional. However, # if you have specific need for serching for files outside # that directory you can do so by supplying a :directory # option to the #find command. Eg. # # Plugin.find('discover.rb', :directory=>'rdoc') # # == A Note on RubyGems # # A way has not yet been devised to isolate the actived version # of a gem from the latest inactive version. Therefore some # overlap can occur if an older version of a plugin-containing # gem has been activated prior to calling Plugin.find(). Such an # occurance will be rare (considering the use cases of plugins), # so it is nothing to be overly concerned about. Moreover, it is # a long-way from the offical Gems plugin policy which is to find # all matching files from *all* versions using Gem.find_files(). # I quote Eric Hodel, "It's an encouragement to make your plugin # files as light as possible, such as requiring an additional file # or calling some very stable API." While an understandable # encouragment, ultimately it is not a robust solution. module Plugin extend self DIRECTORY = 'plugins' # Find plugins, searching through standard $LOAD_PATH, # Roll Libraries and RubyGems. # # Provide a +match+ file glob to find plugins. # # Plugins.find('syckle/*') # def find(match, options={}) plugins = [] plugins.concat find_roll(match, options) plugins.concat find_loadpath(match, options) plugins.concat find_gems(match, options) plugins.uniq end # Shortcut for #find. # # Plugin['syckle/*'] # alias_method :[], :find # Search roll for current or latest libraries. def find_roll(match, options={}) plugins = [] directory = options[:directory] || DIRECTORY if defined?(::Roll) ::Roll::Library.ledger.each do |name, lib| lib = lib.sort.first if Array===lib lib.loadpath.each do |path| find = File.join(lib.location, path, directory, match) list = Dir.glob(find) list = list.map{ |d| d.chomp('/') } plugins.concat(list) end end end plugins end # Search standard $LOAD_PATH. # # Activated gem versions are in here too. def find_loadpath(match, options={}) plugins = [] directory = options[:directory] || DIRECTORY $LOAD_PATH.uniq.each do |path| path = File.expand_path(path) list = Dir.glob(File.join(path, directory, match)) #dirs = dirs.select{ |d| File.directory?(d) } list = list.map{ |d| d.chomp('/') } plugins.concat(list) end plugins end # Search latest gem versions. # # TODO: Is there anyway to skip active gems? def find_gems(match, options={}) plugins = [] directory = options[:directory] || DIRECTORY if defined?(::Gem) ::Gem.latest_load_paths do |path| list = Dir.glob(File.join(path, directory, match)) list = list.map{ |d| d.chomp('/') } plugins.concat(list) end end plugins end end