module Roda::RodaPlugins::Base::RequestMethods
Instance methods for RodaRequest
, mostly related to handling routing for the request.
Constants
- TERM
Attributes
The current captures for the request. This gets modified as routing occurs.
The current path to match requests against.
The current path to match requests against.
Public Class Methods
Source
# File lib/roda/request.rb, line 84 def initialize(scope, env) @scope = scope @captures = [] @remaining_path = _remaining_path(env) @env = env end
Store the roda instance and environment.
Public Instance Methods
Source
# File lib/roda/request.rb, line 93 def block_result(result) res = response if res.empty? && (body = block_result_body(result)) res.write(body) end end
Handle match block return values. By default, if a string is given and the response is empty, use the string as the response body.
Source
# File lib/roda/request.rb, line 103 def get(*args, &block) _verb(args, &block) if is_get? end
Match GET requests. If no arguments are provided, matches all GET requests, otherwise, matches only GET requests where the arguments given fully consume the path.
Source
# File lib/roda/request.rb, line 117 def halt(res=response.finish) throw :halt, res end
Immediately stop execution of the route block and return the given rack response array of status, headers, and body. If no argument is given, uses the current response.
r.halt [200, {'Content-Type'=>'text/html'}, ['Hello World!']] response.status = 200 response['Content-Type'] = 'text/html' response.write 'Hello World!' r.halt
Source
# File lib/roda/request.rb, line 131 def http_version # Prefer SERVER_PROTOCOL as it is required in Rack 3. # Still fall back to HTTP_VERSION if SERVER_PROTOCOL # is not set, in case the server in use is not Rack 3 # compliant. @env['SERVER_PROTOCOL'] || @env['HTTP_VERSION'] end
Source
# File lib/roda/request.rb, line 126 def inspect "#<#{self.class.inspect} #{@env["REQUEST_METHOD"]} #{path}>" end
Show information about current request, including request class, request method and full path.
r.inspect # => '#<Roda::RodaRequest GET /foo/bar>'
Source
# File lib/roda/request.rb, line 192 def is(*args, &block) if args.empty? if empty_path? always(&block) end else args << TERM if_match(args, &block) end end
Does a terminal match on the current path, matching only if the arguments have fully matched the path. If it matches, the match block is executed, and when the match block returns, the rack response is returned.
r.remaining_path # => "/foo/bar" r.is 'foo' do # does not match, as path isn't fully matched (/bar remaining) end r.is 'foo/bar' do # matches as path is empty after matching end
If no arguments are given, matches if the path is already fully matched.
r.on 'foo/bar' do r.is do # matches as path is already empty end end
Note that this matches only if the path after matching the arguments is empty, not if it still contains a trailing slash:
r.remaining_path # => "/foo/bar/" r.is 'foo/bar' do # does not match, as path isn't fully matched (/ remaining) end r.is 'foo/bar/' do # matches as path is empty after matching end r.on 'foo/bar' do r.is "" do # matches as path is empty after matching end end
Source
# File lib/roda/request.rb, line 206 def is_get? @env["REQUEST_METHOD"] == 'GET' end
Optimized method for whether this request is a GET
request. Similar to the default Rack::Request get? method, but can be overridden without changing rack’s behavior.
Source
# File lib/roda/request.rb, line 246 def matched_path e = @env e["SCRIPT_NAME"] + e["PATH_INFO"].chomp(@remaining_path) end
The already matched part of the path, including the original SCRIPT_NAME.
Source
# File lib/roda/request.rb, line 237 def on(*args, &block) if args.empty? always(&block) else if_match(args, &block) end end
Does a match on the path, matching only if the arguments have matched the path. Because this doesn’t fully match the path, this is usually used to setup branches of the routing tree, not for final handling of the request.
r.remaining_path # => "/foo/bar" r.on 'foo' do # matches, path is /bar after matching end r.on 'bar' do # does not match end
Like other routing methods, If it matches, the match block is executed, and when the match block returns, the rack response is returned. However, in general you will call another routing method inside the match block that fully matches the path and does the final handling for the request:
r.on 'foo' do r.is 'bar' do # handle /foo/bar request end end
Source
# File lib/roda/request.rb, line 257 def path e = @env "#{e["SCRIPT_NAME"]}#{e["PATH_INFO"]}" end
This an an optimized version of Rack::Request#path.
r.env['SCRIPT_NAME'] = '/foo' r.env['PATH_INFO'] = '/bar' r.path # => '/foo/bar'
Source
# File lib/roda/request.rb, line 272 def post(*args, &block) _verb(args, &block) if post? end
Match POST requests. If no arguments are provided, matches all POST requests, otherwise, matches only POST requests where the arguments given fully consume the path.
Source
# File lib/roda/request.rb, line 299 def redirect(path=default_redirect_path, status=default_redirect_status) response.redirect(path, status) throw :halt, response.finish end
Immediately redirect to the path using the status code. This ends the processing of the request:
r.redirect '/page1', 301 if r['param'] == 'value1' r.redirect '/page2' # uses 302 status code response.status = 404 # not reached
If you do not provide a path, by default it will redirect to the same path if the request is not a GET
request. This is designed to make it easy to use where a POST
request to a URL changes state, GET
returns the current state, and you want to show the current state after changing:
r.is "foo" do r.get do # show state end r.post do # change state r.redirect end end
Source
# File lib/roda/request.rb, line 310 def response @scope.response end
The response related to the current request. See ResponseMethods
for instance methods for the response, but in general the most common usage is to override the response status and headers:
response.status = 200 response['Header-Name'] = 'Header value'
Source
# File lib/roda/request.rb, line 315 def roda_class self.class.roda_class end
Return the Roda
class related to this request.
Source
# File lib/roda/request.rb, line 366 def root(&block) if @remaining_path == "/" && is_get? always(&block) end end
Match method that only matches GET
requests where the current path is /
. If it matches, the match block is executed, and when the match block returns, the rack response is returned.
[r.request_method, r.remaining_path] # => ['GET', '/'] r.root do # matches end
This is usuable inside other match blocks:
[r.request_method, r.remaining_path] # => ['GET', '/foo/'] r.on 'foo' do r.root do # matches end end
Note that this does not match non-GET
requests:
[r.request_method, r.remaining_path] # => ['POST', '/'] r.root do # does not match end
Use r.post ""
for POST
requests where the current path is /
.
Nor does it match empty paths:
[r.request_method, r.remaining_path] # => ['GET', '/foo'] r.on 'foo' do r.root do # does not match end end
Use r.get true
to handle GET
requests where the current path is empty.
Source
# File lib/roda/request.rb, line 383 def run(app) e = @env path = real_remaining_path sn = "SCRIPT_NAME" pi = "PATH_INFO" script_name = e[sn] path_info = e[pi] begin e[sn] += path_info.chomp(path) e[pi] = path throw :halt, app.call(e) ensure e[sn] = script_name e[pi] = path_info end end
Call the given rack app with the environment and return the response from the rack app as the response for this request. This ends the processing of the request:
r.run(proc{[403, {}, []]}) unless r['letmein'] == '1' r.run(proc{[404, {}, []]}) response.status = 404 # not reached
This updates SCRIPT_NAME/PATH_INFO based on the current remaining_path
before dispatching to another rack app, so the app still works as a URL mapper.
Source
# File lib/roda/request.rb, line 402 def session @env['rack.session'] || raise(RodaError, "You're missing a session handler, try using the sessions plugin.") end
The session for the current request. Raises a RodaError
if a session handler has not been loaded.
Private Instance Methods
Source
# File lib/roda/request.rb, line 411 def _match_array(matcher) matcher.any? do |m| if matched = match(m) if m.is_a?(String) @captures.push(m) end end matched end end
Match any of the elements in the given array. Return at the first match without evaluating future matches. Returns false if no elements in the array match.
Source
# File lib/roda/request.rb, line 427 def _match_class(klass) meth = :"_match_class_#{klass}" if respond_to?(meth, true) # Allow calling private methods, as match methods are generally private send(meth) else unsupported_matcher(klass) end end
Match the given class. Currently, the following classes are supported by default:
- Integer
-
Match an integer segment, yielding result to block as an integer
- String
-
Match any non-empty segment, yielding result to block as a string
Source
# File lib/roda/request.rb, line 445 def _match_class_Integer consume(/\A\/(\d{1,100})(?=\/|\z)/, :_convert_class_Integer) end
Match integer segment of up to 100 decimal characters, and yield resulting value as an integer.
Match any nonempty segment. This should be called without an argument.
Source
# File lib/roda/request.rb, line 438 def _match_hash(hash) # Allow calling private methods, as match methods are generally private hash.all?{|k,v| send("match_#{k}", v)} end
Match the given hash if all hash matchers match.
Source
# File lib/roda/request.rb, line 451 def _match_regexp(re) consume(self.class.cached_matcher(re){re}) end
Match only if all of the arguments in the given array match. Match the given regexp exactly if it matches a full segment.
Source
# File lib/roda/request.rb, line 458 def _match_string(str) rp = @remaining_path length = str.length match = case rp.rindex(str, length) when nil # segment does not match, most common case return when 1 # segment matches, check first character is / rp.getbyte(0) == 47 else # must be 0 # segment matches at first character, only a match if # empty string given and first character is / length == 0 && rp.getbyte(0) == 47 end if match length += 1 case rp.getbyte(length) when 47 # next character is /, update remaining path to rest of string @remaining_path = rp[length, 100000000] when nil # end of string, so remaining path is empty @remaining_path = "" # else # Any other value means this was partial segment match, # so we return nil in that case without updating the # remaining_path. No need for explicit else clause. end end end
Match the given string to the request path. Matches only if the request path ends with the string or if the next character in the request path is a slash (indicating a new segment).
Source
# File lib/roda/request.rb, line 493 def _match_symbol(sym=nil) rp = @remaining_path if rp.getbyte(0) == 47 if last = rp.index('/', 1) @captures << rp[1, last-1] @remaining_path = rp[last, rp.length] elsif (len = rp.length) > 1 @captures << rp[1, len] @remaining_path = "" end end end
Match the given symbol if any segment matches.
Source
# File lib/roda/request.rb, line 510 def _remaining_path(env) env["PATH_INFO"] end
The base remaining path to use.
Source
# File lib/roda/request.rb, line 516 def _verb(args, &block) if args.empty? always(&block) else args << TERM if_match(args, &block) end end
Backbone of the verb method support, using a terminal match if args is not empty, or a regular match if it is empty.
Source
# File lib/roda/request.rb, line 526 def always block_result(yield) throw :halt, response.finish end
Yield to the match block and return rack response after the block returns.
Source
# File lib/roda/request.rb, line 534 def block_result_body(result) case result when String result when nil, false # nothing else unsupported_block_result(result) end end
The body to use for the response if the response does not already have a body. By default, a String is returned directly, and nil is returned otherwise.
Source
# File lib/roda/request.rb, line 549 def consume(pattern, meth=nil) if matchdata = pattern.match(@remaining_path) captures = matchdata.captures if meth return unless captures = scope.send(meth, *captures) # :nocov: elsif defined?(yield) # RODA4: Remove return unless captures = yield(*captures) # :nocov: end @remaining_path = matchdata.post_match if captures.is_a?(Array) @captures.concat(captures) else @captures << captures end end end
Attempts to match the pattern to the current path. If there is no match, returns false without changes. Otherwise, modifies SCRIPT_NAME to include the matched path, removes the matched path from PATH_INFO, and updates captures with any regex captures.
Source
# File lib/roda/request.rb, line 580 def default_redirect_path raise RodaError, "must provide path argument to redirect for get requests" if is_get? path end
The default path to use for redirects when a path is not given. For non-GET requests, redirects to the current path, which will trigger a GET request. This is to make the common case where a POST request will redirect to a GET request at the same location will work fine.
If the current request is a GET request, raise an error, as otherwise it is easy to create an infinite redirect.
Source
# File lib/roda/request.rb, line 587 def default_redirect_status 302 end
The default status to use for redirects if a status is not provided, 302 by default.
Source
# File lib/roda/request.rb, line 592 def empty_path? @remaining_path.empty? end
Whether the current path is considered empty.
Source
# File lib/roda/request.rb, line 599 def if_match(args) path = @remaining_path # For every block, we make sure to reset captures so that # nesting matchers won't mess with each other's captures. captures = @captures.clear if match_all(args) block_result(yield(*captures)) throw :halt, response.finish else @remaining_path = path false end end
If all of the arguments match, yields to the match block and returns the rack response when the block returns. If any of the match arguments doesn’t match, does nothing.
Source
# File lib/roda/request.rb, line 616 def match(matcher) case matcher when String _match_string(matcher) when Class _match_class(matcher) when TERM empty_path? when Regexp _match_regexp(matcher) when true matcher when Array _match_array(matcher) when Hash _match_hash(matcher) when Symbol _match_symbol(matcher) when false, nil matcher when Proc matcher.call else unsupported_matcher(matcher) end end
Attempt to match the argument to the given request, handling common ruby types.
Source
# File lib/roda/request.rb, line 644 def match_all(args) args.all?{|arg| match(arg)} end
Match only if all of the arguments in the given array match.
Source
# File lib/roda/request.rb, line 650 def match_method(type) if type.is_a?(Array) type.any?{|t| match_method(t)} else type.to_s.upcase == @env["REQUEST_METHOD"] end end
Match by request method. This can be an array if you want to match on multiple methods.
Source
# File lib/roda/request.rb, line 660 def unsupported_block_result(result) raise RodaError, "unsupported block result: #{result.inspect}" end
How to handle block results that are not nil, false, or a String. By default raises an exception.
Source
# File lib/roda/request.rb, line 665 def unsupported_matcher(matcher) raise RodaError, "unsupported matcher: #{matcher.inspect}" end
Handle an unsupported matcher.