public
Rubygem
Description: Merb Core: All you need. None you don't.
Homepage: http://www.merbivore.com
Clone URL: git://github.com/wycats/merb-core.git
Merge in forking branch.

* Merb starts with master and worker processes now.
* Logger is now 100% thread safe and works on current JRuby.
* New options for fast redeploy/restart of cluster.
* Reloading machinery should be bullet proof now.

Squashed commit of the following:

commit 4405ac5db172a40d533b71a0a57a3233aea13435
Author: Yehuda Katz <wycats@gmail.com>
Date:   Sun Sep 28 01:24:39 2008 -0400

    Add --fast-deploy option

commit 247779ebe96100b49690581801a897636e72b36a
Author: Yehuda Katz <wycats@gmail.com>
Date:   Sun Sep 28 01:19:39 2008 -0400

    Operator precedence ftl

commit 55dcdc6fbc29257dcaf25997d59cdf66fb66a507
Author: Yehuda Katz <wycats@gmail.com>
Date:   Sun Sep 28 00:31:53 2008 -0400

    Somehow the code that supports a single log file went missing :P

commit afc291cd9d5c660c4071741b21e79a3df62223f2
Author: Yehuda Katz <wycats@gmail.com>
Date:   Sat Sep 27 16:23:28 2008 -0400

    Tests pass: we don't need to fork for class load in test env, plus update 
    config spec for some small changes to the internal API

commit 67e1756c267edb27f0229e0e7f9883736868742a
Author: Yehuda Katz <wycats@gmail.com>
Date:   Sat Sep 27 16:14:16 2008 -0400

    get merb -k and merb -K to work (with and without `all')

commit eb52fcec508a458af4d2d286223960b24a2954c3
Author: Yehuda Katz <wycats@gmail.com>
Date:   Sat Sep 27 16:13:48 2008 -0400

    Fix bug with creating a new log

commit 70a47b4b70c67a0328f1845fab0f9ef258a1ec87
Author: Yehuda Katz <wycats@gmail.com>
Date:   Fri Sep 26 23:58:28 2008 -0700

    Reload spec needs to be added back later; logger specs work again after 
    removing mocks and modifying specs for public API changes

commit fdc4ec0c04073e4ffca7d484152dbaedf02f1f97
Author: Yehuda Katz <wycats@gmail.com>
Date:   Fri Sep 26 23:38:57 2008 -0700

    Fix logger helper

commit d22b8e6c60b48718e7484a1154a2211c5fa17404
Author: Yehuda Katz <wycats@gmail.com>
Date:   Fri Sep 26 22:12:14 2008 -0700

    Attempt threadsafe logger

commit 205f2436a741841abc92901c015f3c470a5b99b7
Author: Yehuda Katz <wycats@gmail.com>
Date:   Thu Sep 25 23:57:07 2008 -0700

    Hide the backtrace for Mongrel timeout errors.

commit 27779e1f857c3555c87451a1011bbeee646fa1f5
Author: Yehuda Katz <wycats@gmail.com>
Date:   Thu Sep 25 23:22:17 2008 -0700

    Respawn processes killed with kill -9

commit f4fd0d39ea76e1722ee115f15f307732603d7ce4
Author: Yehuda Katz <wycats@gmail.com>
Date:   Thu Sep 25 23:18:02 2008 -0700

    Tons of comments; support for various forking configurations

commit acb90a6e57e075717a235f5896df545937880416
Author: Yehuda Katz <wycats@gmail.com>
Date:   Thu Sep 25 22:32:13 2008 -0700

    Forking setup works without any forking.

commit ab4f142a2433452c940696d2f6bd9e23e375ea20
Author: Yehuda Katz <wycats@gmail.com>
Date:   Thu Sep 25 17:59:58 2008 -0700

    Update thin for new stop() API

commit e5937d5a6a08d29fcf0d40b3eb6e18a6dc7191a7
Author: Yehuda Katz <wycats@gmail.com>
Date:   Thu Sep 25 17:58:35 2008 -0700

    Added better errors for failures originating with Merb

commit 04e2a9b4e7e997613d0f712d4d79612304066808
Author: Yehuda Katz <wycats@gmail.com>
Date:   Thu Sep 25 17:57:20 2008 -0700

    Checked in experiments (for posterity)

commit 7e8385254168c99e39208fbadbdb4758c9f52b92
Author: Yehuda Katz <wycats@gmail.com>
Date:   Thu Sep 25 13:16:56 2008 -0700

    All Ruby-based servers work now with forking; NEXT: Make forking only happen 
    when appropriate

commit abe9f500695154adc0da4535652ef8a070435875
Author: Yehuda Katz <wycats@gmail.com>
Date:   Thu Sep 25 01:35:33 2008 -0700

    Help REE make better use of GC

commit 388941a35c0454153f0e80ae9ec9deb44c5d2252
Author: Yehuda Katz <wycats@gmail.com>
Date:   Wed Sep 24 23:47:13 2008 -0700

    Ebb works

commit a5b48f4ba2ac499a020d748466f8cba2a8223fb6
Author: Yehuda Katz <wycats@gmail.com>
Date:   Wed Sep 24 22:26:07 2008 -0700

    Someone should figure out why I had to make the change I needed to in 
    config.rb; abstract up the API for making a new forkable process; thin-turbo 
    appears to have some issues

commit 6e2a107c4bec7281ff944b92dd426f1c47137663
Author: Yehuda Katz <wycats@gmail.com>
Date:   Wed Sep 24 12:10:55 2008 -0700

    Fix some more issues relating to graceful exits; remove pids on exit; TODO: 
    merb -k must work

commit 717d5a8667a1fc206061c007e00528643676fb5e
Author: Yehuda Katz <wycats@gmail.com>
Date:   Wed Sep 24 00:05:26 2008 -0700

    Tighten things up so that ctrl-c works correctly

commit 76eba224d0beed7a32a2e20826f0f0574a4e20c9
Author: Yehuda Katz <wycats@gmail.com>
Date:   Tue Sep 23 21:18:20 2008 -0700

    Reload works again

commit ca5d6b2a3df884a074d289a41eeb56f540849686
Author: Yehuda Katz <wycats@gmail.com>
Date:   Tue Sep 23 18:32:55 2008 -0700

    Finish up making mongrel handle everything correctly. We need to update merb 
    -k to use the new signals.

commit 9ec4de2155eab3f398138889584f3dfd25d226c2
Author: Yehuda Katz <wycats@gmail.com>
Date:   Tue Sep 23 10:16:04 2008 -0700

    More forking support

commit ec7cfae89c157333a3df6c57a9b1987caf51f7e4
Author: Yehuda Katz <wycats@gmail.com>
Date:   Sat Sep 20 00:52:47 2008 -0500

    Initial experiments are a success. TODO: Move the logic out of mongrel and 
    make a proper clustering instead of hardcoding to 4

commit aceea3838234535573aa02c2d914d65c11a1ceb5
Author: Yehuda Katz <wycats@gmail.com>
Date:   Fri Sep 19 18:47:09 2008 -0500

    Split out the code transaction into a method and do some experimentation 
    with cluster forking.

commit 56e09f16c302126864c72adf9ecbdf992b29c8a3
Author: Yehuda Katz <wycats@gmail.com>
Date:   Fri Sep 19 13:43:39 2008 -0700

    Better exit message

commit 2b67c34ac05de5c48260baa9a6f0d95f70101ab4
Author: Yehuda Katz <wycats@gmail.com>
Date:   Fri Sep 19 13:10:19 2008 -0700

    Initial support for fast redeploys and code reloading via forks.

commit b7e9fc3e9f08c7970d1b6d69928e832511c7f2fe
Author: Yehuda Katz <wycats@gmail.com>
Date:   Fri Sep 26 23:58:28 2008 -0700

    Reload spec needs to be added back later; logger specs work again after 
    removing mocks and modifying specs for public API changes

commit bdf5c2d82d7420f4604e48f60e69e2d2f037858a
Author: Yehuda Katz <wycats@gmail.com>
Date:   Fri Sep 26 23:38:57 2008 -0700

    Fix logger helper

commit 8b09d459156158673d174a2d86cc32bf5175049c
Author: Yehuda Katz <wycats@gmail.com>
Date:   Fri Sep 26 22:12:14 2008 -0700

    Attempt threadsafe logger

commit b3b0eb25e3037031a34588683be8e6d6c8db621c
Author: Yehuda Katz <wycats@gmail.com>
Date:   Thu Sep 25 23:57:07 2008 -0700

    Hide the backtrace for Mongrel timeout errors.

commit 315d89c21f13b32c6db4d58ce945be361061ca6f
Author: Yehuda Katz <wycats@gmail.com>
Date:   Thu Sep 25 23:22:17 2008 -0700

    Respawn processes killed with kill -9

commit a7ba4dbb14aa0a3e6a56757713f18820b241c982
Author: Yehuda Katz <wycats@gmail.com>
Date:   Thu Sep 25 23:18:02 2008 -0700

    Tons of comments; support for various forking configurations

commit 093d38fcb65f67ac44e786e266475c88f7c16d38
Author: Yehuda Katz <wycats@gmail.com>
Date:   Thu Sep 25 22:32:13 2008 -0700

    Forking setup works without any forking.

commit 9e4d8c861806c782b99b00bcbc98ee1223113fc7
Author: Yehuda Katz <wycats@gmail.com>
Date:   Thu Sep 25 17:59:58 2008 -0700

    Update thin for new stop() API

commit 7c376ee3dd3e797026b5e0917a58c897d8a623f5
Author: Yehuda Katz <wycats@gmail.com>
Date:   Thu Sep 25 17:58:35 2008 -0700

    Added better errors for failures originating with Merb

commit 0ea49649648e307f7289edee928a8485ba96f75f
Author: Yehuda Katz <wycats@gmail.com>
Date:   Thu Sep 25 17:57:20 2008 -0700

    Checked in experiments (for posterity)

commit da7611a85255b5fb80e728bb7af48dfba8ff44d0
Author: Yehuda Katz <wycats@gmail.com>
Date:   Thu Sep 25 13:16:56 2008 -0700

    All Ruby-based servers work now with forking; NEXT: Make forking only happen 
    when appropriate

commit 5cd1901d7edd8cc98a2b83da45b4a85ff741f0e8
Author: Yehuda Katz <wycats@gmail.com>
Date:   Thu Sep 25 01:35:33 2008 -0700

    Help REE make better use of GC

commit 291c3127375786fd64d0808327670d702157f30c
Author: Yehuda Katz <wycats@gmail.com>
Date:   Wed Sep 24 23:47:13 2008 -0700

    Ebb works

commit 956782efc0ab3877eb0161c0b9140fb118db4d4d
Author: Yehuda Katz <wycats@gmail.com>
Date:   Wed Sep 24 22:26:07 2008 -0700

    Someone should figure out why I had to make the change I needed to in 
    config.rb; abstract up the API for making a new forkable process; thin-turbo 
    appears to have some issues

commit 099dc18ff81094a707790e99a922504e661aeba0
Author: Yehuda Katz <wycats@gmail.com>
Date:   Wed Sep 24 12:10:55 2008 -0700

    Fix some more issues relating to graceful exits; remove pids on exit; TODO: 
    merb -k must work

commit d209193a88ab5d522213e4f1747942e1b04b21f3
Author: Yehuda Katz <wycats@gmail.com>
Date:   Wed Sep 24 00:05:26 2008 -0700

    Tighten things up so that ctrl-c works correctly

commit bd10fe6dfcd197b602474f9afd6075db337fb005
Author: Yehuda Katz <wycats@gmail.com>
Date:   Tue Sep 23 21:18:20 2008 -0700

    Reload works again

commit 6c2ebcc54a48ac0ef678ac544ebe22858ed5029c
Author: Yehuda Katz <wycats@gmail.com>
Date:   Tue Sep 23 18:32:55 2008 -0700

    Finish up making mongrel handle everything correctly. We need to update merb 
    -k to use the new signals.

commit 412c4c163dfa363a7c64e047ec4e54c1b61832a4
Author: Yehuda Katz <wycats@gmail.com>
Date:   Tue Sep 23 10:16:04 2008 -0700

    More forking support

commit 94c5eb8d213b107b1c82314cb972649226629376
Author: Yehuda Katz <wycats@gmail.com>
Date:   Sat Sep 20 00:52:47 2008 -0500

    Initial experiments are a success. TODO: Move the logic out of mongrel and 
    make a proper clustering instead of hardcoding to 4

commit 9a57de6ab3253280b90e3e4bf9dc49b1b2a51c61
Author: Yehuda Katz <wycats@gmail.com>
Date:   Fri Sep 19 18:47:09 2008 -0500

    Split out the code transaction into a method and do some experimentation 
    with cluster forking.

commit 71306ed36084efcafcb51f07bb426509f9fafe4a
Author: Yehuda Katz <wycats@gmail.com>
Date:   Fri Sep 19 13:43:39 2008 -0700

    Better exit message

commit b7dac300dc08fc8d89e1e2344c4ec77a5e0ad5b6
Author: Yehuda Katz <wycats@gmail.com>
Date:   Fri Sep 19 13:10:19 2008 -0700

    Initial support for fast redeploys and code reloading via forks.
Sat Sep 27 23:06:08 -0700 2008
commit  a6f6a986ab3aa1414368d24892a808c448ecb564
tree    bd147d0913731b6aaffcc209d49c8d1c3d6fd355
parent  0f4957f2e03ba79eb914563507b0582738fb5d37
...
36
37
38
 
39
40
41
...
93
94
95
 
 
96
97
98
99
100
 
 
101
102
103
104
105
 
106
107
 
 
108
109
110
...
263
264
265
266
 
 
 
 
 
 
 
 
 
267
268
269
270
271
272
273
274
275
276
277
278
279
280
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
281
282
283
...
335
336
337
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
338
339
340
...
36
37
38
39
40
41
42
...
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
 
110
111
112
113
114
115
116
117
...
270
271
272
 
273
274
275
276
277
278
279
280
281
282
283
284
285
286
 
 
 
 
 
 
 
 
 
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
...
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
0
@@ -36,6 +36,7 @@ module Merb
0
   module GlobalHelpers; end
0
   
0
   class << self
0
+    attr_accessor :exiting
0
 
0
     # Merge environment settings
0
     # Can allow you to have a "localdev" that runs like your "development"
0
@@ -93,18 +94,24 @@ module Merb
0
     # argv<String, Hash>::
0
     #   The config arguments to start Merb with. Defaults to +ARGV+.
0
     def start(argv=ARGV)
0
+      # Hardcode the log stream to STDOUT
0
+      Merb::Config[:log_stream] = STDOUT
0
       if Hash === argv
0
         Merb::Config.setup(argv)
0
       else
0
         Merb::Config.parse_args(argv)
0
       end
0
+      Merb::Config[:log_stream] = STDOUT
0
+      
0
       Merb.environment = Merb::Config[:environment]
0
       Merb.root = Merb::Config[:merb_root]
0
       case Merb::Config[:action]
0
       when :kill
0
-        Merb::Server.kill(Merb::Config[:port], 1)
0
+        Merb::Server.kill(Merb::Config[:port], 2)
0
       when :kill_9
0
         Merb::Server.kill(Merb::Config[:port], 9)
0
+      when :fast_deploy
0
+        Merb::Server.kill("main", "HUP")
0
       else
0
         Merb::Server.start(Merb::Config[:port], Merb::Config[:cluster])
0
       end
0
@@ -263,21 +270,43 @@ module Merb
0
     end
0
 
0
     # Logger settings
0
-    attr_accessor :logger
0
+    def logger
0
+      Thread.current[:merb_logger] ||= Merb::Logger.new
0
+    end
0
+
0
+    def logger=(obj)
0
+      unless obj
0
+        Thread.current[:merb_logger] = nil
0
+      end
0
+    end
0
 
0
     # ==== Returns
0
     # String::
0
     #   The path to the log file. If this Merb instance is running as a daemon
0
     #   this will return +STDOUT+.
0
-    def log_file
0
-      if Merb::Config[:log_file]
0
-        Merb::Config[:log_file]
0
-      elsif Merb.testing?
0
-        log_path / "merb_test.log"
0
-      elsif !(Merb::Config[:daemonize] || Merb::Config[:cluster])
0
-        STDOUT
0
-      else
0
-        log_path / "merb.#{Merb::Config[:port]}.log"
0
+    def log_stream(port = "main")
0
+      @streams ||= {}
0
+      @streams[port] ||= begin
0
+        log = if Merb.testing?
0
+          log_path / "merb_test.log"
0
+        elsif !Merb::Config[:daemonize]
0
+          STDOUT
0
+        else
0
+          log_path / "merb.#{port}.log"
0
+        end        
0
+        
0
+        if log.is_a?(IO)
0
+          stream = log
0
+        elsif File.exist?(log)
0
+          stream = File.open(log, (File::WRONLY | File::APPEND))
0
+        else
0
+          FileUtils.mkdir_p(File.dirname(log))
0
+          stream = File.open(log, (File::WRONLY | File::APPEND | File::CREAT))
0
+          stream.write("#{Time.now.httpdate} #{Merb::Config[:log_delimiter]} " \
0
+            "info #{Merb::Config[:log_delimiter]} Logfile created\n")
0
+        end
0
+        stream.sync = true
0
+        stream
0
       end
0
     end
0
 
0
@@ -335,6 +364,28 @@ module Merb
0
         r.default_routes
0
       end
0
     end
0
+    
0
+    def fatal!(str, e = nil)
0
+      Merb.logger.fatal!
0
+      Merb.logger.fatal!("\e[1;31;47mFATAL: #{str}\e[0m")
0
+      Merb.logger.fatal!
0
+
0
+      print_colorized_backtrace(e) if e && Merb::Config[:verbose]
0
+      exit(1)
0
+    end
0
+    
0
+    def print_colorized_backtrace(e)
0
+      e.backtrace.map! do |line|
0
+        line.gsub!(/^#{Merb.framework_root}/, "\e[34mFRAMEWORK_ROOT\e[31m")
0
+      end
0
+      
0
+      Merb.logger.fatal! "\e[34mFRAMEWORK_ROOT\e[0m = #{Merb.framework_root}"
0
+      Merb.logger.fatal!
0
+      Merb.logger.fatal! "\e[31m#{e.class}: \e[1;31;47m#{e.message}\e[0m"
0
+      e.backtrace.each do |line|
0
+        Merb.logger.fatal! "\e[31m#{line}\e[0m"
0
+      end      
0
+    end
0
 
0
     # Set up default variables under Merb
0
     attr_accessor :klass_hashes, :orm, :test_framework, :template_engine
...
6
7
8
9
 
 
10
11
12
...
144
145
146
147
 
 
 
 
 
 
 
 
 
 
148
149
150
151
152
153
 
 
154
155
156
...
164
165
166
167
 
168
169
170
...
269
270
271
 
272
273
 
274
275
276
277
278
279
 
 
 
280
281
282
...
356
357
358
359
360
361
362
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
363
364
365
...
373
374
375
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
376
377
378
379
380
381
382
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
383
384
385
...
403
404
405
406
 
 
 
 
 
407
408
409
...
489
490
491
492
 
 
493
494
495
 
496
497
498
...
681
682
683
684
 
685
686
687
...
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
 
 
 
 
709
710
 
711
712
713
714
 
 
 
 
 
 
 
 
 
 
 
 
 
 
715
716
717
...
6
7
8
 
9
10
11
12
13
...
145
146
147
 
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
 
163
164
165
166
167
...
175
176
177
 
178
179
180
181
...
280
281
282
283
284
285
286
287
288
289
290
 
 
291
292
293
294
295
296
...
370
371
372
 
 
 
 
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
...
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
 
 
 
 
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
...
559
560
561
 
562
563
564
565
566
567
568
569
...
649
650
651
 
652
653
654
655
 
656
657
658
659
...
842
843
844
 
845
846
847
848
...
853
854
855
 
 
 
 
 
 
 
 
856
857
858
859
860
861
862
863
864
865
866
 
867
868
 
 
 
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
0
@@ -6,7 +6,8 @@ module Merb
0
     #---
0
     # @semipublic
0
     cattr_accessor :subclasses, :after_load_callbacks, :before_load_callbacks, :finished
0
-    self.subclasses, self.after_load_callbacks, self.before_load_callbacks, self.finished = [], [], [], []
0
+    self.subclasses, self.after_load_callbacks, 
0
+      self.before_load_callbacks, self.finished = [], [], [], []
0
 
0
     class << self
0
 
0
@@ -144,13 +145,23 @@ class Merb::BootLoader::Logger < Merb::BootLoader
0
 
0
   # Sets Merb.logger to a new logger created based on the config settings.
0
   def self.run
0
-    Merb.logger = Merb::Logger.new(Merb.log_file, Merb::Config[:log_level], Merb::Config[:log_delimiter], Merb::Config[:log_auto_flush])
0
+    Merb::Config[:log_level] ||= begin
0
+      if Merb.environment == "production"
0
+        Merb::Logger::Levels[:warn]
0
+      else
0
+        Merb::Logger::Levels[:debug]
0
+      end          
0
+    end
0
+    
0
+    Merb::Config[:log_stream] = Merb.log_stream
0
+    
0
     print_warnings
0
   end
0
   
0
   def self.print_warnings
0
     if Gem::Version.new(Gem::RubyGemsVersion) < Gem::Version.new("1.1")
0
-      Merb.logger.warn! "Merb requires Rubygems 1.1 and later. Please upgrade RubyGems with gem update --system."
0
+      Merb.fatal! "Merb requires Rubygems 1.1 and later. " \
0
+        "Please upgrade RubyGems with gem update --system."
0
     end
0
   end
0
 end
0
@@ -164,7 +175,7 @@ class Merb::BootLoader::DropPidFile <  Merb::BootLoader
0
 
0
     # Stores a PID file if Merb is running daemonized or clustered.
0
     def run
0
-      Merb::Server.store_pid(Merb::Config[:port]) if Merb::Config[:daemonize] || Merb::Config[:cluster]
0
+      Merb::Server.store_pid("main") #if Merb::Config[:daemonize] || Merb::Config[:cluster]
0
     end
0
   end
0
 end
0
@@ -269,14 +280,17 @@ class Merb::BootLoader::Dependencies < Merb::BootLoader
0
   end
0
 
0
   def self.enable_json_gem
0
+    gem "json"
0
     require "json/ext"
0
   rescue LoadError
0
+    gem "json_pure"
0
     require "json/pure"
0
   end
0
 
0
   def self.update_logger
0
-    updated_logger_options = [ Merb.log_file, Merb::Config[:log_level], Merb::Config[:log_delimiter], Merb::Config[:log_auto_flush] ]
0
-    Merb::BootLoader::Logger.run if updated_logger_options != Merb.logger.init_args
0
+    # Clear out the logger so that any changes in init.rb will be
0
+    # picked up
0
+    Merb.logger = nil
0
   end
0
 
0
   private
0
@@ -356,10 +370,24 @@ class Merb::BootLoader::LoadClasses < Merb::BootLoader
0
     # Load all classes from Merb's native load paths.
0
     def run
0
       # Add models, controllers, helpers and lib to the load path
0
-      $LOAD_PATH.unshift Merb.dir_for(:model)
0
-      $LOAD_PATH.unshift Merb.dir_for(:controller)
0
-      $LOAD_PATH.unshift Merb.dir_for(:lib)
0
-      $LOAD_PATH.unshift Merb.dir_for(:helper)
0
+      unless @ran
0
+        $LOAD_PATH.unshift Merb.dir_for(:model)
0
+        $LOAD_PATH.unshift Merb.dir_for(:controller)
0
+        $LOAD_PATH.unshift Merb.dir_for(:lib)
0
+        $LOAD_PATH.unshift Merb.dir_for(:helper)
0
+      end
0
+
0
+      @ran = true
0
+      $0 = "merb: master"
0
+      
0
+      if Merb::Config[:fork_for_class_load] && Merb.env != "test"
0
+        start_transaction
0
+      else
0
+        trap('INT') do 
0
+          Merb.logger.warn! "Killing children"
0
+          kill_children
0
+        end
0
+      end
0
 
0
       # Load application file if it exists - for flat applications
0
       load_file Merb.dir_for(:application) if File.file?(Merb.dir_for(:application))
0
@@ -373,13 +401,141 @@ class Merb::BootLoader::LoadClasses < Merb::BootLoader
0
       Merb::Controller.send :include, Merb::GlobalHelpers
0
     end
0
     
0
+    # Wait for any children to exit, remove the "main" PID, and
0
+    # exit.
0
+    def exit_gracefully
0
+      Process.waitall
0
+      Merb::Server.remove_pid("main")
0
+      exit
0
+    end
0
+    
0
+    # If using fork-based code reloading, set up the BEGIN
0
+    # point and set up any signals in the parent and child.
0
+    def start_transaction
0
+      Merb.logger.warn! "Parent pid: #{Process.pid}"
0
+      reader, writer = nil, nil
0
+      
0
+      if GC.respond_to?(:copy_on_write_friendly=)
0
+        GC.copy_on_write_friendly = true
0
+      end      
0
+            
0
+      loop do
0
+        reader, @writer = IO.pipe
0
+        pid = Kernel.fork
0
+        
0
+        # pid means we're in the parent; only stay in the loop in that case
0
+        break unless pid
0
+        @writer.close
0
+
0
+        Merb::Server.store_pid("main")
0
+        
0
+        if Merb::Config[:console_trap]
0
+          trap("INT") {}
0
+        else
0
+          trap("INT") do 
0
+            Merb.logger.warn! "Killing children"
0
+            begin
0
+              Process.kill("ABRT", pid)
0
+            rescue SystemCallError
0
+            end
0
+            exit_gracefully
0
+          end
0
+        end
0
+        
0
+        trap("HUP") do 
0
+          Merb.logger.warn! "Doing a fast deploy\n"
0
+          Process.kill("HUP", pid)
0
+        end
0
+
0
+        reader_ary = [reader]
0
+        loop do
0
+          if exit_status = Process.wait2(pid, Process::WNOHANG)
0
+            exit_status[1] == 128 ? break : exit
0
+          end
0
+          if select(reader_ary, nil, nil, 0.25)
0
+            begin
0
+              next if reader.eof?
0
+              msg = reader.readline
0
+              if msg =~ /128/
0
+                break
0
+              else
0
+                exit_gracefully
0
+              end
0
+            rescue SystemCallError
0
+              exit_gracefully
0
+            end
0
+          end
0
+        end
0
+      end
0
0
+      reader.close
0
0
+      # add traps to the child
0
+      if Merb::Config[:console_trap]
0
+        Merb::Server.add_irb_trap
0
+        at_exit { kill_children }
0
+      else
0
+        trap('INT') {}
0
+        trap('ABRT') { kill_children }
0
+        trap('HUP') { kill_children(128) }
0
+      end
0
+    end
0
+    
0
+    # Kill any children of the spawner process and exit with
0
+    # an appropriate status code.
0
+    #
0
+    # Note that exiting the spawner process with a status code
0
+    # of 128 when a master process exists will cause the
0
+    # spawner process to be recreated, and the app code reloaded.
0
+    #
0
+    # @param status<Integer> The status code to exit with
0
+    def kill_children(status = 0)
0
+      Merb.exiting = true unless status == 128
0
+      
0
+      begin
0
+        @writer.puts(status.to_s) if @writer
0
+      rescue SystemCallError
0
+      end
0
+      
0
+      threads = []
0
+      
0
+      ($CHILDREN || []).each do |p|
0
+        threads << Thread.new do
0
+          begin
0
+            Process.kill("ABRT", p)
0
+            Process.wait2(p)
0
+          rescue SystemCallError
0
+          end
0
+        end
0
+      end
0
+      threads.each {|t| t.join }
0
+      exit(status)
0
+    end
0
+    
0
     # ==== Parameters
0
     # file<String>:: The file to load.
0
     def load_file(file)
0
-      klasses = ObjectSpace.classes.dup
0
-      load file
0
-      LOADED_CLASSES[file] = ObjectSpace.classes - klasses
0
-      MTIMES[file] = File.mtime(file)
0
+      # Don't do this expensive operation unless we need to
0
+      unless Merb::Config[:fork_for_class_load]
0
+        klasses = ObjectSpace.classes.dup
0
+      end
0
+      
0
+      # Ignore the file for syntax errors. The next time
0
+      # the file is changed, it'll be reloaded again
0
+      begin
0
+        load file
0
+      rescue SyntaxError
0
+        return
0
+      ensure
0
+        if Merb::Config[:reload_classes]
0
+          MTIMES[file] = File.mtime(file)
0
+        end
0
+      end
0
+      
0
+      # Don't do this expensive operation unless we need to
0
+      unless Merb::Config[:fork_for_class_load]
0
+        LOADED_CLASSES[file] = ObjectSpace.classes - klasses
0
+      end
0
     end
0
     
0
     # Load classes from given paths - using path/glob pattern.
0
@@ -403,7 +559,11 @@ class Merb::BootLoader::LoadClasses < Merb::BootLoader
0
     # ==== Parameters
0
     # file<String>:: The file to reload.
0
     def reload(file)
0
-      remove_classes_in_file(file) { |f| load_file(f) }
0
+      if !Merb::Config[:fork_for_class_load]
0
+        remove_classes_in_file(file) { |f| load_file(f) }
0
+      else
0
+        kill_children(128)
0
+      end
0
     end
0
     
0
     # Reload the router to regenerate all routes.
0
@@ -489,10 +649,11 @@ class Merb::BootLoader::LoadClasses < Merb::BootLoader
0
         if klasses.size == size_at_start && klasses.size != 0
0
           # Write all remaining failed classes and their exceptions to the log
0
           messages = error_map.only(*failed_classes).map do |klass, e|
0
-            ["Could not load #{klass}:\n\n#{e.message} - (#{e.class})", "#{(e.backtrace || []).join("\n")}"]
0
+            ["Could not load #{klass}:\n\n#{e.message} - (#{e.class})", 
0
+              "#{(e.backtrace || []).join("\n")}"]
0
           end
0
           messages.each { |msg, trace| Merb.logger.fatal!("#{msg}\n\n#{trace}") }
0
-          raise LoadError, "#{messages[0][0]} (see log for details)"
0
+          Merb.fatal! "#{failed_classes.join(", ")} failed to load."
0
         end
0
         break if(klasses.size == size_at_start || klasses.size == 0)
0
       end
0
@@ -681,7 +842,7 @@ class Merb::BootLoader::ReloadClasses < Merb::BootLoader
0
       Thread.new do
0
         loop do
0
           sleep( seconds )
0
-          block.call
0
+          yield
0
         end
0
         Thread.exit
0
       end
0
@@ -692,26 +853,33 @@ class Merb::BootLoader::ReloadClasses < Merb::BootLoader
0
   def self.run
0
     return unless Merb::Config[:reload_classes]
0
 
0
-    TimedExecutor.every(Merb::Config[:reload_time] || 0.5) do
0
-      reload
0
-    end
0
-    
0
-  end
0
-
0
-  # Reloads all files.
0
-  def self.reload
0
     paths = []
0
     Merb.load_paths.each do |path_name, file_info|
0
       path, glob = file_info
0
       next unless glob
0
       paths << Dir[path / glob]
0
     end
0
+  
0
+    if Merb.dir_for(:application) && File.file?(Merb.dir_for(:application))
0
+      paths << Merb.dir_for(:application)
0
+    end
0
 
0
-    paths << Merb.dir_for(:application) if Merb.dir_for(:application) && File.file?(Merb.dir_for(:application))
0
+    paths.flatten!
0
 
0
-    paths.flatten.each do |file|
0
-      next if Merb::BootLoader::LoadClasses::MTIMES[file] && Merb::BootLoader::LoadClasses::MTIMES[file] == File.mtime(file)
0
-      Merb::BootLoader::LoadClasses.reload(file)
0
+    TimedExecutor.every(Merb::Config[:reload_time] || 0.5) do
0
+      GC.start
0
+      reload(paths)
0
+    end
0
+    
0
+  end
0
+
0
+  # Reloads all files.
0
+  def self.reload(paths)
0
+    paths.each do |file|
0
+      next if LoadClasses::MTIMES[file] &&  
0
+        LoadClasses::MTIMES[file] == File.mtime(file)
0
+          
0
+      LoadClasses.reload(file)
0
     end
0
   end
0
 end
...
14
15
16
 
17
18
19
...
102
103
104
 
 
 
 
 
 
105
106
107
...
122
123
124
125
126
127
128
129
 
 
 
 
 
 
 
 
 
130
131
132
133
 
 
 
 
134
135
136
137
 
 
138
139
 
 
 
 
 
140
141
 
 
 
142
143
144
145
 
 
146
147
148
149
 
 
150
151
152
153
 
 
 
154
155
156
157
 
 
158
159
160
161
 
 
 
 
 
 
162
163
164
165
 
 
166
167
168
169
 
 
 
170
171
172
173
174
 
 
 
 
 
 
175
176
177
 
 
178
179
180
181
 
 
 
182
183
184
185
 
 
 
186
187
188
189
 
 
 
190
191
192
193
 
 
 
 
194
195
196
197
 
 
 
198
199
200
201
202
 
 
203
204
205
206
207
 
 
 
208
 
209
210
211
212
 
 
 
213
 
214
215
 
 
 
 
 
216
217
 
 
 
 
218
219
220
...
226
227
228
229
 
 
 
230
231
232
 
 
233
234
235
...
249
250
251
252
 
 
 
 
 
253
254
255
...
14
15
16
17
18
19
20
...
103
104
105
106
107
108
109
110
111
112
113
114
...
129
130
131
 
 
 
 
 
132
133
134
135
136
137
138
139
140
141
142
143
 
144
145
146
147
148
149
150
 
151
152
153
154
155
156
157
158
159
160
 
161
162
163
164
165
166
 
167
168
169
170
171
 
172
173
174
175
176
 
177
178
179
180
181
182
 
183
184
185
186
187
 
188
189
190
191
192
193
194
195
196
 
197
198
199
200
201
 
202
203
204
205
206
207
 
 
208
209
210
211
212
213
214
215
 
216
217
218
219
220
 
221
222
223
224
225
226
 
227
228
229
230
231
232
 
233
234
235
236
237
238
 
239
240
241
242
243
244
245
 
246
247
248
249
250
251
252
 
253
254
255
256
257
258
 
259
260
261
262
263
264
265
266
 
267
268
269
270
271
272
273
274
275
276
277
278
279
 
280
281
282
283
284
285
286
...
292
293
294
 
295
296
297
298
299
 
300
301
302
303
304
...
318
319
320
 
321
322
323
324
325
326
327
328
0
@@ -14,6 +14,7 @@ module Merb
0
           :port                   => "4000",
0
           :adapter                => "runner",
0
           :reload_classes         => true,
0
+          :fork_for_class_load    => !RUBY_PLATFORM.in?("windows", "java"),
0
           :environment            => "development",
0
           :merb_root              => Dir.pwd,
0
           :use_mutex              => true,
0
@@ -102,6 +103,12 @@ module Merb
0
       #   Configuration settings to use. These are merged with the defaults.
0
       def setup(settings = {})
0
         @configuration = defaults.merge(settings)
0
+        
0
+        unless @configuration[:reload_classes]
0
+          @configuration[:fork_for_class_load] = false
0
+        end
0
+        
0
+        @configuration
0
       end
0
 
0
       # Parses the command line arguments and stores them in the config.
0
@@ -122,99 +129,158 @@ module Merb
0
 
0
           opts.banner = "Usage: merb [uGdcIpPhmailLerkKX] [argument]"
0
           opts.define_head "Merb. Pocket rocket web framework"
0
-          opts.separator '*'*80
0
-          opts.separator 'If no flags are given, Merb starts in the foreground on port 4000.'
0
-          opts.separator '*'*80
0
-
0
-          opts.on("-u", "--user USER", "This flag is for having merb run as a user other than the one currently logged in. Note: if you set this you must also provide a --group option for it to take effect.") do |user|
0
+          opts.separator '*' * 80
0
+          opts.separator "If no flags are given, Merb starts in the " \
0
+            "foreground on port 4000."
0
+          opts.separator '*' * 80
0
+
0
+          opts.on("-u", "--user USER", "This flag is for having merb run " \
0
+                  "as a user other than the one currently logged in. Note: " \
0
+                  "if you set this you must also provide a --group option " \
0
+                  "for it to take effect.") do |user|
0
             options[:user] = user
0
           end
0
 
0
-          opts.on("-G", "--group GROUP", "This flag is for having merb run as a group other than the one currently logged in. Note: if you set this you must also provide a --user option for it to take effect.") do |group|
0
+          opts.on("-G", "--group GROUP", "This flag is for having merb run " \
0
+                  "as a group other than the one currently logged in. Note: " \
0
+                  "if you set this you must also provide a --user option " \
0
+                  "for it to take effect.") do |group|
0
             options[:group] = group
0
           end
0
 
0
-          opts.on("-d", "--daemonize", "This will run a single merb in the background.") do |daemon|
0
+          opts.on("-d", "--daemonize", "This will run a single merb in the " \
0
+                  "background.") do |daemon|
0
             options[:daemonize] = true
0
           end
0
+          
0
+          opts.on("-N", "--no-daemonize", "This will allow you to run a " \
0
+                  "cluster in console mode") do |no_daemon|
0
+            options[:daemonize] = false
0
+          end
0
 
0
-          opts.on("-c", "--cluster-nodes NUM_MERBS", "Number of merb daemons to run.") do |nodes|
0
+          opts.on("-c", "--cluster-nodes NUM_MERBS", Integer, 
0
+                  "Number of merb daemons to run.") do |nodes|
0
+            options[:daemonize] = true unless options.key?(:daemonize)
0
             options[:cluster] = nodes
0
           end
0
 
0
-          opts.on("-I", "--init-file FILE", "File to use for initialization on load, defaults to config/init.rb") do |init_file|
0
+          opts.on("-I", "--init-file FILE", "File to use for initialization " \
0
+                  "on load, defaults to config/init.rb") do |init_file|
0
             options[:init_file] = init_file
0
           end
0
 
0
-          opts.on("-p", "--port PORTNUM", "Port to run merb on, defaults to 4000.") do |port|
0
+          opts.on("-p", "--port PORTNUM", Integer, "Port to run merb on, " \
0
+                  "defaults to 4000.") do |port|
0
             options[:port] = port
0
           end
0
 
0
-          opts.on("-o", "--socket-file FILE", "Socket file to run merb on, defaults to [Merb.root]/log/merb.sock") do |port|
0
+          opts.on("-o", "--socket-file FILE", "Socket file to run merb on, " \
0
+                  "defaults to [Merb.root]/log/merb.sock. This is for " \
0
+                  "web servers, like thin, that use sockets.") do |port|
0
             options[:socket_file] = port
0
           end
0
 
0
-          opts.on("-s", "--socket SOCKNUM", "Socket number to run merb on, defaults to 0.") do |port|
0
+          opts.on("-s", "--socket SOCKNUM", Integer, "Socket number to run " \
0
+                  "merb on, defaults to 0.") do |port|
0
             options[:socket] = port
0
           end
0
 
0
-          opts.on("-P", "--pid PIDFILE", "PID file, defaults to [Merb.root]/log/merb.[port_number].pid") do |pid_file|
0
+          opts.on("-P", "--pid PIDFILE", "PID file, defaults to " \
0
+                  "[Merb.root]/log/merb.main.pid for the master process and" \
0
+                  "[Merb.root]/log/merb.[port number].pid for worker " \
0
+                  "processes. For clusters, use %s to specify where " \
0
+                  "in the file merb should place the port number. For " \
0
+                  "instance: -P myapp.%s.pid") do |pid_file|
0
             options[:pid_file] = pid_file
0
           end
0
 
0
-          opts.on("-h", "--host HOSTNAME", "Host to bind to (default is 0.0.0.0).") do |host|
0
+          opts.on("-h", "--host HOSTNAME", "Host to bind to " \
0
+                  "(default is 0.0.0.0).") do |host|
0
             options[:host] = host
0
           end
0
 
0
-          opts.on("-m", "--merb-root /path/to/approot", "The path to the Merb.root for the app you want to run (default is current working dir).") do |root|
0
+          opts.on("-m", "--merb-root /path/to/approot", "The path to the " \
0
+                  "Merb.root for the app you want to run " \
0
+                  "(default is current working directory).") do |root|
0
             options[:merb_root] = File.expand_path(root)
0
           end
0
 
0
-          opts.on("-a", "--adapter mongrel", "The rack adapter to use to run merb[mongrel, emongrel, thin, ebb, fastcgi, webrick, runner, irb]") do |adapter|
0
-            options[:adapter] = adapter
0
+          adapters = [:mongrel, :emongrel, :thin, :ebb, :fastcgi, :webrick]
0
+
0
+          opts.on("-a", "--adapter ADAPTER",
0
+                  "The rack adapter to use to run merb (default is mongrel)" \
0
+                  "[#{adapters.join(', ')}]") do |adapter|
0
+            options[:adapter] ||= adapter
0
           end
0
 
0
-          opts.on("-R", "--rackup FILE", "Load an alternate Rack config file (default is config/rack.rb)") do |rackup|
0
+          opts.on("-R", "--rackup FILE", "Load an alternate Rack config " \
0
+                  "file (default is config/rack.rb)") do |rackup|
0
             options[:rackup] = rackup
0
           end
0
 
0
-          opts.on("-i", "--irb-console", "This flag will start merb in irb console mode. All your models and other classes will be available for you in an irb session.") do |console|
0
+          opts.on("-i", "--irb-console", "This flag will start merb in " \
0
+                  "irb console mode. All your models and other classes will " \
0
+                  "be available for you in an irb session.") do |console|
0
             options[:adapter] = 'irb'
0
           end
0
 
0
-          opts.on("-S", "--sandbox", "This flag will enable a sandboxed irb console. If your ORM supports transactions, all edits will be rolled back on exit.") do |sandbox|
0
+          opts.on("-S", "--sandbox", "This flag will enable a sandboxed irb " \
0
+                  "console. If your ORM supports transactions, all edits will " \
0
+                  "be rolled back on exit.") do |sandbox|
0
             options[:sandbox] = true
0
           end
0
 
0
-          opts.on("-l", "--log-level LEVEL", "Log levels can be set to any of these options: debug < info < warn < error < fatal") do |log_level|
0
+          opts.on("-l", "--log-level LEVEL", "Log levels can be set to any of " \
0
+                  "these options: debug < info < warn < error < " \
0
+                  "fatal (default is info)") do |log_level|
0
             options[:log_level] = log_level.to_sym
0
           end
0
 
0
-          opts.on("-L", "--log LOGFILE", "A string representing the logfile to use.") do |log_file|
0
+          opts.on("-L", "--log LOGFILE", "A string representing the logfile to " \
0
+                  "use. Defaults to [Merb.root]/log/merb.[main].log for the " \
0
+                  "master process and [Merb.root]/log/merb[port number].log" \
0
+                  "for worker processes") do |log_file|
0
             options[:log_file] = log_file
0
           end
0
 
0
-          opts.on("-e", "--environment STRING", "Run merb in the correct mode(development, production, testing)") do |env|
0
+          opts.on("-e", "--environment STRING", "Environment to run Merb " \
0
+                  "under [development, production, testing] " \
0
+                  "(default is development)") do |env|
0
             options[:environment] = env
0
           end
0
 
0
           opts.on("-r", "--script-runner ['RUBY CODE'| FULL_SCRIPT_PATH]",
0
-          "Command-line option to run scripts and/or code in the merb app.") do |code_or_file|
0
+                  "Command-line option to run scripts and/or code in the " \
0
+                  "merb app.") do |code_or_file|
0
             options[:runner_code] = code_or_file
0
             options[:adapter] = 'runner'
0
           end
0
 
0
-          opts.on("-K", "--graceful PORT or all", "Gracefully kill one merb proceses by port number.  Use merb -K all to gracefully kill all merbs.") do |ports|
0
+          opts.on("-K", "--graceful PORT or all", "Gracefully kill one " \
0
+                  "merb proceses by port number.  Use merb -K all to " \
0
+                  "gracefully kill all merbs.") do |ports|
0
             options[:action] = :kill
0
+            ports = "main" if ports == "all"
0
             options[:port] = ports
0
           end
0
 
0
-          opts.on("-k", "--kill PORT or all", "Kill one merb proceses by port number.  Use merb -k all to kill all merbs.") do |port|
0
+          opts.on("-k", "--kill PORT", "Force kill one merb worker " \
0
+                  "by port number. This will cause the worker to" \
0
+                  "be respawned. If you want to kill ") do |port|
0
             options[:action] = :kill_9
0
+            port = "main" if port == "all"
0
             options[:port] = port
0
           end
0
+          
0
+          opts.on("--fast-deploy", "Reload the code, but not your" \
0
+            "init.rb or gems") do
0
+              options[:action] = :fast_deploy
0
+          end
0
 
0
-          opts.on("-X", "--mutex on/off", "This flag is for turning the mutex lock on and off.") do |mutex|
0
+          # @todo Do we really need this flag? It seems unlikely to want to
0
+          #   change the mutex from the command-line.
0
+          opts.on("-X", "--mutex on/off", "This flag is for turning the " \
0
+                  "mutex lock on and off.") do |mutex|
0
             if mutex == "off"
0
               options[:use_mutex] = false
0
             else
0
@@ -226,10 +292,13 @@ module Merb
0
             begin
0
               require "ruby-debug"
0
               Debugger.start
0
-              Debugger.settings[:autoeval] = true if Debugger.respond_to?(:settings)
0
+              if Debugger.respond_to?(:settings)
0
+                Debugger.settings[:autoeval] = true
0
+              end
0
               puts "Debugger enabled"
0
             rescue LoadError
0
-              puts "You need to install ruby-debug to run the server in debugging mode. With gems, use 'gem install ruby-debug'"
0
+              puts "You need to install ruby-debug to run the server in " \
0
+                "debugging mode. With gems, use `gem install ruby-debug'"
0
               exit
0
             end
0
           end
0
@@ -249,7 +318,11 @@ module Merb
0
         end
0
 
0
         # Parse what we have on the command line
0
-        opts.parse!(argv)
0
+        begin
0
+          opts.parse!(argv)
0
+        rescue OptionParser::InvalidOption => e
0
+          Merb.fatal! e.message, e
0
+        end
0
         Merb::Config.setup(options)
0
       end
0
 
...
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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
 
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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
0
@@ -1,203 +1,215 @@
0
-Merb::Logger = Extlib::Logger
0
+# Merb::Logger = Extlib::Logger
0
+
0
+class Merb::Logger < Extlib::Logger
0
+  def verbose!(message, level = :warn)
0
+    send("#{level}!", message) if Merb::Config[:verbose]
0
+  end
0
+
0
+  def verbose(message, level = :warn)
0
+    send(level, message) if Merb::Config[:verbose]
0
+  end
0
+end
0
+
0
 # require "time" # httpdate
0
-# # ==== Public Merb Logger API
0
-# #
0
-# # To replace an existing logger with a new one:
0
-# #  Merb::Logger.set_log(log{String, IO},level{Symbol, String})
0
-# #
0
-# # Available logging levels are
0
-# #   Merb::Logger::{ Fatal, Error, Warn, Info, Debug }
0
-# #
0
-# # Logging via:
0
-# #   Merb.logger.fatal(message<String>,&block)
0
-# #   Merb.logger.error(message<String>,&block)
0
-# #   Merb.logger.warn(message<String>,&block)
0
-# #   Merb.logger.info(message<String>,&block)
0
-# #   Merb.logger.debug(message<String>,&block)
0
-# #
0
-# # Logging with autoflush:
0
-# #   Merb.logger.fatal!(message<String>,&block)
0
-# #   Merb.logger.error!(message<String>,&block)
0
-# #   Merb.logger.warn!(message<String>,&block)
0
-# #   Merb.logger.info!(message<String>,&block)
0
-# #   Merb.logger.debug!(message<String>,&block)
0
-# #
0
-# # Flush the buffer to 
0
-# #   Merb.logger.flush
0
-# #
0
-# # Remove the current log object
0
-# #   Merb.logger.close
0
-# # 
0
-# # ==== Private Merb Logger API
0
-# # 
0
-# # To initialize the logger you create a new object, proxies to set_log.
0
-# #   Merb::Logger.new(log{String, IO},level{Symbol, String})
0
-# module Merb
0
-# 
0
-#   class << self
0
-#     attr_accessor :logger
0
-#   end
0
-# 
0
-#   class Logger
0
-# 
0
-#     attr_accessor :level
0
-#     attr_accessor :delimiter
0
-#     attr_accessor :auto_flush
0
-#     attr_reader   :buffer
0
-#     attr_reader   :log
0
-#     attr_reader   :init_args
0
-# 
0
-#     # ==== Notes
0
-#     # Ruby (standard) logger levels:
0
-#     # :fatal:: An unhandleable error that results in a program crash
0
-#     # :error:: A handleable error condition
0
-#     # :warn:: A warning
0
-#     # :info:: generic (useful) information about system operation
0
-#     # :debug:: low-level information for developers
0
-#     Levels = 
0
-#     {
0
-#       :fatal => 7, 
0
-#       :error => 6, 
0
-#       :warn  => 4,
0
-#       :info  => 3,
0
-#       :debug => 0
0
-#     } unless const_defined?(:Levels)
0
-# 
0
-#     private
0
-# 
0
-#     # Readies a log for writing.
0
-#     #
0
-#     # ==== Parameters
0
-#     # log<IO, String>:: Either an IO object or a name of a logfile.
0
-#     def initialize_log(log)
0
-#       close if @log # be sure that we don't leave open files laying around.
0
-# 
0
-#       if log.respond_to?(:write)
0
-#         @log = log
0
-#       elsif File.exist?(log)
0
-#         @log = open(log, (File::WRONLY | File::APPEND))
0
-#         @log.sync = true
0
-#       else
0
-#         FileUtils.mkdir_p(File.dirname(log)) unless File.directory?(File.dirname(log))
0
-#         @log = open(log, (File::WRONLY | File::APPEND | File::CREAT))
0
-#         @log.sync = true
0
-#         @log.write("#{Time.now.httpdate} #{delimiter} info #{delimiter} Logfile created\n")
0
-#       end
0
-#     end
0
-# 
0
-#     public
0
-# 
0
-#     # To initialize the logger you create a new object, proxies to set_log.
0
-#     #
0
-#     # ==== Parameters
0
-#     # *args:: Arguments to create the log from. See set_logs for specifics.
0
-#     def initialize(*args)
0
-#       @init_args = args
0
-#       set_log(*args)
0
-#     end
0
-# 
0
-#     # Replaces an existing logger with a new one.
0
-#     # 
0
-#     # ==== Parameters
0
-#     # log<IO, String>:: Either an IO object or a name of a logfile.
0
-#     # log_level<~to_sym>::
0
-#     #   The log level from, e.g. :fatal or :info. Defaults to :error in the
0
-#     #   production environment and :debug otherwise.
0
-#     # delimiter<String>::
0
-#     #   Delimiter to use between message sections. Defaults to " ~ ".
0
-#     # auto_flush<Boolean>::
0
-#     #   Whether the log should automatically flush after new messages are
0
-#     #   added. Defaults to false.
0
-#     def set_log(log, log_level = nil, delimiter = " ~ ", auto_flush = false)
0
-#       if log_level && Levels[log_level.to_sym]
0
-#         @level = Levels[log_level.to_sym]
0
-#       elsif Merb.environment == "production"
0
-#         @level = Levels[:warn]
0
-#       else
0
-#         @level = Levels[:debug]
0
-#       end
0
-#       @buffer     = []
0
-#       @delimiter  = delimiter
0
-#       @auto_flush = auto_flush
0
-# 
0
-#       initialize_log(log)
0
-# 
0
-#       Merb.logger = self
0
-#     end
0
-# 
0
-#     # Flush the entire buffer to the log object.
0
-#     def flush
0
-#       return unless @buffer.size > 0
0
-#       @log.write(@buffer.slice!(0..-1).to_s)
0
-#     end
0
-# 
0
-#     # Close and remove the current log object.
0
-#     def close
0
-#       flush
0
-#       @log.close if @log.respond_to?(:close) && !@log.tty?
0
-#       @log = nil
0
-#     end
0
-# 
0
-#     # Appends a message to the log. The methods yield to an optional block and
0
-#     # the output of this block will be appended to the message.
0
-#     #
0
-#     # ==== Parameters
0
-#     # string<String>:: The message to be logged. Defaults to nil.
0
-#     #
0
-#     # ==== Returns
0
-#     # String:: The resulting message added to the log file.
0
-#     def <<(string = nil)
0
-#       message = ""
0
-#       message << delimiter
0
-#       message << string if string
0
-#       message << "\n" unless message[-1] == ?\n
0
-#       @buffer << message
0
-#       flush if @auto_flush
0
-# 
0
-#       message
0
-#     end
0
-#     alias :push :<<
0
-# 
0
-#     # Generate the logging methods for Merb.logger for each log level.
0
-#     Levels.each_pair do |name, number|
0
-#       class_eval <<-LEVELMETHODS, __FILE__, __LINE__
0
-# 
0
-#       # Appends a message to the log if the log level is at least as high as
0
-#       # the log level of the logger.
0
-#       #
0
-#       # ==== Parameters
0
-#       # string<String>:: The message to be logged. Defaults to nil.
0
-#       #
0
-#       # ==== Returns
0
-#       # self:: The logger object for chaining.
0
-#       def #{name}(message = nil)
0
-#         self << message if #{number} >= level
0
-#         self
0
-#       end
0
-# 
0
-#       # Appends a message to the log if the log level is at least as high as
0
-#       # the log level of the logger. The bang! version of the method also auto
0
-#       # flushes the log buffer to disk.
0
-#       #
0
-#       # ==== Parameters
0
-#       # string<String>:: The message to be logged. Defaults to nil.
0
-#       #
0
-#       # ==== Returns
0
-#       # self:: The logger object for chaining.
0
-#       def #{name}!(message = nil)
0
-#         self << message if #{number} >= level
0
-#         flush if #{number} >= level
0
-#         self
0
-#       end
0
-# 
0
-#       # ==== Returns
0
-#       # Boolean:: True if this level will be logged by this logger.
0
-#       def #{name}?
0
-#         #{number} >= level
0
-#       end
0
-#       LEVELMETHODS
0
-#     end
0
-# 
0
-#   end
0
-#   
0
-# end
0
+# ==== Public Merb Logger API
0
+#
0
+# To replace an existing logger with a new one:
0
+#  Merb::Logger.set_log(log{String, IO},level{Symbol, String})
0
+#
0
+# Available logging levels are
0
+#   Merb::Logger::{ Fatal, Error, Warn, Info, Debug }
0
+#
0
+# Logging via:
0
+#   Merb.logger.fatal(message<String>,&block)
0
+#   Merb.logger.error(message<String>,&block)
0
+#   Merb.logger.warn(message<String>,&block)
0
+#   Merb.logger.info(message<String>,&block)
0
+#   Merb.logger.debug(message<String>,&block)
0
+#
0
+# Logging with autoflush:
0
+#   Merb.logger.fatal!(message<String>,&block)
0
+#   Merb.logger.error!(message<String>,&block)
0
+#   Merb.logger.warn!(message<String>,&block)
0
+#   Merb.logger.info!(message<String>,&block)
0
+#   Merb.logger.debug!(message<String>,&block)
0
+#
0
+# Flush the buffer to 
0
+#   Merb.logger.flush
0
+#
0
+# Remove the current log object
0
+#   Merb.logger.close
0
+# 
0
+# ==== Private Merb Logger API
0
+# 
0
+# To initialize the logger you create a new object, proxies to set_log.
0
+#   Merb::Logger.new(log{String, IO},level{Symbol, String})
0
+module Merb
0
+
0
+  class Logger
0
+
0
+    attr_accessor :level
0
+    attr_accessor :delimiter
0
+    attr_accessor :auto_flush
0
+    attr_reader   :buffer
0
+    attr_reader   :log
0
+    attr_reader   :init_args
0
+
0
+    # ==== Notes
0
+    # Ruby (standard) logger levels:
0
+    # :fatal:: An unhandleable error that results in a program crash
0
+    # :error:: A handleable error condition
0
+    # :warn:: A warning
0
+    # :info:: generic (useful) information about system operation
0
+    # :debug:: low-level information for developers
0
+    Levels = 
0
+    {
0
+      :fatal => 7, 
0
+      :error => 6, 
0
+      :warn  => 4,
0
+      :info  => 3,
0
+      :debug => 0
0
+    } unless const_defined?(:Levels)
0
+
0
+    @@mutex = {}
0
+
0
+    private
0
+
0
+    # Readies a log for writing.
0
+    #
0
+    # ==== Parameters
0
+    # log<IO, String>:: Either an IO object or a name of a logfile.
0
+    def initialize_log(log)
0
+      close if @log # be sure that we don't leave open files laying around.
0
+
0
+      if log.respond_to?(:write)
0
+        @log = log
0
+      elsif File.exist?(log)
0
+        @log = open(log, (File::WRONLY | File::APPEND))
0
+        @log.sync = true
0
+      else
0
+        FileUtils.mkdir_p(File.dirname(log)) unless File.directory?(File.dirname(log))
0
+        @log = open(log, (File::WRONLY | File::APPEND | File::CREAT))
0
+        @log.sync = true
0
+        @log.write("#{Time.now.httpdate} #{delimiter} info #{delimiter} Logfile created\n")
0
+      end
0
+    end
0
+
0
+    public
0
+
0
+    # To initialize the logger you create a new object, proxies to set_log.
0
+    #
0
+    # ==== Parameters
0
+    # *args:: Arguments to create the log from. See set_logs for specifics.
0
+    def initialize(*args)
0
+      set_log(*args)
0
+    end
0
+
0
+    # Replaces an existing logger with a new one.
0
+    # 
0
+    # ==== Parameters
0
+    # log<IO, String>:: Either an IO object or a name of a logfile.
0
+    # log_level<~to_sym>::
0
+    #   The log level from, e.g. :fatal or :info. Defaults to :error in the
0
+    #   production environment and :debug otherwise.
0
+    # delimiter<String>::
0
+    #   Delimiter to use between message sections. Defaults to " ~ ".
0
+    # auto_flush<Boolean>::
0
+    #   Whether the log should automatically flush after new messages are
0
+    #   added. Defaults to false.
0
+    def set_log(stream = Merb::Config[:log_stream],
0
+      log_level = Merb::Config[:log_level], 
0
+      delimiter = Merb::Config[:log_delimiter], 
0
+      auto_flush = Merb::Config[:log_auto_flush])
0
+      
0
+      @buffer                   = []
0
+      @delimiter                = delimiter
0
+      @auto_flush               = auto_flush
0
+      
0
+      if Levels[log_level]
0
+        @level                  = Levels[log_level]
0
+      else
0
+        @level                  = log_level
0
+      end
0
+
0
+      @log                      = stream
0
+      @mutex = (@@mutex[@log] ||= Mutex.new)
0
+    end
0
+
0
+    # Flush the entire buffer to the log object.
0
+    def flush
0
+      return unless @buffer.size > 0
0
+      @mutex.synchronize do
0
+        @log.write(@buffer.slice!(0..-1).to_s)
0
+      end
0
+    end
0
+
0
+    # Close and remove the current log object.
0
+    def close
0
+      flush
0
+      @log.close if @log.respond_to?(:close) && !@log.tty?
0
+      @log = nil
0
+    end
0
+
0
+    # Appends a message to the log. The methods yield to an optional block and
0
+    # the output of this block will be appended to the message.
0
+    #
0
+    # ==== Parameters
0
+    # string<String>:: The message to be logged. Defaults to nil.
0
+    #
0
+    # ==== Returns
0
+    # String:: The resulting message added to the log file.
0
+    def <<(string = nil)
0
+      message = ""
0
+      message << delimiter
0
+      message << string if string
0
+      message << "\n" unless message[-1] == ?\n
0
+      @buffer << message
0
+      flush if @auto_flush
0
+
0
+      message
0
+    end
0
+    alias :push :<<
0
+
0
+    # Generate the logging methods for Merb.logger for each log level.
0
+    Levels.each_pair do |name, number|
0
+      class_eval <<-LEVELMETHODS, __FILE__, __LINE__
0
+
0
+      # Appends a message to the log if the log level is at least as high as
0
+      # the log level of the logger.
0
+      #
0
+      # ==== Parameters
0
+      # string<String>:: The message to be logged. Defaults to nil.
0
+      #
0
+      # ==== Returns
0
+      # self:: The logger object for chaining.
0
+      def #{name}(message = nil)
0
+        self << message if #{number} >= level
0
+        self
0
+      end
0
+
0
+      # Appends a message to the log if the log level is at least as high as
0
+      # the log level of the logger. The bang! version of the method also auto
0
+      # flushes the log buffer to disk.
0
+      #
0
+      # ==== Parameters
0
+      # string<String>:: The message to be logged. Defaults to nil.
0
+      #
0
+      # ==== Returns
0
+      # self:: The logger object for chaining.
0
+      def #{name}!(message = nil)
0
+        self << message if #{number} >= level
0
+        flush if #{number} >= level
0
+        self
0
+      end
0
+
0
+      # ==== Returns
0
+      # Boolean:: True if this level will be logged by this logger.
0
+      def #{name}?
0
+        #{number} >= level
0
+      end
0
+      LEVELMETHODS
0
+    end
0
+
0
+  end
0
+  
0
+end
...
3
4
5
 
6
7
8
...
3
4
5
6
7
8
9
0
@@ -3,6 +3,7 @@ module Merb
0
   module Rack
0
     autoload :Application,         'merb-core/rack/application'
0
     autoload :Adapter,             'merb-core/rack/adapter'
0
+    autoload :AbstractAdapter,     'merb-core/rack/adapter/abstract'
0
     autoload :Ebb,                 'merb-core/rack/adapter/ebb'
0
     autoload :EventedMongrel,      'merb-core/rack/adapter/evented_mongrel'    
0
     autoload :FastCGI,             'merb-core/rack/adapter/fcgi'
...
11
12
13
14
 
 
 
 
 
15
16
17
...
36
37
38
39
 
40
41
42
43
44
 
45
...
11
12
13
 
14
15
16
17
18
19
20
21
...
40
41
42
 
43
44
45
46
 
 
47
48
0
@@ -11,7 +11,11 @@ module Merb
0
         # ==== Returns.
0
         # Class:: The adapter class.
0
         def get(id)
0
-          Object.full_const_get(@adapters[id])
0
+          if @adapters[id.to_s]
0
+            Object.full_const_get(@adapters[id.to_s])
0
+          else
0
+            Merb.fatal! "The adapter #{id} did not exist"
0
+          end
0
         end
0
 
0
         # Registers a new Rack adapter.
0
@@ -36,9 +40,8 @@ module Merb
0
     Adapter.register %w{runner},         :Runner
0
     Adapter.register %w{smongrel swift}, :SwiftipliedMongrel
0
     Adapter.register %w{thin},           :Thin
0
-    Adapter.register %w{thin-turbo},     :ThinTurbo
0
+    Adapter.register %w{thin-turbo tt},  :ThinTurbo
0
     Adapter.register %w{webrick},        :WEBrick
0
     
0
   end # Rack
0
-end # Merb
0
-
0
+end # Merb
0
\ No newline at end of file
...
3
4
5
6
 
7
8
9
10
11
12
13
14
15
16
17
 
18
19
20
21
 
 
 
 
 
 
 
 
 
 
22
23
24
...
3
4
5
 
6
7
8
 
 
 
 
 
 
 
 
 
9
10
 
 
 
11
12
13
14
15
16
17
18
19
20
21
22
23
0
@@ -3,22 +3,21 @@ module Merb
0
   
0
   module Rack
0
 
0
-    class Ebb
0
+    class Ebb < Merb::Rack::AbstractAdapter
0
       # start an Ebb server on given host and port.
0
       
0
-      # ==== Parameters
0
-      # opts<Hash>:: Options for Ebb (see below).
0
-      #
0
-      # ==== Options (opts)
0
-      # :host<String>:: The hostname that Ebb should serve.
0
-      # :port<Fixnum>:: The port Ebb should bind to.
0
-      # :app:: The application
0
-      def self.start(opts={})
0
-        Merb.logger.warn!("Using Ebb adapter")
0
+      def self.new_server(port)
0
         Merb::Dispatcher.use_mutex = false
0
-        th = Thread.new { ::Ebb.start_server(opts[:app], opts) }
0
-        Merb::Server.change_privilege
0
-        th.join
0
+        opts = @opts.merge(:port => port)
0
+        @th = Thread.new { Thread.current[:server] = ::Ebb.start_server(opts[:app], opts) }
0
+      end
0
+      
0
+      def self.start_server
0
+        @th.join
0
+      end
0
+      
0
+      def self.stop(status = 0)
0
+        ::Ebb.stop_server
0
       end
0
     end
0
   end
...
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 
18
19
20
21
22
 
23
24
25
...
4
5
6
 
 
 
 
 
 
 
 
 
 
 
7
8
 
 
 
 
9
10
11
12
0
@@ -4,22 +4,9 @@ module Merb
0
   module Rack
0
 
0
     class EventedMongrel < Mongrel
0
-      # Starts Mongrel as evented.
0
-      #
0
-      # ==== Parameters
0
-      # opts<Hash>:: Options for Mongrel (see below).
0
-      #
0
-      # ==== Options (opts)
0
-      # :host<String>:: The hostname that Mongrel should serve.
0
-      # :port<Fixnum>:: The port Mongrel should bind to.
0
-      # :app<String>>:: The application name.
0
-      def self.start(opts={})
0
-        Merb.logger.warn!("Using EventedMongrel adapter")
0
+      def self.new_server(port)
0
         Merb::Dispatcher.use_mutex = false
0
-        server = ::Mongrel::HttpServer.new(opts[:host], opts[:port].to_i)
0
-        Merb::Server.change_privilege
0
-        server.register('/', ::Merb::Rack::Handler::Mongrel.new(opts[:app]))
0
-        server.run.join
0
+        super
0
       end
0
     end
0
   end
...
4
5
6
7
8
 
 
 
 
 
 
 
 
 
 
 
 
 
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 
 
23
 
 
 
 
 
 
24
 
25
26
27
...
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
0
@@ -4,23 +4,30 @@ module Merb
0
 
0
   module Rack
0
 
0
-    class Mongrel
0
-      # start server on given host and port.
0
+    class Mongrel < Merb::Rack::AbstractAdapter
0
+
0
+      def self.stop(status = 0)
0
+        if @server
0
+          begin
0
+            @server.stop(true)
0
+          rescue Mongrel::TimeoutError
0
+            Merb.logger.fatal! "Your process took too long to shut " \
0
+              "down, so mongrel killed it."
0
+          end
0
+          true
0
+        end
0
+      end
0
       
0
-      # ==== Parameters
0
-      # opts<Hash>:: Options for Mongrel (see below).
0
-      #
0
-      # ==== Options (opts)
0
-      # :host<String>:: The hostname that Mongrel should serve.
0
-      # :port<Fixnum>:: The port Mongrel should bind to.
0
-      # :app<String>>:: The application name.
0
-      def self.start(opts={})
0
-        Merb.logger.warn!("Using Mongrel adapter")
0
-        server = ::Mongrel::HttpServer.new(opts[:host], opts[:port].to_i)
0
-        Merb::Server.change_privilege
0
-        server.register('/', ::Merb::Rack::Handler::Mongrel.new(opts[:app]))
0
-        server.run.join
0
+      def self.new_server(port)
0
+        @server = ::Mongrel::HttpServer.new(@opts[:host], port)
0
       end
0
+      
0
+      def self.start_server
0
+        @server.register('/', ::Merb::Rack::Handler::Mongrel.new(@opts[:app]))
0
+        @server.run.join
0
+      end
0
+      
0
     end
0
+    
0
   end
0
 end
0
\ No newline at end of file
...
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 
18
19
20
21
22
23
 
 
24
 
25
26
27
...
4
5
6
 
 
 
 
 
 
 
 
 
 
 
7
8
 
 
 
 
 
9
10
11
12
13
14
15
0
@@ -4,23 +4,11 @@ module Merb
0
   module Rack
0
 
0
     class SwiftipliedMongrel < Mongrel
0
-      # Starts Mongrel as swift.
0
-      #
0
-      # ==== Parameters
0
-      # opts<Hash>:: Options for Mongrel (see below).
0
-      #
0
-      # ==== Options (opts)
0
-      # :host<String>:: The hostname that Mongrel should serve.
0
-      # :port<Fixnum>:: The port Mongrel should bind to.
0
-      # :app<String>>:: The application name.
0
-      def self.start(opts={})
0
-        Merb.logger.warn!("Using SwiftipliedMongrel adapter")
0
+      def self.new_server(port)
0
         Merb::Dispatcher.use_mutex = false
0
-        server = ::Mongrel::HttpServer.new(opts[:host], opts[:port].to_i)
0
-        Merb::Server.change_privilege
0
-        server.register('/', ::Merb::Rack::Handler::Mongrel.new(opts[:app]))
0
-        server.run.join
0
-      end
0
+        super
0
+      end      
0
     end
0
+    
0
   end
0
 end
0
\ No newline at end of file
...
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
...
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
0
@@ -4,35 +4,36 @@ module Merb
0
 
0
   module Rack
0
 
0
-    class Thin
0
+    class Thin < Merb::Rack::AbstractAdapter
0
       # start a Thin server on given host and port.
0
 
0
-      # ==== Parameters
0
-      # opts<Hash>:: Options for Thin (see below).
0
-      #
0
-      # ==== Options (opts)
0
-      # :host<String>:: The hostname that Thin should serve.
0
-      # :port<Fixnum>:: The port Thin should bind to.
0
-      # :socket<Fixnum>>:: The socket number that thin should bind to.
0
-      # :socket_file<String>>:: The socket file that thin should attach to.
0
-      # :app<String>>:: The application name.
0
-      def self.start(opts={})
0
+      def self.new_server(port)
0
         Merb::Dispatcher.use_mutex = false
0
-        if opts[:socket] || opts[:socket_file]
0
-          socket = opts[:socket] || "0"
0
-          socket_file = opts[:socket_file] || "#{Merb.root}/log/merb.#{socket}.sock"
0
+        
0
+        if @opts[:socket] || @opts[:socket_file]
0
+          socket = port.to_s
0
+          socket_file = @opts[:socket_file] || "#{Merb.root}/log/merb.#{socket}.sock"
0
           Merb.logger.warn!("Using Thin adapter with socket file #{socket_file}.")
0
-          server = ::Thin::Server.new(socket_file, opts[:app], opts)
0
+          @server = ::Thin::Server.new(socket_file, @opts[:app], @opts)
0
         else
0
-          Merb.logger.warn!("Using Thin adapter on host #{opts[:host]} and port #{opts[:port]}.")
0
-          if opts[:host].include?('/')
0
-            opts[:host] = "#{opts[:host]}-#{opts[:port]}"
0
+          Merb.logger.warn!("Using Thin adapter on host #{@opts[:host]} and port #{port}.")
0
+          if @opts[:host].include?('/')
0
+            @opts[:host] = "#{@opts[:host]}-#{port}"
0
           end
0
-          server = ::Thin::Server.new(opts[:host], opts[:port].to_i, opts[:app], opts)
0
+          @server = ::Thin::Server.new(@opts[:host], port, @opts[:app], @opts)
0
         end
0
-        Merb::Server.change_privilege
0
+      end
0
+
0
+      def self.start_server
0
         ::Thin::Logging.silent = true
0
-        server.start
0
+        @server.start
0
+      end
0
+      
0
+      def self.stop(status = 0)
0
+        if @server
0
+          @server.stop
0
+          true
0
+        end
0
       end
0
     end
0
   end
...
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 
 
 
21
 
22
23
24
...
7
8
9
 
 
 
 
 
 
 
 
 
 
 
10
11
12
13
14
15
16
17
0
@@ -7,18 +7,11 @@ module Merb
0
     class ThinTurbo < Thin
0
       # start a Thin Turbo server on given host and port.
0
 
0
-      # ==== Parameters
0
-      # opts<Hash>:: Options for Thin Turbo (see below).
0
-      #
0
-      # ==== Options (opts)
0
-      # :host<String>:: The hostname that Thin Turbo should serve.
0
-      # :port<Fixnum>:: The port Thin Turbo should bind to.
0
-      # :socket<Fixnum>>:: The socket number that thin should bind to.
0
-      # :socket_file<String>>:: The socket file that thin should attach to.
0
-      # :app<String>>:: The application name.
0
-      def self.start(opts={})
0
-        super(opts.merge(:backend => ::Thin::Backends::Turbo))
0
+      def self.new_server(port)
0
+        @opts.merge!(:backend => ::Thin::Backends::Turbo)
0
+        super
0
       end
0
+
0
     end
0
   end
0
 end
...
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
...
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
0
@@ -1,36 +1,72 @@
0
 require 'webrick'
0
+require 'webrick/utils'
0
 require 'rack/handler/webrick'
0
 module Merb
0
   module Rack
0
 
0
-    class WEBrick
0
-      # start WEBrick server on given host and port.
0
+    class WEBrick < Merb::Rack::AbstractAdapter
0
       
0
-      # ==== Parameters
0
-      # opts<Hash>:: Options for WEBrick (see below).
0
-      #
0
-      # ==== Options (opts)
0
-      # :host<String>:: The hostname that WEBrick should serve.
0
-      # :port<Fixnum>:: The port WEBrick should bind to.
0
-      # :app<String>>:: The application name.
0
-      def self.start(opts={})
0
-        Merb.logger.warn!("Using Webrick adapter")
0
+      class << self
0
+        attr_accessor :server
0
+      end
0
       
0
+      def self.new_server(port)
0
         options = {
0
-          :Port        => opts[:port],
0
-          :BindAddress => opts[:host],
0
+          :Port        => port,
0
+          :BindAddress => @opts[:host],
0
           :Logger      => Merb.logger,
0
           :AccessLog   => [
0
             [Merb.logger, ::WEBrick::AccessLog::COMMON_LOG_FORMAT],
0
             [Merb.logger, ::WEBrick::AccessLog::REFERER_LOG_FORMAT]
0
           ]
0
         }
0
-     
0
-        server = ::WEBrick::HTTPServer.new(options)
0
-        Merb::Server.change_privilege
0
-        server.mount("/", ::Rack::Handler::WEBrick, opts[:app])
0
-        server.start
0
+
0
+        sockets = ::WEBrick::Utils.create_listeners nil, port
0
+        @server = ::WEBrick::HTTPServer.new(options.merge(:DoNotListen => true))
0
+        @server.listeners.replace sockets
0
       end
0
+      
0
+      def self.start_server
0
+        @server.mount("/", ::Rack::Handler::WEBrick, @opts[:app])
0
+        @server.start
0
+        exit(@status)
0
+      end
0
+      
0
+      def self.stop(status = 0)
0
+        @status = status
0
+        @server.shutdown
0
+      end
0
+      
0
+      def self.exit_process(status = 0)
0
+      end      
0
+      
0
+      # start WEBrick server on given host and port.
0
+      
0
+      # ==== Parameters
0
+      # opts<Hash>:: Options for WEBrick (see below).
0
+      #
0
+      # ==== Options (opts)
0
+      # :host<String>:: The hostname that WEBrick should serve.
0
+      # :port<Fixnum>:: The port WEBrick should bind to.
0
+      # :app<String>>:: The application name.
0
+      # def self.start(opts={})
0
+      #   Merb.logger.warn!("Using Webrick adapter")
0
+      # 
0
+      #   options = {
0
+      #     :Port        => opts[:port],
0
+      #     :BindAddress => opts[:host],
0
+      #     :Logger      => Merb.logger,
0
+      #     :AccessLog   => [
0
+      #       [Merb.logger, ::WEBrick::AccessLog::COMMON_LOG_FORMAT],
0
+      #       [Merb.logger, ::WEBrick::AccessLog::REFERER_LOG_FORMAT]
0
+      #     ]
0
+      #   }
0
+      #      
0
+      #   server = ::WEBrick::HTTPServer.new(options)
0
+      #   Merb::Server.change_privilege
0
+      #   server.mount("/", ::Rack::Handler::WEBrick, opts[:app])
0
+      #   server.start
0
+      # end
0
     end
0
   end
0
 end
0
\ No newline at end of file
...
32
33
34
35
 
36
37
38
39
 
 
 
 
 
 
 
40
41
42
...
32
33
34
 
35
36
 
 
 
37
38
39
40
41
42
43
44
45
46
0
@@ -32,11 +32,15 @@ module Merb
0
         #   The hostname on which the app should run. Defaults to "0.0.0.0"
0
         # :Port<Fixnum>:: The port for the app. Defaults to 8080.
0
         def self.run(app, options={})
0
-          server = ::Mongrel::HttpServer.new(options[:Host] || '0.0.0.0',
0
+          @server = ::Mongrel::HttpServer.new(options[:Host] || '0.0.0.0',
0
                                              options[:Port] || 8080)
0
-          server.register('/', ::Merb::Rack::Handler::Mongrel.new(app))
0
-          yield server  if block_given?
0
-          server.run.join
0
+          @server.register('/', ::Merb::Rack::Handler::Mongrel.new(app))
0
+          yield @server  if block_given?
0
+          @server.run.join
0
+        end
0
+  
0
+        def self.stop(block = true)
0
+          @server.stop
0
         end
0
   
0
         # ==== Parameters
...
1
 
2
3
4
...
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
...
71
72
73
74
 
75
76
77
78
 
79
 
 
80
81
82
...
86
87
88
89
 
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
 
 
 
110
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
112
113
114
115
116
117
...
122
123
124
 
125
126
127
128
129
130
 
 
 
 
 
 
131
132
133
134
 
135
 
 
 
 
 
 
 
 
 
 
 
136
137
138
139
140
141
142
143
144
145
146
 
 
 
 
 
 
 
 
 
 
147
148
149
...
160
161
162
163
164
 
 
 
 
165
166
167
...
176
177
178
179
180
181
182
183
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
185
186
...
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
 
 
211
212
213
...
218
219
220
221
222
223
224
 
225
226
227
...
240
241
242
243
 
244
245
246
247
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
248
249
250
...
252
253
254
 
255
256
 
 
257
258
259
260
261
 
 
 
 
 
262
263
264
...
1
2
3
4
5
...
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
...
51
52
53
 
54
55
56
57
 
58
59
60
61
62
63
64
...
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
105
106
107
108
109
110
111
112
113
 
 
 
114
115
116
...
121
122
123
124
125
126
127
128
 
 
129
130
131
132
133
134
135
136
 
 
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
 
 
 
 
 
 
 
 
153
154
155
156
157
158
159
160
161
162
163
164
165
...
176
177
178
 
 
179
180
181
182
183
184
185
...
194
195
196
 
 
 
 
 
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
...
224
225
226
 
 
 
 
 
 
 
 
 
 
 
 
 
 
227
228
229
230
231
...
236
237
238
 
 
 
 
239
240
241
242
...
255
256
257
 
258
259
260
 
 
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
...
281
282
283
284
285
 
286
287
288
289
290
291
 
292
293
294
295
296
297
298
299
0
@@ -1,4 +1,5 @@
0
 require 'etc'
0
+
0
 module Merb
0
 
0
   # Server encapsulates the management of Merb daemons.
0
@@ -18,46 +19,25 @@ module Merb
0
       # If cluster is left out, then one process will be started. This process
0
       # will be daemonized if Merb::Config[:daemonize] is true.
0
       def start(port, cluster=nil)
0
+        
0
         @port = port
0
-        @cluster = cluster
0
-        if @cluster
0
-          @port.to_i.upto(@port.to_i + @cluster.to_i-1) do |port|
0
-            pidfile = pid_file(port)
0
-            pid = IO.read(pidfile).chomp.to_i if File.exist?(pidfile)
0
+        @cluster = cluster        
0
 
0
-            unless alive?(port)
0
-              remove_pid_file(port)
0
-              puts "Starting merb server on port #{port}, pid file: #{pidfile} and process id is #{pid}" if Merb::Config[:verbose]
0
-              daemonize(port)
0
-            else
0
-              raise "Merb is already running: port is #{port}, pid file: #{pidfile}, process id is #{pid}"
0
-            end
0
-          end
0
-        elsif Merb::Config[:daemonize]
0
+        if Merb::Config[:daemonize]
0
           pidfile = pid_file(port)
0
-          pid = IO.read(pidfile).chomp.to_i if File.exist?(pidfile)
0
+          pid = File.read(pidfile).chomp.to_i if File.exist?(pidfile)
0
 
0
           unless alive?(@port)
0
             remove_pid_file(@port)
0
             puts "Daemonizing..." if Merb::Config[:verbose]
0
             daemonize(@port)
0
           else
0
-            raise "Merb is already running: port is #{port}, pid file: #{pidfile}, process id is #{pid}"
0
+            Merb.fatal! "Merb is already running on port #{port}.\n" \
0
+              "\e[0m   \e[1;31;47mpid file: \e[34;47m#{pidfile}" \
0
+              "\e[1;31;47m, process id is \e[34;47m#{pid}."
0
           end
0
         else
0
-          trap('TERM') { exit }
0
-
0
-          if Merb::Config[:console_trap]
0
-            add_irb_trap
0
-          else
0
-            trap('INT') { puts "\nExiting"; exit }
0
-          end
0
-
0
-          puts "Running bootloaders..." if Merb::Config[:verbose]
0
-          BootLoader.run
0
-          puts "Starting Rack adapter..." if Merb::Config[:verbose]
0
-          Merb.logger.info! "Starting Merb server listening at #{Merb::Config[:host]}:#{port}"
0
-          Merb.adapter.start(Merb::Config.to_hash)
0
+          bootup
0
         end
0
       end
0
 
0
@@ -71,12 +51,14 @@ module Merb
0
         puts "About to check if port #{port} is alive..." if Merb::Config[:verbose]
0
         pidfile = pid_file(port)
0
         puts "Pidfile is #{pidfile}..." if Merb::Config[:verbose]
0
-        pid = IO.read(pidfile).chomp.to_i
0
+        pid = File.read(pidfile).chomp.to_i
0
         puts "Process id is #{pid}" if Merb::Config[:verbose]
0
         Process.kill(0, pid)
0
         true
0
-      rescue
0
+      rescue Errno::ESRCH, Errno::ENOENT
0
         false
0
+      rescue Errno::EACCES => e
0
+        Merb.fatal!("You don't have access to the PID file at #{pidfile}.", e)
0
       end
0
 
0
       # ==== Parameters
0
@@ -86,32 +68,49 @@ module Merb
0
       # ==== Alternatives
0
       # If you pass "all" as the port, the signal will be sent to all Merb
0
       # processes.
0
-      def kill(port, sig=9)
0
+      def kill(port, sig="INT")
0
         Merb::BootLoader::BuildFramework.run
0
         begin
0
-          pidfiles = port == "all" ?
0
-            pid_files : [ pid_file(port) ]
0
-
0
-          pidfiles.each do |f|
0
-            pid = IO.read(f).chomp.to_i
0
-            begin
0
-              Process.kill(sig, pid)
0
-              FileUtils.rm(f) if File.exist?(f)
0
-              puts "killed PID #{pid} with signal #{sig}"
0
-            rescue Errno::EINVAL
0
-              puts "Failed to kill PID #{pid}: '#{sig}' is an invalid or unsupported signal number."
0
-            rescue Errno::EPERM
0
-              puts "Failed to kill PID #{pid}: Insufficient permissions."
0
-            rescue Errno::ESRCH
0
-              puts "Failed to kill PID #{pid}: Process is deceased or zombie."
0
-              FileUtils.rm f
0
-            rescue Exception => e
0
-              puts "Failed to kill PID #{pid}: #{e.message}"
0
+          if sig == 9 && port == "main"
0
+            Dir["#{Merb.log_path}" / "*.pid"].each do |file|
0
+              kill_pid(9, File.read(file).chomp.to_i, file)
0
             end
0
+          else
0
+            pid = File.read(pid_file(port)).chomp.to_i
0
+            kill_pid(sig, pid, pid_file(port))
0
+          end
0
+        rescue Errno::EACCES => e
0
+          Merb.fatal! e.message, e
0
+        rescue Errno::ENOENT => e
0
+          Merb.fatal! "Could not find a PID file at #{pid_file(port)}", e           
0
+        end
0
+        if sig.is_a?(Integer)
0
+          sig = Signal.list.invert[sig]
0
+        end
0
+        if sig == "KILL" && port == "main"
0
+          Merb.fatal! "Killed all PIDs with signal KILL"
0
+        else
0
+          Merb.fatal! "Killed #{port} with signal #{sig}"
0
+        end
0
+      end
0
+      
0
+      def kill_pid(sig, pid, file)
0
+        begin
0
+          Process.kill(sig, pid)
0
+          FileUtils.rm(file) if File.exist?(file)
0
+        rescue Errno::EINVAL
0
+          Merb.fatal! "Failed to kill PID #{pid}: '#{sig}' is an invalid " \
0
+            "or unsupported signal number."
0
+        rescue Errno::EPERM
0
+          Merb.fatal! "Failed to kill PID #{pid}: Insufficient permissions."
0
+        rescue Errno::ESRCH
0
+          FileUtils.rm file
0
+          Merb.fatal! "Failed to kill PID #{pid}: Process is " \
0
+            "deceased or zombie."
0
+        rescue Exception => e
0
+          if !e.is_a?(SystemExit)
0
+            Merb.fatal! "Failed to kill PID #{pid}", e
0
           end
0
-        ensure
0
-          Merb.started = false
0
-          exit
0
         end
0
       end
0
 
0
@@ -122,28 +121,45 @@ module Merb
0
         fork do
0
           Process.setsid
0
           exit if fork
0
+          Merb.logger.warn! "In #{Process.pid}" if Merb.logger
0
           File.umask 0000
0
           STDIN.reopen "/dev/null"
0
           STDOUT.reopen "/dev/null", "a"
0
           STDERR.reopen STDOUT
0
-          trap("TERM") { exit }
0
-          Dir.chdir Merb::Config[:merb_root]
0
+          begin
0
+            Dir.chdir Merb::Config[:merb_root]
0
+          rescue Errno::EACCES => e
0
+            Merb.fatal! "You specified #{Merb::Config[:merb_root]} " \
0
+              "as the Merb root, but you did not have access to it.", e
0
+          end
0
           at_exit { remove_pid_file(port) }
0
           Merb::Config[:port] = port
0
-          BootLoader.run
0
-          Merb.adapter.start(Merb::Config.to_hash)
0
+          bootup
0
         end
0
+      rescue NotImplementedError => e
0
+        Merb.fatal! "Daemonized mode is not supported on your platform", e
0
+      end
0
+      
0
+      def bootup
0
+        trap('TERM') { exit }
0
+
0
+        puts "Running bootloaders..." if Merb::Config[:verbose]
0
+        BootLoader.run
0
+        puts "Starting Rack adapter..." if Merb::Config[:verbose]
0
+        Merb.adapter.start(Merb::Config.to_hash)
0
       end
0
 
0
       def change_privilege
0
-        if Merb::Config[:user]
0
-          if Merb::Config[:group]
0
-            puts "About to change privilege to group #{Merb::Config[:group]} and user #{Merb::Config[:user]}" if Merb::Config[:verbose]
0
-            _change_privilege(Merb::Config[:user], Merb::Config[:group])
0
-          else
0
-            puts "About to change privilege to user #{Merb::Config[:user]}" if Merb::Config[:verbose]
0
-            _change_privilege(Merb::Config[:user])
0
-          end
0
+        if Merb::Config[:user] && Merb::Config[:group]
0
+          Merb.logger.verbose! "About to change privilege to group " \
0
+            "#{Merb::Config[:group]} and user #{Merb::Config[:user]}"
0
+          _change_privilege(Merb::Config[:user], Merb::Config[:group])
0
+        elsif Merb::Config[:user]
0
+          Merb.logger.verbose! "About to change privilege to user " \
0
+            "#{Merb::Config[:user]}"
0
+          _change_privilege(Merb::Config[:user])
0
+        else
0
+          return true
0
         end
0
       end
0
 
0
@@ -160,8 +176,10 @@ module Merb
0
       # instead of the port based PID file.
0
       def remove_pid_file(port)
0
         pidfile = pid_file(port)
0
-        puts "Removing pid file #{pidfile} (port is #{port})..."
0
-        FileUtils.rm(pidfile) if File.exist?(pidfile)
0
+        if File.exist?(pidfile)
0
+          puts "Removing pid file #{pidfile} (port is #{port})..."
0
+          FileUtils.rm(pidfile)
0
+        end
0
       end
0
 
0
       # Stores a PID file on the filesystem.
0
@@ -176,11 +194,23 @@ module Merb
0
       # If Merb::Config[:pid_file] has been specified, that will be used
0
       # instead of the port based PID file.
0
       def store_pid(port)
0
-        pidfile = pid_file(port)
0
-        puts "Storing pid file to #{pidfile}..."
0
-        FileUtils.mkdir_p(File.dirname(pidfile)) unless File.directory?(File.dirname(pidfile))
0
-        puts "Created directory, writing process id..." if Merb::Config[:verbose]
0
-        File.open(pidfile, 'w'){ |f| f.write("#{Process.pid}") }
0
+        store_details(port)
0
+      end
0
+
0
+      def remove_pid(port)
0
+        FileUtils.rm(pid_file(port)) if File.file?(pid_file(port))
0
+      end
0
+
0
+      def store_details(port = nil)
0
+        file = pid_file(port)
0
+        begin
0
+          FileUtils.mkdir_p(File.dirname(file))
0
+        rescue Errno::EACCES => e
0
+          Merb.fatal! "You tried to store Merb logs in #{File.dirname(file)}, " \
0
+            "but you did not have access.", e
0
+        end
0
+        Merb.logger.warn! "Storing #{type} file to #{file}..." if Merb::Config[:verbose]
0
+        File.open(file, 'w'){ |f| f.write(Process.pid.to_s) }
0
       end
0
 
0
       # Gets the pid file for the specified port.
0
@@ -194,20 +224,8 @@ module Merb
0
       #   Location of pid file for specified port. If clustered and pid_file option
0
       #   is specified, it adds the port value to the path.
0
       def pid_file(port)
0
-        if Merb::Config[:pid_file]
0
-          pidfile = Merb::Config[:pid_file]
0
-          if Merb::Config[:cluster]
0
-            ext = File.extname(Merb::Config[:pid_file])
0
-            base = File.basename(Merb::Config[:pid_file], ext)
0
-            dir = File.dirname(Merb::Config[:pid_file])
0
-            File.join(dir, "#{base}.#{port}#{ext}")
0
-          else
0
-            Merb::Config[:pid_file]
0
-          end
0
-        else
0
-          pidfile = Merb.log_path / "merb.#{port}.pid"
0
-          Merb.log_path / "merb.#{port}.pid"
0
-        end
0
+        pidfile = Merb::Config[:pid_file] || (Merb.log_path / "merb.%s.pid")
0
+        pidfile % port
0
       end
0
 
0
       # Get a list of the pid files.
0
@@ -218,10 +236,7 @@ module Merb
0
       def pid_files
0
         if Merb::Config[:pid_file]
0
           if Merb::Config[:cluster]
0
-            ext = File.extname(Merb::Config[:pid_file])
0
-            base = File.basename(Merb::Config[:pid_file], ext)
0
-            dir = File.dirname(Merb::Config[:pid_file])
0
-            Dir[dir / "#{base}.*#{ext}"]
0
+            Dir[Merb::Config[:pid_file] % "*"]
0
           else
0
             [ Merb::Config[:pid_file] ]
0
           end
0
@@ -240,11 +255,25 @@ module Merb
0
       # If group is left out, the user will be used as the group.
0
       def _change_privilege(user, group=user)
0
 
0
-        puts "Changing privileges to #{user}:#{group}"
0
+        Merb.logger.warn! "Changing privileges to #{user}:#{group}"
0
 
0
         uid, gid = Process.euid, Process.egid
0
-        target_uid = Etc.getpwnam(user).uid
0
-        target_gid = Etc.getgrnam(group).gid
0
+        
0
+        begin
0
+          target_uid = Etc.getpwnam(user).uid
0
+        rescue ArgumentError => e
0
+          Merb.fatal!(
0
+            "You tried to use user #{user}, but no such user was found", e)
0
+          return false
0
+        end
0
+        
0
+        begin
0
+          target_gid = Etc.getgrnam(group).gid
0
+        rescue ArgumentError => e
0
+          Merb.fatal!(
0
+            "You tried to use group #{group}, but no such group was found", e)
0
+          return false
0
+        end
0
 
0
         if uid != target_uid || gid != target_gid
0
           # Change process ownership
0
@@ -252,13 +281,19 @@ module Merb
0
           Process::GID.change_privilege(target_gid)
0
           Process::UID.change_privilege(target_uid)
0
         end
0
+        true
0
       rescue Errno::EPERM => e
0
-        puts "Couldn't change user and group to #{user}:#{group}: #{e}"
0
+        Merb.fatal! "Couldn't change user and group to #{user}:#{group}", e
0
+        false
0
       end
0
 
0
       def add_irb_trap
0
         trap('INT') do
0
-          exit if @interrupted
0
+          if @interrupted
0
+            puts "Exiting\n"
0
+            exit
0
+          end
0
+          
0
           @interrupted = true
0
           puts "Interrupt a second time to quit"
0
           Kernel.sleep 1.5
...
1
2
 
3
4
5
...
1
2
3
4
5
6
0
@@ -1,5 +1,6 @@
0
 module Merb::Test::MultipartRequestHelper
0
   require 'rubygems'
0
+  gem "mime-types"
0
   require 'mime/types'
0
 
0
   class Param
...
56
57
58
59
 
60
61
62
63
64
 
65
66
67
...
76
77
78
79
 
80
81
82
83
84
 
85
86
87
...
96
97
98
99
 
100
101
102
...
144
145
146
147
 
148
149
150
151
152
 
153
154
155
...
56
57
58
 
59
60
61
62
63
 
64
65
66
67
...
76
77
78
 
79
80
81
82
83
 
84
85
86
87
...
96
97
98
 
99
100
101
102
...
144
145
146
 
147
148
149
150
151
 
152
153
154
155
0
@@ -56,12 +56,12 @@ describe Merb::Config do
0
 
0
   it "should support -c to set the number of cluster nodes" do
0
     Merb::Config.parse_args(["-c", "4"])
0
-    Merb::Config[:cluster].should == "4"
0
+    Merb::Config[:cluster].should == 4
0
   end
0
 
0
   it "should support -p to set the port number" do
0
     Merb::Config.parse_args(["-p", "6000"])
0
-    Merb::Config[:port].should == "6000"
0
+    Merb::Config[:port].should == 6000
0
   end
0
 
0
   it "should support -o to set the socket filename" do
0
@@ -76,12 +76,12 @@ describe Merb::Config do
0
 
0
   it "should support -s to set the socket number" do
0
     Merb::Config.parse_args(["-s", "0"])
0
-    Merb::Config[:socket].should == "0"
0
+    Merb::Config[:socket].should == 0
0
   end
0
 
0
   it "should support --socket to set the socket number" do
0
     Merb::Config.parse_args(["--socket", "3"])
0
-    Merb::Config[:socket].should == "3"
0
+    Merb::Config[:socket].should == 3
0
   end
0
 
0
   it "should support -P to set the PIDfile" do
0
@@ -96,7 +96,7 @@ describe Merb::Config do
0
   end
0
 
0
   it "should support setting of PIDfile with cluster nodes" do
0
-    Merb::Config.parse_args(["-P", "/tmp/merb.pidfile", "-c", "2", "-p", "6000"])
0
+    Merb::Config.parse_args(["-P", "/tmp/merb.%s.pidfile", "-c", "2", "-p", "6000"])
0
     Merb::Server.pid_file(6000).should == "/tmp/merb.6000.pidfile"
0
     Merb::Server.pid_file(6001).should == "/tmp/merb.6001.pidfile"
0
 
0
@@ -144,12 +144,12 @@ describe Merb::Config do
0
   end
0
 
0
   it "should support -K for a graceful kill" do
0
-    Merb::Server.should_receive(:kill).with("all", 1)
0
+    Merb::Server.should_receive(:kill).with("main", 2)
0
     Merb.start(["-K", "all"])
0
   end
0
 
0
   it "should support -k for a hard kill" do
0
-    Merb::Server.should_receive(:kill).with("all", 9)
0
+    Merb::Server.should_receive(:kill).with("main", 9)
0
     Merb.start(["-k", "all"])
0
   end
0
 
...
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
105
106
107
108
 
 
109
110
111
112
113
 
 
 
 
 
114
115
116
117
118
 
 
 
 
 
 
 
119
120
121
122
 
 
 
 
 
 
 
123
124
125
 
 
 
 
 
 
126
127
128
129
130
131
 
 
 
 
 
 
132
133
134
135
136
137
 
 
 
 
 
 
138
139
140
141
142
143
 
 
 
 
 
 
144
145
146
147
148
149
 
 
 
 
 
 
150
151
152
153
154
155
 
 
 
 
 
156
157
158
159
160
 
161
162
163
164
 
165
166
167
168
 
169
170
171
172
 
173
174
175
176
 
177
178
179
...
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
105
106
107
108
109
 
 
 
110
111
112
113
114
115
116
117
 
 
 
118
119
120
121
122
123
124
125
 
 
126
127
128
129
130
131
132
133
 
 
 
 
134
135
136
137
138
139
140
141
 
 
 
 
142
143
144
145
146
147
148
149
 
 
 
 
150
151
152
153
154
155
156
157
 
 
 
 
158
159
160
161
162
163
164
165
 
 
 
 
166
167
168
169
170
171
172
173
174
 
175
176
177
178
 
179
180
181
182
 
183
184
185
186
 
187
188
189
190
 
191
192
193
194
0
@@ -31,149 +31,164 @@ describe Merb::Logger do
0
   describe "#set_log" do
0
 
0
     before(:each) do
0
-      @logger = Merb::Logger.new(Merb.log_file)
0
+      @logger = Merb::Logger.new(Merb.log_file)
0
     end
0
 
0
     it "should set the log level to :warn (4) when second parameter is :warn" do
0
-      Merb::Logger.new(Merb.log_file, :warn).level.should eql(4)
0
+      Merb::Config[:log_level] = :warn
0
+      Merb.logger = nil
0
+      Merb.logger.level.should == 4
0
     end
0
 
0
     it "should set the log level to :debug (0) when Merb.environment is development" do
0
-      Merb.should_receive(:environment).and_return("development")
0
-      @logger.set_log(Merb.log_path / "development.log")
0
-      @logger.level.should eql(0)
0
+      Merb.environment = "development"
0
+      Merb::Config.delete(:log_level)
0
+      Merb.logger = nil
0
+      Merb::BootLoader::Logger.run
0
+      Merb.logger.level.should == 0
0
     end
0
     
0
     it "should set the log level to :error (6) when Merb.environment is production" do
0
-      Merb.should_receive(:environment).and_return("production")
0
-      @logger.set_log(Merb.log_path / "production.log")
0
-      @logger.level.should eql(4)
0
+      Merb.environment = "production"
0
+      Merb::Config.delete(:log_level)
0
+      Merb.logger = nil
0
+      Merb::BootLoader::Logger.run
0
+      Merb.logger.level.should == 4
0
     end
0
     
0
-    it "should initialize the buffer to an empty array" do
0
-      @logger.buffer.should eql([])
0
-    end
0
-
0
     it "should default the delimiter to ' ~ '" do
0
-      @logger.delimiter.should eql(" ~ ")
0
-    end
0
-    
0
-    it "should assign the newly created object to Merb.logger" do
0
-      @logger = Merb::Logger.new(Merb.log_file)
0
-      Merb.logger.should eql(@logger)
0
-    end
0
+      Merb.logger.delimiter.should eql(" ~ ")
0
+    end    
0
     
0
   end
0
   
0
   describe "#flush" do
0
 
0
-    before(:each) do
0
-      @logger = Merb::Logger.new(Merb.log_file)
0
-    end
0
-    
0
     it "should immediately return if the buffer is empty" do
0
-      @logger.should_not_receive(:write)
0
-      @logger.flush
0
+      Merb::Config[:log_stream] = StringIO.new
0
+      Merb.logger = nil
0
+      
0
+      Merb.logger.flush
0
+      Merb::Config[:log_stream].string.should == ""
0
     end
0
 
0
     it "should call the write_method with the stringified contents of the buffer if the buffer is non-empty" do
0
-      @logger.send(:<<, "a message")
0
-      @logger.send(:<<, "another message")
0
-      @logger.log.should_receive(:write).with(" ~ a message\n ~ another message\n")
0
-      @logger.flush
0
+      Merb::Config[:log_stream] = StringIO.new
0
+      Merb.logger = nil
0
+      
0
+      Merb.logger << "a message"
0
+      Merb.logger << "another message"
0
+      Merb.logger.flush
0
+      
0
+      Merb::Config[:log_stream].string.should == " ~ a message\n ~ another message\n"
0
     end
0
 
0
   end
0
   
0
-  describe "#close" do
0
-    before(:each) do
0
-      @logger = Merb::Logger.new(Merb.log_file)
0
-    end
0
+  # There were close specs here, but the logger isn't an IO anymore, and
0
+  # shares a stream with other loggers, so it shouldn't be closing the
0
+  # stream.
0
 
0
-    it "should flush the buffer before closing" do
0
-      # TODO: how to specify order? eg. flush then close
0
-      @logger.should_receive(:flush)
0
-      @logger.log.should_receive(:close)
0
-      @logger.close
0
-    end
0
+  describe "level methods" do
0
 
0
-    it "should call the close method if the log responds to close" do
0
-      @logger.log.should_receive(:close)
0
-      @logger.close
0
+    def set_level(level)
0
+      Merb::Config[:log_level] = level
0
+      Merb.logger = nil
0
     end
0
 
0
-    it "shouldn't call the close method if the log is a terminal" do
0
-      @logger.log.should_receive(:tty?).and_return(true)
0
-      @logger.log.should_not_receive(:close)
0
-      @logger.close
0
+    before(:each) do
0
+      @stream = Merb::Config[:log_stream] = StringIO.new
0
     end
0
 
0
-    it "should set the stored log attribute to nil" do
0
-      @logger.close
0
-      @logger.log.should eql(nil)
0
+    it "should provide a #debug method which adds to the buffer in level :debug" do
0
+      set_level(:debug)
0
+      Merb.logger.debug("message")
0
+      Merb.logger.flush
0
+      @stream.string.should == " ~ message\n"
0
     end
0
 
0
-  end
0
-
0
-  describe "<<" do
0
+    it "should provide a #debug method which does not add to the buffer " \
0
+      "in level :info or higher" do
0
+      set_level(:info)
0
+      Merb.logger.debug("message")
0
+      Merb.logger.flush
0
+      @stream.string.should == ""
0
+    end
0
     
0
-  end
0
-
0
-  describe "level methods" do
0
+    it "should provide an #info method which adds to the buffer in " \
0
+      "level :info or below" do
0
+      set_level(:info)
0
+      Merb.logger.info("message")
0
+      Merb.logger.flush
0
+      @stream.string.should == " ~ message\n"
0
+    end
0
 
0
-    before(:all) do
0
-      @logger = Merb::Logger.new(Merb.log_file)
0
+    it "should provide a #info method which does not add to the buffer " \
0
+      "in level :warn or higher" do
0
+      set_level(:warn)
0
+      Merb.logger.info("message")
0
+      Merb.logger.flush
0
+      @stream.string.should == ""
0
     end
0
 
0
-    it "should provide a #debug method which can be used to log" do
0
-      @logger.should respond_to(:debug)
0
-      @logger.should_receive(:<<).with("message").and_return(true)
0
-      @logger.debug("message")
0
+    it "should provide a #warn method which adds to the buffer in " \
0
+      "level :warn or below" do
0
+      set_level(:warn)
0
+      Merb.logger.warn("message")
0
+      Merb.logger.flush
0
+      @stream.string.should == " ~ message\n"
0
     end
0
 
0
-    it "should provide a #info method which can be used to log" do
0
-      @logger.should respond_to(:info)
0
-      @logger.should_receive(:<<).with("message").and_return(true)
0
-      @logger.info("message")
0
+    it "should provide a #warn method which does not add to the buffer " \
0
+      "in level :error or higher" do
0
+      set_level(:error)
0
+      Merb.logger.warn("message")
0
+      Merb.logger.flush
0
+      @stream.string.should == ""
0
     end
0
 
0
-    it "should provide a #warn method which can be used to log" do
0
-      @logger.should respond_to(:warn)
0
-      @logger.should_receive(:<<).with("message").and_return(true)
0
-      @logger.warn("message")
0
+    it "should provide a #error method which adds to the buffer in " \
0
+      "level :error or below" do
0
+      set_level(:error)
0
+      Merb.logger.error("message")
0
+      Merb.logger.flush
0
+      @stream.string.should == " ~ message\n"
0
     end
0
 
0
-    it "should provide a #error method which can be used to log" do
0
-      @logger.should respond_to(:error)
0
-      @logger.should_receive(:<<).with("message").and_return(true)
0
-      @logger.error("message")
0
+    it "should provide a #error method which does not add to the buffer " \
0
+      "in level :fatal or higher" do
0
+      set_level(:fatal)
0
+      Merb.logger.error("message")
0
+      Merb.logger.flush
0
+      @stream.string.should == ""
0
     end
0
 
0
-    it "should provide a #fatal method which can be used to log" do
0
-      @logger.should respond_to(:fatal)
0
-      @logger.should_receive(:<<).with("message").and_return(true)
0
-      @logger.fatal("message")
0
+    it "should provide a #fatal method which always logs" do
0
+      set_level(:fatal)
0
+      Merb.logger.fatal("message")
0
+      Merb.logger.flush
0
+      @stream.string.should == " ~ message\n"
0
     end
0
     
0
     # TODO: add positive and negative tests for each of the methods below:
0
     it "should provide a #debug? method" do
0
-      @logger.should respond_to(:debug?)
0
+      Merb.logger.should respond_to(:debug?)
0
     end
0
 
0
     it "should provide a #info? method" do
0
-      @logger.should respond_to(:info?)
0
+      Merb.logger.should respond_to(:info?)
0
     end
0
 
0
     it "should provide a #warn? method" do
0
-      @logger.should respond_to(:warn?)
0
+      Merb.logger.should respond_to(:warn?)
0
     end
0
 
0
     it "should provide a #error? method" do
0
-      @logger.should respond_to(:error?)
0
+      Merb.logger.should respond_to(:error?)
0
     end
0
 
0
     it "should provide a #fatal? method" do
0
-      @logger.should respond_to(:fatal?)
0
+      Merb.logger.should respond_to(:fatal?)
0
     end
0
 
0
   end