diff --git a/spec/middleware/filters_spec.cr b/spec/middleware/filters_spec.cr index 3dc2e9e..599e7fe 100644 --- a/spec/middleware/filters_spec.cr +++ b/spec/middleware/filters_spec.cr @@ -143,6 +143,46 @@ describe "Kemal::Middleware::Filters" do client_response = HTTP::Client::Response.from_io(io_with_context, decompress: false) client_response.body.should eq("false") end + + it "executes 3 differents blocks after all request" do + test_filter = FilterTest.new + test_filter.modified = "false" + test_filter_second = FilterTest.new + test_filter_second.modified = "false" + test_filter_third = FilterTest.new + test_filter_third.modified = "false" + + filter_middleware = Kemal::Middleware::Filter.new + filter_middleware._add_route_filter("ALL", "/greetings", :before) { test_filter.modified = test_filter.modified == "true" ? "false" : "true" } + filter_middleware._add_route_filter("ALL", "/greetings", :before) { test_filter_second.modified = test_filter_second.modified == "true" ? "false" : "true" } + filter_middleware._add_route_filter("ALL", "/greetings", :before) { test_filter_third.modified = test_filter_third.modified == "true" ? "false" : "true" } + + kemal = Kemal::RouteHandler::INSTANCE + kemal.add_route "GET", "/greetings" { test_filter.modified } + kemal.add_route "POST", "/greetings" { test_filter_second.modified } + kemal.add_route "PUT", "/greetings" { test_filter_third.modified } + + test_filter.modified.should eq("false") + test_filter_second.modified.should eq("false") + test_filter_third.modified.should eq("false") + request = HTTP::Request.new("GET", "/greetings") + create_request_and_return_io(filter_middleware, request) + io_with_context = create_request_and_return_io(kemal, request) + client_response = HTTP::Client::Response.from_io(io_with_context, decompress: false) + client_response.body.should eq("true") + + request = HTTP::Request.new("POST", "/greetings") + create_request_and_return_io(filter_middleware, request) + io_with_context = create_request_and_return_io(kemal, request) + client_response = HTTP::Client::Response.from_io(io_with_context, decompress: false) + client_response.body.should eq("false") + + request = HTTP::Request.new("PUT", "/greetings") + create_request_and_return_io(filter_middleware, request) + io_with_context = create_request_and_return_io(kemal, request) + client_response = HTTP::Client::Response.from_io(io_with_context, decompress: false) + client_response.body.should eq("true") + end end class FilterTest diff --git a/src/kemal/middleware/filters.cr b/src/kemal/middleware/filters.cr index 941af18..0402087 100644 --- a/src/kemal/middleware/filters.cr +++ b/src/kemal/middleware/filters.cr @@ -21,55 +21,50 @@ module Kemal::Middleware context end - # This checks is filter is already defined for the verb/path/type combination - def filter_for_path_type_defined?(verb, path, type) - lookup = @tree.find radix_path(verb, path, type) - lookup.found? && lookup.payload.is_a? Block - end - # :nodoc: This shouldn't be called directly, it's not private because I need to call it for testing purpose since I can't call the macros in the spec. # It adds the block for the corresponding verb/path/type combination to the tree. def _add_route_filter(verb, path, type, &block : HTTP::Server::Context -> _) - node = radix_path(verb, path, type) - @tree.add node, Block.new &block + lookup = lookup_filters_for_path_type(verb, path, type) + if lookup.found? && lookup.payload.is_a?(Array(Block)) + (lookup.payload as Array(Block)) << Block.new(&block) + else + @tree.add radix_path(verb, path, type), [Block.new(&block)] + end end # This can be called directly but it's simpler to just use the macros, it will check if another filter is not already defined for this verb/path/type and proceed to call `add_route_filter` def before(verb, path = "*", &block : HTTP::Server::Context -> _) - raise Kemal::Middleware::Filter::BeforeFilterAlreadyDefinedException.new(verb, path) if filter_for_path_type_defined?(verb, path, :before) _add_route_filter verb, path, :before, &block end # This can be called directly but it's simpler to just use the macros, it will check if another filter is not already defined for this verb/path/type and proceed to call `add_route_filter` def after(verb, path = "*", &block : HTTP::Server::Context -> _) - raise Kemal::Middleware::Filter::AfterFilterAlreadyDefinedException.new(verb, path) if filter_for_path_type_defined?(verb, path, :after) _add_route_filter verb, path, :after, &block end # This will fetch the block for the verb/path/type from the tree and call it. private def call_block_for_path_type(verb, path, type, context) - lookup = @tree.find radix_path(verb, path, type) - if lookup.found? && lookup.payload.is_a? Block - block = lookup.payload as Block - block.block.call(context) + lookup = lookup_filters_for_path_type(verb, path, type) + if lookup.found? && lookup.payload.is_a? Array(Block) + blocks = lookup.payload as Array(Block) + blocks.each { |block| block.call(context) } end end + # This checks is filter is already defined for the verb/path/type combination + private def filter_for_path_type_defined?(verb, path, type) + lookup = @tree.find radix_path(verb, path, type) + lookup.found? && lookup.payload.is_a? Block + end + + # This returns a lookup for verb/path/type + private def lookup_filters_for_path_type(verb, path, type) + @tree.find radix_path(verb, path, type) + end + private def radix_path(verb, path, type : Symbol) "#{type}/#{verb}/#{path}" end - - class BeforeFilterAlreadyDefinedException < Exception - def initialize(verb, path) - super "A before-filter is already defined for path: '#{verb}:#{path}'." - end - end - - class AfterFilterAlreadyDefinedException < Exception - def initialize(verb, path) - super "An after-filter is already defined for path: '#{verb}:#{path}'." - end - end end class Block @@ -77,6 +72,10 @@ module Kemal::Middleware def initialize(&@block : HTTP::Server::Context -> _) end + + def call(context) + @block.call(context) + end end end