merb 0.3.4 → 0.3.7
Sign up to get free protection for your applications and to get access to all the features.
- data/README +206 -197
- data/Rakefile +12 -21
- data/bin/merb +1 -1
- data/examples/skeleton/Rakefile +6 -20
- data/examples/skeleton/dist/app/mailers/layout/application.erb +1 -0
- data/examples/skeleton/dist/conf/database.yml +23 -0
- data/examples/skeleton/dist/conf/environments/development.rb +1 -0
- data/examples/skeleton/dist/conf/environments/production.rb +1 -0
- data/examples/skeleton/dist/conf/environments/test.rb +1 -0
- data/examples/skeleton/dist/conf/merb.yml +32 -28
- data/examples/skeleton/dist/conf/merb_init.rb +16 -13
- data/examples/skeleton/dist/conf/router.rb +9 -9
- data/examples/skeleton/dist/schema/migrations/001_add_sessions_table.rb +2 -2
- data/lib/merb.rb +23 -18
- data/lib/merb/caching/fragment_cache.rb +3 -7
- data/lib/merb/caching/store/memcache.rb +20 -0
- data/lib/merb/core_ext/merb_array.rb +0 -0
- data/lib/merb/core_ext/merb_class.rb +44 -4
- data/lib/merb/core_ext/merb_enumerable.rb +43 -1
- data/lib/merb/core_ext/merb_hash.rb +200 -122
- data/lib/merb/core_ext/merb_kernel.rb +2 -0
- data/lib/merb/core_ext/merb_module.rb +41 -0
- data/lib/merb/core_ext/merb_numeric.rb +57 -5
- data/lib/merb/core_ext/merb_object.rb +172 -6
- data/lib/merb/generators/merb_app/merb_app.rb +15 -9
- data/lib/merb/merb_abstract_controller.rb +193 -0
- data/lib/merb/merb_constants.rb +26 -1
- data/lib/merb/merb_controller.rb +143 -234
- data/lib/merb/merb_dispatcher.rb +28 -20
- data/lib/merb/merb_drb_server.rb +2 -3
- data/lib/merb/merb_exceptions.rb +194 -49
- data/lib/merb/merb_handler.rb +34 -26
- data/lib/merb/merb_mail_controller.rb +200 -0
- data/lib/merb/merb_mailer.rb +33 -13
- data/lib/merb/merb_part_controller.rb +42 -0
- data/lib/merb/merb_plugins.rb +293 -0
- data/lib/merb/merb_request.rb +6 -4
- data/lib/merb/merb_router.rb +99 -65
- data/lib/merb/merb_server.rb +65 -21
- data/lib/merb/merb_upload_handler.rb +2 -1
- data/lib/merb/merb_view_context.rb +36 -15
- data/lib/merb/mixins/basic_authentication_mixin.rb +5 -5
- data/lib/merb/mixins/controller_mixin.rb +67 -28
- data/lib/merb/mixins/erubis_capture_mixin.rb +1 -8
- data/lib/merb/mixins/form_control_mixin.rb +280 -42
- data/lib/merb/mixins/render_mixin.rb +127 -45
- data/lib/merb/mixins/responder_mixin.rb +5 -7
- data/lib/merb/mixins/view_context_mixin.rb +260 -94
- data/lib/merb/session.rb +23 -0
- data/lib/merb/session/merb_ar_session.rb +28 -16
- data/lib/merb/session/merb_mem_cache_session.rb +108 -0
- data/lib/merb/session/merb_memory_session.rb +65 -20
- data/lib/merb/template/erubis.rb +22 -13
- data/lib/merb/template/haml.rb +5 -16
- data/lib/merb/template/markaby.rb +5 -3
- data/lib/merb/template/xml_builder.rb +17 -5
- data/lib/merb/test/merb_fake_request.rb +63 -0
- data/lib/merb/test/merb_multipart.rb +58 -0
- data/lib/tasks/db.rake +2 -0
- data/lib/tasks/merb.rake +20 -8
- metadata +24 -25
- data/examples/skeleton.tar +0 -0
data/lib/merb/merb_dispatcher.rb
CHANGED
@@ -11,31 +11,36 @@ module Merb
|
|
11
11
|
|
12
12
|
@@mutex = Mutex.new
|
13
13
|
@@use_mutex = ::Merb::Server.use_mutex
|
14
|
-
# This is where we grab the incoming request REQUEST_URI
|
15
|
-
#
|
16
|
-
#
|
17
|
-
# returns a 2 element tuple of:
|
18
|
-
# [controller, action]
|
14
|
+
# This is where we grab the incoming request REQUEST_URI and use that in
|
15
|
+
# the merb RouteMatcher to determine which controller and method to run.
|
16
|
+
# Returns a 2 element tuple of: [controller, action]
|
19
17
|
def handle(request, response)
|
20
|
-
|
18
|
+
start = Time.now
|
19
|
+
|
20
|
+
request_uri = request.params[Merb::Const::REQUEST_URI]
|
21
21
|
request_uri.sub!(path_prefix, '') if path_prefix
|
22
22
|
route = route_path(request_uri)
|
23
23
|
|
24
24
|
allowed = route.delete(:allowed)
|
25
25
|
rest = route.delete(:rest)
|
26
|
-
|
27
|
-
|
26
|
+
namespace = route.delete(:namespace)
|
27
|
+
|
28
|
+
cont = namespace ? "#{namespace}/#{route[:controller]}" : route[:controller]
|
29
|
+
|
30
|
+
klass = resolve_controller(cont)
|
31
|
+
controller = klass.build(request.body, request.params, route, response)
|
28
32
|
|
29
33
|
if rest
|
30
34
|
method = controller.request.method
|
31
35
|
if allowed.keys.include?(method) && action = allowed[method]
|
32
36
|
controller.params[:action] = action
|
33
37
|
else
|
34
|
-
raise Merb::
|
38
|
+
raise Merb::HTTPMethodNotAllowed.new(method, allowed)
|
35
39
|
end
|
36
40
|
else
|
37
41
|
action = route[:action]
|
38
42
|
end
|
43
|
+
controller._benchmarks[:setup_time] = Time.now - start
|
39
44
|
if @@use_mutex
|
40
45
|
@@mutex.synchronize {
|
41
46
|
controller.dispatch(action)
|
@@ -52,20 +57,23 @@ module Merb
|
|
52
57
|
Merb::Router.match(path)
|
53
58
|
end
|
54
59
|
|
55
|
-
#
|
56
|
-
#
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
60
|
+
# Take a controller class name string and reload or require the right
|
61
|
+
# controller file then CamelCase it and return the class object.
|
62
|
+
def resolve_controller(controller_name)
|
63
|
+
segments = controller_name.split('/').map{|s| s.snake_case}
|
64
|
+
path = "#{DIST_ROOT}/app/controllers/#{controller_name}.rb"
|
65
|
+
cnt = segments.map{|s| s.camel_case }.join('::')
|
66
|
+
|
67
|
+
if !File.exist?(path)
|
68
|
+
raise "Bad controller! #{cnt}"
|
62
69
|
end unless $TESTING
|
70
|
+
|
63
71
|
begin
|
64
|
-
|
65
|
-
Object.send(:remove_const,
|
66
|
-
load(
|
72
|
+
if MERB_ENV == 'development'
|
73
|
+
Object.send(:remove_const, cnt) rescue nil
|
74
|
+
load(path)
|
67
75
|
end
|
68
|
-
return Object.
|
76
|
+
return Object.full_const_get(cnt)
|
69
77
|
rescue RuntimeError
|
70
78
|
warn "Error getting instance of '#{controller_name.camel_case}': #{$!}"
|
71
79
|
raise $!
|
data/lib/merb/merb_drb_server.rb
CHANGED
data/lib/merb/merb_exceptions.rb
CHANGED
@@ -4,19 +4,170 @@ rescue LoadError => ex
|
|
4
4
|
end
|
5
5
|
|
6
6
|
module Merb
|
7
|
+
|
8
|
+
module ControllerExceptions
|
9
|
+
# ControllerExceptions are a way of simplifying controller code by placing
|
10
|
+
# exceptional logic elsewhere. ControllerExceptions are like
|
11
|
+
# mini-controllers which have one action. The exception action is called
|
12
|
+
# to render a web page when the exception is raised. Additionally all
|
13
|
+
# ControllerExceptions have an HTTP status code associated with them
|
14
|
+
# which is given to the browser when it's action is rendered.
|
15
|
+
#
|
16
|
+
# ControllerExceptions::Base is an abstract base class, it cannot be
|
17
|
+
# raised. Derived from Base are many exceptions, one for each HTTP status
|
18
|
+
# code. EG Unauthorized (401), PaymentRequired (402), and Forbidden (403).
|
19
|
+
#
|
20
|
+
# These exceptions can be raised by your controller without any further
|
21
|
+
# work. For example
|
22
|
+
#
|
23
|
+
# def show
|
24
|
+
# product = Product.find(params[:id])
|
25
|
+
# raise NotFound if product.nil?
|
26
|
+
# [...]
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# By default the NotFound exception will look in for a template at
|
30
|
+
# app/views/exceptions/not_found.{rhtml, haml, whatevs}
|
31
|
+
# If the template is not found then a simple message will be sent.
|
32
|
+
#
|
33
|
+
# To extend the use of the ControllerExceptions one may derive a new
|
34
|
+
# exception from one of the already defined ones. Then one must add a
|
35
|
+
# special method called error_response which is like a controller action
|
36
|
+
# preparing the context for rendering a template at
|
37
|
+
# app/views/exceptions/my_exception_snake_cased.whatevs
|
38
|
+
#
|
39
|
+
# As an example we will create an exception called AdminAccessRequired.
|
40
|
+
# First we decide on what HTTP status code such an error might have.
|
41
|
+
# 401, Unauthorized seems to fit.
|
42
|
+
#
|
43
|
+
# class AdminAccessRequired < Merb::ControllerExceptions::Unauthorized
|
44
|
+
# def error_response
|
45
|
+
# @tried_to_access request.uri
|
46
|
+
# render :layout => 'error_page'
|
47
|
+
# end
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
# At app/views/exceptions/admin_access_required.rhtml we write
|
51
|
+
#
|
52
|
+
# <h1>You're not an administrator!</h1>
|
53
|
+
# <p>You tried to access <%= @tried_to_access %> but that URL is
|
54
|
+
# restricted to administrators.</p>
|
55
|
+
#
|
56
|
+
# The layout 'error_page' works like any other controller, it would be
|
57
|
+
# looked for at app/views/layouts
|
58
|
+
#
|
59
|
+
# TODO: if you need to pass data to your exception use a constructor.
|
60
|
+
class Base < StandardError
|
61
|
+
include Merb::RenderMixin
|
62
|
+
include Merb::ControllerMixin
|
63
|
+
include Merb::ResponderMixin
|
64
|
+
|
65
|
+
def _template_root
|
66
|
+
@controller._template_root
|
67
|
+
end
|
68
|
+
|
69
|
+
# The filename of the exception template. usually something like
|
70
|
+
# views/exceptions/not_found.rhtml
|
71
|
+
def template_path
|
72
|
+
_template_root / 'exceptions' / self.class.name.snake_case.split('::').last
|
73
|
+
end
|
74
|
+
|
75
|
+
def set_controller(controller)
|
76
|
+
@controller = controller
|
77
|
+
end
|
78
|
+
|
79
|
+
def request; @controller.request; end
|
80
|
+
def params; @controller.params; end
|
81
|
+
def cookies; @controller.cookies; end
|
82
|
+
def headers; @controller.headers; end
|
83
|
+
def session; @controller.session; end
|
84
|
+
def response; @controller.response; end
|
85
|
+
|
86
|
+
def status
|
87
|
+
self.class.const_get(:STATUS)
|
88
|
+
end
|
89
|
+
|
90
|
+
def call_action
|
91
|
+
# TODO: this method is meaningless at the moment but it could call
|
92
|
+
# filters
|
93
|
+
# before filters?
|
94
|
+
error_response
|
95
|
+
# after filters?
|
96
|
+
end
|
97
|
+
|
98
|
+
def error_response
|
99
|
+
if File.exists?(template_path)
|
100
|
+
render
|
101
|
+
else
|
102
|
+
"Error #{status} #{self.class.name.split('::').last}!"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
module HTTPErrors
|
107
|
+
# created with:
|
108
|
+
# ruby -r rubygems -e "require 'mongrel'; puts Mongrel::HTTP_STATUS_CODES.keys.sort.map {|code| %Q{class #{Mongrel::HTTP_STATUS_CODES[code].gsub /[^\w]/,''} < Merb::ControllerExceptions::Base; STATUS = #{code}; end} }.join(%Q{\n})"
|
109
|
+
# we could do some magic here, but i think it's more instructive to just
|
110
|
+
# have these values copied
|
111
|
+
class Continue < Merb::ControllerExceptions::Base; STATUS = 100; end
|
112
|
+
class SwitchingProtocols < Merb::ControllerExceptions::Base; STATUS = 101; end
|
113
|
+
class OK < Merb::ControllerExceptions::Base; STATUS = 200; end
|
114
|
+
class Created < Merb::ControllerExceptions::Base; STATUS = 201; end
|
115
|
+
class Accepted < Merb::ControllerExceptions::Base; STATUS = 202; end
|
116
|
+
class NonAuthoritativeInformation < Merb::ControllerExceptions::Base; STATUS = 203; end
|
117
|
+
class NoContent < Merb::ControllerExceptions::Base; STATUS = 204; end
|
118
|
+
class ResetContent < Merb::ControllerExceptions::Base; STATUS = 205; end
|
119
|
+
class PartialContent < Merb::ControllerExceptions::Base; STATUS = 206; end
|
120
|
+
class MultipleChoices < Merb::ControllerExceptions::Base; STATUS = 300; end
|
121
|
+
class MovedPermanently < Merb::ControllerExceptions::Base; STATUS = 301; end
|
122
|
+
class MovedTemporarily < Merb::ControllerExceptions::Base; STATUS = 302; end
|
123
|
+
class SeeOther < Merb::ControllerExceptions::Base; STATUS = 303; end
|
124
|
+
class NotModified < Merb::ControllerExceptions::Base; STATUS = 304; end
|
125
|
+
class UseProxy < Merb::ControllerExceptions::Base; STATUS = 305; end
|
126
|
+
class BadRequest < Merb::ControllerExceptions::Base; STATUS = 400; end
|
127
|
+
class Unauthorized < Merb::ControllerExceptions::Base; STATUS = 401; end
|
128
|
+
class PaymentRequired < Merb::ControllerExceptions::Base; STATUS = 402; end
|
129
|
+
class Forbidden < Merb::ControllerExceptions::Base; STATUS = 403; end
|
130
|
+
class NotFound < Merb::ControllerExceptions::Base; STATUS = 404; end
|
131
|
+
class MethodNotAllowed < Merb::ControllerExceptions::Base; STATUS = 405; end
|
132
|
+
class NotAcceptable < Merb::ControllerExceptions::Base; STATUS = 406; end
|
133
|
+
class ProxyAuthenticationRequired < Merb::ControllerExceptions::Base; STATUS = 407; end
|
134
|
+
class RequestTimeout < Merb::ControllerExceptions::Base; STATUS = 408; end
|
135
|
+
class Conflict < Merb::ControllerExceptions::Base; STATUS = 409; end
|
136
|
+
class Gone < Merb::ControllerExceptions::Base; STATUS = 410; end
|
137
|
+
class LengthRequired < Merb::ControllerExceptions::Base; STATUS = 411; end
|
138
|
+
class PreconditionFailed < Merb::ControllerExceptions::Base; STATUS = 412; end
|
139
|
+
class RequestEntityTooLarge < Merb::ControllerExceptions::Base; STATUS = 413; end
|
140
|
+
class RequestURITooLarge < Merb::ControllerExceptions::Base; STATUS = 414; end
|
141
|
+
class UnsupportedMediaType < Merb::ControllerExceptions::Base; STATUS = 415; end
|
142
|
+
class InternalServerError < Merb::ControllerExceptions::Base; STATUS = 500; end
|
143
|
+
class NotImplemented < Merb::ControllerExceptions::Base; STATUS = 501; end
|
144
|
+
class BadGateway < Merb::ControllerExceptions::Base; STATUS = 502; end
|
145
|
+
class ServiceUnavailable < Merb::ControllerExceptions::Base; STATUS = 503; end
|
146
|
+
class GatewayTimeout < Merb::ControllerExceptions::Base; STATUS = 504; end
|
147
|
+
class HTTPVersionNotSupported < Merb::ControllerExceptions::Base; STATUS = 505; end
|
148
|
+
end
|
149
|
+
|
150
|
+
include HTTPErrors
|
151
|
+
end
|
152
|
+
|
7
153
|
class MerbError < StandardError; end
|
8
154
|
class Noroutefound < MerbError; end
|
9
155
|
class MissingControllerFile < MerbError; end
|
10
156
|
class MerbControllerError < MerbError; end
|
11
|
-
class
|
157
|
+
class HTTPMethodNotAllowed < MerbError
|
12
158
|
def initialize(method, allowed)
|
13
|
-
super("
|
159
|
+
super("HTTPMethodNotAllowed: #{method}\n"+ "Allowed: #{allowed.keys.join(' ')})")
|
14
160
|
end
|
15
161
|
|
16
162
|
end
|
17
|
-
|
163
|
+
|
164
|
+
# Format exception message for browser display.
|
18
165
|
def self.html_exception(e)
|
19
|
-
::Merb::Server.show_error
|
166
|
+
if ::Merb::Server.show_error
|
167
|
+
ErrorResponse.new(e).out
|
168
|
+
else
|
169
|
+
IO.read(DIST_ROOT / 'public/500.html') rescue "500 Internal Server Error!"
|
170
|
+
end
|
20
171
|
end
|
21
172
|
|
22
173
|
def self.exception(e)
|
@@ -34,7 +185,7 @@ module Merb
|
|
34
185
|
backtrace = @error.backtrace
|
35
186
|
|
36
187
|
colors = []
|
37
|
-
min =
|
188
|
+
min = 200
|
38
189
|
max = 255
|
39
190
|
step = -((max - min) / backtrace.size).abs
|
40
191
|
max.step(min, step) do |color|
|
@@ -55,8 +206,8 @@ module Merb
|
|
55
206
|
error_page(colors, error, *backtrace)
|
56
207
|
end
|
57
208
|
|
58
|
-
#
|
59
|
-
# traceback
|
209
|
+
# This method offers highlighting for the sourcecode-chunks from the
|
210
|
+
# traceback. Just 'gem install coderay'.
|
60
211
|
def error_page(colors, title, *backtrace)
|
61
212
|
@backtrace = backtrace
|
62
213
|
@colors = colors
|
@@ -71,39 +222,26 @@ module Merb
|
|
71
222
|
<title><%= @title %></title>
|
72
223
|
<style type="text/css">
|
73
224
|
<!--
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
}
|
95
|
-
tr.source_container div table tr {
|
96
|
-
text-align:center;
|
97
|
-
}
|
98
|
-
tr.source_container div table tr.source {
|
99
|
-
text-align:left;
|
100
|
-
}
|
101
|
-
tr a {
|
102
|
-
color: black;
|
103
|
-
}
|
104
|
-
div.source {
|
105
|
-
background: #fff;
|
106
|
-
}
|
225
|
+
table {border:0px}
|
226
|
+
tr {line-height:1.3}
|
227
|
+
td {font-size:.8em; padding:4px}
|
228
|
+
h1 {font-size:2em}
|
229
|
+
h1, td {font-family:'courier new', 'courier', monospace}
|
230
|
+
td.line {color:#000; text-align:center}
|
231
|
+
td.method {font-weight:bold; text-align:right}
|
232
|
+
td.file a {color:#000}
|
233
|
+
td.file a:hover {color:#F00}
|
234
|
+
tr.clickable:hover {background:#FFA}
|
235
|
+
.clickable {cursor: help }
|
236
|
+
#title {float: left;}
|
237
|
+
.source
|
238
|
+
{
|
239
|
+
border:1px #000 solid;
|
240
|
+
margin:2px;
|
241
|
+
padding:8px;
|
242
|
+
background:#F0F0F0;
|
243
|
+
}
|
244
|
+
|
107
245
|
-->
|
108
246
|
<% if @coderay %>
|
109
247
|
<%= CodeRay::Encoders[:html]::CSS.new.stylesheet %>
|
@@ -132,17 +270,24 @@ module Merb
|
|
132
270
|
</script>
|
133
271
|
</head>
|
134
272
|
<body>
|
135
|
-
<
|
136
|
-
<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"/>
|
137
|
-
|
138
|
-
|
273
|
+
<div>
|
274
|
+
<h1><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"/>
|
275
|
+
<%= @title %></h1>
|
276
|
+
</div>
|
277
|
+
|
139
278
|
<table class="main">
|
140
|
-
<
|
141
|
-
<
|
142
|
-
|
279
|
+
<thead>
|
280
|
+
<tr>
|
281
|
+
<td class="method">Method</td>
|
282
|
+
<td class="line">Line</td>
|
283
|
+
<td class="file">File</td>
|
284
|
+
</tr>
|
285
|
+
</thead>
|
143
286
|
<% @backtrace.each do |lines, hash, file, lineno, meth| %>
|
144
|
-
<tr id="line_<%= hash %>" style="background:rgb(
|
145
|
-
<td
|
287
|
+
<tr id="line_<%= hash %>" onclick="toggle('source_<%= hash %>'); return false"; class="clickable" style="background:rgb(250,250,<%= @colors.shift %>);">
|
288
|
+
<td class="method"><%= meth %></td>
|
289
|
+
<td class="line"><%= lineno %></td>
|
290
|
+
<td class="file"><%= file %></td>
|
146
291
|
</tr>
|
147
292
|
<tr id="source_<%= hash %>" <%= @coderay? " class='CodeRay' " : ''%> style="display:none;">
|
148
293
|
<td colspan="3">
|
data/lib/merb/merb_handler.rb
CHANGED
@@ -9,12 +9,10 @@ class Mongrel::HttpResponse
|
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
12
|
-
|
13
|
-
|
14
12
|
class MerbHandler < Mongrel::HttpHandler
|
15
13
|
@@file_only_methods = ["GET","HEAD"]
|
16
14
|
|
17
|
-
#
|
15
|
+
# Take the name of a directory and use that as the doc root or public
|
18
16
|
# directory of your site. This is set to the root of your merb app + '/public'
|
19
17
|
# by default.
|
20
18
|
def initialize(dir, opts = {})
|
@@ -22,21 +20,21 @@ class MerbHandler < Mongrel::HttpHandler
|
|
22
20
|
end
|
23
21
|
|
24
22
|
# process incoming http requests and do a number of things
|
25
|
-
# 1.
|
26
|
-
#
|
27
|
-
#
|
28
|
-
# 2. Serve static asset and html files directly from public/ if
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
# controller
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
# and your controller can go on processing other requests.
|
23
|
+
# 1. Check for rails style cached pages. add .html to the url and see if
|
24
|
+
# there is a static file in public that matches. serve that file directly
|
25
|
+
# without invoking Merb and be done with it.
|
26
|
+
# 2. Serve static asset and html files directly from public/ if they exist.
|
27
|
+
# 3. If none of the above apply, we take apart the request url and feed it
|
28
|
+
# into Merb::RouteMatcher to let it decide which controller and method will
|
29
|
+
# serve the request.
|
30
|
+
# 4. After the controller has done its thing, we check for the X-SENDFILE
|
31
|
+
# header. if you set this header to the path to a file in your controller
|
32
|
+
# then mongrel will serve the file directly and your controller can go on
|
33
|
+
# processing other requests.
|
37
34
|
def process(request, response)
|
38
35
|
|
39
36
|
start = Time.now
|
37
|
+
benchmarks = {}
|
40
38
|
|
41
39
|
if response.socket.closed?
|
42
40
|
return
|
@@ -44,9 +42,9 @@ class MerbHandler < Mongrel::HttpHandler
|
|
44
42
|
|
45
43
|
MERB_LOGGER.info("\nRequest: REQUEST_URI: #{request.params[Mongrel::Const::REQUEST_URI]} (#{Time.now.strftime("%Y-%m-%d %H:%M:%S")})")
|
46
44
|
|
47
|
-
# Rails style page caching. Check the public dir first for
|
48
|
-
#
|
49
|
-
#
|
45
|
+
# Rails style page caching. Check the public dir first for .html pages and
|
46
|
+
# serve directly. Otherwise fall back to Merb routing and request
|
47
|
+
# dispatching.
|
50
48
|
path_info = request.params[Mongrel::Const::PATH_INFO]
|
51
49
|
page_cached = path_info + ".html"
|
52
50
|
get_or_head = @@file_only_methods.include? request.params[Mongrel::Const::REQUEST_METHOD]
|
@@ -65,8 +63,12 @@ class MerbHandler < Mongrel::HttpHandler
|
|
65
63
|
# Let Merb:Dispatcher find the route and call the filter chain and action
|
66
64
|
controller = nil
|
67
65
|
controller, action = Merb::Dispatcher.handle(request, response)
|
68
|
-
|
69
|
-
|
66
|
+
|
67
|
+
benchmarks.merge!(controller._benchmarks)
|
68
|
+
benchmarks[:controller] = controller.class.to_s
|
69
|
+
benchmarks[:action] = action
|
70
|
+
|
71
|
+
MERB_LOGGER.info("Routing to controller: #{controller.class} action: #{action}\nRoute Recognition & Parsing HTTP Input took: #{benchmarks[:setup_time]} seconds")
|
70
72
|
rescue Object => e
|
71
73
|
response.start(500) do |head,out|
|
72
74
|
head["Content-Type"] = "text/html"
|
@@ -78,8 +80,8 @@ class MerbHandler < Mongrel::HttpHandler
|
|
78
80
|
|
79
81
|
sendfile, clength = nil
|
80
82
|
response.status = controller.status
|
81
|
-
#
|
82
|
-
#
|
83
|
+
# Check for the X-SENDFILE header from your Merb::Controller and serve
|
84
|
+
# the file directly instead of buffering.
|
83
85
|
controller.headers.each do |k, v|
|
84
86
|
if k =~ /^X-SENDFILE$/i
|
85
87
|
sendfile = v
|
@@ -93,14 +95,15 @@ class MerbHandler < Mongrel::HttpHandler
|
|
93
95
|
end
|
94
96
|
|
95
97
|
if sendfile
|
96
|
-
|
98
|
+
benchmarks[:sendfile_time] = Time.now - start
|
99
|
+
MERB_LOGGER.info("X-SENDFILE: #{sendfile}\nComplete Request took: #{benchmarks[:sendfile_time]} seconds")
|
97
100
|
file_status = File.stat(sendfile)
|
98
101
|
response.status = 200
|
99
102
|
# Set the last modified times as well and etag for all files
|
100
103
|
response.header[Mongrel::Const::LAST_MODIFIED] = file_status.mtime.httpdate
|
101
104
|
# Calculated the same as apache, not sure how well the works on win32
|
102
105
|
response.header[Mongrel::Const::ETAG] = Mongrel::Const::ETAG_FORMAT % [file_status.mtime.to_i, file_status.size, file_status.ino]
|
103
|
-
#
|
106
|
+
# Send a status with out content length
|
104
107
|
response.send_status(file_status.size)
|
105
108
|
response.send_header
|
106
109
|
response.send_file(sendfile)
|
@@ -116,13 +119,18 @@ class MerbHandler < Mongrel::HttpHandler
|
|
116
119
|
elsif Proc === controller.body
|
117
120
|
controller.body.call
|
118
121
|
else
|
119
|
-
|
120
|
-
# render response from successful controller
|
122
|
+
# Render response from successful controller
|
121
123
|
body = (controller.body.to_s rescue '')
|
122
124
|
response.send_status(body.length)
|
123
125
|
response.send_header
|
124
126
|
response.write(body)
|
125
127
|
end
|
128
|
+
|
129
|
+
total_request_time = Time.now - start
|
130
|
+
benchmarks[:total_request_time] = total_request_time
|
131
|
+
|
132
|
+
MERB_LOGGER.info("Request Times: #{benchmarks.inspect}")
|
133
|
+
MERB_LOGGER.info("Response status: #{response.status}\nComplete Request took: #{total_request_time} seconds, #{1.0/total_request_time} Requests/Second\n\n")
|
126
134
|
end
|
127
135
|
end
|
128
136
|
|