diff options
author | zedshaw <zedshaw@19e92222-5c0b-0410-8929-a290d50e31e9> | 2006-02-28 07:04:41 +0000 |
---|---|---|
committer | zedshaw <zedshaw@19e92222-5c0b-0410-8929-a290d50e31e9> | 2006-02-28 07:04:41 +0000 |
commit | d1a01c03f71c14e5d4fe66d93c5ab444c0aba554 (patch) | |
tree | d7d4df48f1e708dfceea7bd78907177e5b1be889 /lib/mongrel | |
parent | 4e5132f63a210beb766ebfe52bea7424903403ae (diff) | |
download | unicorn-d1a01c03f71c14e5d4fe66d93c5ab444c0aba554.tar.gz |
git-svn-id: svn+ssh://rubyforge.org/var/svn/mongrel/trunk@65 19e92222-5c0b-0410-8929-a290d50e31e9
Diffstat (limited to 'lib/mongrel')
-rw-r--r-- | lib/mongrel/command.rb | 38 | ||||
-rw-r--r-- | lib/mongrel/plugins.rb | 78 | ||||
-rw-r--r-- | lib/mongrel/rails.rb | 68 |
3 files changed, 149 insertions, 35 deletions
diff --git a/lib/mongrel/command.rb b/lib/mongrel/command.rb index 3625438..04af386 100644 --- a/lib/mongrel/command.rb +++ b/lib/mongrel/command.rb @@ -1,7 +1,6 @@ require 'singleton' require 'optparse' -require 'pluginfactory' - +require 'mongrel/plugins' module Mongrel @@ -10,17 +9,10 @@ module Mongrel module Command - # A Command pattern implementation used to create the set of command available to the user # from Mongrel. The script uses objects which implement this interface to do the # user's bidding. - # - # Creating a new command is very easy, and you can do it without modifying the source - # of Mongrel thanks to PluginFactory. What you do is the following: - # - # 1. - class Command - include PluginFactory + module Command attr_reader :valid, :done_validating @@ -65,12 +57,6 @@ module Mongrel @opt.parse! argv end - # Tells the PluginFactory where to look for additional commands. By default - # it's just a "plugins" directory wherever we are located. - def self.derivativeDirs - return ["plugins"] - end - # Returns true/false depending on whether the command is configured properly. def validate return @valid @@ -119,8 +105,6 @@ module Mongrel end end - - # A Singleton class that manages all of the available commands # and handles running them. class Registry @@ -128,15 +112,9 @@ module Mongrel # Builds a list of possible commands from the Command derivates list def commands - list = Command.derivatives() - match = Regexp.new("(.*::.*)|(.*command.*)", Regexp::IGNORECASE) - - results = [] - list.keys.each do |key| - results << key.to_s unless match.match(key.to_s) - end - - return results.sort + pmgr = PluginManager.instance + list = pmgr.available["/commands"] + return list.sort end # Prints a list of available commands. @@ -144,7 +122,7 @@ module Mongrel puts "Available commands are:\n\n" self.commands.each do |name| - puts " - #{name}\n" + puts " - #{name[1 .. -1]}\n" end puts "\nEach command takes -h as an option to get help." @@ -165,8 +143,8 @@ module Mongrel # command exists, set it up and validate it begin - command = Command.create(cmd_name, args) - rescue FactoryError + command = PluginManager.instance.create("/commands/#{cmd_name}", args) + rescue STDERR.puts "INVALID COMMAND: #$!" print_command_list return diff --git a/lib/mongrel/plugins.rb b/lib/mongrel/plugins.rb index 9778e9b..f312e36 100644 --- a/lib/mongrel/plugins.rb +++ b/lib/mongrel/plugins.rb @@ -1,6 +1,47 @@ require 'singleton' module Mongrel + + # 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. class PluginManager include Singleton @@ -8,6 +49,9 @@ module Mongrel @plugins = URIClassifier.new end + # 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. def load(path) Dir.chdir(path) do Dir["**/*.rb"].each do |rbfile| @@ -16,6 +60,9 @@ module Mongrel end end + # Not necessary for you to call directly, but this is + # how Mongrel::PluginBase.inherited actually adds a + # plugin to a category. def register(category, name, klass) cat, ignored, map = @plugins.resolve(category) if not cat @@ -26,17 +73,21 @@ module Mongrel end end - + # 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. def create(name, options = {}) category, plugin, map = @plugins.resolve(name) if category and plugin and plugin.length > 0 - STDERR.puts "found: #{category} #{plugin} for #{name}" map[plugin].new(options) else raise "Plugin #{name} does not exist" end end + # Returns a map of URIs->[handlers] that you can + # use to investigate available handlers. def available map = {} @plugins.uris.each do |u| @@ -50,19 +101,36 @@ module Mongrel end + # 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. class PluginBase + # See Mongrel::Plugin for an explanation. def PluginBase.inherited(klass) - - PluginManager.instance.register(@@category, klass.to_s.downcase, klass) + name = "/" + klass.to_s.downcase + PluginManager.instance.register(@@category, name, klass) end + # See Mongrel::Plugin for an explanation. def PluginBase.category=(category) @@category = category end end - def Plugin(c) + # 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) PluginBase.category = c PluginBase end diff --git a/lib/mongrel/rails.rb b/lib/mongrel/rails.rb new file mode 100644 index 0000000..73d4272 --- /dev/null +++ b/lib/mongrel/rails.rb @@ -0,0 +1,68 @@ +require 'mongrel' + + +# Implements a handler that can run Rails and serve files out of the +# Rails application's public directory. This lets you run your Rails +# application with Mongrel during development and testing, then use it +# also in production behind a server that's better at serving the +# static files. +# +# The RailsHandler takes a mime_map parameter which is a simple suffix=mimetype +# mapping that it should add to the list of valid mime types. +# +# It also supports page caching directly and will try to resolve a request +# in the following order: +# +# * If the requested exact PATH_INFO exists as a file then serve it. +# * If it exists at PATH_INFO+".html" exists then serve that. +# * Finally, construct a Mongrel::CGIWrapper and run Dispatcher.dispath to have Rails go. +# +# This means that if you are using page caching it will actually work with Mongrel +# and you should see a decent speed boost (but not as fast as if you use lighttpd). +class RailsHandler < Mongrel::HttpHandler + def initialize(dir, mime_map = {}) + @files = Mongrel::DirHandler.new(dir,false) + @guard = Mutex.new + + # register the requested mime types + mime_map.each {|k,v| Mongrel::DirHandler::add_mime_type(k,v) } + end + + # Attempts to resolve the request as follows: + # + # + # * If the requested exact PATH_INFO exists as a file then serve it. + # * If it exists at PATH_INFO+".html" exists then serve that. + # * Finally, construct a Mongrel::CGIWrapper and run Dispatcher.dispath to have Rails go. + def process(request, response) + return if response.socket.closed? + + path_info = request.params["PATH_INFO"] + page_cached = request.params["PATH_INFO"] + ".html" + + if @files.can_serve(path_info) + # File exists as-is so serve it up + @files.process(request,response) + elsif @files.can_serve(page_cached) + # possible cached page, serve it up + request.params["PATH_INFO"] = page_cached + @files.process(request,response) + else + cgi = Mongrel::CGIWrapper.new(request, response) + + begin + @guard.synchronize do + # Rails is not thread safe so must be run entirely within synchronize + Dispatcher.dispatch(cgi, ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS, response.body) + end + + # This finalizes the output using the proper HttpResponse way + cgi.out {""} + rescue Object => rails_error + STDERR.puts "calling Dispatcher.dispatch #{rails_error}" + STDERR.puts rails_error.backtrace.join("\n") + end + end + end + +end |