2006-02-28 00:17:23 -05:00
|
|
|
require 'singleton'
|
|
|
|
|
|
|
|
module Mongrel
|
2006-02-28 02:04:41 -05:00
|
|
|
|
|
|
|
# Implements the main method of managing plugins for Mongrel.
|
|
|
|
# "Plugins" in this sense are any classes which get registered
|
|
|
|
# with Mongrel for possible use when it's operating. These can
|
|
|
|
# be Handlers, Commands, or other classes. When you create a
|
|
|
|
# Plugin you register it into a URI-like namespace that makes
|
|
|
|
# it easy for you (and others) to reference it later during
|
|
|
|
# configuration.
|
|
|
|
#
|
|
|
|
# PluginManager is used as nothing more than a holder of all the
|
|
|
|
# plugins that have registered themselves. Let's say you have:
|
|
|
|
#
|
|
|
|
# class StopNow < Plugin "/commands"
|
|
|
|
# ...
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# Then you can get at this plugin with:
|
|
|
|
#
|
|
|
|
# cmd = PluginManager.create("/commands/stopnow")
|
|
|
|
#
|
|
|
|
# The funky syntax for StopNow is a weird trick borrowed from
|
|
|
|
# the Camping framework. See the Mongrel::Plugin *function* (yes,
|
|
|
|
# function). What this basically does is register it
|
|
|
|
# into the namespace for plugins at /commands. You could go
|
|
|
|
# as arbitrarily nested as you like.
|
|
|
|
#
|
|
|
|
# Why this strange almost second namespace? Why not just use
|
|
|
|
# the ObjectSpace and/or Modules? The main reason is speed and
|
|
|
|
# to avoid cluttering the Ruby namespace with what is really a
|
|
|
|
# configuration statement. This lets implementors put code
|
|
|
|
# into the Ruby structuring they need, and still have Plugins
|
|
|
|
# available to Mongrel via simple URI-like names.
|
|
|
|
#
|
|
|
|
# The alternative (as pluginfactory does it) is to troll through
|
|
|
|
# ObjectSpace looking for stuff that *might* be plugins every time
|
|
|
|
# one is needed. This alternative also means that you are stuck
|
|
|
|
# naming your commands in specific ways and putting them in specific
|
|
|
|
# modules in order to configure how Mongrel should use them.
|
|
|
|
#
|
|
|
|
# One downside to this is that you need to subclass plugin to
|
|
|
|
# make it work. In this case use mixins to add other functionality.
|
2006-02-28 00:17:23 -05:00
|
|
|
class PluginManager
|
|
|
|
include Singleton
|
|
|
|
|
|
|
|
def initialize
|
|
|
|
@plugins = URIClassifier.new
|
|
|
|
end
|
|
|
|
|
2006-02-28 02:04:41 -05:00
|
|
|
# Tell the PluginManager to scan the given path (recursively)
|
|
|
|
# and load the *.rb files found there. This is how you'd
|
|
|
|
# setup your own plugin directory.
|
2006-02-28 00:17:23 -05:00
|
|
|
def load(path)
|
|
|
|
Dir.chdir(path) do
|
|
|
|
Dir["**/*.rb"].each do |rbfile|
|
2006-03-01 22:54:32 -05:00
|
|
|
STDERR.puts "Loading plugins from #{rbfile}"
|
2006-02-28 00:17:23 -05:00
|
|
|
require rbfile
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2006-02-28 02:04:41 -05:00
|
|
|
# Not necessary for you to call directly, but this is
|
|
|
|
# how Mongrel::PluginBase.inherited actually adds a
|
|
|
|
# plugin to a category.
|
2006-02-28 00:17:23 -05:00
|
|
|
def register(category, name, klass)
|
|
|
|
cat, ignored, map = @plugins.resolve(category)
|
2006-03-01 22:54:32 -05:00
|
|
|
|
|
|
|
if not cat or ignored.length > 0
|
2006-02-28 00:17:23 -05:00
|
|
|
map = {name => klass}
|
|
|
|
@plugins.register(category, map)
|
2006-03-01 22:54:32 -05:00
|
|
|
elsif not map
|
|
|
|
raise "Unknown category #{category}"
|
2006-02-28 00:17:23 -05:00
|
|
|
else
|
|
|
|
map[name] = klass
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2006-02-28 02:04:41 -05:00
|
|
|
# Resolves the given name (should include /category/name) to
|
|
|
|
# find the plugin class and create an instance. It uses
|
|
|
|
# the same URIClassifier that the rest of Mongrel does so it
|
|
|
|
# is fast.
|
2006-02-28 00:17:23 -05:00
|
|
|
def create(name, options = {})
|
|
|
|
category, plugin, map = @plugins.resolve(name)
|
2006-03-01 22:54:32 -05:00
|
|
|
STDERR.puts "found: #{category} #{plugin} #{map.inspect} #{map[plugin].inspect}"
|
2006-02-28 00:17:23 -05:00
|
|
|
if category and plugin and plugin.length > 0
|
|
|
|
map[plugin].new(options)
|
|
|
|
else
|
|
|
|
raise "Plugin #{name} does not exist"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2006-02-28 02:04:41 -05:00
|
|
|
# Returns a map of URIs->[handlers] that you can
|
|
|
|
# use to investigate available handlers.
|
2006-02-28 00:17:23 -05:00
|
|
|
def available
|
|
|
|
map = {}
|
|
|
|
@plugins.uris.each do |u|
|
|
|
|
cat, name, plugins = @plugins.resolve(u)
|
|
|
|
map[cat] ||= []
|
|
|
|
map[cat] += plugins.keys
|
|
|
|
end
|
|
|
|
|
|
|
|
return map
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
|
2006-02-28 02:04:41 -05:00
|
|
|
# This base class for plugins reallys does nothing
|
|
|
|
# more than wire up the new class into the right category.
|
|
|
|
# It is not thread-safe yet but will be soon.
|
2006-02-28 00:17:23 -05:00
|
|
|
class PluginBase
|
|
|
|
|
2006-03-01 22:54:32 -05:00
|
|
|
attr_reader :options
|
|
|
|
|
|
|
|
|
2006-02-28 02:04:41 -05:00
|
|
|
# See Mongrel::Plugin for an explanation.
|
2006-02-28 00:17:23 -05:00
|
|
|
def PluginBase.inherited(klass)
|
2006-02-28 02:04:41 -05:00
|
|
|
name = "/" + klass.to_s.downcase
|
|
|
|
PluginManager.instance.register(@@category, name, klass)
|
2006-03-01 22:54:32 -05:00
|
|
|
@@category = nil
|
2006-02-28 00:17:23 -05:00
|
|
|
end
|
|
|
|
|
2006-02-28 02:04:41 -05:00
|
|
|
# See Mongrel::Plugin for an explanation.
|
2006-02-28 00:17:23 -05:00
|
|
|
def PluginBase.category=(category)
|
|
|
|
@@category = category
|
|
|
|
end
|
2006-03-01 22:54:32 -05:00
|
|
|
|
|
|
|
def initialize(options = {})
|
|
|
|
@options = options
|
|
|
|
end
|
|
|
|
|
2006-02-28 00:17:23 -05:00
|
|
|
end
|
|
|
|
|
2006-02-28 02:04:41 -05:00
|
|
|
# This nifty function works with the PluginBase to give you
|
|
|
|
# the syntax:
|
|
|
|
#
|
|
|
|
# class MyThing < Plugin "/things"
|
|
|
|
# ...
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# What it does is temporarily sets the PluginBase.category, and then
|
|
|
|
# returns PluginBase. Since the next immediate thing Ruby does is
|
|
|
|
# use this returned class to create the new class, PluginBase.inherited
|
|
|
|
# gets called. PluginBase.inherited then uses the set category, class name,
|
|
|
|
# and class to register the plugin in the right way.
|
|
|
|
def Mongrel::Plugin(c)
|
2006-02-28 00:17:23 -05:00
|
|
|
PluginBase.category = c
|
|
|
|
PluginBase
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|