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.
Files changed (52) hide show
  1. data/Rakefile +22 -13
  2. data/lib/merb-core/bootloader.rb +3 -2
  3. data/lib/merb-core/config.rb +1 -1
  4. data/lib/merb-core/constants.rb +3 -1
  5. data/lib/merb-core/controller/abstract_controller.rb +25 -39
  6. data/lib/merb-core/controller/exceptions.rb +14 -16
  7. data/lib/merb-core/controller/merb_controller.rb +2 -2
  8. data/lib/merb-core/controller/mime.rb +2 -1
  9. data/lib/merb-core/controller/mixins/render.rb +12 -0
  10. data/lib/merb-core/controller/mixins/responder.rb +31 -7
  11. data/lib/merb-core/core_ext/hash.rb +7 -0
  12. data/lib/merb-core/core_ext/kernel.rb +31 -34
  13. data/lib/merb-core/core_ext.rb +1 -0
  14. data/lib/merb-core/dispatch/default_exception/default_exception.rb +3 -3
  15. data/lib/merb-core/dispatch/dispatcher.rb +128 -135
  16. data/lib/merb-core/dispatch/request.rb +11 -11
  17. data/lib/merb-core/dispatch/router/behavior.rb +6 -6
  18. data/lib/merb-core/dispatch/router.rb +5 -5
  19. data/lib/merb-core/logger.rb +203 -202
  20. data/lib/merb-core/rack/application.rb +19 -5
  21. data/lib/merb-core/rack/middleware/conditional_get.rb +23 -0
  22. data/lib/merb-core/rack/middleware/content_length.rb +18 -0
  23. data/lib/merb-core/rack/middleware/tracer.rb +20 -0
  24. data/lib/merb-core/rack/middleware.rb +1 -7
  25. data/lib/merb-core/rack.rb +19 -16
  26. data/lib/merb-core/test/matchers/route_matchers.rb +1 -0
  27. data/lib/merb-core/test/matchers/view_matchers.rb +36 -0
  28. data/lib/merb-core/test/run_specs.rb +2 -1
  29. data/lib/merb-core/version.rb +1 -1
  30. data/lib/merb-core.rb +39 -33
  31. data/spec/private/config/merb_spec.rb +34 -0
  32. data/spec/private/dispatch/fixture/log/merb_test.log +372 -0
  33. data/spec/private/router/fixture/log/merb_test.log +42 -0
  34. data/spec/public/abstract_controller/controllers/filters.rb +50 -1
  35. data/spec/public/abstract_controller/filter_spec.rb +25 -0
  36. data/spec/public/controller/base_spec.rb +41 -1
  37. data/spec/public/controller/controllers/base.rb +16 -0
  38. data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/class_provides/index.html.erb +1 -1
  39. data/spec/public/controller/dispatcher_spec.rb +24 -25
  40. data/spec/public/controller/responder_spec.rb +6 -0
  41. data/spec/public/core_ext/kernel_spec.rb +79 -0
  42. data/spec/public/directory_structure/directory/log/merb_test.log +245 -0
  43. data/spec/public/rack/conditinal_get_middleware_spec.rb +139 -0
  44. data/spec/public/rack/rack_middleware_spec.rb +99 -0
  45. data/spec/public/rack/shared_example_groups.rb +35 -0
  46. data/spec/public/reloading/directory/log/merb_test.log +40 -0
  47. data/spec/public/request/request_spec.rb +0 -5
  48. data/spec/public/router/fixture/log/merb_test.log +348 -0
  49. data/spec/public/test/route_matchers_spec.rb +4 -0
  50. data/spec/spec_helper.rb +7 -1
  51. metadata +42 -5
  52. 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
- start = Time.now
30
- Merb.logger.info "Started request handling: #{start.to_s}"
29
+ request.handle
30
+ end
31
+ end
32
+ end
33
+
34
+ class Request
35
+ include Merb::ControllerExceptions
31
36
 
32
- request.find_route!
33
- return redirect(request) if request.redirects?
34
-
35
- klass = request.controller
36
- Merb.logger.debug("Routed to: #{request.params.inspect}")
37
-
38
- unless klass < Controller
39
- raise NotFound,
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
- if klass.name == "Application"
47
- raise NotFound, "The 'Application' controller has no public actions"
48
- end
46
+ klass = controller
47
+ Merb.logger.debug("Routed to: #{params.inspect}")
49
48
 
50
- controller = dispatch_action(klass, request.params[:action], request)
51
- controller._benchmarks[:dispatch_time] = Time.now - start
52
- Merb.logger.info controller._benchmarks.inspect
53
- Merb.logger.flush
54
- controller
55
- rescue Object => exception
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
- # Re-route the current request to the Exception controller if it is
84
- # available, and try to render the exception nicely.
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
- # If no custom actions are available to render an exception then the errors
128
- # will end up here for processing
129
- #
130
- # ==== Parameters
131
- # request<Merb::Request>::
132
- # The Merb request that produced the original error.
133
- # status<Integer>::
134
- # The status code to return with the Exception.
135
- #
136
- # ==== Returns
137
- # Merb::Dispatcher::DefaultException::
138
- # The DefaultException that was dispatched to.
139
- def dispatch_default_exception(request, status)
140
- Merb::Dispatcher::DefaultException.new(request, status)._dispatch
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
- # Set up a faux controller to do redirection from the router
144
- #
145
- # ==== Parameters
146
- # request<Merb::Request>::
147
- # The Merb::Request object that was created in #handle
148
- # status<Integer>::
149
- # The status code to return with the controller
150
- # url<String>::
151
- # The URL to return
152
- #
153
- # ==== Example
154
- # r.match("/my/old/crusty/url").redirect("http://example.com/index.html")
155
- #
156
- # ==== Returns
157
- # Merb::Controller::
158
- # Merb::Controller set with redirect headers and a 301/302 status
159
- def redirect(request)
160
- status, url = request.redirect_status, request.redirect_url
161
- controller = Merb::Controller.new(request, status)
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
- Merb.logger.info("Dispatcher redirecting to: #{url} (#{status})")
164
- Merb.logger.flush
145
+ begin
146
+ e = exceptions.first
165
147
 
166
- controller.headers['Location'] = url
167
- controller.body = "<html><body>You are being <a href=\"#{url}\">redirected</a>.</body></html>"
168
- controller
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
- # Initial the request object.
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
- # qs<String>:: The query string.
523
- # d<String>:: The query string divider. Defaults to "&".
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(qs, d = '&;', preserve_order = false)
533
- qh = preserve_order ? Dictionary.new : {}
534
- (qs||'').split(/[#{d}] */n).inject(qh) { |h,p|
535
- key, value = unescape(p).split('=',2)
536
- normalize_params(h, key, value)
537
- }
538
- preserve_order ? qh : qh.to_mash
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
- # Addational conditions that the request must meet in order to match.
112
- # the keys must be methods that the Merb::Request instance will respond
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 Following have special meaning:
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 of the browser string contains MSIE or Gecko
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.mathc('/foo', :method=>:post).to(:action=>'create')
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
- # Most interesting method of Router (and heart of
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.