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:
Jocelyn Fiat
2012-09-19 10:50:27 +02:00
parent 0f59535696
commit ace897ea2b
31 changed files with 668 additions and 1359 deletions
@@ -1,50 +0,0 @@
note
description: "[
Default router based on URI map
]"
date: "$Date$"
revision: "$Revision$"
class
WSF_URI_ROUTER
inherit
WSF_URI_ROUTER_I [WSF_HANDLER [WSF_URI_HANDLER_CONTEXT], WSF_URI_HANDLER_CONTEXT]
redefine
map_agent_with_request_methods, map_agent_response_with_request_methods
end
create
make
feature -- Mapping agent
map_agent_with_request_methods (a_id: READABLE_STRING_8; a_action: PROCEDURE [ANY, TUPLE [ctx: WSF_URI_HANDLER_CONTEXT; req: WSF_REQUEST; res: WSF_RESPONSE]];
rqst_methods: detachable WSF_ROUTER_METHODS)
local
h: WSF_AGENT_HANDLER [WSF_URI_HANDLER_CONTEXT]
do
create h.make (a_action)
map_with_request_methods (a_id, h, rqst_methods)
end
map_agent_response_with_request_methods (a_id: READABLE_STRING_8; a_action: FUNCTION [ANY, TUPLE [ctx: WSF_URI_HANDLER_CONTEXT; req: WSF_REQUEST], WSF_RESPONSE_MESSAGE];
rqst_methods: detachable WSF_ROUTER_METHODS)
local
h: WSF_AGENT_RESPONSE_HANDLER [WSF_URI_HANDLER_CONTEXT]
do
create h.make (a_action)
map_with_request_methods (a_id, h, rqst_methods)
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
@@ -1,35 +0,0 @@
note
description: "[
WSF_URI_ROUTING_HANDLER is a default descendant of WSF_URI_ROUTING_HANDLER_I
for WSF_URI_ROUTER
]"
date: "$Date$"
revision: "$Revision$"
class
WSF_URI_ROUTING_HANDLER
inherit
WSF_URI_ROUTING_HANDLER_I [WSF_HANDLER [WSF_URI_HANDLER_CONTEXT], WSF_URI_HANDLER_CONTEXT]
redefine
router
end
create
make
feature {NONE} -- Routing
router: WSF_URI_ROUTER
;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
@@ -1,33 +1,35 @@
note
description: "[
This class helps to build Routing handler based for WSF_URI_ROUTER
]"
description: "Summary description for {WSF_AGENT_HANDLER}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
WSF_URI_ROUTING_HANDLER_I [H -> WSF_HANDLER [C],
C -> WSF_URI_HANDLER_CONTEXT create make end]
WSF_AGENT_URI_HANDLER
inherit
WSF_ROUTING_HANDLER [H, C]
WSF_URI_HANDLER
create
make
feature {NONE} -- Initialization
make (n: INTEGER)
make (a_action: like action)
do
create router.make (n)
action := a_action
end
feature {NONE} -- Routing
action: PROCEDURE [ANY, TUPLE [request: WSF_REQUEST; response: WSF_RESPONSE]]
router: WSF_URI_ROUTER_I [H, C]
feature -- Execution
;note
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
do
action.call ([req, res])
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: "[
@@ -0,0 +1,44 @@
note
description: "Summary description for {WSF_AGENT_URI_TEMPLATE_RESPONSE_HANDLER}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
WSF_AGENT_URI_TEMPLATE_RESPONSE_HANDLER
inherit
WSF_URI_TEMPLATE_RESPONSE_HANDLER
create
make
feature -- Initialization
make (act: like action)
do
action := act
end
feature -- Access
action: FUNCTION [ANY, TUPLE [ctx: WSF_URI_TEMPLATE_HANDLER_CONTEXT; req: WSF_REQUEST], WSF_RESPONSE_MESSAGE]
feature -- Execution
response (ctx: WSF_URI_TEMPLATE_HANDLER_CONTEXT; req: WSF_REQUEST): WSF_RESPONSE_MESSAGE
do
Result := action.item ([ctx, req])
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
@@ -0,0 +1,19 @@
note
description: "Summary description for {EWF_ROUTER_URI_PATH_HANDLER}."
author: ""
date: "$Date$"
revision: "$Revision$"
deferred class
WSF_URI_HANDLER
inherit
WSF_HANDLER
feature -- Execution
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
deferred
end
end
@@ -1,60 +0,0 @@
note
description: "[
Context for the handler execution
It does not provide additional information compared to {WSF_HANDLER_CONTEXT}
- request: WSF_REQUEST -- Associated request
- path: READABLE_STRING_8 -- Associated path
]"
date: "$Date$"
revision: "$Revision$"
class
WSF_URI_HANDLER_CONTEXT
inherit
WSF_HANDLER_CONTEXT
create
make
feature {NONE} -- Initialization
make (req: WSF_REQUEST; p: like path)
do
request := req
path := p
end
feature -- Request data
apply (req: WSF_REQUEST)
-- <Precursor>
do
end
revert (req: WSF_REQUEST)
-- <Precursor>
do
end
feature -- Item
item (a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE
-- Variable value for parameter or variable `a_name'
-- See `{WSF_REQUEST}.item(s)'
do
Result := request.item (a_name)
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
@@ -0,0 +1,80 @@
note
description: "Summary description for EWF_URI_PATH."
author: ""
date: "$Date$"
revision: "$Revision$"
class
WSF_URI_MAPPING
inherit
WSF_ROUTER_MAPPING
create
make,
make_trailing_slash_ignored
feature {NONE} -- Initialization
make (a_uri: READABLE_STRING_8; h: like handler)
do
handler := h
uri := a_uri
end
make_trailing_slash_ignored (a_uri: READABLE_STRING_8; h: like handler)
do
make (a_uri, h)
trailing_slash_ignored := True
end
feature -- Access
handler: WSF_URI_HANDLER
uri: READABLE_STRING_8
trailing_slash_ignored: BOOLEAN
feature -- Status
routed_handler (req: WSF_REQUEST; res: WSF_RESPONSE; a_router: WSF_ROUTER): detachable WSF_HANDLER
local
p: READABLE_STRING_8
l_uri: like uri
do
p := source_uri (req)
l_uri := based_uri (uri, a_router)
if l_uri.ends_with ("/") then
if not p.ends_with ("/") then
p := p + "/"
end
else
if p.ends_with ("/") then
p := p.substring (1, p.count - 1)
end
end
if p.same_string (l_uri) then
Result := handler
a_router.execute_before (Current)
handler.execute (req, res)
a_router.execute_after (Current)
end
end
feature {NONE} -- Implementation
based_uri (a_uri: like uri; a_router: WSF_ROUTER): like uri
local
s: STRING_8
do
if attached a_router.base_url as l_base_url then
create s.make_from_string (l_base_url)
s.append_string (a_uri)
Result := s
else
Result := a_uri
end
end
end
@@ -1,31 +1,34 @@
note
description: "Summary description for WSF_URI_ROUTED_SERVICE."
description: "Summary description for {WSF_URI_RESPONSE_HANDLER}."
author: ""
date: "$Date$"
revision: "$Revision$"
deferred class
WSF_URI_ROUTED_SERVICE
WSF_URI_RESPONSE_HANDLER
inherit
WSF_ROUTED_SERVICE_I [WSF_HANDLER [WSF_URI_HANDLER_CONTEXT], WSF_URI_HANDLER_CONTEXT]
redefine
router
WSF_URI_HANDLER
feature -- Response
response (req: WSF_REQUEST): WSF_RESPONSE_MESSAGE
require
is_valid_context: is_valid_context (req)
deferred
ensure
Result_attached: Result /= Void
end
feature {NONE} -- Initialization
feature -- Execution
create_router
-- Create router
--| it can be redefine to create with precise count if needed.
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute request handler
do
create router.make (0)
res.send (response (req))
end
feature -- Router
router: WSF_URI_ROUTER
;note
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: "[
@@ -1,251 +0,0 @@
note
description: "[
URL dispatcher/router based on simple URI mapping and request methods if precised
The associated context {WSF_URI_HANDLER_CONTEXT} does not contains any additional information.
The matching check if the same path is mapped, or if a substring of the path is mapped
Examples:
map ("/users/", users_handler)
map_with_request_methods ("/groups/", read_groups_handler, <<"GET">>)
map_with_request_methods ("/groups/", write_groups_handler, <<"POST", "PUT", "DELETE">>)
map_agent_with_request_methods ("/order/", agent do_get_order, <<"GET">>)
map_agent_with_request_methods ("/order/", agent do_post_order, <<"POST">>)
]"
date: "$Date$"
revision: "$Revision$"
class
WSF_URI_ROUTER_I [H -> WSF_HANDLER [C], C -> WSF_URI_HANDLER_CONTEXT create make end]
inherit
WSF_ROUTER [H, C]
create
make,
make_with_base_url
feature -- Initialization
make (n: INTEGER)
do
create handlers.make (n)
handlers.compare_objects
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
feature {WSF_ROUTED_SERVICE_I} -- Status report
handlers_matching_map (a_resource: READABLE_STRING_8; rqst_methods: detachable WSF_ROUTER_METHODS): detachable LIST [H]
local
l_res: READABLE_STRING_8
do
l_res := based_resource (a_resource)
across
handlers as c
loop
if c.item.resource.same_string (l_res) then
if
rqst_methods = Void or else
across rqst_methods as rq some is_matching_request_methods (rq.item, c.item.request_methods) end
then
if Result = Void then
create {ARRAYED_LIST [H]} Result.make (1)
end
Result.extend (c.item.handler)
end
end
end
end
feature {WSF_ROUTED_SERVICE_I} -- Default: implementation
default_handler_context (req: WSF_REQUEST): C
-- <Precursor>
do
Result := handler_context (Void, req)
end
feature -- Registration
map_with_request_methods (p: READABLE_STRING_8; h: H; rqst_methods: detachable WSF_ROUTER_METHODS)
local
l_uri: READABLE_STRING_8
do
if attached base_url as l_base_url then
l_uri := l_base_url + p
else
l_uri := p
end
handlers.force ([h, l_uri, rqst_methods])
h.on_handler_mapped (l_uri, rqst_methods)
end
feature {NONE} -- Implementation
based_resource (a_resource: READABLE_STRING_8): READABLE_STRING_8
do
if attached base_url as l_base_url then
Result := l_base_url + a_resource
else
Result := a_resource
end
end
feature {WSF_ROUTED_SERVICE_I} -- Handler
matching_route (req: WSF_REQUEST): detachable WSF_ROUTE [H, C]
local
h: detachable H
ctx: detachable C
rq_method: READABLE_STRING_8
do
rq_method := request_method (req)
h := handler_by_path (source_uri (req), rq_method)
if h = Void then
if attached smart_handler_by_path (source_uri (req), rq_method) as info then
h := info.handler
ctx := handler_context (info.path, req)
end
end
if h /= Void then
if h.is_valid_context (req) then
if ctx = Void then
ctx := handler_context (Void, req)
end
create Result.make (h, ctx)
else
Result := Void
end
end
end
feature {NONE} -- Access: Implementation
handler_by_path (a_path: READABLE_STRING_GENERAL; rqst_method: READABLE_STRING_8): detachable H
require
a_path_valid: a_path /= Void
local
l_handlers: like handlers
l_item: like handlers.item
do
l_handlers := handlers
from
l_handlers.start
until
l_handlers.after or Result /= Void
loop
l_item := l_handlers.item
if is_matching_request_methods (rqst_method, l_item.request_methods) and a_path.same_string (l_item.resource) then
Result := l_item.handler
end
l_handlers.forth
end
ensure
a_path_unchanged: a_path.same_string (old a_path)
end
smart_handler_by_path (a_path: READABLE_STRING_8; rqst_method: READABLE_STRING_8): detachable TUPLE [path: READABLE_STRING_8; handler: H]
require
a_path_valid: a_path /= Void
local
p: INTEGER
l_context_path, l_path: READABLE_STRING_8
h: detachable H
do
l_context_path := context_path (a_path)
from
p := l_context_path.count + 1
until
p <= 1 or Result /= Void
loop
l_path := l_context_path.substring (1, p - 1)
h := handler_by_path (l_path, rqst_method)
if h /= Void then
Result := [l_path, h]
else
p := l_context_path.last_index_of ('/', p - 1)
end
variant
p
end
ensure
a_path_unchanged: a_path.same_string (old a_path)
end
feature {NONE} -- Context factory
handler_context (p: detachable STRING; req: WSF_REQUEST): C
local
ctx: C
do
if p /= Void then
create ctx.make (req, p)
else
create ctx.make (req, source_uri (req))
end
Result := ctx
end
feature -- Access
new_cursor: ITERATION_CURSOR [TUPLE [handler: H; resource: READABLE_STRING_8; request_methods: detachable WSF_ROUTER_METHODS]]
-- Fresh cursor associated with current structure
do
Result := handlers.new_cursor
end
feature {NONE} -- Implementation
handlers: ARRAYED_LIST [TUPLE [handler: H; resource: READABLE_STRING_8; request_methods: detachable WSF_ROUTER_METHODS]]
-- Handlers indexed by the template expression
-- see `templates'
context_path (a_path: READABLE_STRING_8): READABLE_STRING_8
-- Prepared path from context which match requirement
-- i.e: not empty, starting with '/'
local
p: INTEGER
s: STRING_8
do
Result := a_path
if Result.is_empty then
Result := "/"
else
if Result[1] /= '/' then
create s.make_from_string (Result)
s.prepend_character ('/')
Result := s
end
p := Result.index_of ('.', 1)
if p > 0 then
Result := Result.substring (1, p - 1)
end
end
ensure
result_not_empty: not Result.is_empty
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
@@ -0,0 +1,41 @@
note
description: "Summary description for {WSF_URI_TEMPLATE_RESPONSE_HANDLER}."
author: ""
date: "$Date$"
revision: "$Revision$"
deferred class
WSF_URI_TEMPLATE_RESPONSE_HANDLER
inherit
WSF_URI_TEMPLATE_HANDLER
feature -- Response
response (ctx: WSF_URI_TEMPLATE_HANDLER_CONTEXT; req: WSF_REQUEST): WSF_RESPONSE_MESSAGE
require
is_valid_context: is_valid_context (req)
deferred
ensure
Result_attached: Result /= Void
end
feature -- Execution
execute (ctx: WSF_URI_TEMPLATE_HANDLER_CONTEXT; req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute request handler
do
res.send (response (ctx, req))
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