README.md

![Coveralls](https://img.shields.io/coveralls/github/BartOtten/routex)
[![Build Status](https://github.com/BartOtten/routex/actions/workflows/elixir.yml/badge.svg?event=push)](https://github.com/BartOtten/routex/actions/workflows/elixir.yml)
[![Last Updated](https://img.shields.io/github/last-commit/BartOtten/routex.svg)](https://github.com/BartOtten/routex/commits/main)
[![Hex.pm](https://img.shields.io/hexpm/v/routex)](https://hex.pm/packages/routex)
![Hex.pm](https://img.shields.io/hexpm/l/routex)


# Routex: Supercharge your Phoenix Router

Routex is an feature rich routing framework build on top of Phoenix Router. It
provides additional routing features for common use cases. Situated between
route definition and compilation, Routex has zero to none impact on run time
performance.

## Top Features and Benefits

- **extension driven**: mix and match the features you need.
- **performant**: (close to) zero run time impact.
- **extensible**: write your own routing features with ease.
- **plug-and-play**: enable site wide features with a single config change.
- **easy setup**: test drive Routex with minimal changes to the codebase.


## Documentation

[HexDocs](https://hexdocs.pm/routex) (stable) and [GitHub
Pages](https://bartotten.github.io/routex) (development).

Summaries of [official Routex extensions](#extensions)


## Requirements and Installation

See the [Usage Guide](USAGE.md) for the requirements and installation
instructions.


## Example

Localize your Phoenix website with multilingual URLs and custom template
assigns; enhancing user engagement and content relevance. This example combines
a few of the included extensions: `Alternatives`, `Translations`, `Assigns` and
`Verified Routes`.

                        ⇒ /products/:id/edit                      @loc.locale = "en_US"
    /products/:id/edit  ⇒ /eu/nederland/producten/:id/bewerken    @loc.locale = "nl_NL"
                        ⇒ /eu/espana/producto/:id/editar          @loc.locale = "es_ES"
                        ⇒ /gb/products/:id/edit                   @loc.locale = "en_GB"

- `Alternatives` to generate 4 route branches with custom attributes per branch.
- `Translation` to localize the urls enhancing user engagement and content relevance.
- `Assigns` to access attributes of a route in LiveViews, components and
  controllers using `loc` as namespace (`@loc.locale`)
- `Verified Routes` to keep  `~p"/products/#{product}/edit"` in templates. At run-time the route is
  combined with the set `locale` to pick the localized alternative route.


## Routex vs Cldr Routes vs Phoenix Localized Routes

The capabilities and advancements within `Routex` surpass those of `Phoenix
Localized Routes`, offering a comprehensive array of features. As Phoenix
Localized Routes has stagnated in its development, developers are strongly
advised to transition to Routex for a more robust solution.

When considering `Routex` against `Cldr Routes`, it's akin to comparing Apple to
Linux. Cldr Routes is a limited walled garden but is developed by the main Cldr
developer ensuring maximum compatibility. Routex on the other hand boasts a
wider and more dynamic feature scope providing maximum freedom. Its primary
advantages over Cldr Routes are it's extension mechanism and the minimized
necessity for code modifications throughout a codebase.

But why choose when you can have Cldr through [the Cldr extension for
Routex](`Routex.Extension.Cldr`)?

### Comparison table

| Feature             | Routex     | Cldr Routes | PLR        |
|---------------------|------------|-------------|------------|
| Route encapsulation | Full  [^1] | Limited     | Limited    |
| Route manipulation  | Full  [^2] | Limited     | Limited    |
| Route interpolation | Full       | Limited     | No         |
| Alternative Routes  | Full       | Cldr        | Full       |
| Translation         | ☑          | ☑          |  ☑         |
| Route Helpers       | ☑          | ☑          |  ☑         |
| Verified Routes     | ☑          | ☑          |  ☐         |
| Drop-in replacement | ☑     [^3] | ☐          |  ☑         |
| Standalone          | ☑          | ☐          |  ☐         |
| Modular             | ☑          | ☐          |  ☐         |
| Extendable          | ☑          | ☐          |  ☐         |

[^1]: Routex' `preprocesss_using` is not bound to Phoenix (session) scopes
[^2]: [Crazy example](https://github.com/BartOtten/routex/blob/main/lib/routex/extension/cloak.ex)
[^3]: Routex *can* be configured to shim original Phoenix functionality (for
    example: `~p` and `url/2`) while Cldr Routes mandates code modifications
    (for example: `~p` -> `~q` and `url/2` -> `url_q/2`)


## Extensions

Routex relies on extensions to provide features. Each extension provides a
single feature and should minimize hard dependencies on other extensions.
Instead, Routex advises to make use of `Routex.Attrs` to share attributes;
allowing extensions to work together whithout being coupled.

The documentation of each extension lists any provided or required
`Routex.Attrs`.


### Alternatives

Create alternative routes based on `branches` configured in a Routex backend
module. Branches can be nested and each branch can provide it's own attributes to
be shared with other extensions.

[Alternatives Documentation](`Routex.Extension.Alternatives`)


### Translations

This extension extracts segments of a routes' path to a `routes.po` file for
translation. At compile-time it combines the translated segments to translate
routes. As a result, users can enter URLs using localized terms which can
enhance user engagement and content relevance.

[Translations Documentation](`Routex.Extension.Translations`)


### Multilingual Routes

The Alternatives extension can be combined with the Translations extension to
create multilingual routes. The Alternatives extension can provide the :locale
attribute used by the Translations extension.

	Original            Step 1: Alternatives               Step 2: Translations
						⇒ /products/:id/edit               ⇒ /products/:id/edit
	/products/:id/edit  ⇒ /eu/nederland/products/:id/edit  ⇒ /eu/nederland/producten/:id/bewerken
						⇒ /eu/espana/products/:id/edit	   ⇒ /eu/espana/producto/:id/editar
						⇒ /gb/products/:id/edit            ⇒ /gb/products/:id/edit


### Alternative Getters

Creates a helper function `alternatives/1` to get a list of alternative slugs
and their route attributes.  The current route is also included and has attribute
`current?: true`. As `Routex` sets the `@url` assign you can simply
get all routes to the current page with `alternatives(@url)`, use an attribute in the
route as button text and highlight the current active route button.

[Alternative Getters Documentation](`Routex.Extension.AlternativeGetters`)


### Verified Routes

Routex is fully compatible with Verified Routes.

This extension creates a sigil (default: `~l`) with the ability to branch based
on the current alternative branch of a user. It is able to verify routes even
when thy have been transformed by Routex extensions. Optionally this sigil can
be set to `~p` (Phoenix' default) as it is a drop-in replacement.

It also provides branching variants of `url/{2,3,4}` and `path/{2,3}`.

[Verified Routes Documentation](`Routex.Extension.VerifiedRoutes`)


### Route Helpers

Creates Phoenix Helpers that have the ability to branch based on the current
alternative branch of a user. Optionally these helpers can replace the original
Phoenix Route Helpers as they are drop-ins.

[Route Helpers Documentation](`Routex.Extension.RouteHelpers`)


### Interpolation

With this extension enabled, *any* attribute assigned to a route can be used
for route interpolation. Most effective with an extension which enables
alternative routes generation (such as extension `Alternatives`).

    /product/#{territory}/:id/#{language}  => /product/europe/:id/nl

[Interpolation Documentation](`Routex.Extension.Interpolation`)


### Assigns

With this extension you can add (a subset of) attributes set by other extensions
to Phoenix' assigns making them available in components and controllers with the
`@` assigns operator (optionally under a namespace)

    @namespace.area      =>  :eu_nl
	@namespace.contact   =>  "contact@example.com"

[Assigns Documentation](`Routex.Extension.Assigns`)


### Attribute Getters

Creates a helper function `attrs/1` to get all `Routex.Attrs` of a route. As
`Routex` sets the `@url` assign you can simply get all attributes for the
current page with `attrs(@url)`.

This way the `assigns` can be a subset of the full list of attributes but the
full list can be lazy loaded when needed.

[Attribute Getters Documentation](`Routex.Extension.AttrGetters`)


### Cldr Adaptor
Adapter for projects using :ex_cldr.

[Cldr Adaptor Documentation](`Routex.Extension.Cldr`)


### Cloak (show case)

Transforms routes to be unrecognizable. This extension is a show case and may
change at any given moment to generate other routes without prior notice.

In this example it numbers all routes starting at 01 and increments the counter
for each route. It also shifts the parameter to the left; causing a chaotic
route structure. Do note: this still works with the Verified Routes extension
while using the standard route (e.g. `<.link navigate={~p"/products">`) in
templates.


      Original                 Rewritten     Result (product_id: 88, 89, 90)
      /products                ⇒ /01         ⇒   /01
      /products/:id/edit       ⇒ /:id/02     ⇒ /88/02, /89/02, /90/02 etc...
      /products/:id/show/edit  ⇒ /:id/03     ⇒ /88/03, /89/03, /90/03 etc...


[Cloak Documentation](`Routex.Extension.Cloak`)