gen_router

Elixir library to handle generic routing tasks in Plug.Router and Phoenix router style


Keywords
chat-bot, elixir-library, plug, router, routing
License
MIT

Documentation

GenRouter

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.

Installation

Update your list of dependencies in mix.exs:

def deps do
  [
    {:gen_router, "~> 0.1"}
  ]
end

Usage

Hex docs

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.

match_message

Transforms generic input into %GenRouter.Conn{} object and execute the routing process.

Arguments:

  • router_module - router module which implements GenRouter
  • message - client request payload, %{} by default
  • path - client request path
  • scope - client request scope, %{} by default
  • assigns - data assigned by the system (authorization, locale etc)
  • opts - request options

deliver

Replacement of render function in Phoenix routing and responsible for delivering routing results to your clients.

Arguments:

  • conn - connection object
  • view - view module which will be used for rendering
  • template - name of template to render
  • params - rendering params
  • opts - delivery options

Sample router

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

Sample controller

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

Sample plug

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

Other

This library is in it's early beta, use on your own risk. Pull requests / reports / feedback are welcome.