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.
- data/README +64 -80
- data/Rakefile +25 -12
- data/bin/merb +2 -223
- data/examples/README_EXAMPLES +10 -0
- data/examples/skeleton.tar +0 -0
- data/lib/merb.rb +48 -21
- data/lib/merb/core_ext.rb +12 -2
- data/lib/merb/core_ext/merb_class.rb +21 -21
- data/lib/merb/core_ext/merb_kernel.rb +60 -0
- data/lib/merb/core_ext/merb_object.rb +14 -0
- data/lib/merb/core_ext/merb_string.rb +3 -13
- data/lib/merb/generators/merb_app/merb_app.rb +33 -0
- data/lib/merb/merb_constants.rb +7 -0
- data/lib/merb/merb_controller.rb +31 -23
- data/lib/merb/merb_drb_server.rb +6 -60
- data/lib/merb/merb_exceptions.rb +162 -2
- data/lib/merb/merb_handler.rb +8 -19
- data/lib/merb/merb_mailer.rb +60 -0
- data/lib/merb/merb_router.rb +1 -1
- data/lib/merb/merb_server.rb +240 -0
- data/lib/merb/merb_upload_handler.rb +1 -1
- data/lib/merb/merb_view_context.rb +11 -6
- data/lib/merb/mixins/basic_authentication_mixin.rb +11 -13
- data/lib/merb/mixins/controller_mixin.rb +12 -6
- data/lib/merb/mixins/form_control_mixin.rb +94 -0
- data/lib/merb/mixins/render_mixin.rb +50 -24
- data/lib/merb/mixins/view_context_mixin.rb +122 -0
- data/lib/merb/session/merb_ar_session.rb +13 -14
- data/lib/merb/session/merb_memory_session.rb +105 -0
- metadata +13 -132
- data/examples/app_skeleton/Rakefile +0 -82
- data/examples/app_skeleton/dist/app/helpers/global_helper.rb +0 -6
- data/examples/app_skeleton/dist/conf/merb.yml +0 -11
- data/examples/app_skeleton/dist/conf/merb_init.rb +0 -16
- data/examples/app_skeleton/dist/conf/mup.conf +0 -5
- data/examples/app_skeleton/dist/conf/router.rb +0 -19
- data/examples/app_skeleton/scripts/merb_stop +0 -13
- data/examples/app_skeleton/scripts/new_migration +0 -21
- data/examples/app_skeleton/test/test_helper.rb +0 -1
- data/examples/sample_app/Rakefile +0 -82
- data/examples/sample_app/dist/app/controllers/files.rb +0 -31
- data/examples/sample_app/dist/app/controllers/posts.rb +0 -71
- data/examples/sample_app/dist/app/controllers/test.rb +0 -40
- data/examples/sample_app/dist/app/helpers/global_helper.rb +0 -7
- data/examples/sample_app/dist/app/helpers/posts_helper.rb +0 -4
- data/examples/sample_app/dist/app/models/comment.rb +0 -3
- data/examples/sample_app/dist/app/models/post.rb +0 -4
- data/examples/sample_app/dist/app/views/files/progress.jerb +0 -3
- data/examples/sample_app/dist/app/views/files/start.herb +0 -62
- data/examples/sample_app/dist/app/views/files/upload.herb +0 -6
- data/examples/sample_app/dist/app/views/layout/application.herb +0 -61
- data/examples/sample_app/dist/app/views/layout/foo.herb +0 -6
- data/examples/sample_app/dist/app/views/posts/_comments.herb +0 -11
- data/examples/sample_app/dist/app/views/posts/comment.jerb +0 -1
- data/examples/sample_app/dist/app/views/posts/list.herb +0 -5
- data/examples/sample_app/dist/app/views/posts/new.herb +0 -37
- data/examples/sample_app/dist/app/views/posts/show.herb +0 -37
- data/examples/sample_app/dist/app/views/posts/xml_test.xerb +0 -3
- data/examples/sample_app/dist/app/views/shared/_test.herb +0 -1
- data/examples/sample_app/dist/app/views/test/foo.herb +0 -2
- data/examples/sample_app/dist/app/views/test/hello.herb +0 -5
- data/examples/sample_app/dist/app/views/test/json.jerb +0 -1
- data/examples/sample_app/dist/conf/merb.yml +0 -11
- data/examples/sample_app/dist/conf/merb_init.rb +0 -24
- data/examples/sample_app/dist/conf/mup.conf +0 -5
- data/examples/sample_app/dist/conf/router.rb +0 -19
- data/examples/sample_app/dist/public/images/bg.jpg +0 -0
- data/examples/sample_app/dist/public/images/book.gif +0 -0
- data/examples/sample_app/dist/public/images/booksmall.gif +0 -0
- data/examples/sample_app/dist/public/images/greenright.jpg +0 -0
- data/examples/sample_app/dist/public/images/louiecon.gif +0 -0
- data/examples/sample_app/dist/public/images/menu.gif +0 -0
- data/examples/sample_app/dist/public/images/menuleft.gif +0 -0
- data/examples/sample_app/dist/public/images/menuright.gif +0 -0
- data/examples/sample_app/dist/public/images/mountain.jpg +0 -0
- data/examples/sample_app/dist/public/images/n3.jpg +0 -0
- data/examples/sample_app/dist/public/images/nautica.jpg +0 -0
- data/examples/sample_app/dist/public/javascripts/application.js +0 -0
- data/examples/sample_app/dist/public/javascripts/effects.js +0 -975
- data/examples/sample_app/dist/public/javascripts/mup.js +0 -113
- data/examples/sample_app/dist/public/javascripts/prototype.js +0 -2264
- data/examples/sample_app/dist/public/stylesheets/merb.css +0 -277
- data/examples/sample_app/dist/public/test.html +0 -5
- data/examples/sample_app/dist/schema/migrations/001_add_comments_to_posts.rb +0 -22
- data/examples/sample_app/dist/schema/migrations/002_add_sessions_table.rb +0 -14
- data/examples/sample_app/dist/schema/schema.rb +0 -28
- data/examples/sample_app/foo.txt +0 -0
- data/examples/sample_app/log/merb.4000.pid +0 -1
- data/examples/sample_app/script/merb_stop +0 -13
- data/examples/sample_app/script/new_migration +0 -21
- data/examples/sample_app/test/test_helper.rb +0 -1
- data/lib/merb/mixins/javascript_mixin.rb +0 -147
- data/lib/merb/session/merb_drb_session.rb +0 -65
- data/test/test_helper.rb +0 -1
- data/test/unit/route_matcher_test.rb +0 -46
data/lib/merb/merb_drb_server.rb
CHANGED
|
@@ -3,72 +3,18 @@ require File.dirname(__FILE__)+'/merb_upload_progress'
|
|
|
3
3
|
|
|
4
4
|
module Merb
|
|
5
5
|
|
|
6
|
-
class
|
|
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
|
|
15
|
+
end
|
|
71
16
|
|
|
72
|
-
end
|
|
17
|
+
end
|
|
18
|
+
|
|
73
19
|
|
|
74
20
|
end
|
data/lib/merb/merb_exceptions.rb
CHANGED
|
@@ -1,4 +1,164 @@
|
|
|
1
|
+
begin
|
|
2
|
+
require 'coderay'
|
|
3
|
+
rescue LoadError => ex
|
|
4
|
+
end
|
|
5
|
+
|
|
1
6
|
module Merb
|
|
2
|
-
class
|
|
3
|
-
class
|
|
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
|
data/lib/merb/merb_handler.rb
CHANGED
|
@@ -67,18 +67,17 @@ class MerbHandler < Mongrel::HttpHandler
|
|
|
67
67
|
@guard.synchronize {
|
|
68
68
|
controller.dispatch(action)
|
|
69
69
|
}
|
|
70
|
-
rescue
|
|
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
|
-
|
|
119
|
+
body = (controller.body.to_s rescue '')
|
|
120
|
+
response.send_status(body.length)
|
|
121
121
|
response.send_header
|
|
122
|
-
response.write(
|
|
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?(
|
|
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
|
data/lib/merb/merb_router.rb
CHANGED
|
@@ -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
|
|
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
|