Sometimes you need a routing capabilities which can work with a generic connection object
and you can't use existing routing modules included in Phoenix
and Plug.Router
packages.
One of the use cases is the routing of messages for chatbots.
Update your list of dependencies in mix.exs
:
def deps do
[
{:gen_router, "~> 0.1"}
]
end
Router configuration is similar to Phoenix routing system, but we don't support HTTP methods and define only match
rules.
As with Phoenix, you can capture variables in conn.params
with colon notations.
Route /book/:name/p/:page
will match url like /book/sweet_home/p/3
and will populate
params of your conn
object with %{"name" => "sweet_home", "page" => "3"}
.
All the parsed values will be strings.
You need to define match_message
and deliver
callbacks according to GenRouter.Behaviour
behviour.
Transforms generic input into %GenRouter.Conn{} object and execute the routing process.
Arguments:
router_module
- router module which implements GenRoutermessage
- client request payload, %{} by defaultpath
- client request pathscope
- client request scope, %{} by defaultassigns
- data assigned by the system (authorization, locale etc)opts
- request options
Replacement of render
function in Phoenix routing and responsible for delivering routing results to your clients.
Arguments:
conn
- connection objectview
- view module which will be used for renderingtemplate
- name of template to renderparams
- rendering paramsopts
- delivery options
defmodule App.Router do
use GenRouter
alias App.Controller.FirstController
alias App.Controller.Model4Controller
alias App.Controller.ErrorController
pipeline :authed do
plug App.Plug.Authorize
end
scope :default, "/" do
pipe_through [:authed]
match "/", FirstController, :index
match "/action2", FirstController, :index
match "/model3/:id", FirstController, :protected_action
end
scope :model4_scope, "/model4" do
pipe_through [:authed]
match "/", FirstController, :index
end
match "*", ErrorController, :not_found
@impl true
def match_message(router_module, message, path, scope, assigns, opts) do
conn =
GenRouter.Conn.build(router_module, %{
path: path,
params: %{message: message},
assigns: assigns,
scope: scope
})
router_module.do_match(conn, opts)
end
@impl true
def deliver(conn, _view, _template, _params, _opts) do
conn
end
end
defmodule GenRouter.Controller.FirstController do
use GenRouter.Controller
plug GenRouter.Plug.FetchCommonResource
plug GenRouter.Plug.FetchResource when action in [:protected_action]
@spec index(Conn.t(), any()) :: Conn.t()
def index(conn, _opts) do
complete(conn, "index")
end
@spec protected_action(Conn.t(), any()) :: Conn.t()
def protected_action(conn, _opts) do
complete(conn, "protected_action")
end
@spec not_found(GenRouter.Conn.t(), any()) :: GenRouter.Conn.t()
def not_found(conn, _opts) do
complete(conn, nil, %{}, 404)
end
end
defmodule GenRouter.Plug.Authorize do
@doc false
def call(%{assigns: %{authorized: true}} = conn, _opts), do: conn
def call(conn, _opts), do: GenRouter.Conn.complete(conn, "forbidden", %{}, 403)
end
This library is in it's early beta, use on your own risk. Pull requests / reports / feedback are welcome.