<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>lib/thin/controller.rb</filename>
    </added>
    <added>
      <filename>lib/thin/runner.rb</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -1,168 +1,6 @@
 #!/usr/bin/env ruby
-# &lt;tt&gt;thin start&lt;/tt&gt;: Starts the Rails app in the current directory.
+# Thin command line interface script.
 # Run &lt;tt&gt;thin -h&lt;/tt&gt; to get more usage.
 require File.dirname(__FILE__) + '/../lib/thin'
-require 'optparse'
-require 'yaml'
 
-COMMANDS = %w(start stop restart config)
-
-# Default options values
-options = {
-  :chdir       =&gt; Dir.pwd,
-  :environment =&gt; 'development',
-  :address     =&gt; '0.0.0.0',
-  :port        =&gt; 3000,
-  :timeout     =&gt; 60,
-  :log         =&gt; 'log/thin.log',
-  :pid         =&gt; 'tmp/pids/thin.pid',
-  :servers     =&gt; 1 # no cluster
-}
-
-# NOTE: If you add an option here make sure the key in the +options+ hash is the
-# same as the name of the command line option.
-# +option+ keys are use to build the command line to launch a cluster,
-# see &lt;tt&gt;lib/thin/cluster.rb&lt;/tt&gt;.
-opts = OptionParser.new do |opts|
-  opts.banner = &quot;Usage: thin [options] #{COMMANDS.join('|')}&quot;
-
-  opts.separator &quot;&quot;
-  opts.separator &quot;Server options:&quot;
-
-  opts.on(&quot;-a&quot;, &quot;--address HOST&quot;, &quot;bind to HOST address (default: 0.0.0.0)&quot;)      { |host| options[:address] = host }
-  opts.on(&quot;-p&quot;, &quot;--port PORT&quot;, &quot;use PORT (default: 3000)&quot;)                        { |port| options[:port] = port.to_i }
-  opts.on(&quot;-S&quot;, &quot;--socket PATH&quot;, &quot;bind to unix domain socket&quot;)                    { |file| options[:socket] = file }
-  opts.on(&quot;-e&quot;, &quot;--environment ENV&quot;, &quot;Rails environment (default: development)&quot;)  { |env| options[:environment] = env }
-  opts.on(&quot;-c&quot;, &quot;--chdir PATH&quot;, &quot;Change to dir before starting&quot;)                  { |dir| options[:chdir] = File.expand_path(dir) }
-  opts.on(&quot;-t&quot;, &quot;--timeout SEC&quot;, &quot;Request or command timeout in sec&quot;,            
-                                 &quot;(default: #{options[:timeout]})&quot;)               { |sec| options[:timeout] = sec.to_i }
-  opts.on(      &quot;--prefix PATH&quot;, &quot;Mount the app under PATH (start with /)&quot;)       { |path| options[:prefix] = path }
-  opts.on(      &quot;--stats PATH&quot;, &quot;Mount the Stats adapter under PATH&quot;)             { |path| options[:stats] = path }
-
-  opts.separator &quot;&quot;
-  opts.separator &quot;Daemon options:&quot;
-  
-  opts.on(&quot;-d&quot;, &quot;--daemonize&quot;, &quot;Run daemonized in the background&quot;)                { options[:daemonize] = true }
-  opts.on(&quot;-l&quot;, &quot;--log FILE&quot;, &quot;File to redirect output&quot;,
-                              &quot;(default: #{options[:log]})&quot;)                      { |file| options[:log] = file }
-  opts.on(&quot;-P&quot;, &quot;--pid FILE&quot;, &quot;File to store PID&quot;,
-                              &quot;(default: #{options[:pid]})&quot;)                      { |file| options[:pid] = file }
-  opts.on(&quot;-u&quot;, &quot;--user NAME&quot;, &quot;User to run daemon as (use with -g)&quot;)             { |user| options[:user] = user }
-  opts.on(&quot;-g&quot;, &quot;--group NAME&quot;, &quot;Group to run daemon as (use with -u)&quot;)           { |group| options[:group] = group }
-  
-  opts.separator &quot;&quot;
-  opts.separator &quot;Cluster options:&quot;
-  
-  opts.on(&quot;-s&quot;, &quot;--servers NUM&quot;, &quot;Number of servers to start&quot;,
-                                 &quot;set a value &gt;1 to start a cluster&quot;)             { |num| options[:servers] = num.to_i }
-  opts.on(&quot;-o&quot;, &quot;--only NUM&quot;, &quot;Send command to only one server of the cluster&quot;)   { |only| options[:only] = only }
-  opts.on(&quot;-C&quot;, &quot;--config PATH&quot;, &quot;Load options from a config file&quot;)               { |file| options[:config] = file }
-  
-  opts.separator &quot;&quot;
-  opts.separator &quot;Common options:&quot;
-
-  opts.on_tail(&quot;-D&quot;, &quot;--debug&quot;, &quot;Set debbuging on&quot;)       { $DEBUG = true }
-  opts.on_tail(&quot;-V&quot;, &quot;--trace&quot;, &quot;Set tracing on&quot;)         { $TRACE = true }
-  opts.on_tail(&quot;-h&quot;, &quot;--help&quot;, &quot;Show this message&quot;)       { puts opts; exit }
-  opts.on_tail('-v', '--version', &quot;Show version&quot;)         { puts Thin::SERVER; exit }
-end
-
-
-# == Utilities
-
-def cluster?(options)
-  options[:only] || (options[:servers] &amp;&amp; options[:servers] &gt; 1)
-end
-
-def load_options_from_config_file!(options)
-  if file = options.delete(:config)
-    YAML.load_file(file).each { |key, value| options[key.to_sym] = value }
-  end
-end
-
-
-# == Commands definitions
-
-def start(options)
-  load_options_from_config_file! options
-  
-  if cluster?(options)
-    Thin::Cluster.new(options).start
-  else
-    if options[:socket]
-      server = Thin::Server.new(options[:socket])
-    else
-      server = Thin::Server.new(options[:address], options[:port])
-    end
-  
-    server.pid_file = options[:pid]
-    server.log_file = options[:log]
-    server.timeout  = options[:timeout]
-  
-    if options[:daemonize]
-      server.daemonize
-      server.change_privilege options[:user], options[:group] if options[:user] &amp;&amp; options[:group]
-    end
-  
-    server.app = Rack::Adapter::Rails.new(options.merge(:root =&gt; options[:chdir]))
-    
-    # If a prefix is required, wrap in Rack URL mapper
-    server.app = Rack::URLMap.new(options[:prefix] =&gt; server.app) if options[:prefix]
-    
-    # If a stats are required, wrap in Stats adapter
-    server.app = Thin::Stats::Adapter.new(server.app, options[:stats]) if options[:stats]
-    
-    # Register restart procedure
-    server.on_restart { Thin::Command.run(:start, options) }
-    
-    server.start
-  end  
-end
-
-def stop(options)
-  load_options_from_config_file! options
-  
-  if cluster?(options)
-    Thin::Cluster.new(options).stop
-  else
-    Thin::Server.kill(options[:pid], options[:timeout])
-  end
-end
-
-def restart(options)
-  load_options_from_config_file! options
-  
-  if cluster?(options)
-    Thin::Cluster.new(options).restart
-  else
-    Thin::Server.restart(options[:pid])
-  end
-end
-
-def config(options)
-  config_file = options.delete(:config) || abort('config option required')
-
-  # Stringify keys
-  options.keys.each { |o| options[o.to_s] = options.delete(o) }
-
-  File.open(config_file, 'w') { |f| f &lt;&lt; options.to_yaml }
-  puts &quot;Wrote configuration to #{config_file}&quot;
-end
-
-
-# == Runs the command
-
-opts.parse! ARGV
-command = ARGV[0]
-
-Dir.chdir(options[:chdir])
-
-if COMMANDS.include?(command)
-  send(command, options)
-elsif command.nil?
-  puts &quot;Command required&quot;
-  puts opts
-  exit 1  
-else
-  abort &quot;Invalid command : #{command}&quot;
-end
+Thin::Runner.new(ARGV).run!
\ No newline at end of file</diff>
      <filename>bin/thin</filename>
    </modified>
    <modified>
      <diff>@@ -14,11 +14,13 @@ module Thin
   autoload :Cluster,      'thin/cluster'
   autoload :Command,      'thin/command'
   autoload :Connection,   'thin/connection'
+  autoload :Controller,   'thin/controller'
   autoload :Daemonizable, 'thin/daemonizing'
   autoload :Logging,      'thin/logging'
   autoload :Headers,      'thin/headers'
   autoload :Request,      'thin/request'
   autoload :Response,     'thin/response'
+  autoload :Runner,       'thin/runner'
   autoload :Server,       'thin/server'
   autoload :Stats,        'thin/stats'
 end</diff>
      <filename>lib/thin.rb</filename>
    </modified>
    <modified>
      <diff>@@ -3,25 +3,15 @@ module Thin
   # * Generate start and stop commands and run them.
   # * Inject the port or socket number in the pid and log filenames.
   # Servers are started throught the +thin+ command-line script.
-  class Cluster
-    include Logging
-    
-    # Path to the +thin+ script used to control the servers.
-    # Leave this to default to use the one in the path.
-    attr_accessor :script
-    
+  class Cluster &lt; Controller
     # Number of servers in the cluster.
     attr_accessor :size
-    
-    # Command line options passed to the thin script
-    attr_accessor :options
-    
+        
     # Create a new cluster of servers launched using +options+.
     def initialize(options)
       @options = options.merge(:daemonize =&gt; true)
       @size    = @options.delete(:servers)
       @only    = @options.delete(:only)
-      @script  = $PROGRAM_NAME
       
       if socket
         @options.delete(:address)</diff>
      <filename>lib/thin/cluster.rb</filename>
    </modified>
    <modified>
      <diff>@@ -3,14 +3,16 @@ module Thin
   class Command
     include Logging
     
-    # Path to the +thin+ script used to control the servers.
-    # Leave this to default to use the one in the path.
-    attr_accessor :script
+    class &lt;&lt; self
+      # Path to the +thin+ script used to control the servers.
+      # Leave this to default to use the one in the path.
+      attr_accessor :script
+      @script = $PROGRAM_NAME
+    end
     
     def initialize(name, options={})
       @name    = name
       @options = options
-      @script  = $PROGRAM_NAME
     end
     
     def self.run(*args)
@@ -34,7 +36,7 @@ module Thin
         else                &quot;--#{name.to_s.tr('_', '-')}=#{value.inspect}&quot;
         end
       end
-      &quot;#{@script} #{@name} #{shellified_options.compact.join(' ')}&quot;
+      &quot;#{self.class.script} #{@name} #{shellified_options.compact.join(' ')}&quot;
     end
   end
 end
\ No newline at end of file</diff>
      <filename>lib/thin/command.rb</filename>
    </modified>
    <modified>
      <diff>@@ -10,7 +10,6 @@ describe Cluster, &quot;with host and port&quot; do
                            :log =&gt; 'thin.log',
                            :pid =&gt; 'thin.pid'
                           )
-    @cluster.script = File.dirname(__FILE__) + '/../bin/thin'
     @cluster.silent = true
   end
     
@@ -60,7 +59,6 @@ describe Cluster, &quot;with UNIX socket&quot; do
                            :log =&gt; 'thin.log',
                            :pid =&gt; 'thin.pid'
                           )
-    @cluster.script = File.dirname(__FILE__) + '/../bin/thin'
     @cluster.silent = true
   end
   
@@ -111,7 +109,6 @@ describe Cluster, &quot;controlling only one server&quot; do
                            :pid =&gt; 'thin.pid',
                            :only =&gt; 3001
                           )
-    @cluster.script = File.dirname(__FILE__) + '/../bin/thin'
     @cluster.silent = true
   end
   </diff>
      <filename>spec/cluster_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -3,7 +3,6 @@ require File.dirname(__FILE__) + '/spec_helper'
 describe Command do
   before do
     @command = Command.new(:start, :port =&gt; 3000, :daemonize =&gt; true, :log =&gt; 'hi.log', :pid =&gt; nil)
-    @command.script = File.dirname(__FILE__) + '/../bin/thin'
     @command.silent = true
   end
   </diff>
      <filename>spec/command_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -11,6 +11,7 @@ require 'socket'
 include Thin
 
 FileUtils.mkdir_p File.dirname(__FILE__) + '/../log'
+Command.script = File.dirname(__FILE__) + '/../bin/thin'
 
 class TestRequest &lt; Thin::Request
   def initialize(path, verb='GET', params={})</diff>
      <filename>spec/spec_helper.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>2a7e1025d3e119720dac8fe61df82d2e19fb8434</id>
    </parent>
  </parents>
  <author>
    <name>macournoyer</name>
    <email>macournoyer@gmail.com</email>
  </author>
  <url>http://github.com/macournoyer/thin/commit/401ade74083354c0a180324a00d18c50e7c5f88c</url>
  <id>401ade74083354c0a180324a00d18c50e7c5f88c</id>
  <committed-date>2008-02-01T20:43:41-08:00</committed-date>
  <authored-date>2008-02-01T20:43:41-08:00</authored-date>
  <message>Refactoring thin script in several classes:
* Runner: main CLI runner, parse options and send the command to the controller.
* Controller: Configure and control a server or cluster (Cluster class now inherits from Controller).</message>
  <tree>7e330c734283629bc53547d1d69409620916abb3</tree>
  <committer>
    <name>macournoyer</name>
    <email>macournoyer@gmail.com</email>
  </committer>
</commit>
