| Class | Merb::Request |
| In: |
merb-core/lib/merb-core/dispatch/request.rb
merb-core/lib/merb-core/dispatch/dispatcher.rb |
| Parent: | Object |
| METHODS | = | %w{get post put delete head options} |
| env | [RW] | :api: private |
| exceptions | [RW] | :api: public |
| route | [RW] | :api: private |
| route_params | [R] | :api: private |
Initialize the request object.
| http_request<~params:~[], ~body:IO>: | An object like an HTTP Request. |
:api: private
# File merb-core/lib/merb-core/dispatch/request.rb, line 38
38: def initialize(rack_env)
39: @env = rack_env
40: @body = rack_env[Merb::Const::RACK_INPUT]
41: @route_params = {}
42: end
Processes the return value of a deferred router block and returns the current route params for the current request evaluation
:api: private
# File merb-core/lib/merb-core/dispatch/request.rb, line 126
126: def _process_block_return(retval)
127: # If the return value is an array, then it is a redirect
128: # so we must set the request as a redirect and extract
129: # the redirect params and return it as a hash so that the
130: # dispatcher can handle it
131: matched! if retval.is_a?(Array)
132: retval
133: end
| String: | The HTTP connection. |
:api: private
# File merb-core/lib/merb-core/dispatch/request.rb, line 499
499: def connection
500: @env[Merb::Const::HTTP_CONNECTION]
501: end
| Fixnum: | The request content length. |
:api: public
# File merb-core/lib/merb-core/dispatch/request.rb, line 523
523: def content_length
524: @content_length ||= @env[Merb::Const::CONTENT_LENGTH].to_i
525: end
Returns the controller object for initialization and dispatching the request.
| Class: | The controller class matching the routed request, |
e.g. Posts.
:api: private
# File merb-core/lib/merb-core/dispatch/request.rb, line 52
52: def controller
53: unless params[:controller]
54: raise ControllerExceptions::NotFound,
55: "Route matched, but route did not specify a controller.\n" +
56: "Did you forgot to add :controller => \"people\" or :controller " +
57: "segment to route definition?\nHere is what's specified:\n" +
58: route.inspect
59: end
60: path = [params[:namespace], params[:controller]].compact.join(Merb::Const::SLASH)
61: controller = path.snake_case.to_const_string
62:
63: begin
64: Object.full_const_get(controller)
65: rescue NameError => e
66: msg = "Controller class not found for controller `#{path}'"
67: Merb.logger.warn!(msg)
68: raise ControllerExceptions::NotFound, msg
69: end
70: end
| tld_length<Fixnum>: | Number of domains levels to inlclude in the top level domain. Defaults to 1. |
| String: | The full domain name without the port number. |
:api: public
# File merb-core/lib/merb-core/dispatch/request.rb, line 589
589: def domain(tld_length = 1)
590: host.split(Merb::Const::DOT).last(1 + tld_length).join(Merb::Const::DOT).sub(/:\d+$/,'')
591: end
Find route using requested URI and merges route parameters (:action, :controller and named segments) into request params hash.
:api: private
# File merb-core/lib/merb-core/dispatch/request.rb, line 115
115: def find_route!
116: @route, @route_params = Merb::Router.route_for(self)
117: params.merge! @route_params if @route_params.is_a?(Hash)
118: end
Handles request routing and action dispatch.
| Merb::Controller: | the controller that handled the action dispatch. |
:api: private
# File merb-core/lib/merb-core/dispatch/dispatcher.rb, line 52
52: def handle
53: start = Time.now
54: Merb.logger.info { "Started request handling: #{start.to_s}" }
55:
56: find_route!
57: return rack_response if handled?
58:
59: klass = controller
60: Merb.logger.debug { "Routed to: #{params.inspect}" }
61:
62: unless klass < Controller
63: raise NotFound,
64: "Controller '#{klass}' not found.\n" \
65: "If Merb tries to find a controller for static files, " \
66: "you may need to check your Rackup file, see the Problems " \
67: "section at: http://wiki.merbivore.com/pages/rack-middleware"
68: end
69:
70: if klass.abstract?
71: raise NotFound, "The '#{klass}' controller has no public actions"
72: end
73:
74: controller = dispatch_action(klass, params[:action])
75: controller._benchmarks[:dispatch_time] = Time.now - start
76: Merb.logger.info { controller._benchmarks.inspect }
77: Merb.logger.flush
78: controller.rack_response
79: rescue Object => exception
80: dispatch_exception(exception).rack_response
81: end
If @route_params is an Array, then it will be the rack response. In this case, the request is considered handled.
| Boolean: | true if @route_params is an Array, false otherwise. |
:api: private
# File merb-core/lib/merb-core/dispatch/request.rb, line 170
170: def handled?
171: @route_params.is_a?(Array)
172: end
Value of If-Modified-Since request header.
:api: private
# File merb-core/lib/merb-core/dispatch/request.rb, line 605
605: def if_modified_since
606: if time = @env[Merb::Const::HTTP_IF_MODIFIED_SINCE]
607: Time.rfc2822(time)
608: end
609: end
Value of If-None-Match request header.
:api: private
# File merb-core/lib/merb-core/dispatch/request.rb, line 597
597: def if_none_match
598: @env[Merb::Const::HTTP_IF_NONE_MATCH]
599: end
Sets the request as matched. This will abort evaluating any further deferred procs.
:api: private
# File merb-core/lib/merb-core/dispatch/request.rb, line 139
139: def matched!
140: @matched = true
141: end
Checks whether or not the request has been matched to a route.
:api: private
# File merb-core/lib/merb-core/dispatch/request.rb, line 146
146: def matched?
147: @matched
148: end
| String: | Returns the redirect message Base64 unencoded. |
:api: public
# File merb-core/lib/merb-core/dispatch/request.rb, line 303
303: def message
304: return {} unless params[:_message]
305: begin
306: Marshal.load(Merb::Parse.unescape(params[:_message]).unpack("m").first)
307: rescue ArgumentError, TypeError
308: {}
309: end
310: end
| Symbol: | The name of the request method, e.g. :get. |
If the method is post, then the blocks specified in http_method_overrides will be checked for the masquerading method. The block will get the controller yielded to it. The first matching workaround wins. To disable this behavior, set http_method_overrides = []
:api: public
# File merb-core/lib/merb-core/dispatch/request.rb, line 84
84: def method
85: @method ||= begin
86: request_method = @env[Merb::Const::REQUEST_METHOD].downcase.to_sym
87: case request_method
88: when :get, :head, :put, :delete, :options
89: request_method
90: when :post
91: m = nil
92: self.class.http_method_overrides.each do |o|
93: m ||= o.call(self); break if m
94: end
95: m.downcase! if m
96: METHODS.include?(m) ? m.to_sym : :post
97: else
98: raise "Unknown REQUEST_METHOD: #{@env[Merb::Const::REQUEST_METHOD]}"
99: end
100: end
101: end
| Mash: | All request parameters. |
The order of precedence for the params is XML, JSON, multipart, body and request string.
:api: public
# File merb-core/lib/merb-core/dispatch/request.rb, line 289
289: def params
290: @params ||= begin
291: h = body_and_query_params.merge(route_params)
292: h.merge!(multipart_params) if self.class.parse_multipart_params && multipart_params
293: h.merge!(json_params) if self.class.parse_json_params && json_params
294: h.merge!(xml_params) if self.class.parse_xml_params && xml_params
295: h
296: end
297: end
| String: | The URI without the query string. Strips trailing "/" and reduces duplicate "/" to a single "/". |
:api: public
# File merb-core/lib/merb-core/dispatch/request.rb, line 533
533: def path
534: # Merb::Const::SLASH is /
535: # Merb::Const::QUESTION_MARK is ?
536: path = (uri.empty? ? Merb::Const::SLASH : uri.split(Merb::Const::QUESTION_MARK).first).squeeze(Merb::Const::SLASH)
537: path = path[0..-2] if (path[-1] == ?/) && path.size > 1
538: path
539: end
| (Array, Hash): | the route params for the matched route. |
If the response is an Array then it is considered a direct Rack response to be sent back as a response. Otherwise, the route_params is a Hash with routing data (controller, action, et al).
:api: private
# File merb-core/lib/merb-core/dispatch/request.rb, line 159
159: def rack_response
160: @route_params
161: end
| String: | The remote IP address. |
:api: public
# File merb-core/lib/merb-core/dispatch/request.rb, line 343
343: def remote_ip
344: return @env[Merb::Const::HTTP_CLIENT_IP] if @env.include?(Merb::Const::HTTP_CLIENT_IP)
345:
346: if @env.include?(Merb::Const::HTTP_X_FORWARDED_FOR) then
347: remote_ips = @env[Merb::Const::HTTP_X_FORWARDED_FOR].split(',').reject do |ip|
348: ip =~ Merb::Const::LOCAL_IP_REGEXP
349: end
350:
351: return remote_ips.first.strip unless remote_ips.empty?
352: end
353:
354: return @env[Merb::Const::REMOTE_ADDR]
355: end
| Boolean:: | True if the request is an SSL request. |
:api: public
# File merb-core/lib/merb-core/dispatch/request.rb, line 371
371: def ssl?
372: @env[Merb::Const::UPCASE_HTTPS] == 'on' || @env[Merb::Const::HTTP_X_FORWARDED_PROTO] == Merb::Const::HTTPS
373: end
| tld_length<Fixnum>: | Number of domains levels to inlclude in the top level domain. Defaults to 1. |
| Array: | All the subdomain parts of the host. |
:api: public
# File merb-core/lib/merb-core/dispatch/request.rb, line 575
575: def subdomains(tld_length = 1)
576: parts = host.split(Merb::Const::DOT)
577: parts[0..-(tld_length+2)]
578: end
| Boolean: | If the request is an XML HTTP request. |
:api: public
# File merb-core/lib/merb-core/dispatch/request.rb, line 333
333: def xml_http_request?
334: not Merb::Const::XML_HTTP_REQUEST_REGEXP.match(@env[Merb::Const::HTTP_X_REQUESTED_WITH]).nil?
335: end
| Mash: | The parameters gathered from the query string and the request body, with parameters in the body taking precedence. |
:api: private
# File merb-core/lib/merb-core/dispatch/request.rb, line 210
210: def body_and_query_params
211: # ^-- FIXME a better name for this method
212: @body_and_query_params ||= begin
213: h = query_params
214: h.merge!(body_params) if body_params
215: h.to_mash
216: end
217: end
Parameters passed in the body of the request. Ajax calls from prototype.js and other libraries pass content this way.
| Hash: | The parameters passed in the body. |
:api: private
# File merb-core/lib/merb-core/dispatch/request.rb, line 196
196: def body_params
197: @body_params ||= begin
198: if content_type && content_type.match(Merb::Const::FORM_URL_ENCODED_REGEXP) # or content_type.nil?
199: Merb::Parse.query(raw_post)
200: end
201: end
202: end
Setup the controller and call the chosen action
| klass<Merb::Controller>: | The controller class to dispatch to. |
| action<Symbol>: | The action to dispatch. |
| status<Integer>: | The status code to respond with. |
| Merb::Controller: | The Merb::Controller that was dispatched to. |
:api: private
# File merb-core/lib/merb-core/dispatch/dispatcher.rb, line 96
96: def dispatch_action(klass, action, status=200)
97: # build controller
98: controller = klass.new(self, status)
99: if Dispatcher.use_mutex
100: @@mutex.synchronize { controller._dispatch(action) }
101: else
102: controller._dispatch(action)
103: end
104: controller
105: end
Re-route the current request to the Exception controller if it is available, and try to render the exception nicely.
You can handle exceptions by implementing actions for specific exceptions such as not_found or for entire classes of exceptions such as client_error. You can also implement handlers for exceptions outside the Merb exception hierarchy (e.g. StandardError is caught in standard_error).
| exception<Object>: | The exception object that was created when trying to dispatch the original controller. |
| Exceptions: | The Merb::Controller that was dispatched to. |
:api: private
# File merb-core/lib/merb-core/dispatch/dispatcher.rb, line 126
126: def dispatch_exception(exception)
127: if(exception.is_a?(Merb::ControllerExceptions::Base) &&
128: !exception.is_a?(Merb::ControllerExceptions::ServerError))
129: Merb.logger.info(Merb.exception(exception))
130: else
131: Merb.logger.error(Merb.exception(exception))
132: end
133:
134: self.exceptions = [exception]
135:
136: begin
137: e = exceptions.first
138:
139: if action_name = e.action_name
140: dispatch_action(Exceptions, action_name, e.class.status)
141: else
142: Merb::Dispatcher::DefaultException.new(self, e.class.status)._dispatch
143: end
144: rescue Object => dispatch_issue
145: if e.same?(dispatch_issue) || exceptions.size > 5
146: Merb::Dispatcher::DefaultException.new(self, e.class.status)._dispatch
147: else
148: Merb.logger.error("Dispatching #{e.class} raised another error.")
149: Merb.logger.error(Merb.exception(dispatch_issue))
150:
151: exceptions.unshift dispatch_issue
152: retry
153: end
154: end
155: end
| Hash: | Parameters from body if this is a JSON request. |
If the JSON object parses as a Hash, it will be merged with the parameters hash. If it parses to anything else (such as an Array, or if it inflates to an Object) it will be accessible via the inflated_object parameter.
:api: private
# File merb-core/lib/merb-core/dispatch/request.rb, line 253
253: def json_params
254: @json_params ||= begin
255: if Merb::Const::JSON_MIME_TYPE_REGEXP.match(content_type)
256: begin
257: jobj = JSON.parse(raw_post)
258: jobj = jobj.to_mash if jobj.is_a?(Hash)
259: rescue JSON::ParserError
260: jobj = Mash.new
261: end
262: jobj.is_a?(Hash) ? jobj : { :inflated_object => jobj }
263: end
264: end
265: end
| ControllerExceptions::MultiPartParseError: | Unable to parse the multipart form data. |
| Hash: | The parsed multipart parameters. |
:api: private
# File merb-core/lib/merb-core/dispatch/request.rb, line 227
227: def multipart_params
228: @multipart_params ||=
229: begin
230: # if the content-type is multipart
231: # parse the multipart. Otherwise return {}
232: if (Merb::Const::MULTIPART_REGEXP =~ content_type)
233: Merb::Parse.multipart(@body, $1, content_length)
234: else
235: {}
236: end
237: rescue ControllerExceptions::MultiPartParseError => e
238: @multipart_params = {}
239: raise e
240: end
241: end
| Hash: | Parameters from body if this is an XML request. |
:api: private
# File merb-core/lib/merb-core/dispatch/request.rb, line 271
271: def xml_params
272: @xml_params ||= begin
273: if Merb::Const::XML_MIME_TYPE_REGEXP.match(content_type)
274: Hash.from_xml(raw_post) rescue Mash.new
275: end
276: end
277: end