New ROUTER design, much simpler, less generic, easier to extend, and now one can mix uri map, uri_template map and so on.
Update the "tutorial" example.
This commit is contained in:
@@ -1,111 +1,120 @@
|
||||
note
|
||||
description: "[
|
||||
URL dispatcher/router based on deferred mapping (to be defined in descendant)
|
||||
The associated context {WSF_HANDLER_CONTEXT} does contains information related to the matching at runtime.
|
||||
]"
|
||||
description: "Summary description for {EWF_ROUTER}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
WSF_ROUTER [H -> WSF_HANDLER [C], C -> WSF_HANDLER_CONTEXT]
|
||||
class
|
||||
WSF_ROUTER
|
||||
|
||||
inherit
|
||||
ITERABLE [TUPLE [handler: H; resource: READABLE_STRING_8; request_methods: detachable WSF_ROUTER_METHODS]]
|
||||
ITERABLE [TUPLE [mapping: WSF_ROUTER_MAPPING; request_methods: detachable WSF_ROUTER_METHODS]]
|
||||
|
||||
create
|
||||
make,
|
||||
make_with_base_url
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (n: INTEGER)
|
||||
do
|
||||
create mappings.make (n)
|
||||
initialize
|
||||
end
|
||||
|
||||
make_with_base_url (n: INTEGER; a_base_url: like base_url)
|
||||
-- Make router allocated for at least `n' maps,
|
||||
-- and use `a_base_url' as base_url
|
||||
do
|
||||
make (n)
|
||||
set_base_url (a_base_url)
|
||||
end
|
||||
|
||||
initialize
|
||||
-- Initialize router
|
||||
do
|
||||
create pre_route_execution_actions
|
||||
create mappings.make (10)
|
||||
create pre_execution_actions
|
||||
end
|
||||
|
||||
feature -- Status report
|
||||
|
||||
has_map (a_resource: READABLE_STRING_8; rqst_methods: detachable WSF_ROUTER_METHODS; a_handler: detachable H): BOOLEAN
|
||||
-- Has a map corresponding to `a_resource' and `rqst_methods' other than `a_handler'?
|
||||
do
|
||||
if attached handlers_matching_map (a_resource, rqst_methods) as lst then
|
||||
Result := a_handler = Void or else not lst.has (a_handler)
|
||||
end
|
||||
end
|
||||
|
||||
handlers_matching_map (a_resource: READABLE_STRING_8; rqst_methods: detachable WSF_ROUTER_METHODS): detachable LIST [H]
|
||||
-- Existing handlers matching map with `a_resource' and `rqst_methods'
|
||||
deferred
|
||||
end
|
||||
mappings: ARRAYED_LIST [TUPLE [mapping: WSF_ROUTER_MAPPING; request_methods: detachable WSF_ROUTER_METHODS]]
|
||||
-- Existing mappings
|
||||
|
||||
feature -- Mapping
|
||||
|
||||
map (a_resource: READABLE_STRING_8; h: H)
|
||||
-- Map handler `h' with `a_resource'
|
||||
require
|
||||
has_not_such_map: not has_map (a_resource, Void, h)
|
||||
map (a_mapping: WSF_ROUTER_MAPPING)
|
||||
-- Map `a_mapping'
|
||||
do
|
||||
map_with_request_methods (a_resource, h, Void)
|
||||
map_with_request_methods (a_mapping, Void)
|
||||
end
|
||||
|
||||
map_routing (a_resource: READABLE_STRING_8; h: H)
|
||||
-- Map handler `h' with `a_resource'
|
||||
require
|
||||
has_not_such_map: not has_map (a_resource, Void, h)
|
||||
map_with_request_methods (a_mapping: WSF_ROUTER_MAPPING; rqst_methods: detachable WSF_ROUTER_METHODS)
|
||||
-- Map `a_mapping' for request methods `rqst_methods'
|
||||
do
|
||||
map (a_resource, h)
|
||||
mappings.extend ([a_mapping, rqst_methods])
|
||||
a_mapping.handler.on_mapped (a_mapping, rqst_methods)
|
||||
end
|
||||
|
||||
map_with_request_methods (a_resource: READABLE_STRING_8; h: H; rqst_methods: detachable WSF_ROUTER_METHODS)
|
||||
-- Map handler `h' with `a_resource' and `rqst_methods'
|
||||
require
|
||||
has_not_such_map: not has_map (a_resource, rqst_methods, h)
|
||||
deferred
|
||||
end
|
||||
feature -- Access
|
||||
|
||||
feature -- Mapping agent
|
||||
is_dispatched: BOOLEAN
|
||||
-- `dispatch' set `is_dispatched' to True
|
||||
-- if handler was found and executed
|
||||
|
||||
map_agent (a_resource: READABLE_STRING_8; a_action: PROCEDURE [ANY, TUPLE [ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE]])
|
||||
-- Map `a_action' as an handler with `a_resource'
|
||||
dispatch (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
do
|
||||
map_agent_with_request_methods (a_resource, a_action, Void)
|
||||
end
|
||||
|
||||
map_agent_with_request_methods (a_resource: READABLE_STRING_8; a_action: PROCEDURE [ANY, TUPLE [ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE]];
|
||||
rqst_methods: detachable WSF_ROUTER_METHODS)
|
||||
-- Map `a_action' as an handler with `a_resource' and `rqst_methods'
|
||||
local
|
||||
rah: WSF_AGENT_HANDLER [C]
|
||||
do
|
||||
create rah.make (a_action)
|
||||
if attached {H} rah as h then
|
||||
map_with_request_methods (a_resource, h, rqst_methods)
|
||||
else
|
||||
check valid_agent_handler: False end
|
||||
if attached dispatch_and_return_handler (req, res) then
|
||||
check is_dispatched: is_dispatched end
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Mapping response agent
|
||||
|
||||
map_agent_response (a_resource: READABLE_STRING_8; a_function: FUNCTION [ANY, TUPLE [ctx: C; req: WSF_REQUEST], WSF_RESPONSE_MESSAGE])
|
||||
-- Map response as Result `a_function' as an handler with `a_resource'
|
||||
do
|
||||
map_agent_response_with_request_methods (a_resource, a_function, Void)
|
||||
end
|
||||
|
||||
map_agent_response_with_request_methods (a_resource: READABLE_STRING_8; a_function: FUNCTION [ANY, TUPLE [ctx: C; req: WSF_REQUEST], WSF_RESPONSE_MESSAGE];
|
||||
rqst_methods: detachable WSF_ROUTER_METHODS)
|
||||
-- Map response as Result `a_function' as an handler with `a_resource' and `rqst_methods'
|
||||
dispatch_and_return_handler (req: WSF_REQUEST; res: WSF_RESPONSE): detachable WSF_HANDLER
|
||||
local
|
||||
rah: WSF_AGENT_RESPONSE_HANDLER [C]
|
||||
l_req_method: READABLE_STRING_8
|
||||
m: WSF_ROUTER_MAPPING
|
||||
do
|
||||
create rah.make (a_function)
|
||||
if attached {H} rah as h then
|
||||
map_with_request_methods (a_resource, h, rqst_methods)
|
||||
else
|
||||
check valid_agent_handler: False end
|
||||
is_dispatched := False
|
||||
l_req_method := request_method (req)
|
||||
|
||||
across
|
||||
mappings as c
|
||||
until
|
||||
Result /= Void
|
||||
loop
|
||||
if attached c.item as l_info then
|
||||
if is_matching_request_methods (l_req_method, l_info.request_methods) then
|
||||
m := l_info.mapping
|
||||
if attached m.routed_handler (req, res, Current) as r then
|
||||
is_dispatched := True
|
||||
Result := r
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Hook
|
||||
|
||||
execute_before (a_mapping: WSF_ROUTER_MAPPING)
|
||||
do
|
||||
pre_execution_actions.call ([a_mapping])
|
||||
end
|
||||
|
||||
execute_after (a_mapping: WSF_ROUTER_MAPPING)
|
||||
do
|
||||
end
|
||||
|
||||
pre_execution_actions: ACTION_SEQUENCE [TUPLE [WSF_ROUTER_MAPPING]]
|
||||
-- Action triggered before a route is execute
|
||||
--| Could be used for tracing, logging
|
||||
|
||||
feature -- Base url
|
||||
|
||||
count: INTEGER
|
||||
do
|
||||
Result := mappings.count
|
||||
end
|
||||
|
||||
base_url: detachable READABLE_STRING_8
|
||||
-- Common start of any route url
|
||||
|
||||
@@ -115,6 +124,7 @@ feature -- Element change
|
||||
-- Set `base_url' to `a_base_url'
|
||||
-- make sure no map is already added (i.e: count = 0)
|
||||
require
|
||||
a_valid_base_url: (a_base_url /= Void and then a_base_url.is_empty) implies (a_base_url.starts_with ("/") and not a_base_url.ends_with ("/"))
|
||||
no_handler_set: count = 0
|
||||
do
|
||||
if a_base_url = Void or else a_base_url.is_empty then
|
||||
@@ -124,105 +134,12 @@ feature -- Element change
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Hook
|
||||
|
||||
pre_route_execution_actions: ACTION_SEQUENCE [TUPLE [like route]]
|
||||
-- Action triggered before a route is execute
|
||||
--| Could be used for tracing, logging
|
||||
|
||||
feature -- Routing
|
||||
|
||||
route (req: WSF_REQUEST): detachable WSF_ROUTE [H, C]
|
||||
-- Route matching `req'.
|
||||
do
|
||||
Result := matching_route (req)
|
||||
end
|
||||
|
||||
feature -- Execution
|
||||
|
||||
execute_route (a_route: WSF_ROUTE [H,C]; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Process route `a_route'
|
||||
require
|
||||
a_route_attached: a_route /= Void
|
||||
local
|
||||
ctx: C
|
||||
do
|
||||
pre_route_execution_actions.call ([a_route])
|
||||
ctx := a_route.context
|
||||
ctx.apply (req)
|
||||
a_route.handler.execute (ctx, req, res)
|
||||
ctx.revert (req)
|
||||
rescue
|
||||
a_route.context.revert (req)
|
||||
end
|
||||
|
||||
dispatch (req: WSF_REQUEST; res: WSF_RESPONSE): BOOLEAN
|
||||
-- Dispatch `req, res' to the associated handler
|
||||
-- And return True is handled, otherwise False
|
||||
do
|
||||
if attached route (req) as r then
|
||||
Result := True
|
||||
execute_route (r, req, res)
|
||||
end
|
||||
end
|
||||
|
||||
dispatch_and_return_handler (req: WSF_REQUEST; res: WSF_RESPONSE): detachable H
|
||||
-- Dispatch `req, res' to the associated handler
|
||||
-- And return this handler
|
||||
-- If Result is Void, this means no handler was found.
|
||||
do
|
||||
if attached route (req) as r then
|
||||
Result := r.handler
|
||||
execute_route (r, req, res)
|
||||
end
|
||||
end
|
||||
|
||||
feature {WSF_ROUTED_SERVICE_I} -- Implementation
|
||||
|
||||
default_handler_context (req: WSF_REQUEST): C
|
||||
-- Default handler context associated with `req'.
|
||||
--| It can be used to build a context if needed.
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- status report
|
||||
|
||||
count: INTEGER
|
||||
-- Count of maps handled by current
|
||||
do
|
||||
across
|
||||
Current as curs
|
||||
loop
|
||||
if attached {WSF_ROUTING_HANDLER [H, C]} curs.item.handler as rh then
|
||||
Result := Result + rh.count + 1 --| +1 for the handler itself
|
||||
else
|
||||
Result := Result + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Traversing
|
||||
|
||||
new_cursor: ITERATION_CURSOR [TUPLE [handler: H; resource: READABLE_STRING_8; request_methods: detachable WSF_ROUTER_METHODS]]
|
||||
new_cursor: ITERATION_CURSOR [TUPLE [mapping: WSF_ROUTER_MAPPING; request_methods: detachable WSF_ROUTER_METHODS]]
|
||||
-- Fresh cursor associated with current structure
|
||||
deferred
|
||||
end
|
||||
|
||||
feature {WSF_ROUTED_SERVICE_I} -- Handler
|
||||
|
||||
source_uri (req: WSF_REQUEST): READABLE_STRING_32
|
||||
-- URI to use to find handler.
|
||||
do
|
||||
Result := req.path_info
|
||||
end
|
||||
|
||||
matching_route (req: WSF_REQUEST): detachable WSF_ROUTE [H, C]
|
||||
-- Handler whose map matched with `req' with associated Context
|
||||
require
|
||||
req_valid: source_uri (req) /= Void
|
||||
deferred
|
||||
ensure
|
||||
source_uri_unchanged: source_uri (req).same_string (old source_uri (req))
|
||||
Result := mappings.new_cursor
|
||||
end
|
||||
|
||||
feature -- Request methods helper
|
||||
@@ -343,14 +260,4 @@ feature {NONE} -- Access: Implementation
|
||||
end
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user