Class Merb::Request
In: merb-core/lib/merb-core/dispatch/request.rb
merb-core/lib/merb-core/dispatch/dispatcher.rb
Parent: Object

Methods

Included Modules

Merb::ControllerExceptions

Constants

METHODS = %w{get post put delete head options}

Attributes

env  [RW]  :api: private
exceptions  [RW]  :api: public
route  [RW]  :api: private
route_params  [R]  :api: private

Public Class methods

Initialize the request object.

Parameters

http_request<~params:~[], ~body:IO>:An object like an HTTP Request.

:api: private

[Source]

    # 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

Public Instance methods

Notes

Processes the return value of a deferred router block and returns the current route params for the current request evaluation

:api: private

[Source]

     # 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

Returns

String:The accepted response types. Defaults to "*/*".

:api: private

[Source]

     # File merb-core/lib/merb-core/dispatch/request.rb, line 491
491:     def accept
492:       @env[Merb::Const::HTTP_ACCEPT].blank? ? "*/*" : @env[Merb::Const::HTTP_ACCEPT]
493:     end

Returns

String:The accepted character sets.

:api: public

[Source]

     # File merb-core/lib/merb-core/dispatch/request.rb, line 467
467:     def accept_charset
468:       @env[Merb::Const::HTTP_ACCEPT_CHARSET]
469:     end

Returns

String:The accepted encodings.

:api: private

[Source]

     # File merb-core/lib/merb-core/dispatch/request.rb, line 419
419:     def accept_encoding
420:       @env[Merb::Const::HTTP_ACCEPT_ENCODING]
421:     end

Returns

String:The accepted language.

:api: public

[Source]

     # File merb-core/lib/merb-core/dispatch/request.rb, line 443
443:     def accept_language
444:       @env[Merb::Const::HTTP_ACCEPT_LANGUAGE]
445:     end
ajax?()

Alias for xml_http_request?

Returns

String:HTTP cache control.

:api: public

[Source]

     # File merb-core/lib/merb-core/dispatch/request.rb, line 435
435:     def cache_control
436:       @env[Merb::Const::HTTP_CACHE_CONTROL]
437:     end

Returns

String:The HTTP connection.

:api: private

[Source]

     # File merb-core/lib/merb-core/dispatch/request.rb, line 499
499:     def connection
500:       @env[Merb::Const::HTTP_CONNECTION]
501:     end

Returns

Fixnum:The request content length.

:api: public

[Source]

     # 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

String:The request content type.

:api: private

[Source]

     # File merb-core/lib/merb-core/dispatch/request.rb, line 515
515:     def content_type
516:       @env[Merb::Const::UPCASE_CONTENT_TYPE]
517:     end

Returns the controller object for initialization and dispatching the request.

Returns

Class:The controller class matching the routed request,
  e.g. Posts.

:api: private

[Source]

    # 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

Parameters

tld_length<Fixnum>:Number of domains levels to inlclude in the top level domain. Defaults to 1.

Returns

String:The full domain name without the port number.

:api: public

[Source]

     # 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

Notes

Find route using requested URI and merges route parameters (:action, :controller and named segments) into request params hash.

:api: private

[Source]

     # 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

Returns

String:The full URI, including protocol and host

:api: public

[Source]

     # File merb-core/lib/merb-core/dispatch/request.rb, line 387
387:     def full_uri
388:       protocol + "://" + host + uri
389:     end

Returns

String:The gateway.

:api: public

[Source]

     # File merb-core/lib/merb-core/dispatch/request.rb, line 483
483:     def gateway
484:       @env[Merb::Const::GATEWAY_INTERFACE]
485:     end

Handles request routing and action dispatch.

Returns

Merb::Controller:the controller that handled the action dispatch.

:api: private

[Source]

    # 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.

Returns

Boolean:true if @route_params is an Array, false otherwise.

:api: private

[Source]

     # File merb-core/lib/merb-core/dispatch/request.rb, line 170
170:     def handled?
171:       @route_params.is_a?(Array)
172:     end

Returns

String:The full hostname including the port.

:api: public

[Source]

     # File merb-core/lib/merb-core/dispatch/request.rb, line 561
561:     def host
562:       @env[Merb::Const::HTTP_X_FORWARDED_HOST] || @env[Merb::Const::HTTP_HOST] ||
563:         @env[Merb::Const::SERVER_NAME]
564:     end

Returns

Value of If-Modified-Since request header.

:api: private

[Source]

     # 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

Returns

Value of If-None-Match request header.

:api: private

[Source]

     # 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

Returns

String:Value of HTTP_KEEP_ALIVE.

:api: public

[Source]

     # File merb-core/lib/merb-core/dispatch/request.rb, line 459
459:     def keep_alive
460:       @env[Merb::Const::HTTP_KEEP_ALIVE]
461:     end

Sets the request as matched. This will abort evaluating any further deferred procs.

:api: private

[Source]

     # 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

[Source]

     # File merb-core/lib/merb-core/dispatch/request.rb, line 146
146:     def matched?
147:       @matched
148:     end

Returns

String:Returns the redirect message Base64 unencoded.

:api: public

[Source]

     # 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

Returns

Symbol:The name of the request method, e.g. :get.

Notes

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

[Source]

     # 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

Returns

Mash:All request parameters.

Notes

The order of precedence for the params is XML, JSON, multipart, body and request string.

:api: public

[Source]

     # 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

Returns

String:The URI without the query string. Strips trailing "/" and reduces duplicate "/" to a single "/".

:api: public

[Source]

     # 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

Returns

String:The path info.

:api: public

[Source]

     # File merb-core/lib/merb-core/dispatch/request.rb, line 545
545:     def path_info
546:       @path_info ||= Merb::Parse.unescape(@env[Merb::Const::PATH_INFO])
547:     end

Returns

Fixnum:The server port.

:api: public

[Source]

     # File merb-core/lib/merb-core/dispatch/request.rb, line 553
553:     def port
554:       @env[Merb::Const::SERVER_PORT].to_i
555:     end

Returns

String:The protocol, i.e. either "https" or "http" depending on the HTTPS header.

:api: public

[Source]

     # File merb-core/lib/merb-core/dispatch/request.rb, line 363
363:     def protocol
364:       ssl? ? Merb::Const::HTTPS : Merb::Const::HTTP
365:     end

Returns

String:The query string.

:api: private

[Source]

     # File merb-core/lib/merb-core/dispatch/request.rb, line 507
507:     def query_string
508:       @env[Merb::Const::QUERY_STRING]
509:     end

Returns

(Array, Hash):the route params for the matched route.

Notes

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

[Source]

     # File merb-core/lib/merb-core/dispatch/request.rb, line 159
159:     def rack_response
160:       @route_params
161:     end

Returns

String:The raw post.

:api: private

[Source]

     # File merb-core/lib/merb-core/dispatch/request.rb, line 324
324:     def raw_post
325:       @body.rewind if @body.respond_to?(:rewind)
326:       @raw_post ||= @body.read
327:     end

Returns

String:The HTTP referer.

:api: public

[Source]

     # File merb-core/lib/merb-core/dispatch/request.rb, line 379
379:     def referer
380:       @env[Merb::Const::HTTP_REFERER]
381:     end

Returns

String:The remote IP address.

:api: public

[Source]

     # 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

Notes

Resets the params to a nil value.

:api: private

[Source]

     # File merb-core/lib/merb-core/dispatch/request.rb, line 316
316:     def reset_params!
317:       @params = nil
318:     end

Returns

String:The script name.

:api: public

[Source]

     # File merb-core/lib/merb-core/dispatch/request.rb, line 427
427:     def script_name
428:       @env[Merb::Const::SCRIPT_NAME]
429:     end

Returns

String:The server name.

:api: public

[Source]

     # File merb-core/lib/merb-core/dispatch/request.rb, line 411
411:     def server_name
412:       @env[Merb::Const::SERVER_NAME]
413:     end

Returns

String:The server software.

:api: public

[Source]

     # File merb-core/lib/merb-core/dispatch/request.rb, line 451
451:     def server_software
452:       @env[Merb::Const::SERVER_SOFTWARE]
453:     end

Returns

Boolean::True if the request is an SSL request.

:api: public

[Source]

     # 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

Parameters

tld_length<Fixnum>:Number of domains levels to inlclude in the top level domain. Defaults to 1.

Returns

Array:All the subdomain parts of the host.

:api: public

[Source]

     # 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

Returns

String:The request URI.

:api: public

[Source]

     # File merb-core/lib/merb-core/dispatch/request.rb, line 395
395:     def uri
396:       @env[Merb::Const::REQUEST_PATH] || @env[Merb::Const::REQUEST_URI] || path_info
397:     end

Returns

String:The HTTP user agent.

:api: public

[Source]

     # File merb-core/lib/merb-core/dispatch/request.rb, line 403
403:     def user_agent
404:       @env[Merb::Const::HTTP_USER_AGENT]
405:     end

Returns

String:The HTTP version

:api: private

[Source]

     # File merb-core/lib/merb-core/dispatch/request.rb, line 475
475:     def version
476:       @env[Merb::Const::HTTP_VERSION]
477:     end
xhr?()

Alias for xml_http_request?

Returns

Boolean:If the request is an XML HTTP request.

:api: public

[Source]

     # 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

Private Instance methods

Returns

Mash:The parameters gathered from the query string and the request body, with parameters in the body taking precedence.

:api: private

[Source]

     # 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.

Returns

Hash:The parameters passed in the body.

:api: private

[Source]

     # 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

Parameters

klass<Merb::Controller>:The controller class to dispatch to.
action<Symbol>:The action to dispatch.
status<Integer>:The status code to respond with.

Returns

Merb::Controller:The Merb::Controller that was dispatched to.

:api: private

[Source]

     # 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).

Parameters

exception<Object>:The exception object that was created when trying to dispatch the original controller.

Returns

Exceptions:The Merb::Controller that was dispatched to.

:api: private

[Source]

     # 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

Returns

Hash:Parameters from body if this is a JSON request.

Notes

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

[Source]

     # 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

Raises

ControllerExceptions::MultiPartParseError:Unable to parse the multipart form data.

Returns

Hash:The parsed multipart parameters.

:api: private

[Source]

     # 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

Returns

Hash:Parameters passed from the URL like ?blah=hello.

:api: private

[Source]

     # File merb-core/lib/merb-core/dispatch/request.rb, line 185
185:     def query_params
186:       @query_params ||= Merb::Parse.query(query_string || '')
187:     end

Returns

Hash:Parameters from body if this is an XML request.

:api: private

[Source]

     # 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

[Validate]