module Roda::RodaPlugins::Base::RequestMethods

Instance methods for RodaRequest, mostly related to handling routing for the request.

Constants

EMPTY_STRING
GET_REQUEST_METHOD
PATH_INFO
REQUEST_METHOD
SCRIPT_NAME
SEGMENT
SLASH
TERM
TERM_INSPECT

Attributes

captures[R]

The current captures for the request. This gets modified as routing occurs.

scope[R]

The Roda instance related to this request object. Useful if routing methods need access to the scope of the Roda route block.

Public Class Methods

new(scope, env) click to toggle source

Store the roda instance and environment.

Calls superclass method
# File lib/roda.rb, line 433
def initialize(scope, env)
  @scope = scope
  @captures = []
  super(env)
end

Public Instance Methods

block_result(result) click to toggle source

Handle match block return values. By default, if a string is given and the response is empty, use the string as the response body.

# File lib/roda.rb, line 473
def block_result(result)
  res = response
  if res.empty? && (body = block_result_body(result))
    res.write(body)
  end
end
full_path_info() click to toggle source

As request routing modifies SCRIPT_NAME and PATH_INFO, this exists as a helper method to get the full path of the request.

r.env['SCRIPT_NAME'] = '/foo'
r.env['PATH_INFO'] = '/bar'
r.full_path_info
# => '/foo/bar'
# File lib/roda.rb, line 446
def full_path_info
  "#{@env[SCRIPT_NAME]}#{@env[PATH_INFO]}"
end
halt(res=response.finish) click to toggle source

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
# File lib/roda.rb, line 460
def halt(res=response.finish)
  throw :halt, res
end
inspect() click to toggle source

Show information about current request, including request class, request method and full path.

r.inspect
# => '#<Roda::RodaRequest GET /foo/bar>'
# File lib/roda.rb, line 485
def inspect
  "#<#{self.class.inspect} #{@env[REQUEST_METHOD]} #{full_path_info}>"
end
is(*args, &block) click to toggle source

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.path_info
# => "/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.path_info
# =>  "/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
# File lib/roda.rb, line 532
def is(*args, &block)
  if args.empty?
    if @env[PATH_INFO] == EMPTY_STRING
      always(&block)
    end
  else
    args << TERM
    if_match(args, &block)
  end
end
is_get?() click to toggle source

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.

# File lib/roda.rb, line 467
def is_get?
  @env[REQUEST_METHOD] == GET_REQUEST_METHOD
end
on(*args, &block) click to toggle source

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.path_info
# => "/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
# File lib/roda.rb, line 570
def on(*args, &block)
  if args.empty?
    always(&block)
  else
    if_match(args, &block)
  end
end
redirect(path=default_redirect_path, status=302) click to toggle source

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
# File lib/roda.rb, line 611
def redirect(path=default_redirect_path, status=302)
  response.redirect(path, status)
  throw :halt, response.finish
end
response() click to toggle source

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'
# File lib/roda.rb, line 584
def response
  scope.response
end
root(&block) click to toggle source

Routing matches 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.path_info]
# => ['GET', '/']

r.root do
  # matches
end

This is usuable inside other match blocks:

[r.request_method, r.path_info]
# => ['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.path_info]
# => ['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.path_info]
# => ['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.

# File lib/roda.rb, line 663
def root(&block)
  if @env[PATH_INFO] == SLASH && is_get?
    always(&block)
  end
end
run(app) click to toggle source

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
# File lib/roda.rb, line 676
def run(app)
  throw :halt, app.call(@env)
end

Private Instance Methods

_match_array(matcher) click to toggle source

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.

# File lib/roda.rb, line 685
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_hash(hash) click to toggle source

Match the given hash if all hash matchers match.

# File lib/roda.rb, line 703
def _match_hash(hash)
  hash.all?{|k,v| send("match_#{k}", v)}
end
_match_regexp(re) click to toggle source

Match the given regexp exactly if it matches a full segment.

# File lib/roda.rb, line 698
def _match_regexp(re)
  consume(self.class.cached_matcher(re){re})
end
_match_string(str) click to toggle source

Match the given string to the request path. Regexp escapes the string so that regexp metacharacters are not matched, and recognizes colon tokens for placeholders.

# File lib/roda.rb, line 710
def _match_string(str)
  consume(self.class.cached_matcher(str){Regexp.escape(str).gsub(/:(\w+)/){|m| _match_symbol_regexp($1)}})
end
_match_symbol(sym) click to toggle source

Match the given symbol if any segment matches.

# File lib/roda.rb, line 715
def _match_symbol(sym)
  consume(self.class.cached_matcher(sym){_match_symbol_regexp(sym)})
end
_match_symbol_regexp(s) click to toggle source

The regular expression to use for matching symbols. By default, any non-empty segment matches.

# File lib/roda.rb, line 721
def _match_symbol_regexp(s)
  SEGMENT
end
_verb(args, &block) click to toggle source

Backbone of the verb method support, using a terminal match if args is not empty, or a regular match if it is empty.

# File lib/roda.rb, line 727
def _verb(args, &block)
  if args.empty?
    always(&block)
  else
    args << TERM
    if_match(args, &block)
  end
end
always() { || ... } click to toggle source

Yield to the match block and return rack response after the block returns.

# File lib/roda.rb, line 737
def always
  block_result(yield)
  throw :halt, response.finish
end
block_result_body(result) click to toggle source

The body to use for the response if the response does not return a body. By default, a String is returned directly, and nil is returned otherwise.

# File lib/roda.rb, line 745
def block_result_body(result)
  if result.is_a?(String)
    result
  end
end
consume(pattern) click to toggle source

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.

# File lib/roda.rb, line 755
def consume(pattern)
  env = @env
  return unless matchdata = env[PATH_INFO].match(pattern)

  vars = matchdata.captures

  # Don't mutate SCRIPT_NAME, breaks try
  env[SCRIPT_NAME] += vars.shift
  env[PATH_INFO] = matchdata.post_match

  captures.concat(vars)
end
default_redirect_path() click to toggle source

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.

# File lib/roda.rb, line 776
def default_redirect_path
  raise RodaError, "must provide path argument to redirect for get requests" if is_get?
  full_path_info
end
if_match(args) { |*captures| ... } click to toggle source

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.

# File lib/roda.rb, line 784
def if_match(args)
  env = @env
  script = env[SCRIPT_NAME]
  path = env[PATH_INFO]

  # For every block, we make sure to reset captures so that
  # nesting matchers won't mess with each other's captures.
  captures.clear

  return unless match_all(args)
  block_result(yield(*captures))
  throw :halt, response.finish
ensure
  env[SCRIPT_NAME] = script
  env[PATH_INFO] = path
end
match(matcher) click to toggle source

Attempt to match the argument to the given request, handling common ruby types.

# File lib/roda.rb, line 803
def match(matcher)
  case matcher
  when String
    _match_string(matcher)
  when Regexp
    _match_regexp(matcher)
  when Symbol
    _match_symbol(matcher)
  when TERM
    @env[PATH_INFO] == EMPTY_STRING
  when Hash
    _match_hash(matcher)
  when Array
    _match_array(matcher)
  when Proc
    matcher.call
  else
    matcher
  end
end
match_all(args) click to toggle source

Match only if all of the arguments in the given array match.

# File lib/roda.rb, line 825
def match_all(args)
  args.all?{|arg| match(arg)}
end
match_extension(ext) click to toggle source

Match files with the given extension. Requires that the request path end with the extension.

# File lib/roda.rb, line 831
def match_extension(ext)
  consume(self.class.cached_matcher([:extension, ext]){/([^\\\/]+)\.#{ext}/})
end
match_method(type) click to toggle source

Match by request method. This can be an array if you want to match on multiple methods.

# File lib/roda.rb, line 837
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_param(key) click to toggle source

Match the given parameter if present, even if the parameter is empty. Adds any match to the captures.

# File lib/roda.rb, line 847
def match_param(key)
  if v = self[key]
    captures << v
  end
end
match_param!(key) click to toggle source

Match the given parameter if present and not empty. Adds any match to the captures.

# File lib/roda.rb, line 855
def match_param!(key)
  if (v = self[key]) && !v.empty?
    captures << v
  end
end