diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f0b0c1b..bb33086 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,42 +1,36 @@ --- name: build -on: - push: - branches: - - master - pull_request: - branches: - - master +on: [push, pull_request] jobs: ci: - name: Run checks and tests over ${{matrix.otp_vsn}} and ${{matrix.os}} - runs-on: ${{matrix.os}} + name: Run checks and tests over ${{matrix.otp_vsn}} + runs-on: ubuntu-22.04 strategy: matrix: - otp_vsn: [21, 22, 23, 24] - os: [ubuntu-latest] + otp_vsn: ['24', '25', '26'] + rebar3_vsn: ['3.22'] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: erlef/setup-beam@v1 with: otp-version: ${{matrix.otp_vsn}} - rebar3-version: '3.14' + rebar3-version: ${{matrix.rebar3_vsn}} - run: | rm -rf example/_checkouts # Prevent long running analysis rebar3 test example: - name: Makes sure our example run over ${{matrix.otp_vsn}} and ${{matrix.os}} - runs-on: ${{matrix.os}} + name: Makes sure our example run over ${{matrix.otp_vsn}} + runs-on: ubuntu-22.04 strategy: matrix: - otp_vsn: [21, 22, 23, 24] - os: [ubuntu-latest] + otp_vsn: ['24', '25', '26'] + rebar3_vsn: ['3.22'] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: erlef/setup-beam@v1 with: otp-version: ${{matrix.otp_vsn}} - rebar3-version: '3.14' + rebar3-version: ${{matrix.rebar3_vsn}} - name: test our example run: | cd example @@ -53,6 +47,10 @@ jobs: R=$(curl -s -w "%{http_code}" -XDELETE "localhost:8080/poor-kv/some-key" -H "content-type: text/plain") [[ $R == "404" ]] || exit 1 _build/default/rel/example/bin/example stop + - name: Format check + run: | + cd example + rebar3 format --verify - name: check our example run: | cd example diff --git a/.gitignore b/.gitignore index 8df8f64..dd198d0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,18 +1,6 @@ logs/ -.eunit -deps -*.o -*.beam -*.plt -erl_crash.dump -ebin -rel/example_project -.concrete/DEV_MODE -.rebar -.erlang.mk* -elvis -*.d -_build -/rebar.lock -doc -rebar3.crashdump \ No newline at end of file +_* +doc/ +rebar3.crashdump +.rebar3 +logs diff --git a/LICENSE b/LICENSE index 6f300a5..0209d84 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ Apache License Version 2.0, January 2004 - http://www.apache.org/licenses/ + https://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION @@ -192,7 +192,7 @@ you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, diff --git a/README.md b/README.md index e8b9dda..35a6496 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ If you find any **bugs** or have a **problem** while using this library, please [open an issue](https://github.com/inaka/cowboy-trails/issues/new) in this repo (or a pull request 😄). -And you can check out all of our open-source projects at [inaka.github.io](http://inaka.github.io). +And you can check out all of our open-source projects at [inaka.github.io](https://inaka.github.io). ## Why Cowboy Trails? @@ -104,11 +104,13 @@ Dispatch = cowboy_router:compile(Routes), But now, with `trails`, you're able to define the routes on each of your resource handlers, separately. -These handlers must implement callback `trails/0` or `trails/1` and return the specific +These handlers must implement callback `c:trails_handler:trails/0` or `c:trails_handler:trails/1` +and return the specific routes that define them. For a better understanding, you can check out the -examples in the `test` folder ([trails_test_handler](./test/trails_test_handler.erl)). +examples in the `test` folder ([trails_test_handler](https://github.com/inaka/cowboy-trails/blob/master/test/trails_test_handler.erl)). -Once you have implemented the `trails/0` or `trails/1` callback on your handlers, you can do +Once you have implemented the `c:trails_handler:trails/0` or `c:trails_handler:trails/1` callback +on your handlers, you can do something like this: ```erlang @@ -137,4 +139,4 @@ 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](./example). +functions that it exposes, please check this [example](https://github.com/inaka/cowboy-trails/blob/master/example). diff --git a/elvis.config b/elvis.config index dfc30fc..549ad21 100644 --- a/elvis.config +++ b/elvis.config @@ -1,25 +1,15 @@ -[ - { - elvis, - [ - {config, - [#{dirs => ["src", "test"], - filter => "*.erl", - rules => [{elvis_style, invalid_dynamic_call, #{ignore => [trails_handler]}}, - {elvis_style, dont_repeat_yourself, #{min_complexity => 14}}, - {elvis_style, god_modules, #{ ignore => [trails_SUITE]}}], - ruleset => erl_files - }, - #{dirs => ["."], - filter => "rebar.config", - ruleset => rebar_config - }, - #{dirs => ["."], - filter => "elvis.config", - ruleset => elvis_config - } - ] - } - ] - } -]. +[{elvis, + [{config, + [#{dirs => ["src", "test"], + filter => "*.erl", + rules => + [{elvis_style, invalid_dynamic_call, #{ignore => [trails_handler]}}, + {elvis_style, dont_repeat_yourself, #{min_complexity => 14}}, + {elvis_style, god_modules, #{ignore => [trails_SUITE]}}], + ruleset => erl_files}, + #{dirs => ["."], + filter => "rebar.config", + ruleset => rebar_config}, + #{dirs => ["."], + filter => "elvis.config", + ruleset => elvis_config}]}]}]. diff --git a/example/rebar.config b/example/rebar.config index 1c2fca2..3a6ff8b 100644 --- a/example/rebar.config +++ b/example/rebar.config @@ -1,32 +1,36 @@ -%% -*- mode: erlang;erlang-indent-level: 2;indent-tabs-mode: nil -*- -%% ex: ts=4 sw=4 ft=erlang et +%% == Compiler and Profiles == -%% == Erlang Compiler == - -%% Erlang compiler options {erl_opts, - [warn_unused_vars, - warn_export_all, - warn_shadow_vars, - warn_unused_import, - warn_unused_function, - warn_bif_clash, - warn_unused_record, - warn_deprecated_function, - warn_obsolete_guard, - strict_validation, - warn_export_vars, - warn_exported_vars, - warn_untyped_record, - debug_info]}. - -%% == Dependencies == - -{deps, - [{mixer, "1.2.0", {pkg, inaka_mixer}}, - trails]}. % checked out - -{project_plugins, [rebar3_lint, rebar3_hank, rebar3_format]}. + [warn_unused_import, warn_export_vars, warnings_as_errors, verbose, report, debug_info]}. + +{minimum_otp_vsn, "23"}. + +{alias, [{test, [compile, format, hank, lint, xref, dialyzer]}]}. + +%% == Dependencies and plugins == + +{deps, [{mixer, "1.2.0", {pkg, inaka_mixer}}, trails]}. % checked out + +{project_plugins, + [{rebar3_hank, "~> 1.4.0"}, {rebar3_format, "~> 1.3.0"}, {rebar3_lint, "~> 3.0.1"}]}. + +%% == Format == + +{format, [{files, ["*.config", "src/*"]}]}. + +%% == Hank == + +{hank, [{ignore, ["_build/**", "_checkouts"]}]}. + +%% == Dialyzer + XRef == + +{dialyzer, + [{warnings, [no_return, underspecs, unmatched_returns, error_handling, unknown]}]}. + +{xref_checks, + [undefined_function_calls, deprecated_function_calls, deprecated_functions]}. + +{xref_extra_paths, ["test/**"]}. %% == Shell (for interactive example) == @@ -38,17 +42,5 @@ [{include_src, false}, {extended_start_script, true}, {release, {example, "0.1"}, [example, sasl]}, - {sys_config, "rel/sys.config"}]}. - -%% == Alias == - -{alias, [{test, [dialyzer, lint, hank]}]}. - -%% == hank == - -{hank, [ - {ignore, [ - "_build/**", - "_checkouts/**" - ]} -]}. + {sys_config, "rel/sys.config"}, + {vm_args, "rel/vm.args"}]}. diff --git a/example/rel/sys.config b/example/rel/sys.config index ed118b8..ed7cf30 100644 --- a/example/rel/sys.config +++ b/example/rel/sys.config @@ -1,4 +1 @@ -[{example, - [{http_port, 8080}, - {http_listener_count, 10}]} -]. +[{example, [{http_port, 8080}, {http_listener_count, 10}]}]. diff --git a/example/rel/vm.args b/example/rel/vm.args new file mode 100644 index 0000000..5713fd1 --- /dev/null +++ b/example/rel/vm.args @@ -0,0 +1,2 @@ +-name example@127.0.0.1 +-setcookie bogus diff --git a/example/src/example.erl b/example/src/example.erl index 8135352..e6d52bc 100644 --- a/example/src/example.erl +++ b/example/src/example.erl @@ -29,7 +29,7 @@ stop(_State) -> ok = cowboy:stop_listener(example_http). % start_listeners() -> --spec start_phase(atom(), application:start_type(), []) -> ok | {error, term()}. +-spec start_phase(atom(), application:start_type(), []) -> ok. start_phase(start_trails_http, _StartType, []) -> {ok, Port} = application:get_env(example, http_port), {ok, ListenerCount} = application:get_env(example, http_listener_count), diff --git a/rebar.config b/rebar.config index 457138c..dc1a843 100644 --- a/rebar.config +++ b/rebar.config @@ -1,60 +1,60 @@ -%%% -*- mode: erlang; -*- +%% == Compiler and Profiles == + {erl_opts, - [ - debug_info - ] -}. - -{deps, - [ - {cowboy, "2.8.0"}, - {ranch, "2.0.0"} - ] -}. - -{project_plugins, [ - rebar3_lint, - rebar3_hank, - rebar3_hex -]}. - -{profiles, [ - {test, [ - {cover_enabled, true}, - {cover_opts, [verbose]}, - {deps, [ - {meck, "0.9.2"} - ]} - ]} - ]}. - -{alias, [{test, [xref, dialyzer, lint, hank, ct, cover, edoc]}]}. + [warn_unused_import, warn_export_vars, warnings_as_errors, verbose, report, debug_info]}. + +{minimum_otp_vsn, "23"}. + +{profiles, + [{test, + [{ct_opts, [{verbose, true}]}, + {cover_enabled, true}, + {cover_opts, [verbose]}, + {deps, [{meck, "0.9.2"}]}, + {dialyzer, + [{warnings, [no_return, underspecs, unmatched_returns, error_handling, unknown]}, + {plt_extra_apps, [meck]}]}]}]}. + +{alias, [{test, [compile, format, hank, lint, xref, dialyzer, ct, cover, ex_doc]}]}. + +%% == Dependencies and plugins == + +{deps, [{cowboy, "2.10.0"}, {ranch, "2.1.0"}]}. + +{project_plugins, + [{rebar3_hank, "~> 1.4.0"}, + {rebar3_hex, "~> 7.0.7"}, + {rebar3_format, "~> 1.3.0"}, + {rebar3_lint, "~> 3.0.1"}, + {rebar3_ex_doc, "0.2.18"}]}. + +%% == Documentation == + +{ex_doc, + [{source_url, <<"https://github.com/inaka/cowboy-trails">>} + ]}. + +{hex, [{doc, #{provider => ex_doc}}]}. + +%% == Format == + +{format, + [{files, ["*.config", "src/*", "test/*", "example/{rel,src,.}/*{.config,.erl,.src}"]}]}. + +%% == Hank == + +{hank, + [{ignore, + ["example/_build/**", + "example/_checkouts", + {"test/trails_SUITE.erl", unnecessary_function_arguments}]}]}. + +%% == Dialyzer + XRef == + +{dialyzer, + [{warnings, [no_return, underspecs, unmatched_returns, error_handling, unknown]}]}. {xref_checks, - [ - undefined_function_calls - , locals_not_used - , deprecated_function_calls - ] -}. - -{dialyzer, [ {warnings, [ underspecs - , unmatched_returns - , error_handling - , unknown - ]} - , {plt_apps, top_level_deps} - , {plt_extra_apps, []} - , {plt_location, local} - , {base_plt_apps, [erts, stdlib, kernel]} - , {base_plt_location, global}]}. - -%% == hank == - -{hank, [ - {ignore, [ - "example/_build/**", - "example/_checkouts/**", - {"test/trails_SUITE.erl", unnecessary_function_arguments} - ]} -]}. + [undefined_function_calls, deprecated_function_calls, deprecated_functions]}. + +{xref_extra_paths, ["test/**"]}. diff --git a/rebar.lock b/rebar.lock new file mode 100644 index 0000000..6fd858d --- /dev/null +++ b/rebar.lock @@ -0,0 +1,14 @@ +{"1.2.0", +[{<<"cowboy">>,{pkg,<<"cowboy">>,<<"2.10.0">>},0}, + {<<"cowlib">>,{pkg,<<"cowlib">>,<<"2.12.1">>},1}, + {<<"ranch">>,{pkg,<<"ranch">>,<<"2.1.0">>},0}]}. +[ +{pkg_hash,[ + {<<"cowboy">>, <<"FF9FFEFF91DAE4AE270DD975642997AFE2A1179D94B1887863E43F681A203E26">>}, + {<<"cowlib">>, <<"A9FA9A625F1D2025FE6B462CB865881329B5CAFF8F1854D1CBC9F9533F00E1E1">>}, + {<<"ranch">>, <<"2261F9ED9574DCFCC444106B9F6DA155E6E540B2F82BA3D42B339B93673B72A3">>}]}, +{pkg_hash_ext,[ + {<<"cowboy">>, <<"3AFDCCB7183CC6F143CB14D3CF51FA00E53DB9EC80CDCD525482F5E99BC41D6B">>}, + {<<"cowlib">>, <<"163B73F6367A7341B33C794C4E88E7DBFE6498AC42DCD69EF44C5BC5507C8DB0">>}, + {<<"ranch">>, <<"244EE3FA2A6175270D8E1FC59024FD9DBC76294A321057DE8F803B1479E76916">>}]} +]. diff --git a/src/trails.app.src b/src/trails.app.src index 564559c..31e91fe 100644 --- a/src/trails.app.src +++ b/src/trails.app.src @@ -1,23 +1,15 @@ -{application, trails, - [ - {description, "A couple of improvements over Cowboy Routes"}, +{application, + trails, + [{description, "A couple of improvements over Cowboy Routes"}, {vsn, git}, - {applications, - [kernel, - stdlib, - ranch, - cowboy - ]}, + {applications, [kernel, stdlib, ranch, cowboy]}, {modules, []}, {mod, {trails_app, []}}, {registered, []}, - {maintainers,["Inaka"]}, - {licenses,["Apache 2.0"]}, - {links,[ {"Github","https://github.com/inaka/cowboy-trails"}, - {"Docs", "http://inaka.github.io/cowboy-trails/"}, - {"Blog", "http://inaka.net/blog/2015/07/20/cowboy-trails/"}, - {"Example", "https://github.com/inaka/cowboy-trails/tree/master/example"} - ]}, - {build_tools,["rebar3"]} - ] -}. + {maintainers, ["Inaka"]}, + {licenses, ["Apache 2.0"]}, + {links, + [{"GitHub", "https://github.com/inaka/cowboy-trails"}, + {"Blog", "https://web.archive.org/web/20160422045517/http://inaka.net/blog/2015/07/20/cowboy-trails/"}, + {"Example", "https://github.com/inaka/cowboy-trails/tree/master/example"}]}, + {build_tools, ["rebar3"]}]}. diff --git a/src/trails.erl b/src/trails.erl index d5f49b1..0930ac8 100644 --- a/src/trails.erl +++ b/src/trails.erl @@ -23,225 +23,231 @@ %% Trail specification -opaque trail() :: - #{ path_match => route_match() - , constraints => cowboy:fields() - , handler => module() - , options => any() - , metadata => metadata(any()) - }. --export_type([trail/0]). + #{path_match => route_match(), + constraints => cowboy:fields(), + handler => module(), + options => any(), + metadata => metadata(any())}. + +-export_type([trail/0, route_match/0]). %% Exported from cowboy_router.erl -type route_match() :: '_' | iodata(). --type route_path() :: {Path::route_match(), Handler::module(), Opts::any()} - | {Path::route_match(), cowboy:fields(), Handler::module(), Opts::any()}. --type route_rule() :: {Host::route_match(), Paths::[route_path()]} - | {Host::route_match(), cowboy:fields(), Paths::[route_path()]}. +-type route_path() :: + {Path :: route_match(), Handler :: module(), Opts :: any()} | + {Path :: route_match(), cowboy:fields(), Handler :: module(), Opts :: any()}. +-type route_rule() :: + {Host :: route_match(), Paths :: [route_path()]} | + {Host :: route_match(), cowboy:fields(), Paths :: [route_path()]}. + %% End of exported functions -type trails() :: [trail() | route_path()]. + -export_type([trails/0]). -type method() :: get | put | post | delete | patch | head | options. + -export_type([method/0]). -type metadata(X) :: #{method() => X}. + -export_type([metadata/1]). +-elvis([{elvis_style, no_throw, disable}]). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% API %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% @equiv compile([{'_', Trails}]) --spec single_host_compile(trails()) -> - cowboy_router:dispatch_rules(). +-spec single_host_compile(trails()) -> cowboy_router:dispatch_rules(). single_host_compile(Trails) -> - compile([{'_', Trails}]). + compile([{'_', Trails}]). %% @doc Compiles the given list of trails routes, also compatible with %% `cowboy' routes. --spec compile([{Host::route_match(), Trails::trails()}]) -> - cowboy_router:dispatch_rules(). -compile([]) -> []; +-spec compile([{Host :: route_match(), Trails :: trails()}]) -> + cowboy_router:dispatch_rules(). +compile([]) -> + []; compile(Routes) -> - cowboy_router:compile( - [{Host, to_route_paths(Trails)} || {Host, Trails} <- Routes]). + cowboy_router:compile([{Host, to_route_paths(Trails)} || {Host, Trails} <- Routes]). %% @doc Translates the given trails paths into `cowboy' routes. -spec to_route_paths([trail()]) -> cowboy_router:routes(). to_route_paths(Paths) -> - [to_route_path(Path) || Path <- Paths]. + [to_route_path(Path) || Path <- Paths]. %% @doc Translates a trail path into a route rule. -spec to_route_path(trail()) -> route_rule(). to_route_path(Trail) when is_map(Trail) -> - ApiRoot = api_root(), - PathMatch = maps:get(path_match, Trail), - ModuleHandler = maps:get(handler, Trail), - Options = maps:get(options, Trail, []), - Constraints = maps:get(constraints, Trail, []), - {ApiRoot ++ PathMatch, Constraints, ModuleHandler, Options}; + ApiRoot = api_root(), + PathMatch = maps:get(path_match, Trail), + ModuleHandler = maps:get(handler, Trail), + Options = maps:get(options, Trail, []), + Constraints = maps:get(constraints, Trail, []), + {ApiRoot ++ PathMatch, Constraints, ModuleHandler, Options}; to_route_path(Trail) when is_tuple(Trail) -> - Trail. + Trail. %% @equiv trail(PathMatch, ModuleHandler, [], #{}, []) -spec trail(route_match(), module()) -> trail(). trail(PathMatch, ModuleHandler) -> - trail(PathMatch, ModuleHandler, [], #{}, []). + trail(PathMatch, ModuleHandler, [], #{}, []). %% @equiv trail(PathMatch, ModuleHandler, Options, #{}, []) -spec trail(route_match(), module(), any()) -> trail(). trail(PathMatch, ModuleHandler, Options) -> - trail(PathMatch, ModuleHandler, Options, #{}, []). + trail(PathMatch, ModuleHandler, Options, #{}, []). %% @equiv trail(PathMatch, ModuleHandler, Options, MetaData, []) -spec trail(route_match(), module(), any(), map()) -> trail(). trail(PathMatch, ModuleHandler, Options, MetaData) -> - trail(PathMatch, ModuleHandler, Options, MetaData, []). + trail(PathMatch, ModuleHandler, Options, MetaData, []). %% @doc This function allows you to add additional information to the %% `cowboy' handler, such as: resource path, handler module, %% options and metadata. Normally used to document handlers. --spec trail(route_match() - , module() - , any() - , map() - , cowboy:fields()) -> trail(). +-spec trail(route_match(), module(), any(), map(), cowboy:fields()) -> trail(). trail(PathMatch, ModuleHandler, Options, MetaData, Constraints) -> - #{ path_match => PathMatch - , handler => ModuleHandler - , options => Options - , metadata => MetaData - , constraints => Constraints - }. + #{path_match => PathMatch, + handler => ModuleHandler, + options => Options, + metadata => MetaData, + constraints => Constraints}. %% @doc Gets the `path_match' from the given `trail'. -spec path_match(trail()) -> route_match(). path_match(Trail) -> - maps:get(path_match, Trail, []). + maps:get(path_match, Trail, []). %% @doc Gets the `handler' from the given `trail'. -spec handler(trail()) -> module(). handler(Trail) -> - maps:get(handler, Trail, []). + maps:get(handler, Trail, []). %% @doc Gets the `options' from the given `trail'. -spec options(trail()) -> any(). options(Trail) -> - maps:get(options, Trail, []). + maps:get(options, Trail, []). %% @doc Gets the `metadata' from the given `trail'. -spec metadata(trail()) -> map(). metadata(Trail) -> - maps:get(metadata, Trail, #{}). + maps:get(metadata, Trail, #{}). %% @doc Gets the `constraints' from the given `trail'. -spec constraints(trail()) -> cowboy:fields(). constraints(Trail) -> - maps:get(constraints, Trail, []). + maps:get(constraints, Trail, []). %% @doc This function allows you to define the routes on each resource handler, %% instead of defining them all in one place (as you're required to do -%% with `cowboy'). Your handler must implement the callback `trails/0' +%% with `cowboy'). Your handler must implement the callback `c:trails_handler:trails/0' %% and return the specific routes for that handler. That callback is %% invoked for each given module and then the results are concatenated. --spec trails(module() | [module()]) -> trails(). +-spec trails(module() | [module()] | {module(), map()} | [{module(), map()}]) -> trails(). trails(Handlers) when is_list(Handlers) -> - trails(Handlers, []); + trails(Handlers, []); trails(Handler) -> - trails([Handler], []). + trails([Handler], []). %% @doc Store the given list of trails. --spec store(Trails::trails() | - [{HostMatch::route_match(), Trails::trails()}]) -> ok. +-spec store(Trails :: trails() | [{HostMatch :: route_match(), Trails :: trails()}]) -> + ok. store(Trails) -> - store('_', Trails). + store('_', Trails). --spec store(Server::ranch:ref(), - Trails::trails() | - [{HostMatch::route_match(), Trails::trails()}]) -> ok. +-spec store(Server :: ranch:ref(), + Trails :: trails() | [{HostMatch :: route_match(), Trails :: trails()}]) -> + ok. store(_Server, []) -> - ok; + ok; store(Server, [{HostMatch, Trails} | Hosts]) -> - NormalizedPaths = normalize_store_input(Trails), - do_store(Server, HostMatch, NormalizedPaths), - store(Server, Hosts); + NormalizedPaths = normalize_store_input(Trails), + do_store(Server, HostMatch, NormalizedPaths), + store(Server, Hosts); store(Server, Trails) -> - NormalizedPaths = normalize_store_input(Trails), - do_store(Server, '_', NormalizedPaths). + NormalizedPaths = normalize_store_input(Trails), + do_store(Server, '_', NormalizedPaths). %% @doc Retrieves all stored trails. -spec all() -> [trail()]. all() -> - all('_'). + all('_'). %% @doc Retrieves all stored trails for the given `HostMatch' --spec all(HostMatch::route_match()) -> [trail()]. +-spec all(HostMatch :: route_match()) -> [trail()]. all(HostMatch) -> - all('_', HostMatch). + all('_', HostMatch). %% @doc Retrieves all stored trails for the given `Server' and `HostMatch' --spec all(Server::ranch:ref(), HostMatch::route_match()) -> [trail()]. +-spec all(Server :: ranch:ref(), HostMatch :: route_match()) -> [trail()]. all(Server, HostMatch) -> - case application:get_application(trails) of - {ok, trails} -> - MatchSpec = - case {Server, HostMatch} of - {'_', HostMatch} -> - [{{{'$1', HostMatch, '$2'}, '$3'}, [], ['$$']}]; - {Server, HostMatch} -> - [{{{Server, HostMatch, '$1'}, '$2'}, [], ['$$']}] - end, - Matches = ets:select(trails, MatchSpec), - FoundServers = [Srvr || [Srvr, _PathMatch, _Trail] <- Matches], - % Extract unique elements - Servers = lists:usort(FoundServers), - % There should be no more than one element in this list. - % If there is more than one, it means the same trail is defined - % in other host(s). - case Servers of - [_, _ | _] -> throw(multiple_servers); - _ -> ok - end, - Trails = all_trails(Matches, []), - SortIdFun = - fun(A, B) -> maps:get(trails_id, A) < maps:get(trails_id, B) end, - SortedStoredTrails = lists:sort(SortIdFun, Trails), - lists:map(fun remove_id/1, SortedStoredTrails); - _ -> - throw({not_started, trails}) - end. + case application:get_application(trails) of + {ok, trails} -> + MatchSpec = + case {Server, HostMatch} of + {'_', HostMatch} -> + [{{{'$1', HostMatch, '$2'}, '$3'}, [], ['$$']}]; + {Server, HostMatch} -> + [{{{Server, HostMatch, '$1'}, '$2'}, [], ['$$']}] + end, + Matches = ets:select(trails, MatchSpec), + FoundServers = [Srvr || [Srvr, _PathMatch, _Trail] <- Matches], + % Extract unique elements + Servers = lists:usort(FoundServers), + % There should be no more than one element in this list. + % If there is more than one, it means the same trail is defined + % in other host(s). + case Servers of + [_, _ | _] -> + throw(multiple_servers); + _ -> + ok + end, + Trails = all_trails(Matches, []), + SortIdFun = fun(A, B) -> maps:get(trails_id, A) < maps:get(trails_id, B) end, + SortedStoredTrails = lists:sort(SortIdFun, Trails), + lists:map(fun remove_id/1, SortedStoredTrails); + _ -> + throw({not_started, trails}) + end. %% @doc Fetch the trail that matches with the given path. --spec retrieve(PathMatch::string()) -> trail() | notfound. +-spec retrieve(PathMatch :: string()) -> trail() | notfound. retrieve(PathMatch) -> - retrieve('_', PathMatch). + retrieve('_', PathMatch). %% @doc Fetch the trail that matches with the given host and path. --spec retrieve(HostMatch::route_match(), - PathMatch::string()) -> trail() | notfound. +-spec retrieve(HostMatch :: route_match(), PathMatch :: string()) -> trail() | notfound. retrieve(HostMatch, PathMatch) -> - retrieve('_', HostMatch, PathMatch). + retrieve('_', HostMatch, PathMatch). %% @doc Fetch the trail that matches with the given server and host and path. --spec retrieve(Server::ranch:ref(), - HostMatch::route_match(), - PathMatch::string()) -> trail() | notfound. +-spec retrieve(Server :: ranch:ref(), + HostMatch :: route_match(), + PathMatch :: string()) -> + trail() | notfound. retrieve(Server, HostMatch, PathMatch) -> - case application:get_application(trails) of - {ok, trails} -> - Key = {Server, HostMatch, PathMatch}, - case ets:select(trails, [{{Key, '$1'}, [], ['$$']}]) of - % No elements found - [] -> notfound; - % One element found - [[Trail = #{path_match := PathMatch}]] -> remove_id(Trail); - % More than one element found - [_, _ | _] -> throw(multiple_trails) - end; - _ -> - throw({not_started, trails}) - end. + case application:get_application(trails) of + {ok, trails} -> + Key = {Server, HostMatch, PathMatch}, + case ets:select(trails, [{{Key, '$1'}, [], ['$$']}]) of + % No elements found + [] -> + notfound; + % One element found + [[Trail = #{path_match := PathMatch}]] -> + remove_id(Trail); + % More than one element found + [_, _ | _] -> + throw(multiple_trails) + end; + _ -> + throw({not_started, trails}) + end. %% @doc Get api_root env param value if any, empty otherwise. -spec api_root() -> string(). @@ -251,18 +257,21 @@ api_root() -> %% @doc Set api_root env param to the given Path. -spec api_root(string()) -> ok. api_root(Path) -> - application:set_env(trails, api_root, Path). + application:set_env(trails, api_root, Path). -spec servers() -> [ranch:ref()]. servers() -> - lists:flatten(ets:match(ranch_server, {{conns_sup, '$1', '_'}, '_'})). + lists:flatten( + ets:match(ranch_server, {{conns_sup, '$1', '_'}, '_'})). -spec host_matches(ranch:ref()) -> [route_match()]. host_matches(ServerRef) -> - [Opts] = lists:flatten(ets:match(ranch_server, {{proto_opts, ServerRef}, '$1'})), - Env = maps:get(env, Opts, #{}), - Dispatchs = maps:get(dispatch, Env, []), - lists:flatten([Host || {Host, _, _} <- Dispatchs]). + [Opts] = + lists:flatten( + ets:match(ranch_server, {{proto_opts, ServerRef}, '$1'})), + Env = maps:get(env, Opts, #{}), + Dispatchs = maps:get(dispatch, Env, []), + lists:flatten([Host || {Host, _, _} <- Dispatchs]). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Private API. @@ -270,51 +279,55 @@ host_matches(ServerRef) -> %% @private trails([], Acc) -> - Acc; + Acc; trails([Module | T], Acc) -> - trails(T, Acc ++ trails_handler:trails(Module)). + trails(T, Acc ++ trails_handler:trails(Module)). %% @private --spec do_store(Server::ranch:ref(), - HostMatch::route_match(), - Trails::[route_path()]) -> ok. -do_store(_Server, _HostMatch, []) -> ok; +-spec do_store(Server :: ranch:ref(), + HostMatch :: route_match(), + Trails :: [route_path()]) -> + ok. +do_store(_Server, _HostMatch, []) -> + ok; do_store(Server, HostMatch, [Trail = #{path_match := PathMatch} | Trails]) -> - ets:insert(trails, {{Server, HostMatch, PathMatch}, Trail}), - do_store(Server, HostMatch, Trails). + ets:insert(trails, {{Server, HostMatch, PathMatch}, Trail}), + do_store(Server, HostMatch, Trails). %% @private all_trails([], Acc) -> - Acc; + Acc; all_trails([[_, Trail] | T], Acc) -> - all_trails(T, [Trail | Acc]); + all_trails(T, [Trail | Acc]); all_trails([[_Server, _PathMatch, Trail] | T], Acc) -> - all_trails(T, [Trail | Acc]). + all_trails(T, [Trail | Acc]). %% @private -spec normalize_store_input(trails()) -> [map()]. normalize_store_input(RoutesPaths) -> - normalize_id(normalize_paths(RoutesPaths)). + normalize_id(normalize_paths(RoutesPaths)). -spec normalize_id([route_path()]) -> [map()]. normalize_id(Trails) -> - Length = length(Trails), - AddIdFun = fun(Trail, Id) -> Trail#{ trails_id => Id} end, - lists:zipwith(AddIdFun, Trails, lists:seq(1, Length)). + Length = length(Trails), + AddIdFun = fun(Trail, Id) -> Trail#{trails_id => Id} end, + lists:zipwith(AddIdFun, Trails, lists:seq(1, Length)). %% @private -spec normalize_paths(trails()) -> [trail()]. normalize_paths(RoutesPaths) -> - [normalize_path(Path) || Path <- RoutesPaths]. + [normalize_path(Path) || Path <- RoutesPaths]. %% @private -spec remove_id(trail()) -> trail(). -remove_id(Trail) -> maps:remove(trails_id, Trail). +remove_id(Trail) -> + maps:remove(trails_id, Trail). %% @private -spec normalize_path(route_path() | trail()) -> trail(). normalize_path({PathMatch, ModuleHandler, Options}) -> - trail(PathMatch, ModuleHandler, Options); + trail(PathMatch, ModuleHandler, Options); normalize_path({PathMatch, Constraints, ModuleHandler, Options}) -> - trail(PathMatch, ModuleHandler, Options, #{}, Constraints); -normalize_path(Trail) when is_map(Trail) -> Trail. + trail(PathMatch, ModuleHandler, Options, #{}, Constraints); +normalize_path(Trail) when is_map(Trail) -> + Trail. diff --git a/src/trails_app.erl b/src/trails_app.erl index b951b9c..a83734d 100644 --- a/src/trails_app.erl +++ b/src/trails_app.erl @@ -1,4 +1,4 @@ -%%% @hidden +%%% @private -module(trails_app). -behaviour(application). @@ -8,8 +8,8 @@ -spec start(term(), term()) -> {error, term()} | {ok, pid()}. start(_Type, _Args) -> - trails_sup:start_link(). + trails_sup:start_link(). -spec stop(term()) -> ok. stop(_State) -> - ok. + ok. diff --git a/src/trails_handler.erl b/src/trails_handler.erl index c462a65..4ecfdbd 100644 --- a/src/trails_handler.erl +++ b/src/trails_handler.erl @@ -1,5 +1,5 @@ %%% @doc Trails handler. -%%% This behavior defines the callback `trails/0' which must be +%%% This behavior defines the callback `c:trails/0' which must be %%% implemented by the different `cowboy' handlers in your project. -module(trails_handler). @@ -9,8 +9,11 @@ %% @doc Returns the cowboy routes defined in the called module. -callback trails() -> trails:trails(). -callback trails(Opts :: map()) -> trails:trails(). + -optional_callbacks([trails/0, trails/1]). -spec trails(module() | {module(), map()}) -> trails:trails(). -trails({Module, Opts}) -> Module:trails(Opts); -trails(Module) -> Module:trails(). +trails({Module, Opts}) -> + Module:trails(Opts); +trails(Module) -> + Module:trails(). diff --git a/src/trails_sup.erl b/src/trails_sup.erl index 7bf92ce..a4775d1 100644 --- a/src/trails_sup.erl +++ b/src/trails_sup.erl @@ -1,4 +1,4 @@ -%%% @hidden +%%% @private -module(trails_sup). -behaviour(supervisor). @@ -7,14 +7,15 @@ -export([start_link/0]). -spec start_link() -> {ok, pid()} | {error, term()}. -start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []). +start_link() -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). -spec init([]) -> {ok, {{one_for_one, 10, 60}, []}}. init([]) -> - init_ets_table(), - {ok, {{one_for_one, 10, 60}, []}}. + init_ets_table(), + {ok, {{one_for_one, 10, 60}, []}}. %% @private -spec init_ets_table() -> atom(). init_ets_table() -> - ets:new(trails, [public, named_table]). + ets:new(trails, [public, named_table]). diff --git a/test/cover.spec b/test/cover.spec index 9e84a00..dd6f359 100644 --- a/test/cover.spec +++ b/test/cover.spec @@ -1,5 +1,2 @@ %% Specific modules to include in cover. -{ - incl_mods, - [trails, trails_app, trails_handler, trails_sup] -}. +{incl_mods, [trails, trails_app, trails_handler, trails_sup]}. diff --git a/test/trails_SUITE.erl b/test/trails_SUITE.erl index f53fd9b..418705c 100644 --- a/test/trails_SUITE.erl +++ b/test/trails_SUITE.erl @@ -1,5 +1,4 @@ -module(trails_SUITE). --author('elbrujohalcon@inaka.net'). -export([all/0]). -export([init_per_suite/1]). @@ -34,41 +33,45 @@ -dialyzer([{no_opaque, [trails_api_root/1]}]). -dialyzer([{no_return, [trails_api_root/1]}]). - -type config() :: [{atom(), term()}]. +-export_type([config/0]). + +-elvis([{elvis_style, no_catch_expressions, disable}]). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Common test %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -spec all() -> [atom()]. all() -> - Exports = ?MODULE:module_info(exports), - [F || {F, 1} <- Exports, F /= module_info]. + Exports = ?MODULE:module_info(exports), + [F || {F, 1} <- Exports, F /= module_info]. -spec init_per_suite(config()) -> config(). init_per_suite(Config) -> - _ = application:ensure_all_started(cowboy), - Config. + _ = application:ensure_all_started(cowboy), + Config. -spec end_per_suite(config()) -> config(). end_per_suite(Config) -> - _ = application:stop(trails), - Config. + _ = application:stop(trails), + Config. -spec init_per_testcase(atom(), config()) -> config(). init_per_testcase(trails_api_root, Config) -> - meck:new(cowboy_router, [passthrough]), - meck:expect(cowboy_router, compile, fun([_Routes]) -> ok end), - Config; -init_per_testcase(_, Config) -> Config. + meck:new(cowboy_router, [passthrough]), + meck:expect(cowboy_router, compile, fun([_Routes]) -> ok end), + Config; +init_per_testcase(_, Config) -> + Config. -spec end_per_testcase(atom(), config()) -> - term() | {fail, term()} | {save_config, config()}. + term() | {fail, term()} | {save_config, config()}. end_per_testcase(trails_api_root, Config) -> - meck:unload(cowboy_router), - application:set_env(trails, api_root, ""), - Config; + meck:unload(cowboy_router), + application:set_env(trails, api_root, ""), + Config; end_per_testcase(_, Config) -> Config. @@ -78,140 +81,107 @@ end_per_testcase(_, Config) -> -spec minimal_compile_test(config()) -> {atom(), string()}. minimal_compile_test(_Config) -> - MininalRoute = [{'_', []}], - ExpectedResponse = cowboy_router:compile(MininalRoute), - ExpectedResponse = trails:compile(MininalRoute), - {comment, ""}. + MininalRoute = [{'_', []}], + ExpectedResponse = cowboy_router:compile(MininalRoute), + ExpectedResponse = trails:compile(MininalRoute), + {comment, ""}. -spec empty_compile_test(config()) -> {atom(), string()}. empty_compile_test(_Config) -> - ExpectedResponse = cowboy_router:compile([]), - ExpectedResponse = trails:compile([]), - {comment, ""}. + ExpectedResponse = cowboy_router:compile([]), + ExpectedResponse = trails:compile([]), + {comment, ""}. -spec basic_compile_test(config()) -> {atom(), string()}. basic_compile_test(_Config) -> - BasicRoute = - [ - {"localhost", - [ - {"/such/path", http_such_path_handler, []}, - {"/very", http_very, []} - ]} - ], - ExpectedResponse = cowboy_router:compile(BasicRoute), - ExpectedResponse = trails:compile(BasicRoute), - {comment, ""}. + BasicRoute = + [{"localhost", [{"/such/path", http_such_path_handler, []}, {"/very", http_very, []}]}], + ExpectedResponse = cowboy_router:compile(BasicRoute), + ExpectedResponse = trails:compile(BasicRoute), + {comment, ""}. -spec static_compile_test(config()) -> {atom(), string()}. static_compile_test(_Config) -> - StaticRoute = get_static_route() , - ExpectedResponse = cowboy_router:compile(StaticRoute), - ExpectedResponse = trails:compile(StaticRoute), - {comment, ""}. + StaticRoute = get_static_route(), + ExpectedResponse = cowboy_router:compile(StaticRoute), + ExpectedResponse = trails:compile(StaticRoute), + {comment, ""}. -spec minimal_single_host_compile_test(config()) -> {atom(), string()}. minimal_single_host_compile_test(_Config) -> - MininalRoute = [{'_', []}], - [{_SingleHost, MininalPath}] = MininalRoute, - ExpectedResponse = cowboy_router:compile(MininalRoute), - ExpectedResponse = trails:single_host_compile(MininalPath), - {comment, ""}. + MininalRoute = [{'_', []}], + [{_SingleHost, MininalPath}] = MininalRoute, + ExpectedResponse = cowboy_router:compile(MininalRoute), + ExpectedResponse = trails:single_host_compile(MininalPath), + {comment, ""}. -spec basic_single_host_compile_test(config()) -> {atom(), string()}. basic_single_host_compile_test(_Config) -> - BasicRoute = get_basic_route(), - ExpectedResponse = cowboy_router:compile(BasicRoute), - [{_SingleHost, BasicPath}] = BasicRoute, - ExpectedResponse = trails:single_host_compile(BasicPath), - {comment, ""}. + BasicRoute = get_basic_route(), + ExpectedResponse = cowboy_router:compile(BasicRoute), + [{_SingleHost, BasicPath}] = BasicRoute, + ExpectedResponse = trails:single_host_compile(BasicPath), + {comment, ""}. -spec static_single_host_compile_test(config()) -> {atom(), string()}. static_single_host_compile_test(_Config) -> - StaticRoute = get_static_route(), - [{_SingleHost, StaticPath}] = StaticRoute, - ExpectedResponse = cowboy_router:compile(StaticRoute), - ExpectedResponse = trails:single_host_compile(StaticPath), - {comment, ""}. + StaticRoute = get_static_route(), + [{_SingleHost, StaticPath}] = StaticRoute, + ExpectedResponse = cowboy_router:compile(StaticRoute), + ExpectedResponse = trails:single_host_compile(StaticPath), + {comment, ""}. basic_trails2_constructor(_Config) -> - BasicRoute = - [ - {'_', - [ - trails:trail("/such/path", http_basic_route), - trails:trail("/very", http_very), - trails:trail("/", http_handler) - ]} - ], - BasicRouteCowboy = get_basic_route(), - ExpectedResponse = cowboy_router:compile(BasicRouteCowboy), - ExpectedResponse = trails:compile(BasicRoute), - {comment, ""}. + BasicRoute = + [{'_', + [trails:trail("/such/path", http_basic_route), + trails:trail("/very", http_very), + trails:trail("/", http_handler)]}], + BasicRouteCowboy = get_basic_route(), + ExpectedResponse = cowboy_router:compile(BasicRouteCowboy), + ExpectedResponse = trails:compile(BasicRoute), + {comment, ""}. basic_trails3_constructor(_Config) -> - BasicRoute = - [ - {'_', - [ - trails:trail("/such/path", http_basic_route, []), - trails:trail("/very", http_very, []), - trails:trail("/", http_handler, []) - ]} - ], - BasicRouteCowboy = get_basic_route(), - ExpectedResponse = cowboy_router:compile(BasicRouteCowboy), - ExpectedResponse = trails:compile(BasicRoute), - {comment, ""}. + BasicRoute = + [{'_', + [trails:trail("/such/path", http_basic_route, []), + trails:trail("/very", http_very, []), + trails:trail("/", http_handler, [])]}], + BasicRouteCowboy = get_basic_route(), + ExpectedResponse = cowboy_router:compile(BasicRouteCowboy), + ExpectedResponse = trails:compile(BasicRoute), + {comment, ""}. -spec static_trails3_constructor(config()) -> {atom(), string()}. static_trails3_constructor(_Config) -> - StaticRoute = - [ - {'_', - [ - trails:trail("/", cowboy_static, {private_file, "index.html"}) - ]} - ], - StaticRouteCowboy = get_static_route(), - [{_SingleHost, StaticPath}] = StaticRoute, - ExpectedResponse = cowboy_router:compile(StaticRouteCowboy), - ExpectedResponse = trails:single_host_compile(StaticPath), - {comment, ""}. + StaticRoute = [{'_', [trails:trail("/", cowboy_static, {private_file, "index.html"})]}], + StaticRouteCowboy = get_static_route(), + [{_SingleHost, StaticPath}] = StaticRoute, + ExpectedResponse = cowboy_router:compile(StaticRouteCowboy), + ExpectedResponse = trails:single_host_compile(StaticPath), + {comment, ""}. basic_trails4_constructor(_Config) -> - BasicRouteTrails = - [ - {'_', - [ - trails:trail("/such/path", http_basic_route, [], #{}), - trails:trail("/very", http_very, [], #{}), - trails:trail("/", http_handler, [], #{}) - ]} - ], - BasicRouteCowboy = get_basic_route(), - ExpectedResponse = cowboy_router:compile(BasicRouteCowboy), - ExpectedResponse = trails:compile(BasicRouteTrails), - {comment, ""}. + BasicRouteTrails = + [{'_', + [trails:trail("/such/path", http_basic_route, [], #{}), + trails:trail("/very", http_very, [], #{}), + trails:trail("/", http_handler, [], #{})]}], + BasicRouteCowboy = get_basic_route(), + ExpectedResponse = cowboy_router:compile(BasicRouteCowboy), + ExpectedResponse = trails:compile(BasicRouteTrails), + {comment, ""}. -spec static_trails4_constructor(config()) -> {atom(), string()}. static_trails4_constructor(_Config) -> - StaticRouteTrails = - [ - {'_', - [ - trails:trail("/" - , cowboy_static - , {private_file, "index.html"} - , #{} - , []) - ]} - ], - StaticRouteCowboy = get_static_route(), - [{_SingleHost, StaticPath}] = StaticRouteTrails, - ExpectedResponse = cowboy_router:compile(StaticRouteCowboy), - ExpectedResponse = trails:single_host_compile(StaticPath), - {comment, ""}. + StaticRouteTrails = + [{'_', [trails:trail("/", cowboy_static, {private_file, "index.html"}, #{}, [])]}], + StaticRouteCowboy = get_static_route(), + [{_SingleHost, StaticPath}] = StaticRouteTrails, + ExpectedResponse = cowboy_router:compile(StaticRouteCowboy), + ExpectedResponse = trails:single_host_compile(StaticPath), + {comment, ""}. -spec handler(config()) -> {atom(), string()}. handler(_Config) -> @@ -234,270 +204,244 @@ constraints(_Config) -> Constraints = trails:constraints(Trail), {comment, ""}. - -spec basic_metadata(config()) -> {atom(), string()}. +-spec basic_metadata(config()) -> {atom(), string()}. basic_metadata(_Config) -> - Metadata = #{ option => 1, description => "Basic Metadata"}, - Trail = - trails:trail("/" - , cowboy_static - , {private_file, "index1.html"} - , Metadata - , []), - Metadata = trails:metadata(Trail), - {comment, ""}. - - -spec put_metadata(config()) -> {atom(), string()}. + Metadata = #{option => 1, description => "Basic Metadata"}, + Trail = trails:trail("/", cowboy_static, {private_file, "index1.html"}, Metadata, []), + Metadata = trails:metadata(Trail), + {comment, ""}. + +-spec put_metadata(config()) -> {atom(), string()}. put_metadata(_Config) -> - Metadata = #{ put => #{ description => "Put method"}}, - Trail = - trails:trail("/" - , cowboy_static - , {private_file, "index2.html"} - , Metadata - , []), - Metadata = trails:metadata(Trail), - {comment, ""}. + Metadata = #{put => #{description => "Put method"}}, + Trail = trails:trail("/", cowboy_static, {private_file, "index2.html"}, Metadata, []), + Metadata = trails:metadata(Trail), + {comment, ""}. -spec post_metadata(config()) -> {atom(), string()}. post_metadata(_Config) -> - Metadata = #{ post => #{ description => "Post method"}}, - Trail = - trails:trail("/" - , cowboy_static - , {private_file, "index3.html"} - , Metadata - , []), - Metadata = trails:metadata(Trail), - {comment, ""}. + Metadata = #{post => #{description => "Post method"}}, + Trail = trails:trail("/", cowboy_static, {private_file, "index3.html"}, Metadata, []), + Metadata = trails:metadata(Trail), + {comment, ""}. get_static_route() -> - [ - {'_', - [ - {"/", cowboy_static, {private_file, "index.html"}} - ]} - ]. + [{'_', [{"/", cowboy_static, {private_file, "index.html"}}]}]. get_basic_route() -> - [ - {'_', - [ - {"/such/path", http_basic_route, []}, - {"/very", http_very, []}, - {"/", http_handler, []} - ]} - ]. + [{'_', + [{"/such/path", http_basic_route, []}, + {"/very", http_very, []}, + {"/", http_handler, []}]}]. -spec basic_trails_routes(config()) -> {atom(), string()}. basic_trails_routes(_Config) -> - StaticRoutes = - [ {"/", 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"}} - ], - ExpectedResponse1 = StaticRoutes ++ - [ {"/api/resource1/[:id]", trails_test_handler, []} - , {"/api/:id/resource2", trails_test_handler, [arg0]} - , {"/api/resource3/[:id]", trails_test2_handler, []} - , {"/api/:id/resource4", trails_test2_handler, [arg0]} - ], - ExpectedResponse2 = StaticRoutes ++ - [ {"/api/resource1/[:id]", trails_test_handler, []} - , {"/api/:id/resource2", trails_test_handler, [arg0]} - ], - ExpectedResponse3 = StaticRoutes ++ - [ {"/api/resource3/[:id]", trails_test2_handler, []} - , {"/api/:id/resource4", trails_test2_handler, [arg0]} - , {"/api/resource1/[:id]", trails_test_handler, []} - , {"/api/:id/resource2", trails_test_handler, [arg0]} - ], - ExpectedResponse4 = - [ {"/api/resource5/[:id]", trails_test3_handler, []} - , {"/api/:id/resource6", trails_test3_handler, [#{test_key => test_value}]} - ] ++ ExpectedResponse3, - Handlers1 = [trails_test_handler, trails_test2_handler], - Handlers2 = [trails_test2_handler, trails_test_handler], - Handlers3 = [{trails_test3_handler, #{test_key => test_value}}], - Trails1 = StaticRoutes ++ trails:trails(Handlers1), - ExpectedResponse1 = Trails1, - Trails2 = StaticRoutes ++ trails:trails(trails_test_handler), - ExpectedResponse2 = Trails2, - Trails3 = StaticRoutes ++ trails:trails(Handlers2), - ExpectedResponse3 = Trails3, - Trails4 = trails:trails(Handlers3) ++ Trails3, - ExpectedResponse4 = Trails4, - {comment, ""}. + StaticRoutes = + [{"/", 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"}}], + ExpectedResponse1 = + StaticRoutes + ++ [{"/api/resource1/[:id]", trails_test_handler, []}, + {"/api/:id/resource2", trails_test_handler, [arg0]}, + {"/api/resource3/[:id]", trails_test2_handler, []}, + {"/api/:id/resource4", trails_test2_handler, [arg0]}], + ExpectedResponse2 = + StaticRoutes + ++ [{"/api/resource1/[:id]", trails_test_handler, []}, + {"/api/:id/resource2", trails_test_handler, [arg0]}], + ExpectedResponse3 = + StaticRoutes + ++ [{"/api/resource3/[:id]", trails_test2_handler, []}, + {"/api/:id/resource4", trails_test2_handler, [arg0]}, + {"/api/resource1/[:id]", trails_test_handler, []}, + {"/api/:id/resource2", trails_test_handler, [arg0]}], + ExpectedResponse4 = + [{"/api/resource5/[:id]", trails_test3_handler, []}, + {"/api/:id/resource6", trails_test3_handler, [#{test_key => test_value}]}] + ++ ExpectedResponse3, + Handlers1 = [trails_test_handler, trails_test2_handler], + Handlers2 = [trails_test2_handler, trails_test_handler], + Handlers3 = [{trails_test3_handler, #{test_key => test_value}}], + Trails1 = StaticRoutes ++ trails:trails(Handlers1), + ExpectedResponse1 = Trails1, + Trails2 = StaticRoutes ++ trails:trails(trails_test_handler), + ExpectedResponse2 = Trails2, + Trails3 = StaticRoutes ++ trails:trails(Handlers2), + ExpectedResponse3 = Trails3, + Trails4 = trails:trails(Handlers3) ++ Trails3, + ExpectedResponse4 = Trails4, + {comment, ""}. -spec trails_store(config()) -> {atom(), string()}. trails_store(_Config) -> - TrailsRaw = [ - {"/resource/[:id]", trails_test_handler, []}, - {"/api/:id/resource", [], trails_test2_handler, [arg0]}, - trails:trail("/assets/[...]", cowboy_static, {dir, "www/assets"}), - trails:trail("/such/path", http_basic_route, [], #{}), - trails:trail("/very", http_very, [], #{}), - trails:trail("/", http_handler, []) - ], - {not_started, trails} = (catch trails:all()), - {not_started, trails} = (catch trails:retrieve("/")), - {ok, _} = application:ensure_all_started(trails), - ok = trails:store(TrailsRaw), - Trails = normalize_paths(TrailsRaw), - Length = length(Trails), - Length = length(trails:all()), - Trails = trails:all(), - Trails1 = trails:retrieve("/assets/[...]"), - "/assets/[...]" = trails:path_match(Trails1), - Trails2 = trails:retrieve("/such/path"), - "/such/path" = trails:path_match(Trails2), - Trails3 = trails:retrieve("/very"), - "/very" = trails:path_match(Trails3), - Trails4 = trails:retrieve("/"), - "/" = trails:path_match(Trails4), - Trails5 = trails:retrieve("/resource/[:id]"), - "/resource/[:id]" = trails:path_match(Trails5), - Trails6 = trails:retrieve("/api/:id/resource"), - "/api/:id/resource" = trails:path_match(Trails6), - notfound = trails:retrieve("/other"), - {comment, ""}. + TrailsRaw = + [{"/resource/[:id]", trails_test_handler, []}, + {"/api/:id/resource", [], trails_test2_handler, [arg0]}, + trails:trail("/assets/[...]", cowboy_static, {dir, "www/assets"}), + trails:trail("/such/path", http_basic_route, [], #{}), + trails:trail("/very", http_very, [], #{}), + trails:trail("/", http_handler, [])], + {not_started, trails} = (catch trails:all()), + {not_started, trails} = (catch trails:retrieve("/")), + {ok, _} = application:ensure_all_started(trails), + ok = trails:store(TrailsRaw), + Trails = normalize_paths(TrailsRaw), + Length = length(Trails), + Length = length(trails:all()), + Trails = trails:all(), + Trails1 = trails:retrieve("/assets/[...]"), + "/assets/[...]" = trails:path_match(Trails1), + Trails2 = trails:retrieve("/such/path"), + "/such/path" = trails:path_match(Trails2), + Trails3 = trails:retrieve("/very"), + "/very" = trails:path_match(Trails3), + Trails4 = trails:retrieve("/"), + "/" = trails:path_match(Trails4), + Trails5 = trails:retrieve("/resource/[:id]"), + "/resource/[:id]" = trails:path_match(Trails5), + Trails6 = trails:retrieve("/api/:id/resource"), + "/api/:id/resource" = trails:path_match(Trails6), + notfound = trails:retrieve("/other"), + {comment, ""}. -spec trails_api_root(config()) -> {comment, string()}. trails_api_root(_Config) -> - ok = trails:api_root("/api"), - "/api" = trails:api_root(), - Routes = [trails:trail("/things", the_handler)], - ok = trails:single_host_compile(Routes), - {comment, ""}. - + ok = trails:api_root("/api"), + "/api" = trails:api_root(), + Routes = [trails:trail("/things", the_handler)], + _ = trails:single_host_compile(Routes), + {comment, ""}. -spec minimal_multiple_host_compile_test(config()) -> {comment, string()}. minimal_multiple_host_compile_test(_Config) -> - Trails1 = get_trails1(), - [Trail1, _, Repeated1] = Trails1, - Trails2 = get_trails2(), - % Checks the ability to store same routes for different hosts - ok = trails:store([{"host1", Trails1}, {"host2", Trails2}]), - Trails1 = trails:all("host1"), - Trails2 = trails:all("host2"), - % trails:retrieve/1 will throw an exception if the same trail - % is defined in more than one host or server. - ok = try trails:retrieve("/repeated") - catch - throw:multiple_trails -> ok - end, - Trail1 = trails:retrieve("host1", "/path1"), - Repeated1 = trails:retrieve("host1", "/repeated"), - notfound = trails:retrieve("host2", "/path1"), - notfound = trails:retrieve("unknown_host", "/path1"), - ok = trails:store([{"host3", Trails1}, {"host4", Trails2}]), - {comment, ""}. + Trails1 = get_trails1(), + [Trail1, _, Repeated1] = Trails1, + Trails2 = get_trails2(), + % Checks the ability to store same routes for different hosts + ok = trails:store([{"host1", Trails1}, {"host2", Trails2}]), + Trails1 = trails:all("host1"), + Trails2 = trails:all("host2"), + % trails:retrieve/1 will throw an exception if the same trail + % is defined in more than one host or server. + ok = + try + trails:retrieve("/repeated") + catch + multiple_trails -> + ok + end, + Trail1 = trails:retrieve("host1", "/path1"), + Repeated1 = trails:retrieve("host1", "/repeated"), + notfound = trails:retrieve("host2", "/path1"), + notfound = trails:retrieve("unknown_host", "/path1"), + ok = trails:store([{"host3", Trails1}, {"host4", Trails2}]), + {comment, ""}. -spec minimal_multiple_server_test(config()) -> {comment, string()}. minimal_multiple_server_test(_Config) -> - Trails1 = get_trails1(), - [Trail1, _, Repeated1] = Trails1, - Trails2 = get_trails2(), - [_, _, Repeated2] = Trails2, - ok = trails:store(server1, [{"host1", Trails1}, {"host2", Trails2}]), - ok = trails:store(server2, Trails2), - ok = trails:store(server3, [{"host1", Trails1}]), - Trails1 = trails:all(server1, "host1"), - Trails2 = trails:all(server1, "host2"), - Trails2 = trails:all(server2, '_'), - ok = try trails:all("host1") - catch - throw:multiple_servers -> ok - end, - ok = try trails:retrieve("host1", "/path1") - catch - throw:multiple_trails -> ok - end, - Trail1 = trails:retrieve(server1, "host1", "/path1"), - Trail1 = trails:retrieve(server3, "host1", "/path1"), - Repeated1 = trails:retrieve(server1, "host1", "/repeated"), - Repeated2 = trails:retrieve(server2, '_', "/repeated"), - notfound = trails:retrieve(server1, "host2", "/path1"), - notfound = trails:retrieve(server3, "host2", "/path4"), - notfound = trails:retrieve(unknown_server, "unknown_host", "/path1"), - {comment, ""}. + Trails1 = get_trails1(), + [Trail1, _, Repeated1] = Trails1, + Trails2 = get_trails2(), + [_, _, Repeated2] = Trails2, + ok = trails:store(server1, [{"host1", Trails1}, {"host2", Trails2}]), + ok = trails:store(server2, Trails2), + ok = trails:store(server3, [{"host1", Trails1}]), + Trails1 = trails:all(server1, "host1"), + Trails2 = trails:all(server1, "host2"), + Trails2 = trails:all(server2, '_'), + ok = + try + trails:all("host1") + catch + multiple_servers -> + ok + end, + ok = + try + trails:retrieve("host1", "/path1") + catch + multiple_trails -> + ok + end, + Trail1 = trails:retrieve(server1, "host1", "/path1"), + Trail1 = trails:retrieve(server3, "host1", "/path1"), + Repeated1 = trails:retrieve(server1, "host1", "/repeated"), + Repeated2 = trails:retrieve(server2, '_', "/repeated"), + notfound = trails:retrieve(server1, "host2", "/path1"), + notfound = trails:retrieve(server3, "host2", "/path4"), + notfound = trails:retrieve(unknown_server, "unknown_host", "/path1"), + {comment, ""}. -spec server_hostmatches(config()) -> {comment, string()}. server_hostmatches(_Config) -> - Trails1 = get_trails1(), - Trails2 = get_trails2(), - Routes1 = [{"hostmatch1", Trails1}, {"hostmatch2", Trails2}], - Dispatch1 = trails:compile(Routes1), + Trails1 = get_trails1(), + Trails2 = get_trails2(), + Routes1 = [{"hostmatch1", Trails1}, {"hostmatch2", Trails2}], + Dispatch1 = trails:compile(Routes1), - Trails3 = get_trails3(), - Routes2 = [{"hostmatch3", Trails3}], - Dispatch2 = trails:compile(Routes2), + Trails3 = get_trails3(), + Routes2 = [{"hostmatch3", Trails3}], + Dispatch2 = trails:compile(Routes2), - RanchOptions1 = [{port, 8080}], - CowboyOptions1 = make_cowboy_options(Dispatch1), - {ok, _} = - cowboy:start_clear(server1, RanchOptions1, CowboyOptions1), + RanchOptions1 = [{port, 8080}], + CowboyOptions1 = make_cowboy_options(Dispatch1), + {ok, _} = cowboy:start_clear(server1, RanchOptions1, CowboyOptions1), - RanchOptions2 = [{port, 8081}], - CowboyOptions2 = make_cowboy_options(Dispatch2), - {ok, _} = - cowboy:start_clear(server2, RanchOptions2, CowboyOptions2), + RanchOptions2 = [{port, 8081}], + CowboyOptions2 = make_cowboy_options(Dispatch2), + {ok, _} = cowboy:start_clear(server2, RanchOptions2, CowboyOptions2), - Servers = trails:servers(), - true = lists:member(server1, Servers) andalso lists:member(server2, Servers), + Servers = trails:servers(), + true = lists:member(server1, Servers) andalso lists:member(server2, Servers), - HostMatches1 = trails:host_matches(server1), - true = lists:member(<<"hostmatch1">>, HostMatches1) andalso - lists:member(<<"hostmatch2">>, HostMatches1), + HostMatches1 = trails:host_matches(server1), + true = + lists:member(<<"hostmatch1">>, HostMatches1) + andalso lists:member(<<"hostmatch2">>, HostMatches1), - [<<"hostmatch3">>] = trails:host_matches(server2), + [<<"hostmatch3">>] = trails:host_matches(server2), - ok = cowboy:stop_listener(server1), - ok = cowboy:stop_listener(server2), + ok = cowboy:stop_listener(server1), + ok = cowboy:stop_listener(server2), - {comment, ""}. + {comment, ""}. %% @private normalize_paths(RoutesPaths) -> - [normalize_path(Path) || Path <- RoutesPaths]. + [normalize_path(Path) || Path <- RoutesPaths]. %% @private normalize_path({PathMatch, ModuleHandler, Options}) -> - trails:trail(PathMatch, ModuleHandler, Options); + trails:trail(PathMatch, ModuleHandler, Options); normalize_path({PathMatch, Constraints, ModuleHandler, Options}) -> - trails:trail(PathMatch, ModuleHandler, Options, #{}, Constraints); -normalize_path(Trail) -> Trail. + trails:trail(PathMatch, ModuleHandler, Options, #{}, Constraints); +normalize_path(Trail) -> + Trail. %% @private -spec get_trails1() -> [trails:trail()]. get_trails1() -> - [ - trails:trail("/path1", path1_handler), - trails:trail("/path2", path2_handler), - trails:trail("/repeated", repeated_handler) - ]. + [trails:trail("/path1", path1_handler), + trails:trail("/path2", path2_handler), + trails:trail("/repeated", repeated_handler)]. %% @private -spec get_trails2() -> [trails:trail()]. get_trails2() -> - [ - trails:trail("/path3", path3_handler), - trails:trail("/path4", path4_handler), - trails:trail("/repeated", repeated_handler) - ]. + [trails:trail("/path3", path3_handler), + trails:trail("/path4", path4_handler), + trails:trail("/repeated", repeated_handler)]. %% @private -spec get_trails3() -> [trails:trail()]. get_trails3() -> - [ - trails:trail("/path5", path5_handler), - trails:trail("/path6", path6_handler) - ]. + [trails:trail("/path5", path5_handler), trails:trail("/path6", path6_handler)]. %% @private -spec make_cowboy_options(cowboy_router:dispatch_rules()) -> map(). make_cowboy_options(Dispatch) -> - #{ env => #{dispatch => Dispatch} - , compress => true - , timeout => 12000 - }. + #{env => #{dispatch => Dispatch}, + compress => true, + timeout => 12000}. diff --git a/test/trails_test2_handler.erl b/test/trails_test2_handler.erl index 5f7901f..7fbea65 100644 --- a/test/trails_test2_handler.erl +++ b/test/trails_test2_handler.erl @@ -6,7 +6,5 @@ -export([trails/0]). trails() -> - [ - {"/api/resource3/[:id]", trails_test2_handler, []}, - {"/api/:id/resource4", trails_test2_handler, [arg0]} - ]. + [{"/api/resource3/[:id]", trails_test2_handler, []}, + {"/api/:id/resource4", trails_test2_handler, [arg0]}]. diff --git a/test/trails_test3_handler.erl b/test/trails_test3_handler.erl index 588200c..c4d4d5f 100644 --- a/test/trails_test3_handler.erl +++ b/test/trails_test3_handler.erl @@ -6,7 +6,5 @@ -export([trails/1]). trails(Opts) -> - [ - {"/api/resource5/[:id]", trails_test3_handler, []}, - {"/api/:id/resource6", trails_test3_handler, [Opts]} - ]. + [{"/api/resource5/[:id]", trails_test3_handler, []}, + {"/api/:id/resource6", trails_test3_handler, [Opts]}]. diff --git a/test/trails_test_handler.erl b/test/trails_test_handler.erl index 214059c..7b50b58 100644 --- a/test/trails_test_handler.erl +++ b/test/trails_test_handler.erl @@ -6,7 +6,5 @@ -export([trails/0]). trails() -> - [ - {"/api/resource1/[:id]", trails_test_handler, []}, - {"/api/:id/resource2", trails_test_handler, [arg0]} - ]. + [{"/api/resource1/[:id]", trails_test_handler, []}, + {"/api/:id/resource2", trails_test_handler, [arg0]}].