wycats-merb-core 0.9.8 → 0.9.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. data/CHANGELOG +136 -2
  2. data/CONTRIBUTORS +6 -0
  3. data/PUBLIC_CHANGELOG +15 -0
  4. data/Rakefile +12 -14
  5. data/lib/merb-core.rb +82 -43
  6. data/lib/merb-core/bootloader.rb +268 -60
  7. data/lib/merb-core/config.rb +119 -34
  8. data/lib/merb-core/controller/abstract_controller.rb +58 -18
  9. data/lib/merb-core/controller/exceptions.rb +2 -15
  10. data/lib/merb-core/controller/merb_controller.rb +28 -1
  11. data/lib/merb-core/controller/mime.rb +4 -0
  12. data/lib/merb-core/controller/mixins/controller.rb +14 -17
  13. data/lib/merb-core/controller/mixins/render.rb +23 -28
  14. data/lib/merb-core/controller/mixins/responder.rb +0 -1
  15. data/lib/merb-core/controller/template.rb +44 -20
  16. data/lib/merb-core/core_ext/kernel.rb +8 -3
  17. data/lib/merb-core/dispatch/default_exception/default_exception.rb +1 -1
  18. data/lib/merb-core/dispatch/default_exception/views/_css.html.erb +3 -1
  19. data/lib/merb-core/dispatch/default_exception/views/_javascript.html.erb +71 -67
  20. data/lib/merb-core/dispatch/default_exception/views/index.html.erb +6 -2
  21. data/lib/merb-core/dispatch/dispatcher.rb +5 -9
  22. data/lib/merb-core/dispatch/request.rb +46 -57
  23. data/lib/merb-core/dispatch/router.rb +83 -6
  24. data/lib/merb-core/dispatch/router/behavior.rb +87 -27
  25. data/lib/merb-core/dispatch/router/resources.rb +281 -167
  26. data/lib/merb-core/dispatch/router/route.rb +141 -27
  27. data/lib/merb-core/logger.rb +213 -202
  28. data/lib/merb-core/rack.rb +3 -1
  29. data/lib/merb-core/rack/adapter.rb +7 -4
  30. data/lib/merb-core/rack/adapter/ebb.rb +12 -13
  31. data/lib/merb-core/rack/adapter/evented_mongrel.rb +2 -15
  32. data/lib/merb-core/rack/adapter/irb.rb +3 -2
  33. data/lib/merb-core/rack/adapter/mongrel.rb +22 -15
  34. data/lib/merb-core/rack/adapter/swiftiplied_mongrel.rb +4 -16
  35. data/lib/merb-core/rack/adapter/thin.rb +21 -22
  36. data/lib/merb-core/rack/adapter/thin_turbo.rb +4 -11
  37. data/lib/merb-core/rack/adapter/webrick.rb +54 -18
  38. data/lib/merb-core/rack/handler/mongrel.rb +12 -13
  39. data/lib/merb-core/rack/middleware/csrf.rb +1 -1
  40. data/lib/merb-core/server.rb +135 -98
  41. data/lib/merb-core/tasks/gem_management.rb +50 -12
  42. data/lib/merb-core/tasks/merb.rb +1 -0
  43. data/lib/merb-core/tasks/merb_rake_helper.rb +9 -38
  44. data/lib/merb-core/tasks/stats.rake +2 -2
  45. data/lib/merb-core/test.rb +9 -3
  46. data/lib/merb-core/test/helpers.rb +1 -0
  47. data/lib/merb-core/test/helpers/multipart_request_helper.rb +3 -2
  48. data/lib/merb-core/test/helpers/request_helper.rb +40 -372
  49. data/lib/merb-core/test/helpers/route_helper.rb +15 -7
  50. data/lib/merb-core/test/matchers.rb +1 -0
  51. data/lib/merb-core/test/matchers/controller_matchers.rb +4 -247
  52. data/lib/merb-core/test/matchers/view_matchers.rb +22 -4
  53. data/lib/merb-core/test/run_specs.rb +117 -25
  54. data/lib/merb-core/version.rb +1 -1
  55. metadata +1 -1
  56. data/lib/merb-core/vendor/facets.rb +0 -2
  57. data/lib/merb-core/vendor/facets/dictionary.rb +0 -433
  58. data/lib/merb-core/vendor/facets/inflect.rb +0 -342
@@ -3,6 +3,7 @@ module Merb
3
3
  module Rack
4
4
  autoload :Application, 'merb-core/rack/application'
5
5
  autoload :Adapter, 'merb-core/rack/adapter'
6
+ autoload :AbstractAdapter, 'merb-core/rack/adapter/abstract'
6
7
  autoload :Ebb, 'merb-core/rack/adapter/ebb'
7
8
  autoload :EventedMongrel, 'merb-core/rack/adapter/evented_mongrel'
8
9
  autoload :FastCGI, 'merb-core/rack/adapter/fcgi'
@@ -21,5 +22,6 @@ module Merb
21
22
  autoload :ContentLength, 'merb-core/rack/middleware/content_length'
22
23
  autoload :ConditionalGet, 'merb-core/rack/middleware/conditional_get'
23
24
  autoload :Csrf, 'merb-core/rack/middleware/csrf'
25
+ autoload :StreamWrapper, 'merb-core/rack/stream_wrapper'
24
26
  end # Rack
25
- end # Merb
27
+ end # Merb
@@ -11,7 +11,11 @@ module Merb
11
11
  # ==== Returns.
12
12
  # Class:: The adapter class.
13
13
  def get(id)
14
- Object.full_const_get(@adapters[id])
14
+ if @adapters[id.to_s]
15
+ Object.full_const_get(@adapters[id.to_s])
16
+ else
17
+ Merb.fatal! "The adapter #{id} did not exist"
18
+ end
15
19
  end
16
20
 
17
21
  # Registers a new Rack adapter.
@@ -36,9 +40,8 @@ module Merb
36
40
  Adapter.register %w{runner}, :Runner
37
41
  Adapter.register %w{smongrel swift}, :SwiftipliedMongrel
38
42
  Adapter.register %w{thin}, :Thin
39
- Adapter.register %w{thin-turbo}, :ThinTurbo
43
+ Adapter.register %w{thin-turbo tt}, :ThinTurbo
40
44
  Adapter.register %w{webrick}, :WEBrick
41
45
 
42
46
  end # Rack
43
- end # Merb
44
-
47
+ end # Merb
@@ -3,22 +3,21 @@ module Merb
3
3
 
4
4
  module Rack
5
5
 
6
- class Ebb
6
+ class Ebb < Merb::Rack::AbstractAdapter
7
7
  # start an Ebb server on given host and port.
8
8
 
9
- # ==== Parameters
10
- # opts<Hash>:: Options for Ebb (see below).
11
- #
12
- # ==== Options (opts)
13
- # :host<String>:: The hostname that Ebb should serve.
14
- # :port<Fixnum>:: The port Ebb should bind to.
15
- # :app:: The application
16
- def self.start(opts={})
17
- Merb.logger.warn!("Using Ebb adapter")
9
+ def self.new_server(port)
18
10
  Merb::Dispatcher.use_mutex = false
19
- th = Thread.new { ::Ebb.start_server(opts[:app], opts) }
20
- Merb::Server.change_privilege
21
- th.join
11
+ opts = @opts.merge(:port => port)
12
+ @th = Thread.new { Thread.current[:server] = ::Ebb.start_server(opts[:app], opts) }
13
+ end
14
+
15
+ def self.start_server
16
+ @th.join
17
+ end
18
+
19
+ def self.stop(status = 0)
20
+ ::Ebb.stop_server
22
21
  end
23
22
  end
24
23
  end
@@ -4,22 +4,9 @@ module Merb
4
4
  module Rack
5
5
 
6
6
  class EventedMongrel < Mongrel
7
- # Starts Mongrel as evented.
8
- #
9
- # ==== Parameters
10
- # opts<Hash>:: Options for Mongrel (see below).
11
- #
12
- # ==== Options (opts)
13
- # :host<String>:: The hostname that Mongrel should serve.
14
- # :port<Fixnum>:: The port Mongrel should bind to.
15
- # :app<String>>:: The application name.
16
- def self.start(opts={})
17
- Merb.logger.warn!("Using EventedMongrel adapter")
7
+ def self.new_server(port)
18
8
  Merb::Dispatcher.use_mutex = false
19
- server = ::Mongrel::HttpServer.new(opts[:host], opts[:port].to_i)
20
- Merb::Server.change_privilege
21
- server.register('/', ::Merb::Rack::Handler::Mongrel.new(opts[:app]))
22
- server.run.join
9
+ super
23
10
  end
24
11
  end
25
12
  end
@@ -10,8 +10,9 @@ module Merb
10
10
  #
11
11
  # ==== Alternatives
12
12
  # If name is a hash, it will be merged with params.
13
- def url(name, params={})
14
- Merb::Router.generate(name, params)
13
+ def url(name, *args)
14
+ args << {}
15
+ Merb::Router.url(name, *args)
15
16
  end
16
17
 
17
18
  # Reloads classes using Merb::BootLoader::ReloadClasses.
@@ -4,23 +4,30 @@ module Merb
4
4
 
5
5
  module Rack
6
6
 
7
- class Mongrel
8
- # start server on given host and port.
7
+ class Mongrel < Merb::Rack::AbstractAdapter
8
+
9
+ def self.stop(status = 0)
10
+ if @server
11
+ begin
12
+ @server.stop(true)
13
+ rescue Mongrel::TimeoutError
14
+ Merb.logger.fatal! "Your process took too long to shut " \
15
+ "down, so mongrel killed it."
16
+ end
17
+ true
18
+ end
19
+ end
9
20
 
10
- # ==== Parameters
11
- # opts<Hash>:: Options for Mongrel (see below).
12
- #
13
- # ==== Options (opts)
14
- # :host<String>:: The hostname that Mongrel should serve.
15
- # :port<Fixnum>:: The port Mongrel should bind to.
16
- # :app<String>>:: The application name.
17
- def self.start(opts={})
18
- Merb.logger.warn!("Using Mongrel adapter")
19
- server = ::Mongrel::HttpServer.new(opts[:host], opts[:port].to_i)
20
- Merb::Server.change_privilege
21
- server.register('/', ::Merb::Rack::Handler::Mongrel.new(opts[:app]))
22
- server.run.join
21
+ def self.new_server(port)
22
+ @server = ::Mongrel::HttpServer.new(@opts[:host], port)
23
23
  end
24
+
25
+ def self.start_server
26
+ @server.register('/', ::Merb::Rack::Handler::Mongrel.new(@opts[:app]))
27
+ @server.run.join
28
+ end
29
+
24
30
  end
31
+
25
32
  end
26
33
  end
@@ -4,23 +4,11 @@ module Merb
4
4
  module Rack
5
5
 
6
6
  class SwiftipliedMongrel < Mongrel
7
- # Starts Mongrel as swift.
8
- #
9
- # ==== Parameters
10
- # opts<Hash>:: Options for Mongrel (see below).
11
- #
12
- # ==== Options (opts)
13
- # :host<String>:: The hostname that Mongrel should serve.
14
- # :port<Fixnum>:: The port Mongrel should bind to.
15
- # :app<String>>:: The application name.
16
- def self.start(opts={})
17
- Merb.logger.warn!("Using SwiftipliedMongrel adapter")
7
+ def self.new_server(port)
18
8
  Merb::Dispatcher.use_mutex = false
19
- server = ::Mongrel::HttpServer.new(opts[:host], opts[:port].to_i)
20
- Merb::Server.change_privilege
21
- server.register('/', ::Merb::Rack::Handler::Mongrel.new(opts[:app]))
22
- server.run.join
23
- end
9
+ super
10
+ end
24
11
  end
12
+
25
13
  end
26
14
  end
@@ -4,35 +4,34 @@ module Merb
4
4
 
5
5
  module Rack
6
6
 
7
- class Thin
7
+ class Thin < Merb::Rack::AbstractAdapter
8
8
  # start a Thin server on given host and port.
9
9
 
10
- # ==== Parameters
11
- # opts<Hash>:: Options for Thin (see below).
12
- #
13
- # ==== Options (opts)
14
- # :host<String>:: The hostname that Thin should serve.
15
- # :port<Fixnum>:: The port Thin should bind to.
16
- # :socket<Fixnum>>:: The socket number that thin should bind to.
17
- # :socket_file<String>>:: The socket file that thin should attach to.
18
- # :app<String>>:: The application name.
19
- def self.start(opts={})
10
+ def self.new_server(port)
20
11
  Merb::Dispatcher.use_mutex = false
21
- if opts[:socket] || opts[:socket_file]
22
- socket = opts[:socket] || "0"
23
- socket_file = opts[:socket_file] || "#{Merb.root}/log/merb.#{socket}.sock"
12
+
13
+ if (@opts[:socket] || @opts[:socket_file])
14
+ socket = port.to_s
15
+ socket_file = @opts[:socket_file] || "#{Merb.log_path}/#{Merb::Config[:name]}.#{socket}.sock"
24
16
  Merb.logger.warn!("Using Thin adapter with socket file #{socket_file}.")
25
- server = ::Thin::Server.new(socket_file, opts[:app], opts)
17
+ @server = ::Thin::Server.new(socket_file, @opts[:app], @opts)
26
18
  else
27
- Merb.logger.warn!("Using Thin adapter on host #{opts[:host]} and port #{opts[:port]}.")
28
- if opts[:host].include?('/')
29
- opts[:host] = "#{opts[:host]}-#{opts[:port]}"
30
- end
31
- server = ::Thin::Server.new(opts[:host], opts[:port].to_i, opts[:app], opts)
19
+ Merb.logger.warn!("Using Thin adapter on host #{@opts[:host]} and port #{port}.")
20
+ @opts[:host] = "#{@opts[:host]}-#{port}" if @opts[:host].include?('/')
21
+ @server = ::Thin::Server.new(@opts[:host], port, @opts[:app], @opts)
32
22
  end
33
- Merb::Server.change_privilege
23
+ end
24
+
25
+ def self.start_server
34
26
  ::Thin::Logging.silent = true
35
- server.start
27
+ @server.start
28
+ end
29
+
30
+ def self.stop(status = 0)
31
+ if @server
32
+ @server.stop
33
+ true
34
+ end
36
35
  end
37
36
  end
38
37
  end
@@ -7,18 +7,11 @@ module Merb
7
7
  class ThinTurbo < Thin
8
8
  # start a Thin Turbo server on given host and port.
9
9
 
10
- # ==== Parameters
11
- # opts<Hash>:: Options for Thin Turbo (see below).
12
- #
13
- # ==== Options (opts)
14
- # :host<String>:: The hostname that Thin Turbo should serve.
15
- # :port<Fixnum>:: The port Thin Turbo should bind to.
16
- # :socket<Fixnum>>:: The socket number that thin should bind to.
17
- # :socket_file<String>>:: The socket file that thin should attach to.
18
- # :app<String>>:: The application name.
19
- def self.start(opts={})
20
- super(opts.merge(:backend => ::Thin::Backends::Turbo))
10
+ def self.new_server(port)
11
+ @opts.merge!(:backend => ::Thin::Backends::Turbo)
12
+ super
21
13
  end
14
+
22
15
  end
23
16
  end
24
17
  end
@@ -1,36 +1,72 @@
1
1
  require 'webrick'
2
+ require 'webrick/utils'
2
3
  require 'rack/handler/webrick'
3
4
  module Merb
4
5
  module Rack
5
6
 
6
- class WEBrick
7
- # start WEBrick server on given host and port.
7
+ class WEBrick < Merb::Rack::AbstractAdapter
8
8
 
9
- # ==== Parameters
10
- # opts<Hash>:: Options for WEBrick (see below).
11
- #
12
- # ==== Options (opts)
13
- # :host<String>:: The hostname that WEBrick should serve.
14
- # :port<Fixnum>:: The port WEBrick should bind to.
15
- # :app<String>>:: The application name.
16
- def self.start(opts={})
17
- Merb.logger.warn!("Using Webrick adapter")
9
+ class << self
10
+ attr_accessor :server
11
+ end
18
12
 
13
+ def self.new_server(port)
19
14
  options = {
20
- :Port => opts[:port],
21
- :BindAddress => opts[:host],
15
+ :Port => port,
16
+ :BindAddress => @opts[:host],
22
17
  :Logger => Merb.logger,
23
18
  :AccessLog => [
24
19
  [Merb.logger, ::WEBrick::AccessLog::COMMON_LOG_FORMAT],
25
20
  [Merb.logger, ::WEBrick::AccessLog::REFERER_LOG_FORMAT]
26
21
  ]
27
22
  }
28
-
29
- server = ::WEBrick::HTTPServer.new(options)
30
- Merb::Server.change_privilege
31
- server.mount("/", ::Rack::Handler::WEBrick, opts[:app])
32
- server.start
23
+
24
+ sockets = ::WEBrick::Utils.create_listeners nil, port
25
+ @server = ::WEBrick::HTTPServer.new(options.merge(:DoNotListen => true))
26
+ @server.listeners.replace sockets
33
27
  end
28
+
29
+ def self.start_server
30
+ @server.mount("/", ::Rack::Handler::WEBrick, @opts[:app])
31
+ @server.start
32
+ exit(@status)
33
+ end
34
+
35
+ def self.stop(status = 0)
36
+ @status = status
37
+ @server.shutdown
38
+ end
39
+
40
+ def self.exit_process(status = 0)
41
+ end
42
+
43
+ # start WEBrick server on given host and port.
44
+
45
+ # ==== Parameters
46
+ # opts<Hash>:: Options for WEBrick (see below).
47
+ #
48
+ # ==== Options (opts)
49
+ # :host<String>:: The hostname that WEBrick should serve.
50
+ # :port<Fixnum>:: The port WEBrick should bind to.
51
+ # :app<String>>:: The application name.
52
+ # def self.start(opts={})
53
+ # Merb.logger.warn!("Using Webrick adapter")
54
+ #
55
+ # options = {
56
+ # :Port => opts[:port],
57
+ # :BindAddress => opts[:host],
58
+ # :Logger => Merb.logger,
59
+ # :AccessLog => [
60
+ # [Merb.logger, ::WEBrick::AccessLog::COMMON_LOG_FORMAT],
61
+ # [Merb.logger, ::WEBrick::AccessLog::REFERER_LOG_FORMAT]
62
+ # ]
63
+ # }
64
+ #
65
+ # server = ::WEBrick::HTTPServer.new(options)
66
+ # Merb::Server.change_privilege
67
+ # server.mount("/", ::Rack::Handler::WEBrick, opts[:app])
68
+ # server.start
69
+ # end
34
70
  end
35
71
  end
36
72
  end
@@ -32,11 +32,15 @@ module Merb
32
32
  # The hostname on which the app should run. Defaults to "0.0.0.0"
33
33
  # :Port<Fixnum>:: The port for the app. Defaults to 8080.
34
34
  def self.run(app, options={})
35
- server = ::Mongrel::HttpServer.new(options[:Host] || '0.0.0.0',
35
+ @server = ::Mongrel::HttpServer.new(options[:Host] || '0.0.0.0',
36
36
  options[:Port] || 8080)
37
- server.register('/', ::Merb::Rack::Handler::Mongrel.new(app))
38
- yield server if block_given?
39
- server.run.join
37
+ @server.register('/', ::Merb::Rack::Handler::Mongrel.new(app))
38
+ yield @server if block_given?
39
+ @server.run.join
40
+ end
41
+
42
+ def self.stop(block = true)
43
+ @server.stop
40
44
  end
41
45
 
42
46
  # ==== Parameters
@@ -63,8 +67,7 @@ module Merb
63
67
  "rack.multiprocess" => false, # ???
64
68
  "rack.run_once" => false,
65
69
 
66
- "rack.url_scheme" => "http",
67
- "rack.streaming" => true
70
+ "rack.url_scheme" => "http"
68
71
  })
69
72
  env["QUERY_STRING"] ||= ""
70
73
  env.delete "PATH_INFO" if env["PATH_INFO"] == ""
@@ -79,13 +82,9 @@ module Merb
79
82
  }
80
83
  }
81
84
 
82
- if Proc === body
83
- body.call(response)
84
- else
85
- body.each { |part|
86
- response.body << part
87
- }
88
- end
85
+ body.each { |part|
86
+ response.body << part
87
+ }
89
88
  response.finished
90
89
  ensure
91
90
  body.close if body.respond_to? :close
@@ -10,7 +10,7 @@ module Merb
10
10
 
11
11
  def call(env)
12
12
  status, header, body = @app.call(env)
13
-
13
+ body = body.to_s
14
14
  if env[Merb::Const::REQUEST_METHOD] == Merb::Const::GET
15
15
  body = process_response(body) if valid_content_type?(header[Merb::Const::CONTENT_TYPE])
16
16
  elsif env[Merb::Const::REQUEST_METHOD] == Merb::Const::POST
@@ -1,4 +1,5 @@
1
1
  require 'etc'
2
+
2
3
  module Merb
3
4
 
4
5
  # Server encapsulates the management of Merb daemons.
@@ -18,46 +19,25 @@ module Merb
18
19
  # If cluster is left out, then one process will be started. This process
19
20
  # will be daemonized if Merb::Config[:daemonize] is true.
20
21
  def start(port, cluster=nil)
22
+
21
23
  @port = port
22
- @cluster = cluster
23
- if @cluster
24
- @port.to_i.upto(@port.to_i + @cluster.to_i-1) do |port|
25
- pidfile = pid_file(port)
26
- pid = IO.read(pidfile).chomp.to_i if File.exist?(pidfile)
24
+ @cluster = cluster
27
25
 
28
- unless alive?(port)
29
- remove_pid_file(port)
30
- puts "Starting merb server on port #{port}, pid file: #{pidfile} and process id is #{pid}" if Merb::Config[:verbose]
31
- daemonize(port)
32
- else
33
- raise "Merb is already running: port is #{port}, pid file: #{pidfile}, process id is #{pid}"
34
- end
35
- end
36
- elsif Merb::Config[:daemonize]
26
+ if Merb::Config[:daemonize]
37
27
  pidfile = pid_file(port)
38
- pid = IO.read(pidfile).chomp.to_i if File.exist?(pidfile)
28
+ pid = File.read(pidfile).chomp.to_i if File.exist?(pidfile)
39
29
 
40
30
  unless alive?(@port)
41
31
  remove_pid_file(@port)
42
32
  puts "Daemonizing..." if Merb::Config[:verbose]
43
33
  daemonize(@port)
44
34
  else
45
- raise "Merb is already running: port is #{port}, pid file: #{pidfile}, process id is #{pid}"
35
+ Merb.fatal! "Merb is already running on port #{port}.\n" \
36
+ "\e[0m \e[1;31;47mpid file: \e[34;47m#{pidfile}" \
37
+ "\e[1;31;47m, process id is \e[34;47m#{pid}."
46
38
  end
47
39
  else
48
- trap('TERM') { exit }
49
-
50
- if Merb::Config[:console_trap]
51
- add_irb_trap
52
- else
53
- trap('INT') { puts "\nExiting"; exit }
54
- end
55
-
56
- puts "Running bootloaders..." if Merb::Config[:verbose]
57
- BootLoader.run
58
- puts "Starting Rack adapter..." if Merb::Config[:verbose]
59
- Merb.logger.info! "Starting Merb server listening at #{Merb::Config[:host]}:#{port}"
60
- Merb.adapter.start(Merb::Config.to_hash)
40
+ bootup
61
41
  end
62
42
  end
63
43
 
@@ -71,12 +51,14 @@ module Merb
71
51
  puts "About to check if port #{port} is alive..." if Merb::Config[:verbose]
72
52
  pidfile = pid_file(port)
73
53
  puts "Pidfile is #{pidfile}..." if Merb::Config[:verbose]
74
- pid = IO.read(pidfile).chomp.to_i
54
+ pid = File.read(pidfile).chomp.to_i
75
55
  puts "Process id is #{pid}" if Merb::Config[:verbose]
76
56
  Process.kill(0, pid)
77
57
  true
78
- rescue
58
+ rescue Errno::ESRCH, Errno::ENOENT
79
59
  false
60
+ rescue Errno::EACCES => e
61
+ Merb.fatal!("You don't have access to the PID file at #{pidfile}.", e)
80
62
  end
81
63
 
82
64
  # ==== Parameters
@@ -86,32 +68,51 @@ module Merb
86
68
  # ==== Alternatives
87
69
  # If you pass "all" as the port, the signal will be sent to all Merb
88
70
  # processes.
89
- def kill(port, sig=9)
71
+ def kill(port, sig="INT")
90
72
  Merb::BootLoader::BuildFramework.run
91
- begin
92
- pidfiles = port == "all" ?
93
- pid_files : [ pid_file(port) ]
73
+ if sig == 9 && port == "main"
74
+ kill_pid("INT", pid_file("main"))
75
+ Dir["#{Merb.log_path}" / "*.pid"].each do |file|
76
+ kill_pid(9, file)
77
+ end
78
+ else
79
+ kill_pid(sig, pid_file(port))
80
+ end
81
+
82
+ if sig.is_a?(Integer)
83
+ sig = Signal.list.invert[sig]
84
+ end
94
85
 
95
- pidfiles.each do |f|
96
- pid = IO.read(f).chomp.to_i
97
- begin
98
- Process.kill(sig, pid)
99
- FileUtils.rm(f) if File.exist?(f)
100
- puts "killed PID #{pid} with signal #{sig}"
101
- rescue Errno::EINVAL
102
- puts "Failed to kill PID #{pid}: '#{sig}' is an invalid or unsupported signal number."
103
- rescue Errno::EPERM
104
- puts "Failed to kill PID #{pid}: Insufficient permissions."
105
- rescue Errno::ESRCH
106
- puts "Failed to kill PID #{pid}: Process is deceased or zombie."
107
- FileUtils.rm f
108
- rescue Exception => e
109
- puts "Failed to kill PID #{pid}: #{e.message}"
110
- end
86
+ if sig == "KILL" && port == "main"
87
+ Merb.fatal! "Killed all PIDs with signal KILL"
88
+ else
89
+ Merb.fatal! "Killed #{port} with signal #{sig}"
90
+ end
91
+ end
92
+
93
+ def kill_pid(sig, file)
94
+ begin
95
+ pid = File.read(file).chomp.to_i
96
+ Merb.logger.warn! "Killing pid #{pid}"
97
+ Process.kill(sig, pid)
98
+ FileUtils.rm(file) if File.exist?(file)
99
+ rescue Errno::EINVAL
100
+ Merb.fatal! "Failed to kill PID #{pid}: '#{sig}' is an invalid " \
101
+ "or unsupported signal number."
102
+ rescue Errno::EPERM
103
+ Merb.fatal! "Failed to kill PID #{pid}: Insufficient permissions."
104
+ rescue Errno::ESRCH
105
+ FileUtils.rm file
106
+ Merb.fatal! "Failed to kill PID #{pid}: Process is " \
107
+ "deceased or zombie."
108
+ rescue Errno::EACCES => e
109
+ Merb.fatal! e.message, e
110
+ rescue Errno::ENOENT => e
111
+ Merb.fatal! "Could not find a PID file at #{file}", e
112
+ rescue Exception => e
113
+ if !e.is_a?(SystemExit)
114
+ Merb.fatal! "Failed to kill PID #{pid}", e
111
115
  end
112
- ensure
113
- Merb.started = false
114
- exit
115
116
  end
116
117
  end
117
118
 
@@ -122,28 +123,45 @@ module Merb
122
123
  fork do
123
124
  Process.setsid
124
125
  exit if fork
126
+ Merb.logger.warn! "In #{Process.pid}" if Merb.logger
125
127
  File.umask 0000
126
128
  STDIN.reopen "/dev/null"
127
129
  STDOUT.reopen "/dev/null", "a"
128
130
  STDERR.reopen STDOUT
129
- trap("TERM") { exit }
130
- Dir.chdir Merb::Config[:merb_root]
131
+ begin
132
+ Dir.chdir Merb::Config[:merb_root]
133
+ rescue Errno::EACCES => e
134
+ Merb.fatal! "You specified #{Merb::Config[:merb_root]} " \
135
+ "as the Merb root, but you did not have access to it.", e
136
+ end
131
137
  at_exit { remove_pid_file(port) }
132
138
  Merb::Config[:port] = port
133
- BootLoader.run
134
- Merb.adapter.start(Merb::Config.to_hash)
139
+ bootup
135
140
  end
141
+ rescue NotImplementedError => e
142
+ Merb.fatal! "Daemonized mode is not supported on your platform", e
143
+ end
144
+
145
+ def bootup
146
+ Merb.trap('TERM') { exit }
147
+
148
+ puts "Running bootloaders..." if Merb::Config[:verbose]
149
+ BootLoader.run
150
+ puts "Starting Rack adapter..." if Merb::Config[:verbose]
151
+ Merb.adapter.start(Merb::Config.to_hash)
136
152
  end
137
153
 
138
154
  def change_privilege
139
- if Merb::Config[:user]
140
- if Merb::Config[:group]
141
- puts "About to change privilege to group #{Merb::Config[:group]} and user #{Merb::Config[:user]}" if Merb::Config[:verbose]
142
- _change_privilege(Merb::Config[:user], Merb::Config[:group])
143
- else
144
- puts "About to change privilege to user #{Merb::Config[:user]}" if Merb::Config[:verbose]
145
- _change_privilege(Merb::Config[:user])
146
- end
155
+ if Merb::Config[:user] && Merb::Config[:group]
156
+ Merb.logger.verbose! "About to change privilege to group " \
157
+ "#{Merb::Config[:group]} and user #{Merb::Config[:user]}"
158
+ _change_privilege(Merb::Config[:user], Merb::Config[:group])
159
+ elsif Merb::Config[:user]
160
+ Merb.logger.verbose! "About to change privilege to user " \
161
+ "#{Merb::Config[:user]}"
162
+ _change_privilege(Merb::Config[:user])
163
+ else
164
+ return true
147
165
  end
148
166
  end
149
167
 
@@ -160,8 +178,10 @@ module Merb
160
178
  # instead of the port based PID file.
161
179
  def remove_pid_file(port)
162
180
  pidfile = pid_file(port)
163
- puts "Removing pid file #{pidfile} (port is #{port})..."
164
- FileUtils.rm(pidfile) if File.exist?(pidfile)
181
+ if File.exist?(pidfile)
182
+ puts "Removing pid file #{pidfile} (port is #{port})..."
183
+ FileUtils.rm(pidfile)
184
+ end
165
185
  end
166
186
 
167
187
  # Stores a PID file on the filesystem.
@@ -176,11 +196,23 @@ module Merb
176
196
  # If Merb::Config[:pid_file] has been specified, that will be used
177
197
  # instead of the port based PID file.
178
198
  def store_pid(port)
179
- pidfile = pid_file(port)
180
- puts "Storing pid file to #{pidfile}..."
181
- FileUtils.mkdir_p(File.dirname(pidfile)) unless File.directory?(File.dirname(pidfile))
182
- puts "Created directory, writing process id..." if Merb::Config[:verbose]
183
- File.open(pidfile, 'w'){ |f| f.write("#{Process.pid}") }
199
+ store_details(port)
200
+ end
201
+
202
+ def remove_pid(port)
203
+ FileUtils.rm(pid_file(port)) if File.file?(pid_file(port))
204
+ end
205
+
206
+ def store_details(port = nil)
207
+ file = pid_file(port)
208
+ begin
209
+ FileUtils.mkdir_p(File.dirname(file))
210
+ rescue Errno::EACCES => e
211
+ Merb.fatal! "You tried to store Merb logs in #{File.dirname(file)}, " \
212
+ "but you did not have access.", e
213
+ end
214
+ Merb.logger.warn! "Storing #{type} file to #{file}..." if Merb::Config[:verbose]
215
+ File.open(file, 'w'){ |f| f.write(Process.pid.to_s) }
184
216
  end
185
217
 
186
218
  # Gets the pid file for the specified port.
@@ -194,20 +226,8 @@ module Merb
194
226
  # Location of pid file for specified port. If clustered and pid_file option
195
227
  # is specified, it adds the port value to the path.
196
228
  def pid_file(port)
197
- if Merb::Config[:pid_file]
198
- pidfile = Merb::Config[:pid_file]
199
- if Merb::Config[:cluster]
200
- ext = File.extname(Merb::Config[:pid_file])
201
- base = File.basename(Merb::Config[:pid_file], ext)
202
- dir = File.dirname(Merb::Config[:pid_file])
203
- File.join(dir, "#{base}.#{port}#{ext}")
204
- else
205
- Merb::Config[:pid_file]
206
- end
207
- else
208
- pidfile = Merb.log_path / "merb.#{port}.pid"
209
- Merb.log_path / "merb.#{port}.pid"
210
- end
229
+ pidfile = Merb::Config[:pid_file] || (Merb.log_path / "merb.%s.pid")
230
+ pidfile % port
211
231
  end
212
232
 
213
233
  # Get a list of the pid files.
@@ -218,10 +238,7 @@ module Merb
218
238
  def pid_files
219
239
  if Merb::Config[:pid_file]
220
240
  if Merb::Config[:cluster]
221
- ext = File.extname(Merb::Config[:pid_file])
222
- base = File.basename(Merb::Config[:pid_file], ext)
223
- dir = File.dirname(Merb::Config[:pid_file])
224
- Dir[dir / "#{base}.*#{ext}"]
241
+ Dir[Merb::Config[:pid_file] % "*"]
225
242
  else
226
243
  [ Merb::Config[:pid_file] ]
227
244
  end
@@ -240,11 +257,25 @@ module Merb
240
257
  # If group is left out, the user will be used as the group.
241
258
  def _change_privilege(user, group=user)
242
259
 
243
- puts "Changing privileges to #{user}:#{group}"
260
+ Merb.logger.warn! "Changing privileges to #{user}:#{group}"
244
261
 
245
262
  uid, gid = Process.euid, Process.egid
246
- target_uid = Etc.getpwnam(user).uid
247
- target_gid = Etc.getgrnam(group).gid
263
+
264
+ begin
265
+ target_uid = Etc.getpwnam(user).uid
266
+ rescue ArgumentError => e
267
+ Merb.fatal!(
268
+ "You tried to use user #{user}, but no such user was found", e)
269
+ return false
270
+ end
271
+
272
+ begin
273
+ target_gid = Etc.getgrnam(group).gid
274
+ rescue ArgumentError => e
275
+ Merb.fatal!(
276
+ "You tried to use group #{group}, but no such group was found", e)
277
+ return false
278
+ end
248
279
 
249
280
  if uid != target_uid || gid != target_gid
250
281
  # Change process ownership
@@ -252,13 +283,19 @@ module Merb
252
283
  Process::GID.change_privilege(target_gid)
253
284
  Process::UID.change_privilege(target_uid)
254
285
  end
286
+ true
255
287
  rescue Errno::EPERM => e
256
- puts "Couldn't change user and group to #{user}:#{group}: #{e}"
288
+ Merb.fatal! "Couldn't change user and group to #{user}:#{group}", e
289
+ false
257
290
  end
258
291
 
259
292
  def add_irb_trap
260
- trap('INT') do
261
- exit if @interrupted
293
+ Merb.trap('INT') do
294
+ if @interrupted
295
+ puts "Exiting\n"
296
+ exit
297
+ end
298
+
262
299
  @interrupted = true
263
300
  puts "Interrupt a second time to quit"
264
301
  Kernel.sleep 1.5
@@ -271,7 +308,7 @@ module Merb
271
308
  IRB.conf[:MAIN_CONTEXT] = @irb.context
272
309
  end
273
310
 
274
- trap(:INT) { @irb.signal_handle }
311
+ Merb.trap(:INT) { @irb.signal_handle }
275
312
  catch(:IRB_EXIT) { @irb.eval_input }
276
313
 
277
314
  puts "Exiting IRB mode, back in server mode"