merb-core 0.9.4 → 0.9.5
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/Rakefile +22 -13
- data/lib/merb-core/bootloader.rb +3 -2
- data/lib/merb-core/config.rb +1 -1
- data/lib/merb-core/constants.rb +3 -1
- data/lib/merb-core/controller/abstract_controller.rb +25 -39
- data/lib/merb-core/controller/exceptions.rb +14 -16
- data/lib/merb-core/controller/merb_controller.rb +2 -2
- data/lib/merb-core/controller/mime.rb +2 -1
- data/lib/merb-core/controller/mixins/render.rb +12 -0
- data/lib/merb-core/controller/mixins/responder.rb +31 -7
- data/lib/merb-core/core_ext/hash.rb +7 -0
- data/lib/merb-core/core_ext/kernel.rb +31 -34
- data/lib/merb-core/core_ext.rb +1 -0
- data/lib/merb-core/dispatch/default_exception/default_exception.rb +3 -3
- data/lib/merb-core/dispatch/dispatcher.rb +128 -135
- data/lib/merb-core/dispatch/request.rb +11 -11
- data/lib/merb-core/dispatch/router/behavior.rb +6 -6
- data/lib/merb-core/dispatch/router.rb +5 -5
- data/lib/merb-core/logger.rb +203 -202
- data/lib/merb-core/rack/application.rb +19 -5
- data/lib/merb-core/rack/middleware/conditional_get.rb +23 -0
- data/lib/merb-core/rack/middleware/content_length.rb +18 -0
- data/lib/merb-core/rack/middleware/tracer.rb +20 -0
- data/lib/merb-core/rack/middleware.rb +1 -7
- data/lib/merb-core/rack.rb +19 -16
- data/lib/merb-core/test/matchers/route_matchers.rb +1 -0
- data/lib/merb-core/test/matchers/view_matchers.rb +36 -0
- data/lib/merb-core/test/run_specs.rb +2 -1
- data/lib/merb-core/version.rb +1 -1
- data/lib/merb-core.rb +39 -33
- data/spec/private/config/merb_spec.rb +34 -0
- data/spec/private/dispatch/fixture/log/merb_test.log +372 -0
- data/spec/private/router/fixture/log/merb_test.log +42 -0
- data/spec/public/abstract_controller/controllers/filters.rb +50 -1
- data/spec/public/abstract_controller/filter_spec.rb +25 -0
- data/spec/public/controller/base_spec.rb +41 -1
- data/spec/public/controller/controllers/base.rb +16 -0
- data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/class_provides/index.html.erb +1 -1
- data/spec/public/controller/dispatcher_spec.rb +24 -25
- data/spec/public/controller/responder_spec.rb +6 -0
- data/spec/public/core_ext/kernel_spec.rb +79 -0
- data/spec/public/directory_structure/directory/log/merb_test.log +245 -0
- data/spec/public/rack/conditinal_get_middleware_spec.rb +139 -0
- data/spec/public/rack/rack_middleware_spec.rb +99 -0
- data/spec/public/rack/shared_example_groups.rb +35 -0
- data/spec/public/reloading/directory/log/merb_test.log +40 -0
- data/spec/public/request/request_spec.rb +0 -5
- data/spec/public/router/fixture/log/merb_test.log +348 -0
- data/spec/public/test/route_matchers_spec.rb +4 -0
- data/spec/spec_helper.rb +7 -1
- metadata +42 -5
- data/spec/private/plugins/plugin_spec.rb +0 -166
@@ -1,12 +1,12 @@
|
|
1
1
|
require Merb.framework_root / "merb-core" / "dispatch" / "default_exception" / "default_exception"
|
2
|
+
|
2
3
|
module Merb
|
3
4
|
class Dispatcher
|
4
5
|
class << self
|
5
6
|
include Merb::ControllerExceptions
|
6
7
|
|
7
8
|
attr_accessor :use_mutex
|
8
|
-
|
9
|
-
@@mutex = Mutex.new
|
9
|
+
|
10
10
|
@@work_queue = Queue.new
|
11
11
|
|
12
12
|
def work_queue
|
@@ -14,7 +14,7 @@ module Merb
|
|
14
14
|
end
|
15
15
|
|
16
16
|
Merb::Dispatcher.use_mutex = ::Merb::Config[:use_mutex]
|
17
|
-
|
17
|
+
|
18
18
|
# Dispatch the rack environment. ControllerExceptions are rescued here
|
19
19
|
# and redispatched.
|
20
20
|
#
|
@@ -26,148 +26,141 @@ module Merb
|
|
26
26
|
# Merb::Controller::
|
27
27
|
# The Merb::Controller that was dispatched to
|
28
28
|
def handle(request)
|
29
|
-
|
30
|
-
|
29
|
+
request.handle
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class Request
|
35
|
+
include Merb::ControllerExceptions
|
31
36
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
"Controller '#{klass}' not found.\n" <<
|
41
|
-
"If Merb tries to look for a controller for static files, " <<
|
42
|
-
"you way need to check up your Rackup file, see the Problems " <<
|
43
|
-
"section at: http://wiki.merbivore.com/pages/rack-middleware"
|
44
|
-
end
|
37
|
+
@@mutex = Mutex.new
|
38
|
+
|
39
|
+
def handle
|
40
|
+
start = Time.now
|
41
|
+
Merb.logger.info "Started request handling: #{start.to_s}"
|
42
|
+
|
43
|
+
find_route!
|
44
|
+
return redirect if redirects?
|
45
45
|
|
46
|
-
|
47
|
-
|
48
|
-
end
|
46
|
+
klass = controller
|
47
|
+
Merb.logger.debug("Routed to: #{params.inspect}")
|
49
48
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
dispatch_exception(exception, request)
|
57
|
-
end
|
58
|
-
|
59
|
-
private
|
60
|
-
# Setup the controller and call the chosen action
|
61
|
-
#
|
62
|
-
# ==== Parameters
|
63
|
-
# klass<Merb::Controller>:: The controller class to dispatch to.
|
64
|
-
# action<Symbol>:: The action to dispatch.
|
65
|
-
# request<Merb::Request>::
|
66
|
-
# The Merb::Request object that was created in #handle
|
67
|
-
# status<Integer>:: The status code to respond with.
|
68
|
-
#
|
69
|
-
# ==== Returns
|
70
|
-
# Merb::Controller::
|
71
|
-
# The Merb::Controller that was dispatched to.
|
72
|
-
def dispatch_action(klass, action, request, status=200)
|
73
|
-
# build controller
|
74
|
-
controller = klass.new(request, status)
|
75
|
-
if use_mutex
|
76
|
-
@@mutex.synchronize { controller._dispatch(action) }
|
77
|
-
else
|
78
|
-
controller._dispatch(action)
|
79
|
-
end
|
80
|
-
controller
|
49
|
+
unless klass < Controller
|
50
|
+
raise NotFound,
|
51
|
+
"Controller '#{klass}' not found.\n" \
|
52
|
+
"If Merb tries to find a controller for static files, " \
|
53
|
+
"you may need to check your Rackup file, see the Problems " \
|
54
|
+
"section at: http://wiki.merbivore.com/pages/rack-middleware"
|
81
55
|
end
|
82
56
|
|
83
|
-
|
84
|
-
|
85
|
-
#
|
86
|
-
# You can handle exceptions by implementing actions for specific
|
87
|
-
# exceptions such as not_found or for entire classes of exceptions
|
88
|
-
# such as client_error. You can also implement handlers for
|
89
|
-
# exceptions outside the Merb exception hierarchy (e.g.
|
90
|
-
# StandardError is caught in standard_error).
|
91
|
-
#
|
92
|
-
# ==== Parameters
|
93
|
-
# request<Merb::Request>::
|
94
|
-
# The request object associated with the failed request.
|
95
|
-
# exception<Object>::
|
96
|
-
# The exception object that was created when trying to dispatch the
|
97
|
-
# original controller.
|
98
|
-
#
|
99
|
-
# ==== Returns
|
100
|
-
# Exceptions::
|
101
|
-
# The Merb::Controller that was dispatched to.
|
102
|
-
def dispatch_exception(exception, request)
|
103
|
-
Merb.logger.error(Merb.exception(exception))
|
104
|
-
request.exceptions = [exception]
|
105
|
-
|
106
|
-
begin
|
107
|
-
e = request.exceptions.first
|
108
|
-
|
109
|
-
if action_name = e.action_name
|
110
|
-
dispatch_action(Exceptions, action_name, request, e.class.status)
|
111
|
-
else
|
112
|
-
dispatch_default_exception(request, e.class.status)
|
113
|
-
end
|
114
|
-
rescue Object => dispatch_issue
|
115
|
-
if e.same?(dispatch_issue)
|
116
|
-
dispatch_default_exception(request, e.class.status)
|
117
|
-
else
|
118
|
-
Merb.logger.error("Dispatching #{e.class} raised another error.")
|
119
|
-
Merb.logger.error(Merb.exception(dispatch_issue))
|
120
|
-
|
121
|
-
request.exceptions.unshift dispatch_issue
|
122
|
-
retry
|
123
|
-
end
|
124
|
-
end
|
57
|
+
if klass == Application
|
58
|
+
raise NotFound, "The 'Application' controller has no public actions"
|
125
59
|
end
|
126
60
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
61
|
+
controller = dispatch_action(klass, params[:action])
|
62
|
+
controller._benchmarks[:dispatch_time] = Time.now - start
|
63
|
+
Merb.logger.info controller._benchmarks.inspect
|
64
|
+
Merb.logger.flush
|
65
|
+
controller
|
66
|
+
rescue Object => exception
|
67
|
+
dispatch_exception(exception)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Set up a faux controller to do redirection from the router
|
71
|
+
#
|
72
|
+
# ==== Parameters
|
73
|
+
# request<Merb::Request>::
|
74
|
+
# The Merb::Request object that was created in #handle
|
75
|
+
# status<Integer>::
|
76
|
+
# The status code to return with the controller
|
77
|
+
# url<String>::
|
78
|
+
# The URL to return
|
79
|
+
#
|
80
|
+
# ==== Example
|
81
|
+
# r.match("/my/old/crusty/url").redirect("http://example.com/index.html")
|
82
|
+
#
|
83
|
+
# ==== Returns
|
84
|
+
# Merb::Controller::
|
85
|
+
# Merb::Controller set with redirect headers and a 301/302 status
|
86
|
+
def redirect
|
87
|
+
status, url = redirect_status, redirect_url
|
88
|
+
controller = Merb::Controller.new(self, status)
|
89
|
+
|
90
|
+
Merb.logger.info("Dispatcher redirecting to: #{url} (#{status})")
|
91
|
+
Merb.logger.flush
|
92
|
+
|
93
|
+
controller.headers['Location'] = url
|
94
|
+
controller.body = "<html><body>You are being <a href=\"#{url}\">redirected</a>.</body></html>"
|
95
|
+
controller
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
# Setup the controller and call the chosen action
|
100
|
+
#
|
101
|
+
# ==== Parameters
|
102
|
+
# klass<Merb::Controller>:: The controller class to dispatch to.
|
103
|
+
# action<Symbol>:: The action to dispatch.
|
104
|
+
# request<Merb::Request>::
|
105
|
+
# The Merb::Request object that was created in #handle
|
106
|
+
# status<Integer>:: The status code to respond with.
|
107
|
+
#
|
108
|
+
# ==== Returns
|
109
|
+
# Merb::Controller::
|
110
|
+
# The Merb::Controller that was dispatched to.
|
111
|
+
def dispatch_action(klass, action, status=200)
|
112
|
+
# build controller
|
113
|
+
controller = klass.new(self, status)
|
114
|
+
if Dispatcher.use_mutex
|
115
|
+
@@mutex.synchronize { controller._dispatch(action) }
|
116
|
+
else
|
117
|
+
controller._dispatch(action)
|
141
118
|
end
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
119
|
+
controller
|
120
|
+
end
|
121
|
+
|
122
|
+
# Re-route the current request to the Exception controller if it is
|
123
|
+
# available, and try to render the exception nicely.
|
124
|
+
#
|
125
|
+
# You can handle exceptions by implementing actions for specific
|
126
|
+
# exceptions such as not_found or for entire classes of exceptions
|
127
|
+
# such as client_error. You can also implement handlers for
|
128
|
+
# exceptions outside the Merb exception hierarchy (e.g.
|
129
|
+
# StandardError is caught in standard_error).
|
130
|
+
#
|
131
|
+
# ==== Parameters
|
132
|
+
# request<Merb::Request>::
|
133
|
+
# The request object associated with the failed request.
|
134
|
+
# exception<Object>::
|
135
|
+
# The exception object that was created when trying to dispatch the
|
136
|
+
# original controller.
|
137
|
+
#
|
138
|
+
# ==== Returns
|
139
|
+
# Exceptions::
|
140
|
+
# The Merb::Controller that was dispatched to.
|
141
|
+
def dispatch_exception(exception)
|
142
|
+
Merb.logger.error(Merb.exception(exception))
|
143
|
+
self.exceptions = [exception]
|
162
144
|
|
163
|
-
|
164
|
-
|
145
|
+
begin
|
146
|
+
e = exceptions.first
|
165
147
|
|
166
|
-
|
167
|
-
|
168
|
-
|
148
|
+
if action_name = e.action_name
|
149
|
+
dispatch_action(Exceptions, action_name, e.class.status)
|
150
|
+
else
|
151
|
+
Merb::Dispatcher::DefaultException.new(self, e.class.status)._dispatch
|
152
|
+
end
|
153
|
+
rescue Object => dispatch_issue
|
154
|
+
if e.same?(dispatch_issue) || exceptions.size > 5
|
155
|
+
Merb::Dispatcher::DefaultException.new(self, e.class.status)._dispatch
|
156
|
+
else
|
157
|
+
Merb.logger.error("Dispatching #{e.class} raised another error.")
|
158
|
+
Merb.logger.error(Merb.exception(dispatch_issue))
|
159
|
+
|
160
|
+
exceptions.unshift dispatch_issue
|
161
|
+
retry
|
162
|
+
end
|
169
163
|
end
|
170
|
-
|
171
164
|
end
|
172
165
|
end
|
173
|
-
end
|
166
|
+
end
|
@@ -18,13 +18,13 @@ module Merb
|
|
18
18
|
# Flash, and some older browsers can't use arbitrary
|
19
19
|
# request methods -- i.e., are limited to GET/POST.
|
20
20
|
# These user-agents can make POST requests in combination
|
21
|
-
# with these overrides to participate fully in REST
|
21
|
+
# with these overrides to participate fully in REST.
|
22
22
|
# Common examples are _method or fb_sig_request_method
|
23
23
|
# in the params, or an X-HTTP-Method-Override header
|
24
24
|
cattr_accessor :http_method_overrides
|
25
25
|
self.http_method_overrides = []
|
26
26
|
|
27
|
-
#
|
27
|
+
# Initialize the request object.
|
28
28
|
#
|
29
29
|
# ==== Parameters
|
30
30
|
# http_request<~params:~[], ~body:IO>::
|
@@ -519,8 +519,8 @@ module Merb
|
|
519
519
|
end
|
520
520
|
|
521
521
|
# ==== Parameters
|
522
|
-
#
|
523
|
-
#
|
522
|
+
# query_string<String>:: The query string.
|
523
|
+
# delimiter<String>:: The query string divider. Defaults to "&".
|
524
524
|
# preserve_order<Boolean>:: Preserve order of args. Defaults to false.
|
525
525
|
#
|
526
526
|
# ==== Returns
|
@@ -529,13 +529,13 @@ module Merb
|
|
529
529
|
# ==== Examples
|
530
530
|
# query_parse("bar=nik&post[body]=heya")
|
531
531
|
# # => { :bar => "nik", :post => { :body => "heya" } }
|
532
|
-
def query_parse(
|
533
|
-
|
534
|
-
(
|
535
|
-
key, value = unescape(
|
536
|
-
normalize_params(
|
537
|
-
|
538
|
-
preserve_order ?
|
532
|
+
def query_parse(query_string, delimiter = '&;', preserve_order = false)
|
533
|
+
query = preserve_order ? Dictionary.new : {}
|
534
|
+
for pair in (query_string || '').split(/[#{delimiter}] */n)
|
535
|
+
key, value = unescape(pair).split('=',2)
|
536
|
+
normalize_params(query, key, value)
|
537
|
+
end
|
538
|
+
preserve_order ? query : query.to_mash
|
539
539
|
end
|
540
540
|
|
541
541
|
NAME_REGEX = /Content-Disposition:.* name="?([^\";]*)"?/ni.freeze
|
@@ -96,7 +96,7 @@ module Merb
|
|
96
96
|
|
97
97
|
# Matches a +path+ and any number of optional request methods as
|
98
98
|
# conditions of a route. Alternatively, +path+ can be a hash of
|
99
|
-
# conditions, in which case +conditions+ ignored.
|
99
|
+
# conditions, in which case +conditions+ is ignored.
|
100
100
|
#
|
101
101
|
# ==== Parameters
|
102
102
|
#
|
@@ -108,12 +108,12 @@ module Merb
|
|
108
108
|
# within the regular expression syntax.
|
109
109
|
# +path+ is optional.
|
110
110
|
# conditions<Hash>::
|
111
|
-
#
|
112
|
-
#
|
111
|
+
# Additional conditions that the request must meet in order to match.
|
112
|
+
# The keys must be methods that the Merb::Request instance will respond
|
113
113
|
# to. The value is the string or regexp that matched the returned value.
|
114
114
|
# Conditions are inherited by child routes.
|
115
115
|
#
|
116
|
-
# The
|
116
|
+
# The following have special meaning:
|
117
117
|
# * :method -- Limit this match based on the request method. (GET,
|
118
118
|
# POST, PUT, DELETE)
|
119
119
|
# * :path -- Used internally to maintain URL form information
|
@@ -141,13 +141,13 @@ module Merb
|
|
141
141
|
# f.match("/baz").to(:action => "caz")
|
142
142
|
# end
|
143
143
|
#
|
144
|
-
# #match only
|
144
|
+
# #match only if the browser string contains MSIE or Gecko
|
145
145
|
# r.match ('/foo', :user_agent => /(MSIE|Gecko)/ )
|
146
146
|
# .to({:controller=>'foo', :action=>'popular')
|
147
147
|
#
|
148
148
|
# # Route GET and POST requests to different actions (see also #resources)
|
149
149
|
# r.match('/foo', :method=>:get).to(:action=>'show')
|
150
|
-
# r.
|
150
|
+
# r.match('/foo', :method=>:post).to(:action=>'create')
|
151
151
|
#
|
152
152
|
# # match also takes regular expressions
|
153
153
|
#
|
@@ -3,19 +3,19 @@ require 'merb-core/dispatch/router/behavior'
|
|
3
3
|
require 'merb-core/dispatch/router/route'
|
4
4
|
require 'merb-core/controller/mixins/responder'
|
5
5
|
module Merb
|
6
|
-
# Router stores route definitions and finds first
|
7
|
-
# that matches incoming request URL.
|
6
|
+
# Router stores route definitions and finds the first
|
7
|
+
# route that matches the incoming request URL.
|
8
8
|
#
|
9
9
|
# Then information from route is used by dispatcher to
|
10
10
|
# call action on the controller.
|
11
11
|
#
|
12
12
|
# ==== Routes compilation.
|
13
13
|
#
|
14
|
-
#
|
14
|
+
# The most interesting method of Router (and heart of
|
15
15
|
# route matching machinery) is match method generated
|
16
|
-
# on fly from routes definitions. It is called routes
|
16
|
+
# on the fly from routes definitions. It is called routes
|
17
17
|
# compilation. Generated match method body contains
|
18
|
-
# one if/elsif statement that picks first matching route
|
18
|
+
# one if/elsif statement that picks the first matching route
|
19
19
|
# definition and sets values to named parameters of the route.
|
20
20
|
#
|
21
21
|
# Compilation is synchronized by mutex.
|