merb 0.0.8 → 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
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