Merge pull request #50 from sdogruyol/route-refactor

Route refactor
This commit is contained in:
Serdar Dogruyol 2016-01-13 10:26:27 +02:00
commit 54f25c5880
6 changed files with 30 additions and 68 deletions

View file

@ -2,25 +2,6 @@ require "./spec_helper"
describe "Route" do
describe "match?" do
it "doesn't match because of route" do
route = Route.new("GET", "/foo/bar") { "" }
request = HTTP::Request.new("GET", "/world?message=coco")
route.match?(request).should be_nil
end
it "doesn't match because of method" do
route = Route.new("GET", "/foo/bar") { "" }
request = HTTP::Request.new("POST", "/foo/bar")
route.match?(request).should be_nil
end
it "matches" do
route = Route.new("GET", "/foo/:one/path/:two") { "" }
request = HTTP::Request.new("GET", "/foo/uno/path/dos")
match = route.match?(request)
match.should eq true
end
it "matches the correct route" do
kemal = Kemal::Handler.new
kemal.add_route "GET", "/route1" do |env|

View file

@ -3,10 +3,10 @@
class Kemal::Context
getter request
getter response
getter params
getter route
getter content_type
def initialize(@request, @params)
def initialize(@request, @route)
@response = Kemal::Response.new
end
@ -22,6 +22,10 @@ class Kemal::Context
@response.content_type
end
def params
Kemal::ParamParser.new(@route, @request).parse
end
delegate headers, @request
delegate status_code, :"status_code=", :"content_type=", @response
end

View file

@ -1,5 +1,4 @@
require "http/server"
require "uri"
# Kemal::Handler is the main handler which handles all the HTTP requests. Routing, parsing, rendering e.g
# are done in this handler.
@ -24,11 +23,11 @@ class Kemal::Handler < HTTP::Handler
end
def process_request(request)
url = request.path.not_nil!
@routes.each do |route|
match = route.match?(request)
if match
params = Kemal::ParamParser.new(route, request).parse
context = Context.new(request, params)
url.match(route.pattern as Regex) do |url_params|
request.url_params = url_params
context = Context.new(request, route)
begin
body = route.handler.call(context).to_s
return HTTP::Response.new(context.status_code, body, context.response_headers)

View file

@ -11,17 +11,15 @@ class Kemal::ParamParser
APPLICATION_JSON = "application/json"
def initialize(@route, @request)
@route_components = route.components
@request_components = request.path.not_nil!.split "/"
@params = {} of String => AllParamTypes
end
def parse
parse_components
parse_request
end
def parse_request
parse_url_params
parse_query
parse_body
parse_json
@ -37,6 +35,18 @@ class Kemal::ParamParser
parse_part(@request.query)
end
# Ditto: This needs memoization without the huge AllParamTypes union :|
def parse_url_params
if @request.url_params
url_params = @request.url_params.not_nil!
name_table = url_params.regex.name_table
url_params.size.times do |i|
name = (name_table.fetch(i + 1) { i + 1 }) as String
@params[name] = url_params[i + 1]
end
end
end
# Parses JSON request body if Content-Type is `application/json`.
# If request body is a JSON Hash then all the params are parsed and added into `params`.
# If request body is a JSON Array it's added into `params` as `_json` and can be accessed
@ -45,7 +55,6 @@ class Kemal::ParamParser
return unless @request.body && @request.headers["Content-Type"]? == APPLICATION_JSON
body = @request.body as String
case json = JSON.parse(body).raw
when Hash
json.each do |k, v|
@ -63,13 +72,4 @@ class Kemal::ParamParser
end
end
def parse_components
@route_components.zip(@request_components) do |route_component, req_component|
if route_component.starts_with? ':'
@params[route_component[1..-1]] = req_component
else
return nil unless route_component == req_component
end
end
end
end

View file

@ -1,4 +1,5 @@
# Opening HTTP::Request to add override_method property
class HTTP::Request
property override_method
property url_params
end

View file

@ -3,40 +3,17 @@
# what action to be done if the route is matched.
class Kemal::Route
getter handler
getter components
getter pattern
def initialize(@method, path, &@handler : Kemal::Context -> _)
@components = path.split "/"
@pattern = pattern_to_regex path
end
def match?(request)
check_for_method_override!(request)
return nil unless request.override_method == @method
components = request.path.not_nil!.split "/"
return nil unless components.size == @components.size
@components.zip(components) do |route_component, req_component|
unless route_component.starts_with? ':'
return nil unless route_component == req_component
end
private def pattern_to_regex(pattern)
pattern = pattern.gsub(/\:(?<param>\w+)/) do |_, match|
"(?<#{match["param"]}>.*)"
end
true
Regex.new "^#{pattern}/?$"
end
# Checks if request params contain _method param to override request incoming method
def check_for_method_override!(request)
request.override_method = request.method
if request.method == "POST"
params = Kemal::ParamParser.new(self, request).parse_request
if params.has_key?("_method") && self.override_method_valid?(params["_method"])
request.override_method = params["_method"]
end
end
end
# Checks if method contained in _method param is valid one
def override_method_valid?(override_method)
return false unless override_method.is_a?(String)
override_method = override_method.upcase
return (override_method == "PUT" || override_method == "PATCH" || override_method == "DELETE")
end
end