public
Description: A very fast & simple Ruby web server
Homepage: http://code.macournoyer.com/thin/
Clone URL: git://github.com/macournoyer/thin.git
commit  d39a4de1448b9a7de3e311789311158f6d22252c
tree    f6f40e43f9fb520c1dbfb37a1f38e222c76cd16d
parent  6a7725668ba733c1d23fc1df3c0aa0a0d0d05c3e
thin / lib / thin / cluster.rb
100644 104 lines (85 sloc) 2.939 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
module Thin
  # Control a set of servers. Generate start and stop commands and run them.
  class Cluster
    include Logging
    
    class << self
      # Script to run
      attr_accessor :thin_script
    end
    self.thin_script = 'thin'
    
    # 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 => true)
      @size = @options.delete(:servers)
    end
    
    def first_port; @options[:port] end
    def address; @options[:address] end
    def pid_file; File.expand_path File.join(@options[:chdir], @options[:pid]) end
    def log_file; File.expand_path File.join(@options[:chdir], @options[:log]) end
    
    # Start the servers
    def start
      with_each_server { |port| start_on_port port }
    end
    
    # Start the server on a single port
    def start_on_port(port)
      log "Starting #{address}:#{port} ... "
      
      run :start, @options, port
    end
  
    # Stop the servers
    def stop
      with_each_server { |port| stop_on_port port }
    end
    
    # Stop the server running on +port+
    def stop_on_port(port)
      log "Stopping #{address}:#{port} ... "
      
      run :stop, @options, port
    end
    
    # Stop and start the servers.
    def restart
      stop
      sleep 0.1 # Let's breath a bit shall we ?
      start
    end
    
    def log_file_for(port)
      include_port_number log_file, port
    end
    
    def pid_file_for(port)
      include_port_number pid_file, port
    end
    
    def pid_for(port)
      File.read(pid_file_for(port)).chomp.to_i
    end
    
    private
      # Send the command to the +thin+ script
      def run(cmd, options, port)
        shell_cmd = shellify(cmd, options.merge(:port => port, :pid => pid_file_for(port), :log => log_file_for(port)))
        trace shell_cmd
        ouput = `#{shell_cmd}`.chomp
        log ouput unless ouput.empty?
      end
      
      # Turn into a runnable shell command
      def shellify(cmd, options)
        shellified_options = options.inject([]) do |args, (name, value)|
          args << case value
          when NilClass
          when TrueClass then "--#{name}"
          else "--#{name.to_s.tr('_', '-')}=#{value.inspect}"
          end
        end
        "#{self.class.thin_script} #{cmd} #{shellified_options.compact.join(' ')}"
      end
      
      def with_each_server
        @size.times { |n| yield first_port + n }
      end
      
      # Add the port numbers in the filename
      # so each instance get its own file
      def include_port_number(path, port)
        raise ArgumentError, "filename '#{path}' must include an extension" unless path =~ /\./
        path.gsub(/\.(.+)$/) { ".#{port}.#{$1}" }
      end
  end
end