about summary refs log tree commit homepage
path: root/lib/mongrel
diff options
context:
space:
mode:
Diffstat (limited to 'lib/mongrel')
-rw-r--r--lib/mongrel/debug.rb144
-rw-r--r--lib/mongrel/handlers.rb23
-rw-r--r--lib/mongrel/rails.rb74
3 files changed, 241 insertions, 0 deletions
diff --git a/lib/mongrel/debug.rb b/lib/mongrel/debug.rb
new file mode 100644
index 0000000..8b2dd5c
--- /dev/null
+++ b/lib/mongrel/debug.rb
@@ -0,0 +1,144 @@
+require 'logger'
+require 'set'
+
+
+$mongrel_debugging=true
+
+module MongrelDbg
+  SETTINGS = { :tracing => {}}
+  LOGGING = { }
+
+  def MongrelDbg::configure(log_dir = "mongrel_debug")
+    Dir.mkdir(log_dir) if not File.exist?(log_dir)
+    @log_dir = log_dir
+  end
+
+  
+  def MongrelDbg::trace(target, message)
+    if SETTINGS[:tracing][target]
+      LOGGING[target].log(Logger::DEBUG, message)
+    end
+  end
+
+  def MongrelDbg::begin_trace(target)
+    SETTINGS[:tracing][target] = true
+    if not LOGGING[target]
+      LOGGING[target] = Logger.new(File.join(@log_dir, "#{target.to_s}.log"))
+    end                          
+    MongrelDbg::trace(target, "TRACING ON #{Time.now}")
+  end
+
+  def MongrelDbg::end_trace(target)
+    SETTINGS[:tracing][target] = false
+    MongrelDbg::trace(target, "TRACING OFF #{Time.now}")
+    LOGGING[target].close
+    LOGGING[target] = nil
+  end
+end
+
+
+module ObjectTracker
+  @active_objects = nil
+  @live_object_tracking = false
+
+  def ObjectTracker.configure
+    @active_objects = Set.new
+    ObjectSpace.each_object do |obj|
+      @active_objects << obj.object_id
+    end
+    srand @active_objects.object_id
+    @sample_thread = Thread.new do
+      loop do
+        sleep(rand(3) + (rand(100)/100.0))
+        ObjectTracker.sample
+      end
+    end
+    @sample_thread.priority = 20
+  end
+
+  def ObjectTracker.start
+    @stopit = true
+    @live_object_tracking = true
+    @stopit = false
+  end
+
+  def ObjectTracker.stop
+    @live_object_tracking = false
+  end
+
+  def ObjectTracker.sample
+    ospace = Set.new
+    ObjectSpace.each_object do |obj|
+      ospace << obj.object_id
+    end
+    
+    dead_objects = @active_objects - ospace
+    new_objects = ospace - @active_objects
+    live_objects = ospace & @active_objects
+    
+    STDERR.puts "#{dead_objects.length},#{new_objects.length},#{live_objects.length}"
+
+    @active_objects = live_objects + new_objects
+  end
+
+end
+
+class Class
+  alias_method :orig_new, :new
+  
+  @@count = 0
+  @@stoppit = false
+  @@class_caller_count = Hash.new{|hash,key| hash[key] = Hash.new(0)}
+  
+  def new(*arg,&blk)
+    unless @@stoppit
+      @@stoppit = true
+      @@count += 1
+      @@class_caller_count[self][caller[0]] += 1
+      @@stoppit = false
+    end
+    orig_new(*arg,&blk)
+  end
+
+
+  def Class.report_object_creations
+    @@stoppit = true
+    puts "Number of objects created = #{@@count}"
+    
+    total = Hash.new(0)
+    
+    @@class_caller_count.each_key do |klass|
+      caller_count = @@class_caller_count[klass]
+      caller_count.each_value do |count|
+        total[klass] += count
+      end
+    end
+    
+    klass_list = total.keys.sort{|klass_a, klass_b|
+      a = total[klass_a]
+      b = total[klass_b]
+      if a != b
+        -1* (a <=> b)
+      else
+        klass_a.to_s <=> klass_b.to_s
+      end
+    }
+    klass_list.each do |klass|
+      puts "#{total[klass]}\t#{klass} objects created."
+      caller_count = @@class_caller_count[ klass]
+      caller_count.keys.sort_by{|call| -1*caller_count[call]}.each do |call|
+        puts "\t#{call}\tCreated #{caller_count[call]} #{klass} objects."
+      end
+      puts
+    end
+  end
+
+  def Class.reset_object_creations
+    @@stopit = true
+    @@count = 0
+    @@class_caller_count = Hash.new{|hash,key| hash[key] = Hash.new(0)}
+    @@stoppit = false
+  end
+end
+
+
diff --git a/lib/mongrel/handlers.rb b/lib/mongrel/handlers.rb
index 609a252..7d817bb 100644
--- a/lib/mongrel/handlers.rb
+++ b/lib/mongrel/handlers.rb
@@ -5,9 +5,32 @@ module Mongrel
   # just the minimum necessary for you to handle a request and shoot back
   # a response.  Look at the HttpRequest and HttpResponse objects for how
   # to use them.
+  #
+  # This is used for very simple handlers that don't require much to operate.
+  # More extensive plugins or those you intend to distribute as GemPlugins
+  # should be implemented using the HttpHandlerPlugin mixin.
+  #
   class HttpHandler
+
+    def process(request, response)
+    end
+  end
+
+
+  # This is used when your handler is implemented as a GemPlugin.
+  # The plugin always takes an options hash which you can modify
+  # and then access later.  They are stored by default for
+  # the process method later.
+  module HttpHandlerPlugin
+    attr_reader :options
+
+    def initialize(options={})
+      @options = options
+    end
+
     def process(request, response)
     end
+
   end
 
 
diff --git a/lib/mongrel/rails.rb b/lib/mongrel/rails.rb
index fb4e172..808d7d3 100644
--- a/lib/mongrel/rails.rb
+++ b/lib/mongrel/rails.rb
@@ -1,6 +1,62 @@
 require 'mongrel'
 require 'cgi'
 
+# Creates Rails specific configuration options for people to use
+# instead of the base Configurator.
+class RailsConfigurator < Mongrel::Configurator
+
+  # Used instead of Mongrel::Configurator.uri to setup
+  # a rails application at a particular URI.  Requires
+  # the following options:
+  #
+  # * :docroot => The public dir to serve from.
+  # * :environment => Rails environment to use.
+  #
+  # And understands the following optional settings:
+  #
+  # * :mime => A map of mime types.
+  #
+  # Because of how Rails is designed you can only have
+  # one installed per Ruby interpreter (talk to them
+  # about thread safety).  This function will abort
+  # with an exception if called more than once.
+  def rails(location, options={})
+    ops = resolve_defaults(options)
+    
+    # fix up some defaults
+    ops[:environment] ||= "development"
+    ops[:docroot] ||= "public"
+    ops[:mime] ||= {}
+
+    if @rails_handler
+      raise "You can only register one RailsHandler for the whole Ruby interpreter.  Complain to the ordained Rails core about thread safety."
+    end
+
+    $orig_dollar_quote = $".clone
+    ENV['RAILS_ENV'] = ops[:environment]
+    require 'config/environment'
+    require 'dispatcher'
+    require 'mongrel/rails'
+    
+    @rails_handler = RailsHandler.new(ops[:docroot], ops[:mime])
+  end
+
+
+  # Reloads rails.  This isn't too reliable really, but
+  # should work for most minimal reload purposes.  Only reliable
+  # way it so stop then start the process.
+  def reload!
+    if not @rails_handler
+      raise "Rails was not configured.  Read the docs for RailsConfigurator."
+    end
+
+    STDERR.puts "Reloading rails..."
+    @rails_handler.reload!
+    STDERR.puts "Done reloading rails."
+    
+  end
+end
+
 # 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
@@ -83,3 +139,21 @@ class RailsHandler < Mongrel::HttpHandler
     end
   end
 end
+
+
+if $mongrel_debugging
+
+  # Tweak the rails handler to allow for tracing
+  class RailsHandler
+    alias :real_process :process
+    
+    def process(request, response)
+      MongrelDbg::trace(:rails, "REQUEST #{Time.now}\n" + request.params.to_yaml)
+      
+      real_process(request, response)
+      
+      MongrelDbg::trace(:rails, "REQUEST #{Time.now}\n" + request.params.to_yaml)
+    end
+  end
+  
+end