hanami-router 0.0.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/CHANGELOG.md +101 -0
 - data/LICENSE.md +22 -0
 - data/README.md +667 -9
 - data/hanami-router.gemspec +18 -12
 - data/lib/hanami-router.rb +1 -0
 - data/lib/hanami/router.rb +1160 -3
 - data/lib/hanami/router/version.rb +3 -2
 - data/lib/hanami/routing/endpoint.rb +151 -0
 - data/lib/hanami/routing/endpoint_resolver.rb +225 -0
 - data/lib/hanami/routing/error.rb +7 -0
 - data/lib/hanami/routing/force_ssl.rb +209 -0
 - data/lib/hanami/routing/http_router.rb +187 -0
 - data/lib/hanami/routing/namespace.rb +92 -0
 - data/lib/hanami/routing/parsers.rb +71 -0
 - data/lib/hanami/routing/parsing/json_parser.rb +28 -0
 - data/lib/hanami/routing/parsing/parser.rb +58 -0
 - data/lib/hanami/routing/recognized_route.rb +153 -0
 - data/lib/hanami/routing/resource.rb +116 -0
 - data/lib/hanami/routing/resource/action.rb +387 -0
 - data/lib/hanami/routing/resource/nested.rb +39 -0
 - data/lib/hanami/routing/resource/options.rb +74 -0
 - data/lib/hanami/routing/resources.rb +48 -0
 - data/lib/hanami/routing/resources/action.rb +150 -0
 - data/lib/hanami/routing/route.rb +62 -0
 - data/lib/hanami/routing/routes_inspector.rb +215 -0
 - metadata +94 -16
 - data/.gitignore +0 -9
 - data/Gemfile +0 -4
 - data/Rakefile +0 -2
 - data/bin/console +0 -14
 - data/bin/setup +0 -8
 
| 
         @@ -0,0 +1,187 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'http_router'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'hanami/utils/io'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'hanami/routing/endpoint_resolver'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'hanami/routing/route'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require 'hanami/routing/parsers'
         
     | 
| 
      
 6 
     | 
    
         
            +
            require 'hanami/routing/force_ssl'
         
     | 
| 
      
 7 
     | 
    
         
            +
            require 'hanami/routing/error'
         
     | 
| 
      
 8 
     | 
    
         
            +
            require 'hanami/utils/path_prefix'
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            Hanami::Utils::IO.silence_warnings do
         
     | 
| 
      
 11 
     | 
    
         
            +
              HttpRouter::Route::VALID_HTTP_VERBS = %w{GET POST PUT PATCH DELETE HEAD OPTIONS TRACE}
         
     | 
| 
      
 12 
     | 
    
         
            +
            end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            module Hanami
         
     | 
| 
      
 15 
     | 
    
         
            +
              module Routing
         
     | 
| 
      
 16 
     | 
    
         
            +
                # Invalid route
         
     | 
| 
      
 17 
     | 
    
         
            +
                # This is raised when the router fails to recognize a route, because of the
         
     | 
| 
      
 18 
     | 
    
         
            +
                # given arguments.
         
     | 
| 
      
 19 
     | 
    
         
            +
                #
         
     | 
| 
      
 20 
     | 
    
         
            +
                # @since 0.1.0
         
     | 
| 
      
 21 
     | 
    
         
            +
                class InvalidRouteException < Hanami::Routing::Error
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                # HTTP router
         
     | 
| 
      
 25 
     | 
    
         
            +
                #
         
     | 
| 
      
 26 
     | 
    
         
            +
                # This implementation is based on ::HttpRouter (http_router gem).
         
     | 
| 
      
 27 
     | 
    
         
            +
                #
         
     | 
| 
      
 28 
     | 
    
         
            +
                # Hanami::Router wraps an instance of this class, in order to protect its
         
     | 
| 
      
 29 
     | 
    
         
            +
                # public API from any future change of ::HttpRouter.
         
     | 
| 
      
 30 
     | 
    
         
            +
                #
         
     | 
| 
      
 31 
     | 
    
         
            +
                # @since 0.1.0
         
     | 
| 
      
 32 
     | 
    
         
            +
                # @api private
         
     | 
| 
      
 33 
     | 
    
         
            +
                class HttpRouter < ::HttpRouter
         
     | 
| 
      
 34 
     | 
    
         
            +
                  # Script name - rack enviroment variable
         
     | 
| 
      
 35 
     | 
    
         
            +
                  #
         
     | 
| 
      
 36 
     | 
    
         
            +
                  # @since 0.5.0
         
     | 
| 
      
 37 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 38 
     | 
    
         
            +
                  SCRIPT_NAME = 'SCRIPT_NAME'.freeze
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                  # @since 0.5.0
         
     | 
| 
      
 41 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 42 
     | 
    
         
            +
                  attr_reader :namespace
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                  # Initialize the router.
         
     | 
| 
      
 45 
     | 
    
         
            +
                  #
         
     | 
| 
      
 46 
     | 
    
         
            +
                  # @see Hanami::Router#initialize
         
     | 
| 
      
 47 
     | 
    
         
            +
                  #
         
     | 
| 
      
 48 
     | 
    
         
            +
                  # @since 0.1.0
         
     | 
| 
      
 49 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 50 
     | 
    
         
            +
                  def initialize(options = {}, &blk)
         
     | 
| 
      
 51 
     | 
    
         
            +
                    super(options, &nil)
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                    @namespace        = options[:namespace] if options[:namespace]
         
     | 
| 
      
 54 
     | 
    
         
            +
                    @default_scheme   = options[:scheme]    if options[:scheme]
         
     | 
| 
      
 55 
     | 
    
         
            +
                    @default_host     = options[:host]      if options[:host]
         
     | 
| 
      
 56 
     | 
    
         
            +
                    @default_port     = options[:port]      if options[:port]
         
     | 
| 
      
 57 
     | 
    
         
            +
                    @route_class      = options[:route]    || Routing::Route
         
     | 
| 
      
 58 
     | 
    
         
            +
                    @resolver         = options[:resolver] || Routing::EndpointResolver.new(options)
         
     | 
| 
      
 59 
     | 
    
         
            +
                    @parsers          = Routing::Parsers.new(options[:parsers])
         
     | 
| 
      
 60 
     | 
    
         
            +
                    @prefix           = Utils::PathPrefix.new(options[:prefix] || '')
         
     | 
| 
      
 61 
     | 
    
         
            +
                    @force_ssl        = Hanami::Routing::ForceSsl.new(!!options[:force_ssl], host: @default_host, port: @default_port)
         
     | 
| 
      
 62 
     | 
    
         
            +
                  end
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                  # Separator between controller and action name.
         
     | 
| 
      
 65 
     | 
    
         
            +
                  #
         
     | 
| 
      
 66 
     | 
    
         
            +
                  # @see Hanami::Routing::EndpointResolver::ACTION_SEPARATOR
         
     | 
| 
      
 67 
     | 
    
         
            +
                  #
         
     | 
| 
      
 68 
     | 
    
         
            +
                  # @since 0.1.0
         
     | 
| 
      
 69 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 70 
     | 
    
         
            +
                  def action_separator
         
     | 
| 
      
 71 
     | 
    
         
            +
                    @resolver.action_separator
         
     | 
| 
      
 72 
     | 
    
         
            +
                  end
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                  # Finds a path from the given options.
         
     | 
| 
      
 75 
     | 
    
         
            +
                  #
         
     | 
| 
      
 76 
     | 
    
         
            +
                  # @see Hanami::Routing::EndpointResolver#find
         
     | 
| 
      
 77 
     | 
    
         
            +
                  #
         
     | 
| 
      
 78 
     | 
    
         
            +
                  # @since 0.1.0
         
     | 
| 
      
 79 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 80 
     | 
    
         
            +
                  def find(options)
         
     | 
| 
      
 81 
     | 
    
         
            +
                    @resolver.find(options)
         
     | 
| 
      
 82 
     | 
    
         
            +
                  end
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                  # Generate a relative URL for a specified named route.
         
     | 
| 
      
 85 
     | 
    
         
            +
                  #
         
     | 
| 
      
 86 
     | 
    
         
            +
                  # @see Hanami::Router#path
         
     | 
| 
      
 87 
     | 
    
         
            +
                  #
         
     | 
| 
      
 88 
     | 
    
         
            +
                  # @since 0.1.0
         
     | 
| 
      
 89 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 90 
     | 
    
         
            +
                  def raw_path(route, *args)
         
     | 
| 
      
 91 
     | 
    
         
            +
                    _rescue_url_recognition do
         
     | 
| 
      
 92 
     | 
    
         
            +
                      _custom_path(super(route, *args))
         
     | 
| 
      
 93 
     | 
    
         
            +
                    end
         
     | 
| 
      
 94 
     | 
    
         
            +
                  end
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
      
 96 
     | 
    
         
            +
                  # Generate an absolute URL for a specified named route.
         
     | 
| 
      
 97 
     | 
    
         
            +
                  #
         
     | 
| 
      
 98 
     | 
    
         
            +
                  # @see Hanami::Router#path
         
     | 
| 
      
 99 
     | 
    
         
            +
                  #
         
     | 
| 
      
 100 
     | 
    
         
            +
                  # @since 0.1.0
         
     | 
| 
      
 101 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 102 
     | 
    
         
            +
                  def raw_url(route, *args)
         
     | 
| 
      
 103 
     | 
    
         
            +
                    _rescue_url_recognition do
         
     | 
| 
      
 104 
     | 
    
         
            +
                      _custom_path(super(route, *args))
         
     | 
| 
      
 105 
     | 
    
         
            +
                    end
         
     | 
| 
      
 106 
     | 
    
         
            +
                  end
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
                  # Support for OPTIONS HTTP verb
         
     | 
| 
      
 109 
     | 
    
         
            +
                  #
         
     | 
| 
      
 110 
     | 
    
         
            +
                  # @see Hanami::Router#options
         
     | 
| 
      
 111 
     | 
    
         
            +
                  #
         
     | 
| 
      
 112 
     | 
    
         
            +
                  # @since 0.1.0
         
     | 
| 
      
 113 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 114 
     | 
    
         
            +
                  def options(path, options = {}, &blk)
         
     | 
| 
      
 115 
     | 
    
         
            +
                    add_with_request_method(path, :options, options, &blk)
         
     | 
| 
      
 116 
     | 
    
         
            +
                  end
         
     | 
| 
      
 117 
     | 
    
         
            +
             
     | 
| 
      
 118 
     | 
    
         
            +
                  # Allow to mount a Rack app
         
     | 
| 
      
 119 
     | 
    
         
            +
                  #
         
     | 
| 
      
 120 
     | 
    
         
            +
                  # @see Hanami::Router#mount
         
     | 
| 
      
 121 
     | 
    
         
            +
                  #
         
     | 
| 
      
 122 
     | 
    
         
            +
                  # @since 0.1.1
         
     | 
| 
      
 123 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 124 
     | 
    
         
            +
                  def mount(app, options)
         
     | 
| 
      
 125 
     | 
    
         
            +
                    add("#{ options.fetch(:at) }*").to(
         
     | 
| 
      
 126 
     | 
    
         
            +
                      @resolver.resolve(to: app)
         
     | 
| 
      
 127 
     | 
    
         
            +
                    )
         
     | 
| 
      
 128 
     | 
    
         
            +
                  end
         
     | 
| 
      
 129 
     | 
    
         
            +
             
     | 
| 
      
 130 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 131 
     | 
    
         
            +
                  def raw_call(env, &blk)
         
     | 
| 
      
 132 
     | 
    
         
            +
                    if response = @force_ssl.call(env)
         
     | 
| 
      
 133 
     | 
    
         
            +
                      response
         
     | 
| 
      
 134 
     | 
    
         
            +
                    else
         
     | 
| 
      
 135 
     | 
    
         
            +
                      super(@parsers.call(env))
         
     | 
| 
      
 136 
     | 
    
         
            +
                    end
         
     | 
| 
      
 137 
     | 
    
         
            +
                  end
         
     | 
| 
      
 138 
     | 
    
         
            +
             
     | 
| 
      
 139 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 140 
     | 
    
         
            +
                  def reset!
         
     | 
| 
      
 141 
     | 
    
         
            +
                    uncompile
         
     | 
| 
      
 142 
     | 
    
         
            +
                    @routes, @named_routes, @root = [], Hash.new{|h,k| h[k] = []}, Node::Root.new(self)
         
     | 
| 
      
 143 
     | 
    
         
            +
                    @default_host, @default_port, @default_scheme = 'localhost', 80, 'http'
         
     | 
| 
      
 144 
     | 
    
         
            +
                  end
         
     | 
| 
      
 145 
     | 
    
         
            +
             
     | 
| 
      
 146 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 147 
     | 
    
         
            +
                  def pass_on_response(response)
         
     | 
| 
      
 148 
     | 
    
         
            +
                    super response.to_a
         
     | 
| 
      
 149 
     | 
    
         
            +
                  end
         
     | 
| 
      
 150 
     | 
    
         
            +
             
     | 
| 
      
 151 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 152 
     | 
    
         
            +
                  def no_response(request, env)
         
     | 
| 
      
 153 
     | 
    
         
            +
                    if request.acceptable_methods.any? && !request.acceptable_methods.include?(env['REQUEST_METHOD'])
         
     | 
| 
      
 154 
     | 
    
         
            +
                      [405, {'Allow' => request.acceptable_methods.sort.join(", ")}, []]
         
     | 
| 
      
 155 
     | 
    
         
            +
                    else
         
     | 
| 
      
 156 
     | 
    
         
            +
                      @default_app.call(env)
         
     | 
| 
      
 157 
     | 
    
         
            +
                    end
         
     | 
| 
      
 158 
     | 
    
         
            +
                  end
         
     | 
| 
      
 159 
     | 
    
         
            +
             
     | 
| 
      
 160 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 161 
     | 
    
         
            +
                  # @since 0.5.0
         
     | 
| 
      
 162 
     | 
    
         
            +
                  def rewrite_path_info(env, request)
         
     | 
| 
      
 163 
     | 
    
         
            +
                    super
         
     | 
| 
      
 164 
     | 
    
         
            +
                    env[SCRIPT_NAME] = @prefix + env[SCRIPT_NAME]
         
     | 
| 
      
 165 
     | 
    
         
            +
                  end
         
     | 
| 
      
 166 
     | 
    
         
            +
             
     | 
| 
      
 167 
     | 
    
         
            +
                  private
         
     | 
| 
      
 168 
     | 
    
         
            +
             
     | 
| 
      
 169 
     | 
    
         
            +
                  def _rescue_url_recognition
         
     | 
| 
      
 170 
     | 
    
         
            +
                    yield
         
     | 
| 
      
 171 
     | 
    
         
            +
                  rescue ::HttpRouter::InvalidRouteException,
         
     | 
| 
      
 172 
     | 
    
         
            +
                         ::HttpRouter::TooManyParametersException => e
         
     | 
| 
      
 173 
     | 
    
         
            +
                    raise Routing::InvalidRouteException.new("#{ e.message } - please check given arguments")
         
     | 
| 
      
 174 
     | 
    
         
            +
                  end
         
     | 
| 
      
 175 
     | 
    
         
            +
             
     | 
| 
      
 176 
     | 
    
         
            +
                  def add_with_request_method(path, method, opts = {}, &app)
         
     | 
| 
      
 177 
     | 
    
         
            +
                    super.generate(@resolver, opts, &app)
         
     | 
| 
      
 178 
     | 
    
         
            +
                  end
         
     | 
| 
      
 179 
     | 
    
         
            +
             
     | 
| 
      
 180 
     | 
    
         
            +
                  def _custom_path(uri_string)
         
     | 
| 
      
 181 
     | 
    
         
            +
                    uri = URI.parse(uri_string)
         
     | 
| 
      
 182 
     | 
    
         
            +
                    uri.path = @prefix.join(uri.path)
         
     | 
| 
      
 183 
     | 
    
         
            +
                    uri.to_s
         
     | 
| 
      
 184 
     | 
    
         
            +
                  end
         
     | 
| 
      
 185 
     | 
    
         
            +
                end
         
     | 
| 
      
 186 
     | 
    
         
            +
              end
         
     | 
| 
      
 187 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,92 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'delegate'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'hanami/utils/path_prefix'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            module Hanami
         
     | 
| 
      
 5 
     | 
    
         
            +
              module Routing
         
     | 
| 
      
 6 
     | 
    
         
            +
                # Namespace for routes.
         
     | 
| 
      
 7 
     | 
    
         
            +
                # Implementation of Hanami::Router#namespace
         
     | 
| 
      
 8 
     | 
    
         
            +
                #
         
     | 
| 
      
 9 
     | 
    
         
            +
                # @since 0.1.0
         
     | 
| 
      
 10 
     | 
    
         
            +
                #
         
     | 
| 
      
 11 
     | 
    
         
            +
                # @api private
         
     | 
| 
      
 12 
     | 
    
         
            +
                #
         
     | 
| 
      
 13 
     | 
    
         
            +
                # @see Hanami::Router#namespace
         
     | 
| 
      
 14 
     | 
    
         
            +
                class Namespace < SimpleDelegator
         
     | 
| 
      
 15 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 16 
     | 
    
         
            +
                  # @since 0.1.0
         
     | 
| 
      
 17 
     | 
    
         
            +
                  def initialize(router, name, &blk)
         
     | 
| 
      
 18 
     | 
    
         
            +
                    @router = router
         
     | 
| 
      
 19 
     | 
    
         
            +
                    @name   = Utils::PathPrefix.new(name)
         
     | 
| 
      
 20 
     | 
    
         
            +
                    __setobj__(@router)
         
     | 
| 
      
 21 
     | 
    
         
            +
                    instance_eval(&blk)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 25 
     | 
    
         
            +
                  # @since 0.1.0
         
     | 
| 
      
 26 
     | 
    
         
            +
                  def get(path, options = {}, &endpoint)
         
     | 
| 
      
 27 
     | 
    
         
            +
                    super(@name.join(path), options, &endpoint)
         
     | 
| 
      
 28 
     | 
    
         
            +
                  end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 31 
     | 
    
         
            +
                  # @since 0.1.0
         
     | 
| 
      
 32 
     | 
    
         
            +
                  def post(path, options = {}, &endpoint)
         
     | 
| 
      
 33 
     | 
    
         
            +
                    super(@name.join(path), options, &endpoint)
         
     | 
| 
      
 34 
     | 
    
         
            +
                  end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 37 
     | 
    
         
            +
                  # @since 0.1.0
         
     | 
| 
      
 38 
     | 
    
         
            +
                  def put(path, options = {}, &endpoint)
         
     | 
| 
      
 39 
     | 
    
         
            +
                    super(@name.join(path), options, &endpoint)
         
     | 
| 
      
 40 
     | 
    
         
            +
                  end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 43 
     | 
    
         
            +
                  # @since 0.1.0
         
     | 
| 
      
 44 
     | 
    
         
            +
                  def patch(path, options = {}, &endpoint)
         
     | 
| 
      
 45 
     | 
    
         
            +
                    super(@name.join(path), options, &endpoint)
         
     | 
| 
      
 46 
     | 
    
         
            +
                  end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 49 
     | 
    
         
            +
                  # @since 0.1.0
         
     | 
| 
      
 50 
     | 
    
         
            +
                  def delete(path, options = {}, &endpoint)
         
     | 
| 
      
 51 
     | 
    
         
            +
                    super(@name.join(path), options, &endpoint)
         
     | 
| 
      
 52 
     | 
    
         
            +
                  end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 55 
     | 
    
         
            +
                  # @since 0.1.0
         
     | 
| 
      
 56 
     | 
    
         
            +
                  def trace(path, options = {}, &endpoint)
         
     | 
| 
      
 57 
     | 
    
         
            +
                    super(@name.join(path), options, &endpoint)
         
     | 
| 
      
 58 
     | 
    
         
            +
                  end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 61 
     | 
    
         
            +
                  # @since 0.1.0
         
     | 
| 
      
 62 
     | 
    
         
            +
                  def options(path, options = {}, &endpoint)
         
     | 
| 
      
 63 
     | 
    
         
            +
                    super(@name.join(path), options, &endpoint)
         
     | 
| 
      
 64 
     | 
    
         
            +
                  end
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 67 
     | 
    
         
            +
                  # @since 0.1.0
         
     | 
| 
      
 68 
     | 
    
         
            +
                  def resource(name, options = {})
         
     | 
| 
      
 69 
     | 
    
         
            +
                    super name, options.merge(namespace: @name.relative_join(options[:namespace]))
         
     | 
| 
      
 70 
     | 
    
         
            +
                  end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 73 
     | 
    
         
            +
                  # @since 0.1.0
         
     | 
| 
      
 74 
     | 
    
         
            +
                  def resources(name, options = {})
         
     | 
| 
      
 75 
     | 
    
         
            +
                    super name, options.merge(namespace: @name.relative_join(options[:namespace]))
         
     | 
| 
      
 76 
     | 
    
         
            +
                  end
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 79 
     | 
    
         
            +
                  # @since 0.1.0
         
     | 
| 
      
 80 
     | 
    
         
            +
                  def redirect(path, options = {}, &endpoint)
         
     | 
| 
      
 81 
     | 
    
         
            +
                    super(@name.join(path), options.merge(to: @name.join(options[:to])), &endpoint)
         
     | 
| 
      
 82 
     | 
    
         
            +
                  end
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                  # Supports nested namespaces
         
     | 
| 
      
 85 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 86 
     | 
    
         
            +
                  # @since 0.1.0
         
     | 
| 
      
 87 
     | 
    
         
            +
                  def namespace(name, &blk)
         
     | 
| 
      
 88 
     | 
    
         
            +
                    Routing::Namespace.new(self, name, &blk)
         
     | 
| 
      
 89 
     | 
    
         
            +
                  end
         
     | 
| 
      
 90 
     | 
    
         
            +
                end
         
     | 
| 
      
 91 
     | 
    
         
            +
              end
         
     | 
| 
      
 92 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,71 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'hanami/routing/parsing/parser'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Hanami
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Routing
         
     | 
| 
      
 5 
     | 
    
         
            +
                class Parsers
         
     | 
| 
      
 6 
     | 
    
         
            +
                  CONTENT_TYPE       = 'CONTENT_TYPE'.freeze
         
     | 
| 
      
 7 
     | 
    
         
            +
                  MEDIA_TYPE_MATCHER = /\s*[;,]\s*/.freeze
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                  RACK_INPUT    = 'rack.input'.freeze
         
     | 
| 
      
 10 
     | 
    
         
            +
                  ROUTER_PARAMS = 'router.params'.freeze
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  def initialize(parsers)
         
     | 
| 
      
 13 
     | 
    
         
            +
                    @parsers = prepare(parsers)
         
     | 
| 
      
 14 
     | 
    
         
            +
                    _redefine_call
         
     | 
| 
      
 15 
     | 
    
         
            +
                  end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                  def call(env)
         
     | 
| 
      
 18 
     | 
    
         
            +
                    env
         
     | 
| 
      
 19 
     | 
    
         
            +
                  end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                  private
         
     | 
| 
      
 22 
     | 
    
         
            +
                  def prepare(args)
         
     | 
| 
      
 23 
     | 
    
         
            +
                    result  = Hash.new
         
     | 
| 
      
 24 
     | 
    
         
            +
                    args    = Array(args)
         
     | 
| 
      
 25 
     | 
    
         
            +
                    return result if args.empty?
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                    args.each do |arg|
         
     | 
| 
      
 28 
     | 
    
         
            +
                      parser = Parsing::Parser.for(arg)
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                      parser.mime_types.each do |mime|
         
     | 
| 
      
 31 
     | 
    
         
            +
                        result[mime] = parser
         
     | 
| 
      
 32 
     | 
    
         
            +
                      end
         
     | 
| 
      
 33 
     | 
    
         
            +
                    end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                    result.default = Parsing::Parser.new
         
     | 
| 
      
 36 
     | 
    
         
            +
                    result
         
     | 
| 
      
 37 
     | 
    
         
            +
                  end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                  def _redefine_call
         
     | 
| 
      
 40 
     | 
    
         
            +
                    return if @parsers.empty?
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                    define_singleton_method :call do |env|
         
     | 
| 
      
 43 
     | 
    
         
            +
                      body = env[RACK_INPUT].read
         
     | 
| 
      
 44 
     | 
    
         
            +
                      return env if body.empty?
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                      env[RACK_INPUT].rewind    # somebody might try to read this stream
         
     | 
| 
      
 47 
     | 
    
         
            +
                      env[ROUTER_PARAMS] ||= {} # prepare params
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                      env[ROUTER_PARAMS].merge!(
         
     | 
| 
      
 50 
     | 
    
         
            +
                        @parsers[
         
     | 
| 
      
 51 
     | 
    
         
            +
                          media_type(env)
         
     | 
| 
      
 52 
     | 
    
         
            +
                        ].parse(body)
         
     | 
| 
      
 53 
     | 
    
         
            +
                      )
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                      env
         
     | 
| 
      
 56 
     | 
    
         
            +
                    end
         
     | 
| 
      
 57 
     | 
    
         
            +
                  end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                  def media_type(env)
         
     | 
| 
      
 60 
     | 
    
         
            +
                    if ct = content_type(env)
         
     | 
| 
      
 61 
     | 
    
         
            +
                      ct.split(MEDIA_TYPE_MATCHER, 2).first.downcase
         
     | 
| 
      
 62 
     | 
    
         
            +
                    end
         
     | 
| 
      
 63 
     | 
    
         
            +
                  end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                  def content_type(env)
         
     | 
| 
      
 66 
     | 
    
         
            +
                    content_type = env[CONTENT_TYPE]
         
     | 
| 
      
 67 
     | 
    
         
            +
                    content_type.nil? || content_type.empty? ? nil : content_type
         
     | 
| 
      
 68 
     | 
    
         
            +
                  end
         
     | 
| 
      
 69 
     | 
    
         
            +
                end
         
     | 
| 
      
 70 
     | 
    
         
            +
              end
         
     | 
| 
      
 71 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,28 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'json'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Hanami
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Routing
         
     | 
| 
      
 5 
     | 
    
         
            +
                module Parsing
         
     | 
| 
      
 6 
     | 
    
         
            +
                  class JsonParser < Parser
         
     | 
| 
      
 7 
     | 
    
         
            +
                    def mime_types
         
     | 
| 
      
 8 
     | 
    
         
            +
                      ['application/json', 'application/vnd.api+json']
         
     | 
| 
      
 9 
     | 
    
         
            +
                    end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                    # Parse a json string
         
     | 
| 
      
 12 
     | 
    
         
            +
                    #
         
     | 
| 
      
 13 
     | 
    
         
            +
                    # @param body [String] a json string
         
     | 
| 
      
 14 
     | 
    
         
            +
                    #
         
     | 
| 
      
 15 
     | 
    
         
            +
                    # @return [Hash] the parsed json
         
     | 
| 
      
 16 
     | 
    
         
            +
                    #
         
     | 
| 
      
 17 
     | 
    
         
            +
                    # @raise [Hanami::Routing::Parsing::BodyParsingError] when the body can't be parsed.
         
     | 
| 
      
 18 
     | 
    
         
            +
                    #
         
     | 
| 
      
 19 
     | 
    
         
            +
                    # @since 0.2.0
         
     | 
| 
      
 20 
     | 
    
         
            +
                    def parse(body)
         
     | 
| 
      
 21 
     | 
    
         
            +
                      JSON.parse(body)
         
     | 
| 
      
 22 
     | 
    
         
            +
                    rescue JSON::ParserError => e
         
     | 
| 
      
 23 
     | 
    
         
            +
                      raise BodyParsingError.new(e.message)
         
     | 
| 
      
 24 
     | 
    
         
            +
                    end
         
     | 
| 
      
 25 
     | 
    
         
            +
                  end
         
     | 
| 
      
 26 
     | 
    
         
            +
                end
         
     | 
| 
      
 27 
     | 
    
         
            +
              end
         
     | 
| 
      
 28 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,58 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'hanami/utils/class'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'hanami/utils/string'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'hanami/routing/error'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module Hanami
         
     | 
| 
      
 6 
     | 
    
         
            +
              module Routing
         
     | 
| 
      
 7 
     | 
    
         
            +
                module Parsing
         
     | 
| 
      
 8 
     | 
    
         
            +
                  # Body parsing error
         
     | 
| 
      
 9 
     | 
    
         
            +
                  # This is raised when parser fails to parse the body
         
     | 
| 
      
 10 
     | 
    
         
            +
                  #
         
     | 
| 
      
 11 
     | 
    
         
            +
                  # @since 0.5.0
         
     | 
| 
      
 12 
     | 
    
         
            +
                  class BodyParsingError < Hanami::Routing::Error
         
     | 
| 
      
 13 
     | 
    
         
            +
                  end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                  # @since 0.2.0
         
     | 
| 
      
 16 
     | 
    
         
            +
                  class UnknownParserError < Hanami::Routing::Error
         
     | 
| 
      
 17 
     | 
    
         
            +
                    def initialize(parser)
         
     | 
| 
      
 18 
     | 
    
         
            +
                      super("Unknown Parser: `#{ parser }'")
         
     | 
| 
      
 19 
     | 
    
         
            +
                    end
         
     | 
| 
      
 20 
     | 
    
         
            +
                  end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                  # @since 0.2.0
         
     | 
| 
      
 23 
     | 
    
         
            +
                  class Parser
         
     | 
| 
      
 24 
     | 
    
         
            +
                    # @since 0.2.0
         
     | 
| 
      
 25 
     | 
    
         
            +
                    def self.for(parser)
         
     | 
| 
      
 26 
     | 
    
         
            +
                      case parser
         
     | 
| 
      
 27 
     | 
    
         
            +
                      when String, Symbol
         
     | 
| 
      
 28 
     | 
    
         
            +
                        require_parser(parser)
         
     | 
| 
      
 29 
     | 
    
         
            +
                      else
         
     | 
| 
      
 30 
     | 
    
         
            +
                        parser
         
     | 
| 
      
 31 
     | 
    
         
            +
                      end
         
     | 
| 
      
 32 
     | 
    
         
            +
                    end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                    # @since 0.2.0
         
     | 
| 
      
 35 
     | 
    
         
            +
                    def mime_types
         
     | 
| 
      
 36 
     | 
    
         
            +
                      raise NotImplementedError
         
     | 
| 
      
 37 
     | 
    
         
            +
                    end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                    # @since 0.2.0
         
     | 
| 
      
 40 
     | 
    
         
            +
                    def parse(body)
         
     | 
| 
      
 41 
     | 
    
         
            +
                      Hash.new
         
     | 
| 
      
 42 
     | 
    
         
            +
                    end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                    private
         
     | 
| 
      
 45 
     | 
    
         
            +
                    # @since 0.2.0
         
     | 
| 
      
 46 
     | 
    
         
            +
                    # @api private
         
     | 
| 
      
 47 
     | 
    
         
            +
                    def self.require_parser(parser)
         
     | 
| 
      
 48 
     | 
    
         
            +
                      require "hanami/routing/parsing/#{ parser }_parser"
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                      parser = Utils::String.new(parser).classify
         
     | 
| 
      
 51 
     | 
    
         
            +
                      Utils::Class.load!("Hanami::Routing::Parsing::#{ parser }Parser").new
         
     | 
| 
      
 52 
     | 
    
         
            +
                    rescue LoadError, NameError
         
     | 
| 
      
 53 
     | 
    
         
            +
                      raise UnknownParserError.new(parser)
         
     | 
| 
      
 54 
     | 
    
         
            +
                    end
         
     | 
| 
      
 55 
     | 
    
         
            +
                  end
         
     | 
| 
      
 56 
     | 
    
         
            +
                end
         
     | 
| 
      
 57 
     | 
    
         
            +
              end
         
     | 
| 
      
 58 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,153 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'hanami/utils/string'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Hanami
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Routing
         
     | 
| 
      
 5 
     | 
    
         
            +
                # Represents a result of router path recognition.
         
     | 
| 
      
 6 
     | 
    
         
            +
                #
         
     | 
| 
      
 7 
     | 
    
         
            +
                # @since 0.5.0
         
     | 
| 
      
 8 
     | 
    
         
            +
                #
         
     | 
| 
      
 9 
     | 
    
         
            +
                # @see Hanami::Router#recognize
         
     | 
| 
      
 10 
     | 
    
         
            +
                class RecognizedRoute
         
     | 
| 
      
 11 
     | 
    
         
            +
                  # @since 0.5.0
         
     | 
| 
      
 12 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 13 
     | 
    
         
            +
                  REQUEST_METHOD = 'REQUEST_METHOD'.freeze
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                  # @since 0.5.0
         
     | 
| 
      
 16 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 17 
     | 
    
         
            +
                  NAMESPACE             = '%s::'.freeze
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                  # @since 0.5.0
         
     | 
| 
      
 20 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 21 
     | 
    
         
            +
                  NAMESPACE_REPLACEMENT = ''.freeze
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                  # @since 0.5.0
         
     | 
| 
      
 24 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 25 
     | 
    
         
            +
                  ACTION_PATH_SEPARATOR = '/'.freeze
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                  # @since 0.5.0
         
     | 
| 
      
 28 
     | 
    
         
            +
                  # @api public
         
     | 
| 
      
 29 
     | 
    
         
            +
                  attr_reader :params
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                  # Creates a new instance
         
     | 
| 
      
 32 
     | 
    
         
            +
                  #
         
     | 
| 
      
 33 
     | 
    
         
            +
                  # @param response [HttpRouter::Response] raw response of recognition
         
     | 
| 
      
 34 
     | 
    
         
            +
                  # @param env [Hash] Rack env
         
     | 
| 
      
 35 
     | 
    
         
            +
                  # @param router [Hanami::Routing::HttpRouter] low level router
         
     | 
| 
      
 36 
     | 
    
         
            +
                  #
         
     | 
| 
      
 37 
     | 
    
         
            +
                  # @return [Hanami::Routing::RecognizedRoute]
         
     | 
| 
      
 38 
     | 
    
         
            +
                  #
         
     | 
| 
      
 39 
     | 
    
         
            +
                  # @since 0.5.0
         
     | 
| 
      
 40 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 41 
     | 
    
         
            +
                  def initialize(response, env, router)
         
     | 
| 
      
 42 
     | 
    
         
            +
                    @env = env
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                    unless response.nil?
         
     | 
| 
      
 45 
     | 
    
         
            +
                      @endpoint = response.route.dest
         
     | 
| 
      
 46 
     | 
    
         
            +
                      @params   = response.params
         
     | 
| 
      
 47 
     | 
    
         
            +
                    end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                    @namespace        = router.namespace
         
     | 
| 
      
 50 
     | 
    
         
            +
                    @action_separator = router.action_separator
         
     | 
| 
      
 51 
     | 
    
         
            +
                  end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                  # Rack protocol compatibility
         
     | 
| 
      
 54 
     | 
    
         
            +
                  #
         
     | 
| 
      
 55 
     | 
    
         
            +
                  # @param env [Hash] Rack env
         
     | 
| 
      
 56 
     | 
    
         
            +
                  #
         
     | 
| 
      
 57 
     | 
    
         
            +
                  # @return [Array] serialized Rack response
         
     | 
| 
      
 58 
     | 
    
         
            +
                  #
         
     | 
| 
      
 59 
     | 
    
         
            +
                  # @raise [Hanami::Router::NotRoutableEndpointError] if not routable
         
     | 
| 
      
 60 
     | 
    
         
            +
                  #
         
     | 
| 
      
 61 
     | 
    
         
            +
                  # @since 0.5.0
         
     | 
| 
      
 62 
     | 
    
         
            +
                  # @api public
         
     | 
| 
      
 63 
     | 
    
         
            +
                  #
         
     | 
| 
      
 64 
     | 
    
         
            +
                  # @see Hanami::Routing::RecognizedRoute#routable?
         
     | 
| 
      
 65 
     | 
    
         
            +
                  # @see Hanami::Router::NotRoutableEndpointError
         
     | 
| 
      
 66 
     | 
    
         
            +
                  def call(env)
         
     | 
| 
      
 67 
     | 
    
         
            +
                    if routable?
         
     | 
| 
      
 68 
     | 
    
         
            +
                      @endpoint.call(env)
         
     | 
| 
      
 69 
     | 
    
         
            +
                    else
         
     | 
| 
      
 70 
     | 
    
         
            +
                      raise Hanami::Router::NotRoutableEndpointError.new(@env)
         
     | 
| 
      
 71 
     | 
    
         
            +
                    end
         
     | 
| 
      
 72 
     | 
    
         
            +
                  end
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                  # HTTP verb (aka method)
         
     | 
| 
      
 75 
     | 
    
         
            +
                  #
         
     | 
| 
      
 76 
     | 
    
         
            +
                  # @return [String]
         
     | 
| 
      
 77 
     | 
    
         
            +
                  #
         
     | 
| 
      
 78 
     | 
    
         
            +
                  # @since 0.5.0
         
     | 
| 
      
 79 
     | 
    
         
            +
                  # @api public
         
     | 
| 
      
 80 
     | 
    
         
            +
                  def verb
         
     | 
| 
      
 81 
     | 
    
         
            +
                    @env[REQUEST_METHOD]
         
     | 
| 
      
 82 
     | 
    
         
            +
                  end
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                  # Action name
         
     | 
| 
      
 85 
     | 
    
         
            +
                  #
         
     | 
| 
      
 86 
     | 
    
         
            +
                  # @return [String]
         
     | 
| 
      
 87 
     | 
    
         
            +
                  #
         
     | 
| 
      
 88 
     | 
    
         
            +
                  # @since 0.5.0
         
     | 
| 
      
 89 
     | 
    
         
            +
                  # @api public
         
     | 
| 
      
 90 
     | 
    
         
            +
                  #
         
     | 
| 
      
 91 
     | 
    
         
            +
                  # @see Hanami::Router#recognize
         
     | 
| 
      
 92 
     | 
    
         
            +
                  #
         
     | 
| 
      
 93 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 94 
     | 
    
         
            +
                  #   require 'hanami/router'
         
     | 
| 
      
 95 
     | 
    
         
            +
                  #
         
     | 
| 
      
 96 
     | 
    
         
            +
                  #   router = Hanami::Router.new do
         
     | 
| 
      
 97 
     | 
    
         
            +
                  #     get '/books/:id', to: 'books#show'
         
     | 
| 
      
 98 
     | 
    
         
            +
                  #   end
         
     | 
| 
      
 99 
     | 
    
         
            +
                  #
         
     | 
| 
      
 100 
     | 
    
         
            +
                  #   puts router.recognize('/books/23').action # => "books#show"
         
     | 
| 
      
 101 
     | 
    
         
            +
                  def action
         
     | 
| 
      
 102 
     | 
    
         
            +
                    namespace = NAMESPACE % @namespace
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
      
 104 
     | 
    
         
            +
                    if destination.match(namespace)
         
     | 
| 
      
 105 
     | 
    
         
            +
                      Hanami::Utils::String.new(
         
     | 
| 
      
 106 
     | 
    
         
            +
                        destination.sub(namespace, NAMESPACE_REPLACEMENT)
         
     | 
| 
      
 107 
     | 
    
         
            +
                      ).underscore.rsub(ACTION_PATH_SEPARATOR, @action_separator)
         
     | 
| 
      
 108 
     | 
    
         
            +
                    else
         
     | 
| 
      
 109 
     | 
    
         
            +
                      destination
         
     | 
| 
      
 110 
     | 
    
         
            +
                    end
         
     | 
| 
      
 111 
     | 
    
         
            +
                  end
         
     | 
| 
      
 112 
     | 
    
         
            +
             
     | 
| 
      
 113 
     | 
    
         
            +
                  # Check if routable
         
     | 
| 
      
 114 
     | 
    
         
            +
                  #
         
     | 
| 
      
 115 
     | 
    
         
            +
                  # @return [TrueClass,FalseClass]
         
     | 
| 
      
 116 
     | 
    
         
            +
                  #
         
     | 
| 
      
 117 
     | 
    
         
            +
                  # @since 0.5.0
         
     | 
| 
      
 118 
     | 
    
         
            +
                  # @api public
         
     | 
| 
      
 119 
     | 
    
         
            +
                  #
         
     | 
| 
      
 120 
     | 
    
         
            +
                  # @see Hanami::Router#recognize
         
     | 
| 
      
 121 
     | 
    
         
            +
                  #
         
     | 
| 
      
 122 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 123 
     | 
    
         
            +
                  #   require 'hanami/router'
         
     | 
| 
      
 124 
     | 
    
         
            +
                  #
         
     | 
| 
      
 125 
     | 
    
         
            +
                  #   router = Hanami::Router.new do
         
     | 
| 
      
 126 
     | 
    
         
            +
                  #     get '/', to: 'home#index'
         
     | 
| 
      
 127 
     | 
    
         
            +
                  #   end
         
     | 
| 
      
 128 
     | 
    
         
            +
                  #
         
     | 
| 
      
 129 
     | 
    
         
            +
                  #   puts router.recognize('/').routable?    # => true
         
     | 
| 
      
 130 
     | 
    
         
            +
                  #   puts router.recognize('/foo').routable? # => false
         
     | 
| 
      
 131 
     | 
    
         
            +
                  def routable?
         
     | 
| 
      
 132 
     | 
    
         
            +
                    !!@endpoint
         
     | 
| 
      
 133 
     | 
    
         
            +
                  end
         
     | 
| 
      
 134 
     | 
    
         
            +
             
     | 
| 
      
 135 
     | 
    
         
            +
                  private
         
     | 
| 
      
 136 
     | 
    
         
            +
             
     | 
| 
      
 137 
     | 
    
         
            +
                  # @since 0.5.0
         
     | 
| 
      
 138 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 139 
     | 
    
         
            +
                  #
         
     | 
| 
      
 140 
     | 
    
         
            +
                  # @see Hanami::Routing::Endpoint
         
     | 
| 
      
 141 
     | 
    
         
            +
                  def destination
         
     | 
| 
      
 142 
     | 
    
         
            +
                    @destination ||= begin
         
     | 
| 
      
 143 
     | 
    
         
            +
                      case k = @endpoint.__getobj__
         
     | 
| 
      
 144 
     | 
    
         
            +
                      when Class
         
     | 
| 
      
 145 
     | 
    
         
            +
                        k
         
     | 
| 
      
 146 
     | 
    
         
            +
                      else
         
     | 
| 
      
 147 
     | 
    
         
            +
                        k.class
         
     | 
| 
      
 148 
     | 
    
         
            +
                      end.name
         
     | 
| 
      
 149 
     | 
    
         
            +
                    end
         
     | 
| 
      
 150 
     | 
    
         
            +
                  end
         
     | 
| 
      
 151 
     | 
    
         
            +
                end
         
     | 
| 
      
 152 
     | 
    
         
            +
              end
         
     | 
| 
      
 153 
     | 
    
         
            +
            end
         
     |