module Sinatra::Rroute

Constants

VERSION

Private Class Methods

registered(app) click to toggle source
# File lib/sinatra/rroute.rb, line 4
def self.registered(app)
  app.set :app_paths, {}
  app.set :app_prefixes, []
  app.helpers do
    # @!visibility public
    #
    # Return a path for a given route mask.
    #
    # @param [String] name The name of a route for which a mask has
    #   been specified.
    # @param [Hash{Symbol=>Object}] options Hash of Symbol tokens to be
    #   replaced by the associated value. The token (symbol) has to
    #   have the exact name specified in the mask for this route.
    #
    # @example
    #
    #   # The mask for this route has been defined like this:
    #   #   gget '/[uU]ser/:name/:age/?' => :user_info, :as =>
    #   #     user, :mask => '/user/:name/:age/'
    #   path :user, :name => 'John', :age => 32
    #   # => '/user/John/32/'
    #
    #
    # @return [String] Path for the specified route. Every token for
    #   this route is replaced by the specified value in `options'.
    def path(name, *options)
      keywords = options[0]
      # Take the last string as path mask.
      # (Important: Make a duplicate. Do not reference the original
      # mask/regex in `settings.app_paths'!)
      path = settings.app_paths[name.to_sym][:mask].dup ||
        settings.app_paths[name.to_sym][:regex].dup
      if keywords != nil
        keywords.each do |keyword, value|
          path.gsub! /:#{keyword.to_s}/, value.to_s
        end
      end
      path
    end

    # @visibility private
    #
    # Take the string representation of a nested instance method or of
    # a class method and call the method.
    #
    # Notes:
    #
    # - The name is chosen to be long and unique enough to not be
    #   accidentially overwritten
    # - Arguments are not supported as the methods to use this function
    #   on are controllers.
    # - Unlike the `:to' option of Rails' `match' method (used by
    #   `get'/`post'/...), strings are case-sensitive.
    # - Unlike the `:to' option of Rails' `match' method
    #   methods can be arbitrarily deeply nested.
    #
    # @param [String] string String representation of a method
    #
    # @example
    #
    #   # Instance method
    #   rroute_call_nested_controller_from_string(
    #     'Very::Deeply::Nested#controller')
    #
    #   # Class method
    #   rroute_call_nested_controller_from_string(
    #     'Very::Deeply::Nested::controller')
    #
    # @return [nil] Nothing.
    def rroute_call_nested_controller_from_string(string)
      method_sep = '#'
      nesting_sep = '::'
      if string =~ %r{#{method_sep}}
        tokens = string.split(method_sep)
        const = Kernel.const_get(tokens[0])
        method = tokens[-1]
        const.new.send(method.to_sym)
      else
        tokens = string.split(nesting_sep)
        const = Kernel.const_get(tokens[0..-2].join(nesting_sep))
        method = tokens[-1]
        const.send(method.to_sym)
      end
    end
  end
end

Public Instance Methods

generate_paths() click to toggle source

@!visibility public

Generate all route mappings from the paths in `settings.app_paths'.

@return [nil] Nothing.

# File lib/sinatra/rroute.rb, line 437
def generate_paths
  settings.app_paths.each do |name, value|
    if value[:controller] != nil
      # Paths merged using `paths' should be allowed to to be specified
      # as strings. So they have to be converted here.
      if value[:regex].class != Regexp
        value[:regex] = Regexp.new(value[:regex])
      end
      # Adapt the RegEx representation.
      value[:regex] = value[:regex].source
      send(value[:http_method],
           value[:regex]) do
        if value[:controller].class == Symbol
          send value[:controller]
        else
          rroute_call_nested_controller_from_string(value[:controller]
                                                      .to_s)
        end
      end
    end
  end
end
mmap(regex, mask, name) click to toggle source

@!visibility public

Map a route RegEx to a controller method with a name and a mask for path generation.

Since this method is meant to be used together with Sinatra's built-in `get'/`post'/`patch'/… mapper methods, it does not to map the route to a controller function since this would bypass Sinatra's built-in mapper method.

@param [String] regex Route RegEx. @param [String] mask Mask for route generation. @name [Symbol] name Name to reference this route for route generation

etc.

@example

get mmap('/[Uu]ser/:name/:age/?', '/user/:name/:age/', :user) do
  # ...
end

@return [nil] Nothing.

# File lib/sinatra/rroute.rb, line 414
def mmap(regex, mask, name)
  options = {}
  options[:regex] = regex
  options[:mask] = mask
  options[:as] = name
  options[:controller] = nil
  ppath({regex => nil}, nil, options)

  # Prepend the prefix to the returned RegExp.
  mapping_regex = regex == Regexp ? regex : Regexp.new(regex)
  prefix = settings.app_prefixes.empty? ? '' :
    settings.app_prefixes.join
  output = %r{#{prefix}#{mapping_regex.source}}.source ||
    Regexp.new('')
  # Return the right type of object depending on the input.
  regex.class == Regexp ? Regexp.new(output) : output
end
nnamespace(prefix, &block) click to toggle source

@!visibility public

Namespace a all routes defined within the given block using the mapping methods `gget'/`ppost'/`ppatch'/…

Sinatra's built-in mapping methods `get'/`post'/`patch' are NOT affected by the namespacing.

@param [String] prefix Namespace to be used. HAS to include leading

and/or trailing slashes!

@yield [] Block containing route mapping method calls

(`gget'/`ppost'/`ppatch'/...) and/or other relevant application
code. Can also include calls to Sinatra's built-in mapping methods
(`get'/`post'/`patch'/...) which will not be affected by the
namespacing.

@return [nil] Nothing.

# File lib/sinatra/rroute.rb, line 477
def nnamespace(prefix, &block)
  settings.app_prefixes << prefix
  block.call
  settings.app_prefixes.pop
end
paths(paths) click to toggle source

@!visibility public

Merge paths into `settings.app_paths' which holds all paths defined for the app.

@param [Hash{Symbol=>Hash}] paths Paths to be merged into

`settings.app_paths'. The hash has to have the structure outlined
below.

@example

paths({:route =>
        {:http_method =>:get,
          :regex =>"/route/:name/?",
          :controller =>nil,
          :mask =>"/route/:name"},
        :color =>
        {:http_method =>:get,
          :regex =>"/api/new/color/:name/:value/?",
          :controller =>:show_color,
          :mask =>"/api/new/color/:name/:value"},
        :blue =>
        {:http_method =>:post,
          :regex =>"/important/api/blue/:name/:value/?",
          :controller =>:post_blue,
          :mask =>"/important/api/blue/:name/:value"},
        :something =>
        {:http_method =>:get,
          :regex =>"/something/:name/:value/?",
          :controller =>:api_something,
          :mask =>"/something/:name/:value"}})

@return [nil] Nothing.

# File lib/sinatra/rroute.rb, line 126
def paths(paths)
  settings.app_paths.merge! paths
  generate_paths
end
ppath(mapping, http_method, *options) click to toggle source

@!visibility public

Define a route mapping a path to a controller with a HTTP method, a name and a mask for route generation.

@param [Hash{String=>Symbol}] mapping Mapping of a route RegEx to a

controller function.

@param [Symbol] http_method The HTTP method used for this route

(`:get', `post', `patch', ...)

@param [Hash] options Optional: Options for this mapping. Allowed

values: `:as => :route_name', `:mask => "/mask/for/route/:value/"'

@example

ppath({'/[Pp]ost?/info/:id/?' => :post_info}, :get, :as =>
  :post, :mask => '/post/:info/')

@return [nil] Nothing.

# File lib/sinatra/rroute.rb, line 150
    def ppath(mapping, http_method, *options)
      options = options[0]
      prefix = settings.app_prefixes.empty? ? '' :
        settings.app_prefixes.join
      controller = nil
      if mapping.values[0] != nil
        # controller = mapping.values[0].to_sym
        controller = mapping.values[0]
      end
      if mapping.keys[0].class == Regexp
        mapping_regex = mapping.keys[0]
      else
        mapping_regex = Regexp.new(mapping.keys[0])
      end
      settings.app_paths.merge!({options[:as].to_sym => {
                                    :http_method => http_method || :get,
                                    :regex => %r{#{prefix}\
#{mapping_regex.source}} || Regexp.new(''),
                                    :controller => controller,
                                    :mask => prefix + options[:mask]}}) ||
        ''
      generate_paths
    end

Private Instance Methods

path(name, *options) click to toggle source

@!visibility public

Return a path for a given route mask.

@param [String] name The name of a route for which a mask has

been specified.

@param [Hash{Symbol=>Object}] options Hash of Symbol tokens to be

replaced by the associated value. The token (symbol) has to
have the exact name specified in the mask for this route.

@example

# The mask for this route has been defined like this:
#   gget '/[uU]ser/:name/:age/?' => :user_info, :as =>
#     user, :mask => '/user/:name/:age/'
path :user, :name => 'John', :age => 32
# => '/user/John/32/'

@return [String] Path for the specified route. Every token for

this route is replaced by the specified value in `options'.
# File lib/sinatra/rroute.rb, line 29
def path(name, *options)
  keywords = options[0]
  # Take the last string as path mask.
  # (Important: Make a duplicate. Do not reference the original
  # mask/regex in `settings.app_paths'!)
  path = settings.app_paths[name.to_sym][:mask].dup ||
    settings.app_paths[name.to_sym][:regex].dup
  if keywords != nil
    keywords.each do |keyword, value|
      path.gsub! /:#{keyword.to_s}/, value.to_s
    end
  end
  path
end
rroute_call_nested_controller_from_string(string) click to toggle source

@visibility private

Take the string representation of a nested instance method or of a class method and call the method.

Notes:

  • The name is chosen to be long and unique enough to not be accidentially overwritten

  • Arguments are not supported as the methods to use this function on are controllers.

  • Unlike the `:to' option of Rails' `match' method (used by `get'/`post'/…), strings are case-sensitive.

  • Unlike the `:to' option of Rails' `match' method methods can be arbitrarily deeply nested.

@param [String] string String representation of a method

@example

# Instance method
rroute_call_nested_controller_from_string(
  'Very::Deeply::Nested#controller')

# Class method
rroute_call_nested_controller_from_string(
  'Very::Deeply::Nested::controller')

@return [nil] Nothing.

# File lib/sinatra/rroute.rb, line 73
def rroute_call_nested_controller_from_string(string)
  method_sep = '#'
  nesting_sep = '::'
  if string =~ %r{#{method_sep}}
    tokens = string.split(method_sep)
    const = Kernel.const_get(tokens[0])
    method = tokens[-1]
    const.new.send(method.to_sym)
  else
    tokens = string.split(nesting_sep)
    const = Kernel.const_get(tokens[0..-2].join(nesting_sep))
    method = tokens[-1]
    const.send(method.to_sym)
  end
end