lib/hanami/router.rb in hanami-router-1.3.2 vs lib/hanami/router.rb in hanami-router-2.0.0.alpha1
- old
+ new
@@ -1,12 +1,12 @@
-require 'rack/request'
-require 'hanami/routing/http_router'
-require 'hanami/routing/namespace'
-require 'hanami/routing/resource'
-require 'hanami/routing/resources'
-require 'hanami/routing/error'
+# frozen_string_literal: true
+require "rack/request"
+require "dry/inflector"
+require "hanami/routing"
+require "hanami/utils/hash"
+
# Hanami
#
# @since 0.1.0
module Hanami
# Rack compatible, lightweight and fast HTTP Router.
@@ -73,11 +73,16 @@
# router = Hanami::Router.new do
# mount Api::App, at: '/api'
# end
#
# # All the requests starting with "/api" will be forwarded to Api::App
- class Router
+ #
+ class Router # rubocop:disable Metrics/ClassLength
+ # @since 2.0.0
+ # @api private
+ attr_reader :inflector
+
# This error is raised when <tt>#call</tt> is invoked on a non-routable
# recognized route.
#
# @since 0.5.0
#
@@ -86,29 +91,29 @@
# @see Hanami::Routing::RecognizedRoute#call
# @see Hanami::Routing::RecognizedRoute#routable?
class NotRoutableEndpointError < Hanami::Routing::Error
# @since 0.5.0
# @api private
- REQUEST_METHOD = 'REQUEST_METHOD'.freeze
+ REQUEST_METHOD = "REQUEST_METHOD"
# @since 0.5.0
# @api private
- PATH_INFO = 'PATH_INFO'.freeze
+ PATH_INFO = "PATH_INFO"
# @since 0.5.0
def initialize(env)
- super %(Cannot find routable endpoint for #{ env[REQUEST_METHOD] } "#{ env[PATH_INFO] }")
+ super %(Cannot find routable endpoint for #{env[REQUEST_METHOD]} "#{env[PATH_INFO]}")
end
end
# Defines root path
#
# @since 0.7.0
# @api private
#
# @see Hanami::Router#root
- ROOT_PATH = '/'.freeze
+ ROOT_PATH = "/"
# Returns the given block as it is.
#
# When Hanami::Router is used as a standalone gem and the routes are defined
# into a configuration file, some systems could raise an exception.
@@ -152,12 +157,12 @@
# the route resolver (defaults to `Hanami::Routing::EndpointResolver.new`)
# @option options [Object, #generate] :route the route class
# (defaults to `Hanami::Routing::Route`)
# @option options [String] :action_separator the separator between controller
# and action name (eg. 'dashboard#show', where '#' is the :action_separator)
- # @option options [Array<Symbol,String,Object #mime_types, parse>] :parsers
- # the body parsers for mime types
+ # @option options [Object, #pluralize, #singularize] :inflector
+ # the inflector class (defaults to `Dry::Inflector.new`)
#
# @param blk [Proc] the optional block to define the routes
#
# @return [Hanami::Router] self
#
@@ -176,55 +181,57 @@
# router = Hanami::Router.new do
# get '/', to: endpoint
# end
#
# @example Body parsers
- # require 'json'
- # require 'hanami/router'
#
- # # It parses JSON body and makes the attributes available to the params
+ # require 'hanami/router'
+ # require 'hanami/middleware/body_parser'
#
- # endpoint = ->(env) { [200, {},[env['router.params'].inspect]] }
+ # app = Hanami::Router.new do
+ # patch '/books/:id', to: ->(env) { [200, {},[env['router.params'].inspect]] }
+ # end
#
- # router = Hanami::Router.new(parsers: [:json]) do
- # patch '/books/:id', to: endpoint
- # end
+ # use Hanami::Middleware::BodyParser, :json
+ # run app
#
- # # From the shell
+ # # From the shell
#
- # curl http://localhost:2300/books/1 \
- # -H "Content-Type: application/json" \
- # -H "Accept: application/json" \
- # -d '{"published":"true"}' \
- # -X PATCH
+ # curl http://localhost:2300/books/1 \
+ # -H "Content-Type: application/json" \
+ # -H "Accept: application/json" \
+ # -d '{"published":"true"}' \
+ # -X PATCH
#
# # It returns
#
# [200, {}, ["{:published=>\"true\",:id=>\"1\"}"]]
#
# @example Custom body parser
- # require 'hanami/router'
#
- # class XmlParser
- # def mime_types
- # ['application/xml', 'text/xml']
- # end
+ # require 'hanami/router'
+ # require 'hanami/middleware/body_parser'
#
- # # Parse body and return a Hash
- # def parse(body)
- # # ...
- # end
- # end
#
- # # It parses XML body and makes the attributes available to the params
+ # class XmlParser < Hanami::Middleware::BodyParser::Parser
+ # def mime_types
+ # ['application/xml', 'text/xml']
+ # end
#
- # endpoint = ->(env) { [200, {},[env['router.params'].inspect]] }
+ # # Parse body and return a Hash
+ # def parse(body)
+ # # parse xml
+ # end
+ # end
#
- # router = Hanami::Router.new(parsers: [XmlParser.new]) do
- # patch '/authors/:id', to: endpoint
+ # app = Hanami::Router.new do
+ # patch '/authors/:id', to: ->(env) { [200, {},[env['router.params'].inspect]] }
# end
#
+ # use Hanami::Middleware::BodyParser, XmlParser
+ # run app
+ #
# # From the shell
#
# curl http://localhost:2300/authors/1 \
# -H "Content-Type: application/xml" \
# -H "Accept: application/xml" \
@@ -232,15 +239,35 @@
# -X PATCH
#
# # It returns
#
# [200, {}, ["{:name=>\"LG\",:id=>\"1\"}"]]
- def initialize(options = {}, &blk)
- @router = Routing::HttpRouter.new(options)
- define(&blk)
+ #
+ # rubocop:disable Metrics/MethodLength
+ def initialize(scheme: "http", host: "localhost", port: 80, prefix: "", namespace: nil, configuration: nil, inflector: Dry::Inflector.new, not_found: NOT_FOUND, not_allowed: NOT_ALLOWED, &blk)
+ @routes = []
+ @named = {}
+ @namespace = namespace
+ @configuration = configuration
+ @base = Routing::Uri.build(scheme: scheme, host: host, port: port)
+ @prefix = Utils::PathPrefix.new(prefix)
+ @inflector = inflector
+ @not_found = not_found
+ @not_allowed = not_allowed
+ instance_eval(&blk) unless blk.nil?
+ freeze
end
+ # rubocop:enable Metrics/MethodLength
+ # Freeze the router
+ #
+ # @since 2.0.0
+ def freeze
+ @routes.freeze
+ super
+ end
+
# Returns self
#
# This is a duck-typing trick for compatibility with `Hanami::Application`.
# It's used by `Hanami::Routing::RoutesInspector` to inspect both apps and
# routers.
@@ -251,34 +278,10 @@
# @api private
def routes
self
end
- # To support defining routes in the `define` wrapper.
- #
- # @param blk [Proc] the block to define the routes
- #
- # @return [Hanami::Routing::Route]
- #
- # @since 0.2.0
- #
- # @example In Hanami framework
- # class Application < Hanami::Application
- # configure do
- # routes 'config/routes'
- # end
- # end
- #
- # # In `config/routes`
- #
- # define do
- # get # ...
- # end
- def define(&blk)
- instance_eval(&blk) if block_given?
- end
-
# Check if there are defined routes
#
# @return [TrueClass,FalseClass] the result of the check
#
# @since 0.2.0
@@ -290,11 +293,11 @@
# router.defined? # => false
#
# router = Hanami::Router.new { get '/', to: ->(env) { } }
# router.defined? # => true
def defined?
- @router.routes.any?
+ @routes.any?
end
# Defines a route that accepts a GET request for the given path.
#
# @param path [String] the relative URL to be matched
@@ -407,12 +410,12 @@
# router = Hanami::Router.new
# router.get '/flowers', to: 'flowers#index'
#
# # It will map to Flowers::Index.new, which is the
# # Hanami::Controller convention.
- def get(path, options = {}, &blk)
- @router.get(path, options, &blk)
+ def get(path, to: nil, as: nil, namespace: nil, configuration: nil, **constraints, &blk)
+ add_route(GET, path, to, as, namespace, configuration, constraints, &blk)
end
# Defines a route that accepts a POST request for the given path.
#
# @param path [String] the relative URL to be matched
@@ -426,12 +429,12 @@
# option passed to the constructor
#
# @see Hanami::Router#get
#
# @since 0.1.0
- def post(path, options = {}, &blk)
- @router.post(path, options, &blk)
+ def post(path, to: nil, as: nil, namespace: nil, configuration: nil, **constraints, &blk)
+ add_route(POST, path, to, as, namespace, configuration, constraints, &blk)
end
# Defines a route that accepts a PUT request for the given path.
#
# @param path [String] the relative URL to be matched
@@ -445,12 +448,12 @@
# option passed to the constructor
#
# @see Hanami::Router#get
#
# @since 0.1.0
- def put(path, options = {}, &blk)
- @router.put(path, options, &blk)
+ def put(path, to: nil, as: nil, namespace: nil, configuration: nil, **constraints, &blk)
+ add_route(PUT, path, to, as, namespace, configuration, constraints, &blk)
end
# Defines a route that accepts a PATCH request for the given path.
#
# @param path [String] the relative URL to be matched
@@ -464,12 +467,12 @@
# option passed to the constructor
#
# @see Hanami::Router#get
#
# @since 0.1.0
- def patch(path, options = {}, &blk)
- @router.patch(path, options, &blk)
+ def patch(path, to: nil, as: nil, namespace: nil, configuration: nil, **constraints, &blk)
+ add_route(PATCH, path, to, as, namespace, configuration, constraints, &blk)
end
# Defines a route that accepts a DELETE request for the given path.
#
# @param path [String] the relative URL to be matched
@@ -483,12 +486,12 @@
# option passed to the constructor
#
# @see Hanami::Router#get
#
# @since 0.1.0
- def delete(path, options = {}, &blk)
- @router.delete(path, options, &blk)
+ def delete(path, to: nil, as: nil, namespace: nil, configuration: nil, **constraints, &blk)
+ add_route(DELETE, path, to, as, namespace, configuration, constraints, &blk)
end
# Defines a route that accepts a TRACE request for the given path.
#
# @param path [String] the relative URL to be matched
@@ -502,12 +505,12 @@
# option passed to the constructor
#
# @see Hanami::Router#get
#
# @since 0.1.0
- def trace(path, options = {}, &blk)
- @router.trace(path, options, &blk)
+ def trace(path, to: nil, as: nil, namespace: nil, configuration: nil, **constraints, &blk)
+ add_route(TRACE, path, to, as, namespace, configuration, constraints, &blk)
end
# Defines a route that accepts a LINK request for the given path.
#
# @param path [String] the relative URL to be matched
@@ -521,12 +524,12 @@
# option passed to the constructor
#
# @see Hanami::Router#get
#
# @since 0.8.0
- def link(path, options = {}, &blk)
- @router.link(path, options, &blk)
+ def link(path, to: nil, as: nil, namespace: nil, configuration: nil, **constraints, &blk)
+ add_route(LINK, path, to, as, namespace, configuration, constraints, &blk)
end
# Defines a route that accepts an UNLINK request for the given path.
#
# @param path [String] the relative URL to be matched
@@ -540,14 +543,33 @@
# option passed to the constructor
#
# @see Hanami::Router#get
#
# @since 0.8.0
- def unlink(path, options = {}, &blk)
- @router.unlink(path, options, &blk)
+ def unlink(path, to: nil, as: nil, namespace: nil, configuration: nil, **constraints, &blk)
+ add_route(UNLINK, path, to, as, namespace, configuration, constraints, &blk)
end
+ # Defines a route that accepts a OPTIONS request for the given path.
+ #
+ # @param path [String] the relative URL to be matched
+ #
+ # @param options [Hash] the options to customize the route
+ # @option options [String,Proc,Class,Object#call] :to the endpoint
+ #
+ # @param blk [Proc] the anonymous proc to be used as endpoint for the route
+ #
+ # @return [Hanami::Routing::Route] this may vary according to the :route
+ # option passed to the constructor
+ #
+ # @see Hanami::Router#get
+ #
+ # @since 0.1.0
+ def options(path, to: nil, as: nil, namespace: nil, configuration: nil, **constraints, &blk)
+ add_route(OPTIONS, path, to, as, namespace, configuration, constraints, &blk)
+ end
+
# Defines a root route (a GET route for '/')
#
# @param options [Hash] the options to customize the route
# @option options [String,Proc,Class,Object#call] :to the endpoint
#
@@ -570,33 +592,14 @@
# router = Hanami::Router.new(scheme: 'https', host: 'hanamirb.org')
# router.root to: ->(env) { [200, {}, ['Hello from Hanami!']] }
#
# router.path(:root) # => "/"
# router.url(:root) # => "https://hanamirb.org/"
- def root(options = {}, &blk)
- @router.get(ROOT_PATH, options.merge(as: :root), &blk)
+ def root(to: nil, as: :root, prefix: Utils::PathPrefix.new, namespace: nil, configuration: nil, &blk)
+ add_route(GET, prefix.join(ROOT_PATH), to, as, namespace, configuration, &blk)
end
- # Defines a route that accepts a OPTIONS request for the given path.
- #
- # @param path [String] the relative URL to be matched
- #
- # @param options [Hash] the options to customize the route
- # @option options [String,Proc,Class,Object#call] :to the endpoint
- #
- # @param blk [Proc] the anonymous proc to be used as endpoint for the route
- #
- # @return [Hanami::Routing::Route] this may vary according to the :route
- # option passed to the constructor
- #
- # @see Hanami::Router#get
- #
- # @since 0.1.0
- def options(path, options = {}, &blk)
- @router.options(path, options, &blk)
- end
-
# Defines an HTTP redirect
#
# @param path [String] the path that needs to be redirected
# @param options [Hash] the options to customize the redirect behavior
# @option options [Fixnum] the HTTP status to return (defaults to `301`)
@@ -619,61 +622,79 @@
# @example
# require 'hanami/router'
#
# router = Hanami::Router.new
# router.redirect '/legacy', to: '/new_endpoint'
- def redirect(path, options = {}, &endpoint)
- destination_path = @router.find(options)
- get(path).redirect(destination_path, options[:code] || 301).tap do |route|
- route.dest = Hanami::Routing::RedirectEndpoint.new(destination_path, route.dest)
- end
+ def redirect(path, to:, code: 301)
+ to = Routing::Redirect.new(@prefix.join(to).to_s, code)
+ add_route(GET, path, to)
end
- # Defines a Ruby block: all the routes defined within it will be namespaced
+ # Defines a Ruby block: all the routes defined within it will be prefixed
# with the given relative path.
#
- # Namespaces blocks can be nested multiple times.
+ # Prefix blocks can be nested multiple times.
#
- # @param namespace [String] the relative path where the nested routes will
+ # @param path [String] the relative path where the nested routes will
# be mounted
# @param blk [Proc] the block that defines the resources
#
- # @return [Hanami::Routing::Namespace] the generated namespace.
+ # @return [void]
#
- # @since 0.1.0
+ # @since 2.0.0
#
# @see Hanami::Router
#
# @example Basic example
- # require 'hanami/router'
+ # require "hanami/router"
#
# Hanami::Router.new do
- # namespace 'trees' do
- # get '/sequoia', to: endpoint # => '/trees/sequoia'
+ # prefix "trees" do
+ # get "/sequoia", to: endpoint # => "/trees/sequoia"
# end
# end
#
- # @example Nested namespaces
- # require 'hanami/router'
+ # @example Nested prefix
+ # require "hanami/router"
#
# Hanami::Router.new do
- # namespace 'animals' do
- # namespace 'mammals' do
- # get '/cats', to: endpoint # => '/animals/mammals/cats'
+ # prefix "animals" do
+ # prefix "mammals" do
+ # get "/cats", to: endpoint # => "/animals/mammals/cats"
# end
# end
# end
+ def prefix(path, namespace: nil, configuration: nil, &blk)
+ Routing::Prefix.new(self, path, namespace, configuration, &blk)
+ end
+
+ # Defines a scope for routes.
#
+ # A scope is a combination of a path prefix and a Ruby namespace.
+ #
+ # @param prefix [String] the path prefix
+ # @param namespace [Module] the Ruby namespace where to lookup endpoints
+ # @param configuration [Hanami::Controller::Configuration] the action
+ # configuration
+ # @param blk [Proc] the routes definition block
+ #
+ # @since 2.0.0
+ # @api private
+ #
# @example
- # require 'hanami/router'
+ # require "hanami/router"
+ # require "hanami/controller"
#
- # router = Hanami::Router.new
- # router.namespace 'trees' do
- # get '/sequoia', to: endpoint # => '/trees/sequoia'
+ # configuration = Hanami::Controller::Configuration.new
+ #
+ # Hanami::Router.new do
+ # scope "/admin", namespace: Admin::Controllers, configuration: configuration do
+ # root to: "home#index"
+ # end
# end
- def namespace(namespace, &blk)
- Routing::Namespace.new(self, namespace, &blk)
+ def scope(prefix, namespace:, configuration:, &blk)
+ Routing::Scope.new(self, prefix, namespace, configuration, &blk)
end
# Defines a set of named routes for a single RESTful resource.
# It has a built-in integration for Hanami::Controller.
#
@@ -791,11 +812,11 @@
# # | Verb | Path | Action | Name | Named Route |
# # +------+----------------+----------------+------+----------------+
# # | GET | /identity/keys | Identity::Keys | | :keys_identity |
# # +------+----------------+----------------+------+----------------+
def resource(name, options = {}, &blk)
- Routing::Resource.new(self, name, options.merge(separator: @router.action_separator), &blk)
+ Routing::Resource.new(self, name, options.merge(separator: Routing::Endpoint::ACTION_SEPARATOR), &blk)
end
# Defines a set of named routes for a plural RESTful resource.
# It has a built-in integration for Hanami::Controller.
#
@@ -914,11 +935,11 @@
# # | Verb | Path | Action | Name | Named Route |
# # +------+------------------+------------------+------+------------------+
# # | GET | /articles/search | Articles::Search | | :search_articles |
# # +------+------------------+------------------+------+------------------+
def resources(name, options = {}, &blk)
- Routing::Resources.new(self, name, options.merge(separator: @router.action_separator), &blk)
+ Routing::Resources.new(self, name, options.merge(separator: Routing::Endpoint::ACTION_SEPARATOR), &blk)
end
# Mount a Rack application at the specified path.
# All the requests starting with the specified path, will be forwarded to
# the given application.
@@ -1002,25 +1023,34 @@
# # 1. RackOne is used as it is (class), because it respond to .call
# # 2. RackTwo is initialized, because it respond to #call
# # 3. RackThree is used as it is (object), because it respond to #call
# # 4. That Proc is used as it is, because it respond to #call
# # 5. That string is resolved as Dashboard::Index (Hanami::Controller)
- def mount(app, options)
- @router.mount(app, options)
+ def mount(app, at:, host: nil)
+ app = App.new(@prefix.join(at).to_s, Routing::Endpoint.find(app, @namespace), host: host)
+ @routes.push(app)
end
# Resolve the given Rack env to a registered endpoint and invoke it.
#
# @param env [Hash] a Rack env instance
#
# @return [Rack::Response, Array]
#
# @since 0.1.0
def call(env)
- @router.call(env)
+ (@routes.find { |r| r.match?(env) } || fallback(env)).call(env)
end
+ def fallback(env)
+ if @routes.find { |r| r.match_path?(env) }
+ @not_allowed
+ else
+ @not_found
+ end
+ end
+
# Recognize the given env, path, or name and return a route for testing
# inspection.
#
# If the route cannot be recognized, it still returns an object for testing
# inspection.
@@ -1126,18 +1156,15 @@
# route = router.recognize(:book, {method: :post}, {id: 1})
# route.verb # => "POST"
# route.routable? # => false
# route.params # => {:id=>"1"}
def recognize(env, options = {}, params = nil)
- require 'hanami/routing/recognized_route'
+ env = env_for(env, options, params)
+ # FIXME: this finder is shared with #call and should be extracted
+ route = @routes.find { |r| r.match?(env) }
- env = env_for(env, options, params)
- responses, _ = *@router.recognize(env)
-
- Routing::RecognizedRoute.new(
- responses.nil? ? responses : responses.first,
- env, @router)
+ Routing::RecognizedRoute.new(route, env, @namespace)
end
# Generate an relative URL for a specified named route.
# The additional arguments will be used to compose the relative URL - in
# case it has tokens to match - and for compose the query string.
@@ -1159,12 +1186,14 @@
# router.get '/:name', to: 'frameworks#show', as: :framework
#
# router.path(:login) # => "/login"
# router.path(:login, return_to: '/dashboard') # => "/login?return_to=%2Fdashboard"
# router.path(:framework, name: 'router') # => "/router"
- def path(route, *args)
- @router.path(route, *args)
+ def path(route, args = {})
+ @named.fetch(route).path(args)
+ rescue KeyError
+ raise Hanami::Routing::InvalidRouteException.new("No route could be generated for #{route.inspect} - please check given arguments")
end
# Generate a URL for a specified named route.
# The additional arguments will be used to compose the relative URL - in
# case it has tokens to match - and for compose the query string.
@@ -1186,12 +1215,12 @@
# router.get '/:name', to: 'frameworks#show', as: :framework
#
# router.url(:login) # => "https://hanamirb.org/login"
# router.url(:login, return_to: '/dashboard') # => "https://hanamirb.org/login?return_to=%2Fdashboard"
# router.url(:framework, name: 'router') # => "https://hanamirb.org/router"
- def url(route, *args)
- @router.url(route, *args)
+ def url(route, args = {})
+ @base + path(route, args)
end
# Returns an routes inspector
#
# @since 0.2.0
@@ -1212,12 +1241,12 @@
# # => GET, HEAD / Home::Index
# login GET, HEAD /login Sessions::New
# POST /login Sessions::Create
# logout GET, HEAD /logout Sessions::Destroy
def inspector
- require 'hanami/routing/routes_inspector'
- Routing::RoutesInspector.new(@router.routes, @router.prefix)
+ require "hanami/routing/routes_inspector"
+ Routing::RoutesInspector.new(@routes, @prefix)
end
protected
# Fabricate Rack env for the given Rack env, path or named route
@@ -1231,12 +1260,12 @@
# @since 0.5.0
# @api private
#
# @see Hanami::Router#recognize
# @see http://www.rubydoc.info/github/rack/rack/Rack%2FMockRequest.env_for
- def env_for(env, options = {}, params = nil)
- env = case env
+ def env_for(env, options = {}, params = nil) # rubocop:disable Metrics/MethodLength
+ case env
when String
Rack::MockRequest.env_for(env, options)
when Symbol
begin
url = path(env, params || options)
@@ -1244,9 +1273,93 @@
rescue Hanami::Routing::InvalidRouteException
{}
end
else
env
+ end
+ end
+
+ private
+
+ PATH_INFO = "PATH_INFO"
+ SCRIPT_NAME = "SCRIPT_NAME"
+ SERVER_NAME = "SERVER_NAME"
+ REQUEST_METHOD = "REQUEST_METHOD"
+
+ PARAMS = "router.params"
+
+ GET = "GET"
+ HEAD = "HEAD"
+ POST = "POST"
+ PUT = "PUT"
+ PATCH = "PATCH"
+ DELETE = "DELETE"
+ TRACE = "TRACE"
+ OPTIONS = "OPTIONS"
+ LINK = "LINK"
+ UNLINK = "UNLINK"
+
+ NOT_FOUND = ->(_) { [404, { "Content-Length" => "9" }, ["Not Found"]] }.freeze
+ NOT_ALLOWED = ->(_) { [405, { "Content-Length" => "18" }, ["Method Not Allowed"]] }.freeze
+ ROOT = "/"
+
+ BODY = 2
+
+ attr_reader :configuration
+
+ # Application
+ #
+ # @since 2.0.0
+ # @api private
+ class App
+ def initialize(path, endpoint, host: nil)
+ @path = Mustermann.new(path, type: :rails, version: "5.0")
+ @prefix = path.to_s
+ @endpoint = endpoint
+ @host = host
+ freeze
+ end
+
+ def match?(env)
+ match_path?(env)
+ end
+
+ def match_path?(env)
+ result = env[PATH_INFO].start_with?(@prefix)
+ result &&= @host == env[SERVER_NAME] unless @host.nil?
+
+ result
+ end
+
+ def call(env)
+ env[PARAMS] ||= {}
+ env[PARAMS].merge!(Utils::Hash.deep_symbolize(@path.params(env[PATH_INFO]) || {}))
+
+ env[SCRIPT_NAME] = @prefix
+ env[PATH_INFO] = env[PATH_INFO].sub(@prefix, "")
+ env[PATH_INFO] = "/" if env[PATH_INFO] == ""
+
+ @endpoint.call(env)
+ end
+ end
+
+ def add_route(verb, path, to, as = nil, namespace = nil, config = nil, constraints = {}, &blk)
+ to ||= blk
+ config ||= configuration
+
+ path = path.to_s
+ endpoint = Routing::Endpoint.find(to, namespace || @namespace, config)
+ route = Routing::Route.new(verb_for(verb), @prefix.join(path).to_s, endpoint, constraints)
+
+ @routes.push(route)
+ @named[as] = route unless as.nil?
+ end
+
+ def verb_for(value)
+ if value == GET
+ [GET, HEAD]
+ else
+ [value]
end
end
end
end