Extract Base::Builder and Base::DSL from Kemal::Base

This commit is contained in:
Johannes Müller 2017-07-17 22:11:02 +02:00 committed by sdogruyol
parent f5c80c7b67
commit 29b18c927c
7 changed files with 223 additions and 143 deletions

View file

@ -38,7 +38,7 @@ describe "Config" do
application = Kemal::Application.new
application.add_handler CustomTestHandler.new
application.setup
application.handlers.size.should eq 9
application.handlers.size.should eq 8
end
it "toggles the shutdown message" do

View file

@ -12,7 +12,7 @@ describe "Macros" do
it "adds a custom handler" do
add_handler CustomTestHandler.new
Kemal.application.setup
Kemal.application.handlers.size.should eq 7
Kemal.application.handlers.size.should eq 8
end
end

View file

@ -1,7 +1,6 @@
class Kemal::Application < Kemal::Base
def initialize(config = Config.default)
super config
add_filter_handler(filter_handler)
super(config)
end
# Overload of self.run with the default startup logging

View file

@ -1,4 +1,5 @@
require "./helpers/*"
require "./base/*"
# Kemal Base
# The DSL currently consists of
@ -10,19 +11,18 @@ class Kemal::Base
include FileHelpers
include Templates
include Macros
include Base::DSL
include Base::Builder
HTTP_METHODS = %w(get post put patch delete options)
FILTER_METHODS = %w(get post put patch delete options all)
# :nodoc:
getter route_handler = Kemal::RouteHandler.new
# :nodoc:
getter filter_handler = Kemal::FilterHandler.new
# :nodoc:
getter websocket_handler = Kemal::WebSocketHandler.new
getter handlers = [] of HTTP::Handler
getter custom_handlers = [] of Tuple(Nil | Int32, HTTP::Handler)
getter filter_handlers = [] of HTTP::Handler
getter error_handlers = {} of Int32 => HTTP::Server::Context, Exception -> String
@handler_position = 0
getter config : Config
@ -31,133 +31,11 @@ class Kemal::Base
property? running = false
def initialize(@config = Config.base)
@logger = if @config.logging?
Kemal::LogHandler.new
else
Kemal::NullLogHandler.new
end
add_filter_handler(filter_handler)
end
@filter_handler = FilterHandler.new(self)
@route_handler = RouteHandler.new(self)
@websocket_handler = WebSocketHandler.new(self)
{% for method in HTTP_METHODS %}
def {{method.id}}(path, &block : HTTP::Server::Context -> _)
raise Kemal::Exceptions::InvalidPathStartException.new({{method}}, path) unless Kemal::Utils.path_starts_with_slash?(path)
route_handler.add_route({{method}}.upcase, path, &block)
end
{% end %}
def ws(path, &block : HTTP::WebSocket, HTTP::Server::Context -> Void)
raise Kemal::Exceptions::InvalidPathStartException.new("ws", path) unless Kemal::Utils.path_starts_with_slash?(path)
websocket_handler.add_route path, &block
end
def error(status_code, &block : HTTP::Server::Context, Exception -> _)
add_error_handler status_code, &block
end
# All the helper methods available are:
# - before_all, before_get, before_post, before_put, before_patch, before_delete, before_options
# - after_all, after_get, after_post, after_put, after_patch, after_delete, after_options
{% for type in ["before", "after"] %}
{% for method in FILTER_METHODS %}
def {{type.id}}_{{method.id}}(path = "*", &block : HTTP::Server::Context -> _)
filter_handler.{{type.id}}({{method}}.upcase, path, &block)
end
{% end %}
{% end %}
def clear
@router_included = false
@handler_position = 0
@default_handlers_setup = false
handlers.clear
custom_handlers.clear
filter_handlers.clear
error_handlers.clear
route_handler.clear
websocket_handler.clear
end
def handlers=(handlers : Array(HTTP::Handler))
clear
@handlers.replace(handlers)
end
def add_handler(handler : HTTP::Handler)
@custom_handlers << {nil, handler}
end
def add_handler(handler : HTTP::Handler, position : Int32)
@custom_handlers << {position, handler}
end
def add_filter_handler(handler : HTTP::Handler)
@filter_handlers << handler
end
def add_error_handler(status_code, &handler : HTTP::Server::Context, Exception -> _)
@error_handlers[status_code] = ->(context : HTTP::Server::Context, error : Exception) { handler.call(context, error).to_s }
end
def setup
unless @default_handlers_setup && @router_included
setup_init_handler
setup_log_handler
setup_error_handler
setup_static_file_handler
setup_custom_handlers
setup_filter_handlers
@default_handlers_setup = true
@router_included = true
handlers.insert(handlers.size, websocket_handler)
handlers.insert(handlers.size, route_handler)
end
end
private def setup_init_handler
@handlers.insert(@handler_position, Kemal::InitHandler.new(self))
@handler_position += 1
end
private def setup_log_handler
@handlers.insert(@handler_position, logger)
@handler_position += 1
end
private def setup_error_handler
if @config.always_rescue?
@error_handler ||= Kemal::ExceptionHandler.new
@handlers.insert(@handler_position, @error_handler.not_nil!)
@handler_position += 1
end
end
private def setup_static_file_handler
if @config.serve_static.is_a?(Hash)
@handlers.insert(@handler_position, Kemal::StaticFileHandler.new(@config.public_folder))
@handler_position += 1
end
end
private def setup_custom_handlers
@custom_handlers.each do |ch|
position = ch[0]
if !position
@handlers.insert(@handler_position, ch[1])
@handler_position += 1
else
@handlers.insert(position, ch[1])
@handler_position += 1
end
end
end
private def setup_filter_handlers
@filter_handlers.each do |h|
@handlers.insert(@handler_position, h)
end
initialize_defaults
end
# Overload of self.run with the default startup logging
@ -180,6 +58,11 @@ class Kemal::Base
end
end
# DEPRECATED: This method should be replaced with `#running?`
def running
running?
end
private def prepare_for_server_start
unless @config.env == "test"
Signal::INT.trap do

108
src/kemal/base/builder.cr Normal file
View file

@ -0,0 +1,108 @@
class Kemal::Base
module Builder
getter custom_handlers = [] of Tuple(Nil | Int32, HTTP::Handler)
getter filter_handlers = [] of HTTP::Handler
@handler_position = 0
def clear
@router_included = false
@handler_position = 0
@default_handlers_setup = false
handlers.clear
custom_handlers.clear
filter_handlers.clear
error_handlers.clear
route_handler.clear
websocket_handler.clear
end
def handlers=(handlers : Array(HTTP::Handler))
clear
@handlers.replace(handlers)
end
def add_handler(handler : HTTP::Handler)
@custom_handlers << {nil, handler}
end
def add_handler(handler : HTTP::Handler, position : Int32)
@custom_handlers << {position, handler}
end
def add_filter_handler(handler : HTTP::Handler)
@filter_handlers << handler
end
def add_error_handler(status_code, &handler : HTTP::Server::Context, Exception -> _)
@error_handlers[status_code] = ->(context : HTTP::Server::Context, error : Exception) { handler.call(context, error).to_s }
end
def setup
@logger = if @config.logging?
LogHandler.new
else
NullLogHandler.new
end
unless @default_handlers_setup && @router_included
setup_init_handler
setup_log_handler
setup_error_handler
setup_static_file_handler
setup_custom_handlers
setup_filter_handlers
@default_handlers_setup = true
@router_included = true
handlers.insert(handlers.size, websocket_handler)
handlers.insert(handlers.size, route_handler)
end
end
private def setup_init_handler
@handlers.insert(@handler_position, Kemal::InitHandler.new(self))
@handler_position += 1
end
private def setup_log_handler
@handlers.insert(@handler_position, logger)
@handler_position += 1
end
private def setup_error_handler
if @config.always_rescue?
error_handler = @error_handler ||= Kemal::ExceptionHandler.new
@handlers.insert(@handler_position, error_handler)
@handler_position += 1
end
end
private def setup_static_file_handler
if @config.serve_static.is_a?(Hash)
@handlers.insert(@handler_position, Kemal::StaticFileHandler.new(@config.public_folder))
@handler_position += 1
end
end
# Handle WebSocketHandler
private def setup_custom_handlers
@custom_handlers.each do |ch|
position = ch[0]
if !position
@handlers.insert(@handler_position, ch[1])
@handler_position += 1
else
@handlers.insert(position, ch[1])
@handler_position += 1
end
end
end
private def setup_filter_handlers
@handlers.insert(@handler_position, filter_handler)
@filter_handlers.each do |h|
@handlers.insert(@handler_position, h)
end
end
end
end

91
src/kemal/base/dsl.cr Normal file
View file

@ -0,0 +1,91 @@
class Kemal::Base
module DSL
HTTP_METHODS = %w(get post put patch delete options)
FILTER_METHODS = %w(get post put patch delete options all)
macro included
# :nodoc:
DEFAULT_HANDLERS = [] of {String, String, (HTTP::Server::Context -> Nil)}
# :nodoc:
WEBSOCKET_HANDLERS = [] of {String, (HTTP::WebSocket, HTTP::Server::Context -> Void)}
# :nodoc:
DEFAULT_ERROR_HANDLERS = [] of {Int32, (HTTP::Server::Context, Exception -> Nil)}
# :nodoc:
DEFAULT_FILTERS = [] of {Symbol, String, String, (HTTP::Server::Context -> Nil)}
end
{% for method in HTTP_METHODS %}
def {{method.id}}(path, &block : HTTP::Server::Context -> _)
raise Kemal::Exceptions::InvalidPathStartException.new({{method}}, path) unless Kemal::Utils.path_starts_with_slash?(path)
route_handler.add_route({{method}}.upcase, path, &block)
end
{% end %}
def ws(path, &block : HTTP::WebSocket, HTTP::Server::Context -> Void)
raise Kemal::Exceptions::InvalidPathStartException.new("ws", path) unless Kemal::Utils.path_starts_with_slash?(path)
websocket_handler.add_route path, &block
end
def error(status_code, &block : HTTP::Server::Context, Exception -> _)
add_error_handler status_code, &block
end
# All the helper methods available are:
# - before_all, before_get, before_post, before_put, before_patch, before_delete, before_options
# - after_all, after_get, after_post, after_put, after_patch, after_delete, after_options
{% for type in ["before", "after"] %}
{% for method in FILTER_METHODS %}
def {{type.id}}_{{method.id}}(path = "*", &block : HTTP::Server::Context -> _)
filter_handler.{{type.id}}({{method}}.upcase, path, &block)
end
{% end %}
{% end %}
private def initialize_defaults
DEFAULT_HANDLERS.each do |method, path, block|
route_handler.add_route(method.upcase, path, &block)
end
WEBSOCKET_HANDLERS.each do |path, block|
ws(path, &block)
end
DEFAULT_ERROR_HANDLERS.each do |status_code, block|
add_error_handler status_code, &block
end
DEFAULT_FILTERS.each do |type, method, path, block|
if type == :before
filter_handler.before(method, path, &block)
else
filter_handler.after(method, path, &block)
end
end
end
{% for method in HTTP_METHODS %}
def self.{{method.id}}(path, &block : HTTP::Server::Context -> _)
DEFAULT_HANDLERS << { {{method}}, path, block }
end
{% end %}
def self.ws(path, &block : HTTP::WebSocket, HTTP::Server::Context -> Void)
WEBSOCKET_HANDLERS << {path, block}
end
def self.error(status_code, &block : HTTP::Server::Context, Exception -> _)
DEFAULT_ERROR_HANDLERS << {status_code, block}
end
# All the helper methods available are:
# - before_all, before_get, before_post, before_put, before_patch, before_delete, before_options
# - after_all, after_get, after_post, after_put, after_patch, after_delete, after_options
{% for type in [:before, :after] %}
{% for method in FILTER_METHODS %}
def self.{{type.id}}_{{method.id}}(path = "*", &block : HTTP::Server::Context -> _)
DEFAULT_FILTERS << { {{type}}, {{method}}, path, block }
end
{% end %}
{% end %}
end
end

View file

@ -54,7 +54,6 @@ module Kemal
(config.is_a?(Hash) && config[key]?) || false
end
def extra_options(&@extra_options : OptionParser ->)
end
@ -65,12 +64,12 @@ module Kemal
# Creates a config with basic value (disabled logging, disabled serve_static, disabled shutdown_message)
def self.base
new.tap do |config|
config.logging = false
config.serve_static = false
config.shutdown_message = false
config.always_rescue = false
end
new(
logging: false,
serve_static: false,
shutdown_message: false,
always_rescue: false,
)
end
end
end