# Handle each segment match. They can either be a
# :literal ("foo"), an :identifier (":bar") or a :glob ("*path")
Long story short: What is considered glob-syntax is some/static/base*anything where anything is the dynamic part (and I think that whatever dynamic value is entered will be stored in a field of the %Plug.Conn{} struct called anything in this case, but don’t quote me on that).
In any case:
It is not supported to have two glob-statements right next to each other.
The [!a-e]*-syntax you have in your example is not part of what is known as ‘glob syntax’. It is part of (Perl-style) Regular Expressions. Elixir allows the usage of regular expressions using e.g. ~r{[!a-e]*} which would match any symbol except any of a,b,c,d or e zero or more times. Unfortunately, Phoenix (or more accurately, Plug) does not support Regular Expressions in the routing paths, (it only accepts binary strings).
The Phoenix router only supports trailing globs and in a way that we can rely on binary pattern matching. We don’t support regexes or extended globs because they would be a large perf hit to the way we dispatch in the router. They are also rarely needed, so we prefer explicit handling for those cases via a plain old plug. To get what you want, you can do something like the following:
defmodule Router do
get "/", PageController, :index
... # other routes
get "/*path", GlobRouter, []
end
defmodule GlobRouter do
def init(opts), do: opts
def call(%Plug.Conn{request_path: path} = conn, _opts) do
cond do
Regex.match?(path, ~r{[!a-e]*}) -> to(conn, PageController, :catch_all)
Regex.match?(path, ~r{another}) -> to(conn, AnotherContorller, :other)
true -> raise Phoenix.Router.NoRouteError, conn: conn, router: Router
end
end
defp to(conn, controller, action) do
controller.call(conn, controller.init(action))
end
end
You could also use named captures in your regex and add whatever glob params to the conn.params before calling your controller. Hope that helps!
Thanks @chrismccord for provide the perfect solution.
I have another question related to this:
Does scope care about order in Router.ex ? I found if I use /*path in first scope it will block any other scope after it in source code.
The router patterns translate in a very simple way into a pattern match on the request path. In pattern matching order matters - it also matters in the router. Specifically /*path is like using a variable in the pattern match - everything matches, so no further clauses can ever match.
Probably phoenix marks the generated code as generated (surprise, surprise!) so no warnings are emitted, but I wonder if it would make sense to apply those marks more carefully, so a warning that some routes can never match is actually generated by the compiler analysing the generated pattern matches.
It is just an embedded map on the conn, you can edit it just like any other embedded map.
If it is custom data then this is usually preferred. However if it is something related to the path or params I tend to stick it in the params, else on assigns.