A couple of improvements over Cowboy Routes
Erlang Shell
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
example
src Reapply #83 Apr 30, 2018
test Reapply #83 Apr 30, 2018
.awconfig [#45] Add assisted workflow support Jan 6, 2016
.gitignore [Close #54] move example from erlang.mk to rebar3 Oct 4, 2016
.travis.yml [#72] Remove OTP 18 from build since cowboy 2.0 only works with OTP 19+ Nov 28, 2017
CHANGELOG.md Bump Version Apr 30, 2018
LICENSE
README.md Remove fork comment Apr 30, 2018
ci Plant CI Mar 9, 2017
elvis.config [Close #64] move to rebar3 Aug 11, 2016
rebar.config Update katana-test dep Apr 30, 2018
rebar.config.script Update rebar.config to use Hex dependencies Apr 11, 2016
rebar3 add rebar3 for the sake of elixir mix compilation Jun 24, 2017

README.md

Stories in Ready

cowboy-trails

Cowboy routes on steroids!

Contact Us

If you find any bugs or have a problem while using this library, please open an issue in this repo (or a pull request :)).

And you can check all of our open-source projects at inaka.github.io.

Why Cowboy Trails?

Cowboy-Trails enables you to:

  • Add information to cowboy routes, which can be used later to interact with the server in a higher abstraction level.

  • Define the server routes directly within the module that implements them.

How to Use it?

The first use case for cowboy_trails is to compile cowboy routes.

Normally with cowboy you compile routes in the following way:

Routes = [{'_',
           [ {"/resource1", resource1_handler, []}
           , {"/resource2/[:id]", resource2_handler, []}
           ]
          }
         ],
cowboy_router:compile(Routes),

Trails is also fully compatible with cowboy routes, so you can pass the same routes in order to be processed by trails:

trails:compile(Routes),

So far it seems like there is not any difference, right? But the most common case with cowboy is that you usually work with a single host, even though you're required to keep defining the host parameter within the routes ([{'_', [...]}]).

Well, with trails you have another useful function to compile single host routes:

%% You only define the routes/paths
Routes = [ {"/resource1", resource1_handler, []}
         , {"/resource2/[:id]", resource2_handler, []}
         ],
trails:single_host_compile(Routes),

Now, let's suppose that you want to add additional information (metadata) to cowboy routes related with the semantics of each HTTP method.

Metadata = #{put => #{description => "PUT method"},
             post => #{ description => "POST method"},
             get => #{ description => "GET method"}},
Trail = trails:trail("/",
                     cowboy_static,
                     {private_file, "index2.html"},
                     Metadata,
                     []),
%% You can later retrieve the metadata:
Metadata = trails:metadata(Trail),

This can be used later to generate documentation related to each endpoint.

Normally, when you work with cowboy you have to define all routes in one place:

Routes =
  [{'_',
    [ {"/", cowboy_static, {file, "www/index.html"}}
    , {"/favicon.ico", cowboy_static, {file, "www/assets/favicon.ico"}}
    , {"/assets/[...]", cowboy_static, {dir, "www/assets"}}
    , {"/game/:game_id", cowboy_static, {file, "www/game.html"}}
    , {"/api/status", spts_status_handler,  []}
    , {"/api/games", spts_games_handler, []}
    , {"/api/games/:game_id", spts_single_game_handler, []}
    , {"/api/games/:game_id/serpents", spts_serpents_handler, []}
    , { "/api/games/:game_id/serpents/:token"
      , spts_single_serpent_handler, []
      }
    , {"/api/games/:game_id/news", lasse_handler, [spts_news_handler]}
    ]
   }
  ],
Dispatch = cowboy_router:compile(Routes),

But now with trails you're able to define the routes on each resource handler. The handler must implement the callback trails/0 and return the specific routes for that handler. For a better understanding, you can check out the examples in the test folder (trails_test_handler).

Once you have implemented the trails/0 callback on your handlers, you can do something like this:

Handlers =
  [ spts_status_handler
  , spts_games_handler
  , spts_single_game_handler
  , spts_serpents_handler
  , spts_single_serpent_handler
  , spts_news_handler
  ],
Trails =
  [ {"/", cowboy_static, {file, "www/index.html"}}
  , {"/favicon.ico", cowboy_static, {file, "www/assets/favicon.ico"}}
  , {"/assets/[...]", cowboy_static, {dir, "www/assets"}}
  , {"/game/:game_id", cowboy_static, {file, "www/game.html"}}
  | trails:trails(Handlers)
  ],
trails:single_host_compile(Trails),

This way each handler keeps their own routes, as it should be, and you can merge them easily.

Example

For more information about cowboy_trails, how to use it and the different functions that it exposes, please check this Example.

Testing

This project's test suites include meta testing. Therefore, in order to run the tests, it requires a proper plt. Otherwise, when you try rebar3 ct, you'll get an error similar to:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ktn_meta_SUITE:dialyzer failed on line 60
Reason: {test_case_failed,No plts at ../../*.plt - you need to at least have one}
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

To generate the required plt, just run rebar3 dialyzer once and then you can run rebar3 ct as many times as you like.