merb 0.0.8 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. data/README +64 -80
  2. data/Rakefile +25 -12
  3. data/bin/merb +2 -223
  4. data/examples/README_EXAMPLES +10 -0
  5. data/examples/skeleton.tar +0 -0
  6. data/lib/merb.rb +48 -21
  7. data/lib/merb/core_ext.rb +12 -2
  8. data/lib/merb/core_ext/merb_class.rb +21 -21
  9. data/lib/merb/core_ext/merb_kernel.rb +60 -0
  10. data/lib/merb/core_ext/merb_object.rb +14 -0
  11. data/lib/merb/core_ext/merb_string.rb +3 -13
  12. data/lib/merb/generators/merb_app/merb_app.rb +33 -0
  13. data/lib/merb/merb_constants.rb +7 -0
  14. data/lib/merb/merb_controller.rb +31 -23
  15. data/lib/merb/merb_drb_server.rb +6 -60
  16. data/lib/merb/merb_exceptions.rb +162 -2
  17. data/lib/merb/merb_handler.rb +8 -19
  18. data/lib/merb/merb_mailer.rb +60 -0
  19. data/lib/merb/merb_router.rb +1 -1
  20. data/lib/merb/merb_server.rb +240 -0
  21. data/lib/merb/merb_upload_handler.rb +1 -1
  22. data/lib/merb/merb_view_context.rb +11 -6
  23. data/lib/merb/mixins/basic_authentication_mixin.rb +11 -13
  24. data/lib/merb/mixins/controller_mixin.rb +12 -6
  25. data/lib/merb/mixins/form_control_mixin.rb +94 -0
  26. data/lib/merb/mixins/render_mixin.rb +50 -24
  27. data/lib/merb/mixins/view_context_mixin.rb +122 -0
  28. data/lib/merb/session/merb_ar_session.rb +13 -14
  29. data/lib/merb/session/merb_memory_session.rb +105 -0
  30. metadata +13 -132
  31. data/examples/app_skeleton/Rakefile +0 -82
  32. data/examples/app_skeleton/dist/app/helpers/global_helper.rb +0 -6
  33. data/examples/app_skeleton/dist/conf/merb.yml +0 -11
  34. data/examples/app_skeleton/dist/conf/merb_init.rb +0 -16
  35. data/examples/app_skeleton/dist/conf/mup.conf +0 -5
  36. data/examples/app_skeleton/dist/conf/router.rb +0 -19
  37. data/examples/app_skeleton/scripts/merb_stop +0 -13
  38. data/examples/app_skeleton/scripts/new_migration +0 -21
  39. data/examples/app_skeleton/test/test_helper.rb +0 -1
  40. data/examples/sample_app/Rakefile +0 -82
  41. data/examples/sample_app/dist/app/controllers/files.rb +0 -31
  42. data/examples/sample_app/dist/app/controllers/posts.rb +0 -71
  43. data/examples/sample_app/dist/app/controllers/test.rb +0 -40
  44. data/examples/sample_app/dist/app/helpers/global_helper.rb +0 -7
  45. data/examples/sample_app/dist/app/helpers/posts_helper.rb +0 -4
  46. data/examples/sample_app/dist/app/models/comment.rb +0 -3
  47. data/examples/sample_app/dist/app/models/post.rb +0 -4
  48. data/examples/sample_app/dist/app/views/files/progress.jerb +0 -3
  49. data/examples/sample_app/dist/app/views/files/start.herb +0 -62
  50. data/examples/sample_app/dist/app/views/files/upload.herb +0 -6
  51. data/examples/sample_app/dist/app/views/layout/application.herb +0 -61
  52. data/examples/sample_app/dist/app/views/layout/foo.herb +0 -6
  53. data/examples/sample_app/dist/app/views/posts/_comments.herb +0 -11
  54. data/examples/sample_app/dist/app/views/posts/comment.jerb +0 -1
  55. data/examples/sample_app/dist/app/views/posts/list.herb +0 -5
  56. data/examples/sample_app/dist/app/views/posts/new.herb +0 -37
  57. data/examples/sample_app/dist/app/views/posts/show.herb +0 -37
  58. data/examples/sample_app/dist/app/views/posts/xml_test.xerb +0 -3
  59. data/examples/sample_app/dist/app/views/shared/_test.herb +0 -1
  60. data/examples/sample_app/dist/app/views/test/foo.herb +0 -2
  61. data/examples/sample_app/dist/app/views/test/hello.herb +0 -5
  62. data/examples/sample_app/dist/app/views/test/json.jerb +0 -1
  63. data/examples/sample_app/dist/conf/merb.yml +0 -11
  64. data/examples/sample_app/dist/conf/merb_init.rb +0 -24
  65. data/examples/sample_app/dist/conf/mup.conf +0 -5
  66. data/examples/sample_app/dist/conf/router.rb +0 -19
  67. data/examples/sample_app/dist/public/images/bg.jpg +0 -0
  68. data/examples/sample_app/dist/public/images/book.gif +0 -0
  69. data/examples/sample_app/dist/public/images/booksmall.gif +0 -0
  70. data/examples/sample_app/dist/public/images/greenright.jpg +0 -0
  71. data/examples/sample_app/dist/public/images/louiecon.gif +0 -0
  72. data/examples/sample_app/dist/public/images/menu.gif +0 -0
  73. data/examples/sample_app/dist/public/images/menuleft.gif +0 -0
  74. data/examples/sample_app/dist/public/images/menuright.gif +0 -0
  75. data/examples/sample_app/dist/public/images/mountain.jpg +0 -0
  76. data/examples/sample_app/dist/public/images/n3.jpg +0 -0
  77. data/examples/sample_app/dist/public/images/nautica.jpg +0 -0
  78. data/examples/sample_app/dist/public/javascripts/application.js +0 -0
  79. data/examples/sample_app/dist/public/javascripts/effects.js +0 -975
  80. data/examples/sample_app/dist/public/javascripts/mup.js +0 -113
  81. data/examples/sample_app/dist/public/javascripts/prototype.js +0 -2264
  82. data/examples/sample_app/dist/public/stylesheets/merb.css +0 -277
  83. data/examples/sample_app/dist/public/test.html +0 -5
  84. data/examples/sample_app/dist/schema/migrations/001_add_comments_to_posts.rb +0 -22
  85. data/examples/sample_app/dist/schema/migrations/002_add_sessions_table.rb +0 -14
  86. data/examples/sample_app/dist/schema/schema.rb +0 -28
  87. data/examples/sample_app/foo.txt +0 -0
  88. data/examples/sample_app/log/merb.4000.pid +0 -1
  89. data/examples/sample_app/script/merb_stop +0 -13
  90. data/examples/sample_app/script/new_migration +0 -21
  91. data/examples/sample_app/test/test_helper.rb +0 -1
  92. data/lib/merb/mixins/javascript_mixin.rb +0 -147
  93. data/lib/merb/session/merb_drb_session.rb +0 -65
  94. data/test/test_helper.rb +0 -1
  95. data/test/unit/route_matcher_test.rb +0 -46
@@ -3,72 +3,18 @@ require File.dirname(__FILE__)+'/merb_upload_progress'
3
3
 
4
4
  module Merb
5
5
 
6
- class DRbSession
7
-
6
+ class DrbServiceProvider
8
7
  include DRbUndumped
9
8
 
10
9
  class << self
11
-
12
- def setup(opts={})
13
- @opts = opts
14
- @sessions = Hash.new
15
- @timestamps = Hash.new
16
- @mutex = Mutex.new
17
- @session_ttl = opts.fetch(:session_ttl, 15*60) # default 15 minutes
18
- start_timer
19
- self
20
- end
21
-
22
- def create(opts={})
23
- self[opts[:sess_id]] = opts[:data]
24
- end
25
-
26
- def [](key)
27
- @mutex.synchronize {
28
- @timestamps[key] = Time.now
29
- @sessions[key]
30
- }
31
- end
32
-
33
- def []=(key, val)
34
- @mutex.synchronize {
35
- @timestamps[key] = Time.now
36
- @sessions[key] = val
37
- }
38
- end
39
-
40
- def delete(key)
41
- @mutex.synchronize {
42
- @sessions.delete(key)
43
- }
44
- end
45
-
46
- def reap_old_sessions
47
- @timestamps.each do |key,stamp|
48
- if stamp + @session_ttl < Time.now
49
- delete(key)
50
- end
51
- end
52
- GC.start
53
- end
54
-
55
- def start_timer
56
- Thread.new do
57
- sleep @session_ttl
58
- reap_old_sessions
59
- end
60
- end
61
-
62
- def sessions
63
- @sessions
64
- end
65
10
 
66
11
  def upload_progress
67
- ::Merb::UploadProgress.new
68
- end
12
+ @upload_progress ||= ::Merb::UploadProgress.new
13
+ end
69
14
 
70
- end # end singleton class
15
+ end
71
16
 
72
- end # end DRbSession
17
+ end
18
+
73
19
 
74
20
  end
@@ -1,4 +1,164 @@
1
+ begin
2
+ require 'coderay'
3
+ rescue LoadError => ex
4
+ end
5
+
1
6
  module Merb
2
- class Noroutefound < RuntimeError; end
3
- class MissingControllerFile < RuntimeError; end
7
+ class MerbError < StandardError; end
8
+ class Noroutefound < MerbError; end
9
+ class MissingControllerFile < MerbError; end
10
+ class MerbControllerError < MerbError; end
11
+
12
+ # format exception message for browser display
13
+ def self.html_exception(e)
14
+ ::Merb::Server.show_error ? ErrorResponse.new(e).out : "500 Internal Server Error!"
15
+ end
16
+
17
+ def self.exception(e)
18
+ "#{ e.message } - (#{ e.class })\n" <<
19
+ "#{(e.backtrace or []).join("\n")}"
20
+ end
21
+
22
+ class ErrorResponse
23
+ def initialize error
24
+ @error = error
25
+ end
26
+
27
+ def out
28
+ error = @error
29
+ backtrace = @error.backtrace
30
+
31
+ colors = []
32
+ min = 104
33
+ max = 255
34
+ step = -((max - min) / backtrace.size).abs
35
+ max.step(min, step) do |color|
36
+ colors << color
37
+ end
38
+
39
+ backtrace.map! do |line|
40
+ file, lineno, meth = line.scan(/(.*?):(\d+)(?::in `(.*?)')?/).first
41
+ lines = __caller_lines__(file, lineno, 5)
42
+ [ lines, lines.object_id.abs, file, lineno, meth ]
43
+ end
44
+
45
+ error_page(colors, error, *backtrace)
46
+ end
47
+
48
+ # this method offers highlighting for the sourcecode-chunks from the
49
+ # traceback, just gem install coderay
50
+ def error_page(colors, title, *backtrace)
51
+ @backtrace = backtrace
52
+ @colors = colors
53
+ @title = title
54
+ @coderay = Object.constants.include?('CodeRay')
55
+
56
+
57
+ template =
58
+ <<-HEREDOC
59
+ <html>
60
+ <head>
61
+ <title><%= @title %></title>
62
+ <style type="text/css">
63
+ <!--
64
+ h1.main {
65
+ text-align: center;
66
+ }
67
+ table.main {
68
+ background: #000;
69
+ }
70
+ table.main tr.head {
71
+ background: #fee;
72
+ width: 100%;
73
+ }
74
+ table.main tr.source_container {
75
+ display: none;
76
+ }
77
+ tr.source_container div {
78
+ width: 100%;
79
+ overflow: auto;
80
+ }
81
+ tr.source_container div table {
82
+ background: #ddd;
83
+ width: 100%;
84
+ }
85
+ tr.source_container div table tr {
86
+ text-align:center;
87
+ }
88
+ tr.source_container div table tr.source {
89
+ text-align:left;
90
+ }
91
+ tr a {
92
+ color: black;
93
+ }
94
+ div.source {
95
+ background: #fff;
96
+ }
97
+ -->
98
+ <% if @coderay %>
99
+ <%= CodeRay::Encoders[:html]::CSS.new.stylesheet %>
100
+ <% end %>
101
+ </style>
102
+ <script type="text/javascript">
103
+ $ = function(el){
104
+ if(el === null || el === undefined)
105
+ throw("Argument for $() must be a domRef/domId");
106
+ if( el.constructor === String )
107
+ el = document.getElementById(el);
108
+ if(el === null || !el.nodeType)
109
+ throw("Argument for $() not found in document tree.");
110
+ return el;
111
+ }
112
+
113
+ var toggle = function(el){
114
+ el = $(el);
115
+ var visible = el.style.display != 'none'; // visible? boolean
116
+ if(visible){
117
+ el.style.display = 'none'; // hide
118
+ }else{
119
+ el.style.display = ''; // unhide
120
+ }
121
+ }
122
+ </script>
123
+ </head>
124
+ <body>
125
+ <span style="float:left;">
126
+ <img src="data:image/jpg;base64,/9j/4AAQSkZJRgABAgAAZABkAAD/7AARRHVja3kAAQAEAAAAJQAA/+4ADkFk\nb2JlAGTAAAAAAf/bAIQADQkJCQoJDQoKDRMMCwwTFhENDREWGhUVFhUVGhkU\nFhUVFhQZGR0fIB8dGScnKionJzk4ODg5QEBAQEBAQEBAQAEODAwOEA4RDw8R\nFA4RDhQVERISERUgFRUXFRUgKB0ZGRkZHSgjJiAgICYjLCwoKCwsNzc1NzdA\nQEBAQEBAQEBA/8AAEQgAJgAtAwEiAAIRAQMRAf/EAT8AAAEFAQEBAQEBAAAA\nAAAAAAMAAQIEBQYHCAkKCwEAAQUBAQEBAQEAAAAAAAAAAQACAwQFBgcICQoL\nEAABBAEDAgQCBQcGCAUDDDMBAAIRAwQhEjEFQVFhEyJxgTIGFJGhsUIjJBVS\nwWIzNHKC0UMHJZJT8OHxY3M1FqKygyZEk1RkRcKjdDYX0lXiZfKzhMPTdePz\nRieUpIW0lcTU5PSltcXV5fVWZnaGlqa2xtbm9jdHV2d3h5ent8fX5/cRAAIC\nAQIEBAMEBQYHBwYFNQEAAhEDITESBEFRYXEiEwUygZEUobFCI8FS0fAzJGLh\ncoKSQ1MVY3M08SUGFqKygwcmNcLSRJNUoxdkRVU2dGXi8rOEw9N14/NGlKSF\ntJXE1OT0pbXF1eX1VmZ2hpamtsbW5vYnN0dXZ3eHl6e3x//aAAwDAQACEQMR\nAD8A9OVDK6xRQYa02axuEBvnB7x8Fm/W36x19HqoxmAvyswkMraHFwY2N7va\nCe/5Vy/Seu29RvdXaI2CW6EBw4OhGnZAk9Einrbuvn03Oaw1lvmCCP8ANXPW\n9Zz7shlrM9zKBLnN3Fmg1gnj8An61m14eCbAN9j4ZVWJJc5x2gQPivO3ZnUM\nXLtfY8by73sDmuHw9pPCZRPVNgdH13F+teJU51WbZpX9O4xpPEho4juug9Wr\n0vW3t9Lbv9SRt2xO7dxEL5+blW5I2W2FrdsSJJgdj4r0P1W/+Nf6Un1p9PZJ\nnf8AaPU2+P0fwThdVaLF3TlfXZvUsr63ZIpZY4Y9VDKTWwuIY5vqEe3uXFx+\nSn0XBupy9t+loqDXy1zTJLST7tefJbVDLsjLycwO9Sy/09ANQafUJP8A4IrG\nQGufXltEAjZYPA900kkeCRoXA+sHSeoTXn41xJxnB9bY3e4HRcTbVsfB2h7j\ntDWkkAT7jrPjC9FzCMfpJZU5wDGgN7wNI4HZcJdiNrvP2t+62SNg9vedxnRO\nj2WyQfZy2oOA5kbfGedF0jb8w/Uq2ra/25NcGD9F1dk6/BqrYNODY5rrve4+\n2rGpBcR3Mr0j9iVf83vscD1J3bZMep/NelM+Hsn5oqa3RG0uuf6b9j9dsCRI\n+lx4haGT9g1FgZ6nc0n/AKrSEkko/Kg7vO9XtsxzV+y6a8xjtxu+hWWwPY0f\npBMnv2XN52ZmbyLunYocCZNl1bpdENmS0wOdUkkPsT9rL6rfbf8AnJiPzvT+\nzfpPVrZ6ezb6Ttv0e26OTzC9U3uk+z88ACRu+hz8YSSR6q6P/9k=\n"/>
127
+ </span>
128
+ <h1><%= @title %></h1>
129
+ <table class="main">
130
+ <tr class="head">
131
+ <td>File</td><td>Line</td><td>Method</td>
132
+ </tr>
133
+ <% @backtrace.each do |lines, hash, file, lineno, meth| %>
134
+ <tr id="line_<%= hash %>" style="background:rgb(104,104,<%= @colors.shift %>);">
135
+ <td><a href='#' onclick="toggle('source_<%= hash %>'); return false"><%= file %></a></td><td><%= lineno %></td><td><%= meth %></td>
136
+ </tr>
137
+ <tr id="source_<%= hash %>" <%= @coderay? " class='CodeRay' " : ''%> style="display:none;">
138
+ <td colspan="3">
139
+ <div class="source">
140
+ <table>
141
+ <tr><td colspan='2'><a href='txmt://open?url=file://<%=file%>&line=<%=lineno%>'>Open in TextMate</a></td></tr>
142
+ <% lines.each do |llineno, lcode, lcurrent| %>
143
+ <tr class="source"<%= 'style="background:#faa;"' if lcurrent %>>
144
+ <td><%= llineno %></td>
145
+ <td>
146
+ <%= @coderay ? CodeRay.scan(lcode, :ruby).html : "<pre>\#{lcode}</pre>" %>
147
+ </td>
148
+ </tr>
149
+ <% end %>
150
+ </table>
151
+ </div>
152
+ </td>
153
+ </tr>
154
+ <% end %>
155
+ </table>
156
+ </body>
157
+ </html>
158
+ HEREDOC
159
+ tmpl = Erubis::MEruby.new template
160
+ tmpl.result(binding)
161
+ end
162
+ end
163
+
4
164
  end
@@ -67,18 +67,17 @@ class MerbHandler < Mongrel::HttpHandler
67
67
  @guard.synchronize {
68
68
  controller.dispatch(action)
69
69
  }
70
- rescue Exception => e
70
+ rescue Object => e
71
71
  response.start(500) do |head,out|
72
72
  head["Content-Type"] = "text/html"
73
- MERB_LOGGER.info(exception(e))
74
- out << html_exception(e)
73
+ MERB_LOGGER.info(Merb.exception(e))
74
+ out << Merb.html_exception(e)
75
75
  end
76
76
  return
77
77
  end
78
78
 
79
79
  sendfile, clength = nil
80
80
  response.status = controller.status
81
-
82
81
  # check for the X-SENDFILE header from your Merb::Controller
83
82
  # and serve the file directly instead of buffering.
84
83
  controller.headers.each do |k, v|
@@ -117,9 +116,10 @@ class MerbHandler < Mongrel::HttpHandler
117
116
  else
118
117
  MERB_LOGGER.info("Response status: #{response.status}\nComplete Request took: #{Time.now - start} seconds\n\n")
119
118
  # render response from successful controller
120
- response.send_status((controller.body||='').length)
119
+ body = (controller.body.to_s rescue '')
120
+ response.send_status(body.length)
121
121
  response.send_header
122
- response.write(controller.body)
122
+ response.write(body)
123
123
  end
124
124
  end
125
125
  end
@@ -131,7 +131,7 @@ class MerbHandler < Mongrel::HttpHandler
131
131
  # [controller, action]
132
132
  def handle(request)
133
133
  path = request.params[Mongrel::Const::PATH_INFO].sub(/\/+/, '/')
134
- path = path[0..-2] if (path[-1] == ?/)
134
+ path = path[0..-2] if (path[-1] == ?/) && path.size > 1
135
135
  route = Merb::RouteMatcher.new.route_request(path)
136
136
  [ instantiate_controller(route[:controller], request.body, request.params, route),
137
137
  route[:action] ]
@@ -142,7 +142,7 @@ class MerbHandler < Mongrel::HttpHandler
142
142
  # into a new object passing in the request and response.
143
143
  # this is where your Merb::Controller is instantiated.
144
144
  def instantiate_controller(controller_name, req, env, params)
145
- if !File.exist?(Merb::Server.config[:dist_root]+"/app/controllers/#{controller_name.snake_case}.rb")
145
+ if !File.exist?(DIST_ROOT+"/app/controllers/#{controller_name.snake_case}.rb")
146
146
  raise Merb::MissingControllerFile
147
147
  end
148
148
  begin
@@ -154,15 +154,4 @@ class MerbHandler < Mongrel::HttpHandler
154
154
  end
155
155
  end
156
156
 
157
- # format exception message for browser display
158
- def html_exception(e)
159
- "<html><body><h2>Merb Error!</h2><p>#{ e.message } - (#{ e.class })\n" <<
160
- "#{(e.backtrace or []).join('<br />')}</p></body></html>"
161
- end
162
-
163
- def exception(e)
164
- "#{ e.message } - (#{ e.class })\n" <<
165
- "#{(e.backtrace or []).join("\n")}"
166
- end
167
-
168
157
  end
@@ -0,0 +1,60 @@
1
+ require 'net/smtp'
2
+
3
+ begin
4
+ require 'mailfactory'
5
+ rescue LoadError
6
+ puts "You need to install the mailfactory gem to use Merb::Mailer"
7
+ MERB_LOGGER.fatal "You need to install the mailfactory gem to use Merb::Mailer"
8
+ end
9
+
10
+ module Merb
11
+ class Mailer
12
+
13
+ shared_accessor :config
14
+
15
+ def sendmail
16
+ sendmail = IO.popen("sendmail #{@mail.to}", 'w+')
17
+ sendmail.puts @mail.to_s
18
+ sendmail.close
19
+ end
20
+
21
+ # :plain, :login, or :cram_md5
22
+ def net_smtp
23
+ Net::SMTP.start(config[:host], config[:port].to_i, config[:domain],
24
+ config[:user], config[:pass], (config[:auth].intern||:plain)) { |smtp|
25
+ smtp.send_message(@mail.to_s, @mail.from, @mail.to)
26
+ }
27
+ end
28
+
29
+ def deliver!
30
+ config == :sendmail ? sendmail : net_smtp
31
+ end
32
+
33
+ def initialize(o={})
34
+ self.config = :sendmail if config.nil?
35
+ @mail = returning MailFactory.new() do |m|
36
+ m.to = o[:to]
37
+ m.from = o[:from]
38
+ m.subject = o[:subject]
39
+ m.text = o[:body]
40
+ m.html = o[:html] if o[:html]
41
+ end
42
+ end
43
+
44
+ end
45
+ end
46
+ =begin
47
+ m = Merb::Mailer.new :to => 'foo@bar.com',
48
+ :from => 'bar@foo.com',
49
+ :subject => 'Welcome to whatever!',
50
+ :body => partial(:sometemplate)
51
+ m.deliver!
52
+
53
+ Merb::Mailer.config = {
54
+ :host=>'smtp.yourserver.com',
55
+ :port=>'25',
56
+ :user=>'user',
57
+ :pass=>'pass',
58
+ :auth=>:plain # :plain, :login, or :cram_md5, default :plain
59
+ }
60
+ =end
@@ -69,7 +69,7 @@ module Merb
69
69
  return ' when /\A\/([^\/;.,?]+)(?:\/?\Z|\/([^\/;.,?]+)\/?)(?:\/?\Z|\/([^\/;.,?]+)\/?)\Z/
70
70
  @sections[:controller] = $1
71
71
  @sections[:action] = $2 || \'index\'
72
- @sections[:id] = $3 || nil
72
+ @sections[:id] = $3 if $3
73
73
  return @sections'<<"\n"
74
74
  end
75
75
  code, count = '', 0
@@ -0,0 +1,240 @@
1
+ require 'optparse'
2
+ require 'ostruct'
3
+ require 'fileutils'
4
+ require 'yaml'
5
+ require 'erubis'
6
+
7
+ module Merb
8
+
9
+ class Config
10
+ def self.setup
11
+ defaults = {
12
+ :host => "0.0.0.0",
13
+ :port => "4000",
14
+ :allow_reloading => true,
15
+ :merb_root => Dir.pwd,
16
+ :template_ext => {:html => :herb, :js => :jerb, :xml => :xerb},
17
+ :cache_templates => false
18
+ }
19
+ begin
20
+ options = defaults.merge(YAML.load(Erubis::Eruby.new(IO.read("#{defaults[:merb_root]}/dist/conf/merb.yml")).result))
21
+ rescue
22
+ options = defaults
23
+ end
24
+
25
+ options
26
+ end
27
+ end
28
+
29
+ class Server
30
+
31
+ class << self
32
+
33
+ def merb_config
34
+ options = Merb::Config.setup
35
+
36
+ opts = OptionParser.new do |opts|
37
+ opts.banner = "Usage: merb [fdcphmisl] [argument]"
38
+ opts.define_head "Merb Mongrel+ Erb. Lightweight replacement for ActionPack"
39
+ opts.separator '*'*80
40
+ opts.separator 'If no flags are given, Merb starts in the foreground on port 4000'
41
+ opts.separator '*'*80
42
+
43
+ opts.on("-f", "--config-file FILENAME", "This flag is for adding extra config files for things like the upload progress module") do |config|
44
+ options[:config] = config
45
+ end
46
+
47
+ opts.on("-d", "--daemonize", "This will run a single merb in the background") do |config|
48
+ options[:daemonize] = true
49
+ end
50
+
51
+ opts.on("-c", "--cluster-nodes NUM_MERBS", "Number of merb daemons to run") do |nodes|
52
+ options[:cluster] = nodes
53
+ end
54
+
55
+ opts.on("-p", "--port PORTNUM", "Port to run merb on, defaults to 4000") do |port|
56
+ options[:port] = port
57
+ end
58
+
59
+ opts.on("-h", "--host HOSTNAME", "Host to bind to(default is all IP's)") do |host|
60
+ options[:host] = host
61
+ end
62
+
63
+ opts.on("-m", "--merb-root MERB_ROOT", "the path to the MERB_ROOT for the app you want to run") do |merb_root|
64
+ options[:merb_root] = File.expand_path(merb_root)
65
+ end
66
+
67
+ 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|
68
+ options[:console] = true
69
+ end
70
+
71
+ opts.on("-s", "--drb-server-port PORTNUM", "This is the port number to run the drb daemon on for sessions and uplod progress monitoring.") do |drb_port|
72
+ options[:drb_server_port] = drb_port
73
+ end
74
+
75
+ opts.on("-l", "--log-level LEVEL", "Log levels can be set to any of these options: DEBUG < INFO < WARN < ERROR < FATAL < UNKNOWN") do |loglevel|
76
+ options[:log_level] = loglevel
77
+ end
78
+
79
+ opts.on("-e", "--environment STRING", "Run merb in the correct mode(development, production, testing)") do |env|
80
+ options[:environment] = env
81
+ end
82
+
83
+ opts.on("-g", "--generate-app PATH", "Generate a fresh merb app at PATH") do |path|
84
+ options[:generate] = path || Dir.pwd
85
+ end
86
+
87
+ opts.on("-?", "--help", "Show this help message") do
88
+ puts opts
89
+ exit
90
+ end
91
+
92
+ end
93
+
94
+ opts.parse!(@@merb_raw_opts)
95
+
96
+
97
+ @@merb_opts = options
98
+ unless options[:generate] || options[:console]
99
+ puts %{Merb started with these options:}
100
+ puts @@merb_opts.to_yaml; puts
101
+ end
102
+ end
103
+
104
+ def initialize_merb
105
+ require 'merb'
106
+ require @@merb_opts[:merb_root]+'/dist/conf/router.rb'
107
+ require @@merb_opts[:merb_root]+'/dist/conf/merb_init.rb'
108
+ end
109
+
110
+ def run
111
+ @@merb_raw_opts = ARGV
112
+ merb_config
113
+
114
+ case @@merb_opts[:environment].to_s
115
+ when 'development'
116
+ @@merb_opts[:allow_reloading] ||= true
117
+ @@merb_opts[:show_error] ||= true
118
+ when 'production'
119
+ @@merb_opts[:allow_reloading] = false
120
+ @@merb_opts[:show_error] ||= false
121
+ @@merb_opts[:cache_templates] = true
122
+ when 'test'
123
+ @@merb_opts[:allow_reloading] ||= true
124
+ @@merb_opts[:show_error] ||= true
125
+ else
126
+ @@merb_opts[:allow_reloading] ||= true
127
+ @@merb_opts[:show_error] ||= true
128
+ end
129
+
130
+ @@merb_opts[:dist_root] = @@merb_opts[:merb_root]+'/dist'
131
+ $LOAD_PATH.unshift( File.join(@@merb_opts[:dist_root] , '/app/controllers') )
132
+ $LOAD_PATH.unshift( File.join(@@merb_opts[:dist_root] , '/app/models') )
133
+ $LOAD_PATH.unshift( File.join(@@merb_opts[:dist_root] , '/lib') )
134
+
135
+ if @@merb_opts[:generate]
136
+ require 'merb/generators/merb_app/merb_app'
137
+ ::Merb::AppGenerator.run @@merb_opts[:generate]
138
+ exit!
139
+ end
140
+
141
+ if @@merb_opts[:console]
142
+ initialize_merb
143
+ ARGV.clear # Avoid passing args to IRB
144
+ require 'irb'
145
+ require 'irb/completion'
146
+ def exit
147
+ exit!
148
+ end
149
+ if File.exists? ".irbrc"
150
+ ENV['IRBRC'] = ".irbrc"
151
+ end
152
+ IRB.start
153
+ exit!
154
+ end
155
+
156
+ if @@merb_opts[:drb_server_port]
157
+ puts "Starting merb drb server on port: #{@@merb_opts[:drb_server_port]}"
158
+ start(@@merb_opts[:drb_server_port], :drbserver_start)
159
+ end
160
+
161
+ if @@merb_opts[:cluster]
162
+ delete_pidfiles
163
+ @@merb_opts[:port].to_i.upto(@@merb_opts[:port].to_i+@@merb_opts[:cluster].to_i-1) do |port|
164
+ puts "Starting merb server on port: #{port}"
165
+ start(port)
166
+ end
167
+ elsif @@merb_opts[:daemonize]
168
+ delete_pidfiles(@@merb_opts[:port])
169
+ start(@@merb_opts[:port])
170
+ else
171
+ initialize_merb
172
+ trap('TERM') { exit }
173
+ mongrel_start(@@merb_opts[:port])
174
+ end
175
+
176
+ end
177
+
178
+ def store_pid(pid,port)
179
+ File.open("#{@@merb_opts[:merb_root]}/log/merb.#{port}.pid", 'w'){|f| f.write("#{Process.pid}\n")}
180
+ end
181
+
182
+ def start(port,what=:mongrel_start)
183
+ fork do
184
+ Process.setsid
185
+ exit if fork
186
+ if what == :mongrel_start
187
+ store_pid(Process.pid, port)
188
+ else
189
+ store_pid(Process.pid, "drb.#{port}")
190
+ end
191
+ Dir.chdir @@merb_opts[:merb_root]
192
+ File.umask 0000
193
+ STDIN.reopen "/dev/null"
194
+ STDOUT.reopen "/dev/null", "a"
195
+ STDERR.reopen STDOUT
196
+ trap("TERM") { exit }
197
+ send(what, port)
198
+ end
199
+ end
200
+
201
+ def delete_pidfiles(portor_star='*')
202
+ Dir["#{@@merb_opts[:merb_root]}/log/merb.#{portor_star}.pid"].each do |pid|
203
+ FileUtils.rm(pid) rescue nil
204
+ end
205
+ end
206
+
207
+ def drbserver_start(port)
208
+ puts "Starting merb drb server on port: #{port}"
209
+ require 'merb/merb_drb_server'
210
+ DRb.start_service("druby://#{@@merb_opts[:host]}:#{port}", Merb::DrbServiceProvider)
211
+ DRb.thread.join
212
+ end
213
+
214
+ def mongrel_start(port)
215
+ @@merb_opts[:port] = port
216
+ initialize_merb
217
+
218
+ mconfig = Mongrel::Configurator.new :host => (@@merb_opts[:host]||"0.0.0.0"), :port => (port ||4000) do
219
+ yconfig = YAML.load(Erubis::Eruby.new(IO.read(File.expand_path(@@merb_opts[:config]))).result) if @@merb_opts[:config]
220
+ listener do
221
+ uri( "/", :handler => MerbUploadHandler.new(yconfig), :in_front => true) if @@merb_opts[:config]
222
+ uri "/", :handler => MerbHandler.new(@@merb_opts[:dist_root]+'/public')
223
+ uri "/favicon.ico", :handler => Mongrel::Error404Handler.new("")
224
+ end
225
+
226
+ trap("INT") { stop }
227
+ run
228
+ end
229
+ mconfig.join
230
+ end
231
+
232
+ def config
233
+ @@merb_opts
234
+ end
235
+
236
+ end
237
+
238
+ end # Server
239
+
240
+ end # Merb