Commit 7592ca87 authored by Guilherme Andrade's avatar Guilherme Andrade

Merge branch 'develop'

parents b70b0e4f 8681f52f
......@@ -10,22 +10,19 @@ jobs:
tag:
description: The Erlang/OTP docker image tag to use
type: string
codecov_flag:
description: String the coverage reports are grouped by
type: string
executor:
name: rebar3/erlang
tag: <<parameters.tag>>
steps:
- checkout
- run:
name: "Clone submodules"
command: |
git submodule update --init --recursive
- rebar3/with_deps_cache:
cache_key_postfix: -<<parameters.tag>>
steps:
- rebar3/compile
- rebar3/dialyzer
- run: rebar3 as test xref
- rebar3/ct
# TODO `analyze`
- run: RUNNING_ON_CI=yes make ci_test
- store_test_results:
path: ~/project/_build/test/logs/
......@@ -37,8 +34,5 @@ workflows:
run_all:
jobs:
- build_and_test:
name: "otp-21"
tag: "21.3"
- build_and_test:
name: "otp-20"
tag: "20.3"
name: "otp-18"
tag: "18.3"
sudo: false
language: erlang
otp_release:
- 17.4
- 17.5
- 18.0
- 18.1
- 18.2
- 18.3
#- 18.0
#- 18.1
#- 18.2
#- 18.3
- 19.0
- 19.1
- 19.2
#- 19.1
#- 19.2
- 19.3
- 20.0
- 20.1
- 20.2
#- 20.2
- 20.3
- 21.0
- 21.1
- 21.2
- 21.3
- 22.0
script: RUNNING_ON_TRAVIS=yes make travis_test
script: RUNNING_ON_CI=yes make ci_test
......@@ -4,6 +4,28 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## [1.7.0] - 2019-08-12
### Added
- stacktrace of caught exceptions to event reporting (including custom logger)
- ability of launching database loaders under library consumers' own supervisors
- `wait_for_loaders/2` API method for waiting on multiple databases concurrently
### Changed
- log level of HTTP and filesystem database loading failures from warning to error
- HTTP and filesystem loaders into a single loader codebase
- supervision structure as to launch database loaders as transient processes under a new `simple_one_for_one` supervisor
- caching of HTTP databases as to store and load compressed `.mmdb` files rather than tarballs
- dependency versions:
- `certifi` [2.4.2 => 2.5.1]
- `ssl_verify_fun` [1.1.4 => 1.1.5]
### Removed
- support for OTP 17.4 and 17.5
- undocumented support for rebar 2
- half-baked and unwarranted support for `file://`-prefixed URLs
### Fixed
- case-sensitive search of `.mmdb` file within tarball
- overly verbose `logger` messages on OTP 21.1+
- HTTPS certificate validation test cases on OTP 22
## [1.6.2] - 2019-03-16
### Fixed
- Dialyzer warning on OTP 21.3
......
......@@ -4,7 +4,7 @@ ifeq ($(wildcard rebar3),rebar3)
REBAR3 = $(CURDIR)/rebar3
endif
ifdef RUNNING_ON_TRAVIS
ifdef RUNNING_ON_CI
REBAR3 = ./rebar3
else
REBAR3 ?= $(shell test -e `which rebar3` 2>/dev/null && which rebar3 || echo "./rebar3")
......@@ -17,10 +17,10 @@ endif
CLI_ARTIFACT_PATH = _build/escriptize/bin/locus
.PHONY: all build clean check dialyzer xref
.PHONY: test travis_test cover
.PHONY: test ci_test cover
.PHONY: console doc publish cli
.NOTPARALLEL: check cover test travis_test
.NOTPARALLEL: check cover test ci_test
all: build
......@@ -46,8 +46,8 @@ test: $(REBAR3) cli
@$(REBAR3) ct
./locus analyze --log-level debug test/priv/GeoLite2-Country.tar.gz
travis_test: $(REBAR3) cli
@$(REBAR3) as travis_test ct
ci_test: $(REBAR3) cli
@$(REBAR3) as ci_test ct
./locus analyze --log-level debug test/priv/GeoLite2-Country.tar.gz
cover: $(REBAR3) test
......@@ -73,6 +73,6 @@ publish: $(REBAR3)
@$(REBAR3) as publish hex publish
@$(REBAR3) as publish hex docs
cli:
cli: $(REBAR3)
@$(REBAR3) as escriptize escriptize
cp -p "$(CLI_ARTIFACT_PATH)" ./
......@@ -2,6 +2,7 @@
[![](https://img.shields.io/hexpm/v/locus.svg?style=flat)](https://hex.pm/packages/locus)
[![](https://travis-ci.org/g-andrade/locus.png?branch=master)](https://travis-ci.org/g-andrade/locus)
[![](https://circleci.com/gh/g-andrade/locus/tree/master.svg?style=svg)](https://circleci.com/gh/g-andrade/locus/tree/master)
`locus` is library for Erlang/OTP and Elixir that allows you to pinpoint
the country, city or ASN of IP addresses using MaxMind GeoIP2.
......@@ -17,8 +18,7 @@ towards MaxMind.
#### Usage
Clone the repository and run `make console` to bring up a
shell.
Clone the repository and run `make console` to bring up a shell.
##### 1\. Start the database loader
......@@ -26,7 +26,7 @@ shell.
URL = "https://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.tar.gz",
ok = locus:start_loader(country, URL).
% URL can also be a local path, e.g. "/opt/MaxMind/GeoLite2-Country.tar.gz"
% URL can also be a local path, e.g. "/usr/share/GeoIP/GeoLite2-City.mmdb"
```
##### 2\. Wait for the database to load (optional)
......@@ -84,7 +84,7 @@ ok = locus:start_loader(country, URL).
#### Documentation
1. [File Formats](#file-formats)
1. [Supported File Formats](#supported-file-formats)
2. [Database Types and Loading](#database-types-and-loading)
3. [Database Validation](#database-validation)
4. [HTTP URLs: Downloading and
......@@ -100,13 +100,18 @@ ok = locus:start_loader(country, URL).
12. [Alternative Libraries (Erlang)](#alternative-libraries-erlang)
13. [Alternative Libraries (Elixir)](#alternative-libraries-elixir)
##### File Formats
##### Supported File Formats
- Only gzip-compressed tarballs are supported as of this moment
- The first file to be found, within the tarball, with an .mmdb
extension, is the one that's chosen for loading
- The implementation of [MaxMind DB
format](https://maxmind.github.io/MaxMind-DB/) is mostly complete
- gzip-compressed tarballs (`.tar.gz`, `.tgz`)
- plain tarballs (`.tar`)
- MMDB files (`.mmdb`)
- gzip-compressed MMDB files (`.mmdb.gz`)
For tarballs, the first file to be found with an `.mmdb` extension is
the one that's chosen for loading.
The implementation of [MaxMind DB
format](https://maxmind.github.io/MaxMind-DB/) is mostly complete.
##### Database Types and Loading
......@@ -135,8 +140,7 @@ the `locus` CLI utility:
1. Run `make cli` to build the script, named `locus`, which will be
deployed to the current directory.
2. Run
analysis:
2. Run analysis:
``` shell
./locus analyze https://geolite.maxmind.com/download/geoip/database/GeoLite2-ASN.tar.gz
......@@ -152,11 +156,12 @@ arguments.
##### HTTP URLs: Downloading and Updating
- The downloaded tarballs are uncompressed in memory
- The downloaded database files, when compressed, are inflated in
memory
- The `last-modified` response header, if present, is used to
condition subsequent download attempts (using `if-modified-since`
request headers) in order to save bandwidth
- The downloaded tarballs are cached on the filesystem in order to
- The downloaded databases are cached on the filesystem in order to
more quickly achieve readiness on future launches of the database
loader
- Until a HTTP database loader achieves readiness, download attempts
......@@ -179,8 +184,8 @@ arguments.
- A caching directory named `locus_erlang` is created under the
['user\_cache'
basedir](http://erlang.org/doc/man/filename.html#basedir-3)
- Cached tarballs are named after the SHA256 hash of their source URL
- Modification time of the tarballs is extracted from `last-modified`
- Cached databases are named after the SHA256 hash of their source URL
- Modification time of the databases is extracted from `last-modified`
response header (when present) and used to condition downloads on
subsequent boots and save bandwidth
- Caching can be disabled by specifying the `no_cache` option when
......@@ -188,19 +193,24 @@ arguments.
##### Filesystem URLs: Loading and Updating
- The loaded tarballs are uncompressed in memory
- The loaded databases, when compressed, are inflated in memory
- Until a filesystem database loader achieves readiness, load attempts
are made every 5 seconds; once readiness is achieved, this interval
increases to every 30 seconds and load attempts are dismissed as
long as the tarball modification timestamp keeps unchanged
long as the database file modification timestamp keeps unchanged
##### Logging
- Five logging levels are supported: `debug`, `info`, `warning`,
`error` and `none`
- The backend is
[error\_logger](http://erlang.org/doc/man/error_logger.html); this
usually plays nicely with `lager`
- The chosen backend ON OTP 21.1+ is
[logger](http://erlang.org/doc/man/logger.html) **if**
[lager](https://github.com/erlang-lager/lager/) is either missing or
it hasn't
[removed](https://github.com/erlang-lager/lager/issues/492)
`logger`'s default handler; for all other scenarios,
[error\_logger](http://erlang.org/doc/man/error_logger.html) is
picked instead
- The default log level is `error`; it can be changed in the
application's `env` config
- To tweak the log level in runtime, use `locus_logger:set_loglevel/1`
......@@ -223,7 +233,7 @@ The API reference can be found on [HexDocs](https://hexdocs.pm/locus/).
##### Tested setup
- Erlang/OTP 17.4 or newer
- Erlang/OTP 18.0 or newer
- rebar3
##### License
......@@ -266,8 +276,7 @@ released under the Apache License 2.0.
- [geoip](https://github.com/manifest/geoip): Returns the location of
an IP address; based on the ipinfodb.com web service
- [geolite2data](https://hex.pm/packages/geolite2data): Periodically
fetches the free MaxMind GeoLite2
databases
fetches the free MaxMind GeoLite2 databases
- [ip2location-erlang](https://github.com/ip2location/ip2location-erlang):
Uses IP2Location geolocation database
......@@ -286,8 +295,7 @@ released under the Apache License 2.0.
location to a Plug connection based upon the client IP address by
using MaxMind's GeoIP2 database
- [tz\_world](https://hex.pm/packages/tz_world): Resolve timezones
from a location efficiently using PostGIS and
Ecto
from a location efficiently using PostGIS and Ecto
-----
......
......@@ -3,8 +3,10 @@
<a target="_parent" href="https://hex.pm/packages/locus" alt="Hex.pm Package">
<img src="https://img.shields.io/hexpm/v/locus.svg?style=flat"/></a>
<a target="_parent" href="https://travis-ci.org/g-andrade/locus" alt="Build Status">
<a target="_parent" href="https://travis-ci.org/g-andrade/locus" alt="Travis CI Build Status">
<img src="https://travis-ci.org/g-andrade/locus.png?branch=master"/></a>
<a target="_parent" href="https://circleci.com/gh/g-andrade/locus/tree/master" alt="Circle CI Build Status">
<img src="https://circleci.com/gh/g-andrade/locus/tree/master.svg?style=svg"/></a>
`locus' is library for Erlang/OTP and Elixir that allows you to pinpoint the country, city or ASN
of IP addresses using MaxMind GeoIP2.
......@@ -25,7 +27,7 @@ Clone the repository and run `make console' to bring up a shell.
URL = "https://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.tar.gz",
ok = locus:start_loader(country, URL).
% URL can also be a local path, e.g. "/opt/MaxMind/GeoLite2-Country.tar.gz"
% URL can also be a local path, e.g. "/usr/share/GeoIP/GeoLite2-City.mmdb"
</pre>
<h5>2. Wait for the database to load (optional)</h5>
......@@ -85,7 +87,7 @@ ok = locus:start_loader(country, URL).
<h4>Documentation</h4>
<ol>
<li><a href="#file-formats">File Formats</a></li>
<li><a href="#supported-file-formats">Supported File Formats</a></li>
<li><a href="#database-types-and-loading">Database Types and Loading</a></li>
<li><a href="#database-validation">Database Validation</a></li>
<li><a href="#http-urls-downloading-and-updating">HTTP URLs: Downloading and Updating</a></li>
......@@ -100,15 +102,20 @@ ok = locus:start_loader(country, URL).
<li><a href="#alternative-libraries-elixir">Alternative Libraries (Elixir)</a></li>
</ol>
<h5 id="file-formats">File Formats</h5>
<h5 id="supported-file-formats">Supported File Formats</h5>
<ul>
<li>Only gzip-compressed tarballs are supported as of this moment</li>
<li>The first file to be found, within the tarball, with an .mmdb extension, is the one that's chosen for loading</li>
<li>The implementation of <a target="_parent" href="https://maxmind.github.io/MaxMind-DB/">MaxMind DB format</a>
is mostly complete</li>
<li>gzip-compressed tarballs (`.tar.gz', `.tgz')</li>
<li>plain tarballs (`.tar')</li>
<li>MMDB files (`.mmdb')</li>
<li>gzip-compressed MMDB files (`.mmdb.gz')</li>
</ul>
For tarball files, the first file to be found within it with an `.mmdb' extension is the one that's chosen for loading.
The implementation of <a target="_parent" href="https://maxmind.github.io/MaxMind-DB/">MaxMind DB format</a>
is mostly complete.
<h5 id="database-types-and-loading">Database Types and Loading</h5>
<ul>
......@@ -147,11 +154,11 @@ Run `./locus analyze --help' for a description of supported options and argument
<h5 id="http-urls-downloading-and-updating">HTTP URLs: Downloading and Updating</h5>
<ul>
<li>The downloaded tarballs are uncompressed in memory</li>
<li>The downloaded database files, when compressed, are inflated in memory</li>
<li>The `last-modified' response header, if present, is used to condition subsequent download
attempts (using `if-modified-since' request headers) in order to save bandwidth
</li>
<li>The downloaded tarballs are cached on the filesystem in order to more quickly achieve readiness
<li>The downloaded databases are cached on the filesystem in order to more quickly achieve readiness
on future launches of the database loader
</li>
<li>Until a HTTP database loader achieves readiness, download attempts are made every minute;
......@@ -172,8 +179,8 @@ Run `./locus analyze --help' for a description of supported options and argument
<ul>
<li>Caching is a best effort; the system falls back to relying exclusively on the network if needed</li>
<li>A caching directory named `locus_erlang' is created under the <a target="_parent" href="http://erlang.org/doc/man/filename.html#basedir-3">'user_cache' basedir</a></li>
<li>Cached tarballs are named after the SHA256 hash of their source URL</li>
<li>Modification time of the tarballs is extracted from `last-modified' response header (when present)
<li>Cached databases are named after the SHA256 hash of their source URL</li>
<li>Modification time of the databases is extracted from `last-modified' response header (when present)
and used to condition downloads on subsequent boots and save bandwidth
</li>
<li>Caching can be disabled by specifying the `no_cache' option when running `:start_loader'</li>
......@@ -182,10 +189,10 @@ Run `./locus analyze --help' for a description of supported options and argument
<h5 id="filesystem-urls-loading-and-updating">Filesystem URLs: Loading and Updating</h5>
<ul>
<li>The loaded tarballs are uncompressed in memory</li>
<li>The loaded databases, when compressed, are inflated in memory</li>
<li>Until a filesystem database loader achieves readiness, load attempts are made every 5 seconds;
once readiness is achieved, this interval increases to every 30 seconds and load attempts are
dismissed as long as the tarball modification timestamp keeps unchanged
dismissed as long as the database file modification timestamp keeps unchanged
</li>
</ul>
......@@ -193,7 +200,12 @@ Run `./locus analyze --help' for a description of supported options and argument
<ul>
<li>Five logging levels are supported: `debug', `info', `warning', `error' and `none'</li>
<li>The backend is <a target="_parent" href="http://erlang.org/doc/man/error_logger.html">error_logger</a>; this usually plays nicely with `lager'</li>
<li>The chosen backend ON OTP 21.1+ is <a target="_parent" href="http://erlang.org/doc/man/logger.html">logger</a>
<b>if</b> <a target="_parent" href="https://github.com/erlang-lager/lager/">lager</a> is either missing or
it hasn't <a target="_parent" href="https://github.com/erlang-lager/lager/issues/492">removed</a> `logger''s
default handler; for all other scenarios,
<a target="_parent" href="http://erlang.org/doc/man/error_logger.html">error_logger</a> is picked instead
</li>
<li>The default log level is `error'; it can be changed in the application's `env' config</li>
<li>To tweak the log level in runtime, use `locus_logger:set_loglevel/1'</li>
</ul>
......@@ -218,7 +230,7 @@ The API reference can be found on <a target="_parent" href="https://hexdocs.pm/l
<h5 id="tested-setup">Tested setup</h5>
<ul>
<li>Erlang/OTP 17.4 or newer</li>
<li>Erlang/OTP 18.0 or newer</li>
<li>rebar3</li>
</ul>
......
......@@ -11,29 +11,26 @@
warn_unused_import,
warnings_as_errors,
{parse_transform, stacktrace_transform},
{platform_define, "^17.4", 'BAD_HTTPC'},
{platform_define, "^1[89]", 'POST_OTP_17'},
{platform_define, "^[2-9]", 'POST_OTP_17'},
{platform_define, "^19", 'POST_OTP_18'},
{platform_define, "^[2-9]", 'POST_OTP_18'},
{platform_define, "^1[78]", 'NO_GEN_STATEM'},
{platform_define, "^19.[0-2]", 'NO_GEN_STATEM'},
{platform_define, "^1[7-9]", 'SSL_OLD_CLIENT_OPTIONS'},
{platform_define, "^19", 'POST_OTP_18'},
{platform_define, "^[2-9]", 'POST_OTP_18'},
{platform_define, "^1[89]", 'SSL_OLD_CLIENT_OPTIONS'},
{platform_define, "^20", 'SSL_OLD_CLIENT_OPTIONS'},
{platform_define, "^21.[0-2]", 'SSL_OLD_CLIENT_OPTIONS'}
{platform_define, "^21.[0-2]", 'SSL_OLD_CLIENT_OPTIONS'},
{platform_define, "^1", 'NO_LOGGER'},
{platform_define, "^20", 'NO_LOGGER'},
{platform_define, "^21.0", 'NO_LOGGER'} % `:set_application_level/2` requires 21.1+
]}.
{minimum_otp_vsn, "18"}.
{deps,
[{certifi, "2.4.2"},
{ssl_verify_fun, "1.1.4"},
[{certifi, "2.5.1"},
{ssl_verify_fun, "1.1.5"},
{stacktrace_compat, "1.0.2"}
]}.
{minimum_otp_vsn, "17.4"}.
{erl_first_files,
["src/locus_gen_statem_compat.erl",
"src/locus_event_subscriber.erl"
["src/locus_event_subscriber.erl"
]}.
{dialyzer,
......@@ -88,12 +85,12 @@
nowarnings_as_errors]}
]},
{travis_test,
{ci_test,
[{erl_opts,
[debug_info,
nowarn_export_all,
nowarn_missing_spec,
nowarnings_as_errors,
{d, 'RUNNING_ON_TRAVIS'}]}
{d, 'RUNNING_ON_CI'}]}
]}
]}.
% vim: set ft=erlang:
case erlang:function_exported(rebar3, main, 1) of
true ->
% rebar3
CONFIG;
false ->
% rebar 2.x
{_, ErlOpts} = lists:keyfind(erl_opts, 1, CONFIG),
OverridenErlOpts = ErlOpts -- [warn_missing_spec],
OverridenDeps =
[{stacktrace_compat, ".*", {git, "https://github.com/g-andrade/stacktrace_compat.git",
{tag, "1.0.2"}}},
{certifi, ".*", {git, "https://github.com/certifi/erlang-certifi.git",
{tag, "2.4.2"}}},
%% XXX we don't actually need parse_trans, but certifi imports it
%% from hex.pm, which would make it impossible to build it on rebar 2.
{parse_trans, ".*", {git, "https://github.com/uwiger/parse_trans.git",
{tag, "3.3.0"}}},
{ssl_verify_fun, ".*", {git, "https://github.com/deadtrickster/ssl_verify_fun.erl.git",
{tag, "1.1.4"}}}],
Config2 = lists:keystore(deps, 1, CONFIG, {deps,OverridenDeps}),
lists:keystore(erl_opts, 1, Config2, {erl_opts,OverridenErlOpts})
end.
{"1.1.0",
[{<<"certifi">>,{pkg,<<"certifi">>,<<"2.4.2">>},0},
[{<<"certifi">>,{pkg,<<"certifi">>,<<"2.5.1">>},0},
{<<"parse_trans">>,{pkg,<<"parse_trans">>,<<"3.3.0">>},1},
{<<"ssl_verify_fun">>,{pkg,<<"ssl_verify_fun">>,<<"1.1.4">>},0},
{<<"ssl_verify_fun">>,{pkg,<<"ssl_verify_fun">>,<<"1.1.5">>},0},
{<<"stacktrace_compat">>,{pkg,<<"stacktrace_compat">>,<<"1.0.2">>},0}]}.
[
{pkg_hash,[
{<<"certifi">>, <<"75424FF0F3BAACCFD34B1214184B6EF616D89E420B258BB0A5EA7D7BC628F7F0">>},
{<<"certifi">>, <<"867CE347F7C7D78563450A18A6A28A8090331E77FA02380B4A21962A65D36EE5">>},
{<<"parse_trans">>, <<"09765507A3C7590A784615CFD421D101AEC25098D50B89D7AA1D66646BC571C1">>},
{<<"ssl_verify_fun">>, <<"F0EAFFF810D2041E93F915EF59899C923F4568F4585904D010387ED74988E77B">>},
{<<"ssl_verify_fun">>, <<"6EAF7AD16CB568BB01753DBBD7A95FF8B91C7979482B95F38443FE2C8852A79B">>},
{<<"stacktrace_compat">>, <<"8AD31C32C9A0EADB1EB298F04DC8B0C8D79BCC6233A638B02791FFCA4F331275">>}]}
].
This diff is collapsed.
......@@ -40,9 +40,15 @@
%% application Function Definitions
%% ------------------------------------------------------------------
-spec start(term(), list()) -> {ok, pid()}.
-spec start(term(), list()) -> {ok, pid()} | {error, term()}.
start(_StartType, _StartArgs) ->
locus_sup:start_link().
case locus_sup:start_link() of
{ok, Pid} ->
locus_logger:on_app_start(),
{ok, Pid};
{error, Reason} ->
{error, Reason}
end.
-spec stop(term()) -> ok.
stop(_State) ->
......
......@@ -87,7 +87,7 @@ handle_analysis_command(CmdArgs) ->
prepare_analysis(DatabaseURL, LoadTimeout) ->
DatabaseId = cli_analysis,
WaitRef = make_ref(),
BaseOpts = [{internal, {async_waiter, {self(),WaitRef}}}],
BaseOpts = [{internal, {async_waiter, WaitRef, self()}}],
ExtraOpts =
case http_uri:parse(DatabaseURL) of
{ok, _} -> [no_cache];
......
......@@ -25,36 +25,60 @@
%% released under the Apache License 2.0.
%% @private
-module(locus_maps_compat).
-include("locus_pre_otp19_compat.hrl").
-ifndef(POST_OTP_18).
-export([take/2]).
-export([update_with/3]).
-export([update_with/4]).
-spec take(term(), map()) -> {term(), map()} | error.
take(Key, Map) ->
case maps:find(Key, Map) of
{ok, Value} ->
{Value, maps:remove(Key, Map)};
error ->
error
end.
-module(locus_database_sup).
-behaviour(supervisor).
%% ------------------------------------------------------------------
%% API Function Exports
%% ------------------------------------------------------------------
-export([start_link/0]). -ignore_xref({start_link,0}).
-export([start_child/1]).
%% ------------------------------------------------------------------
%% supervisor Function Exports
%% ------------------------------------------------------------------
-export([init/1]).
%% ------------------------------------------------------------------
%% Macro Definitions
%% ------------------------------------------------------------------
-spec update_with(term(), fun ((term()) -> term()), map()) -> map().
update_with(Key, Fun, Map) ->
Value = maps:get(Key, Map),
NewValue = Fun(Value),
maps:update(Key, NewValue, Map).
-spec update_with(term(), fun ((term()) -> term()), term(), map()) -> map().
update_with(Key, Fun, Default, Map) ->
case maps:find(Key, Map) of
{ok, Value} ->
NewValue = Fun(Value),
maps:update(Key, NewValue, Map);
error ->
maps:put(Key, Default, Map)
-define(SERVER, ?MODULE).
%% ------------------------------------------------------------------
%% API Function Definitions
%% ------------------------------------------------------------------
-spec start_link() -> {ok, pid()}.
start_link() ->
supervisor:start_link({local,?SERVER}, ?MODULE, []).
-spec start_child([term()]) -> {ok,pid()} | {error,term()}.
start_child(Args) ->
try supervisor:start_child(?SERVER, Args) of
Result -> Result
catch
exit:{Reason,{gen_server,call,[?SERVER|_]}}
when Reason =:= noproc;
Reason =:= normal;
Reason =:= shutdown;
(tuple_size(Reason) =:= 2 andalso element(1, Reason) =:= shutdown) ->
{error, application_not_running}
end.
-endif.
%% ------------------------------------------------------------------
%% supervisor Function Definitions
%% ------------------------------------------------------------------
-spec init([])
-> {ok, {supervisor:sup_flags(), [supervisor:child_spec(), ...]}}.
init([]) ->
SupFlags =
#{strategy => simple_one_for_one,
intensity => 10,
period => 5
},
ChildSpec = locus_database:dynamic_child_spec(database),
{ok, {SupFlags, [ChildSpec]}}.
......@@ -46,7 +46,5 @@
%% Type Definitions
%% ------------------------------------------------------------------
-type event() ::
locus_http_loader:event() |
locus_filesystem_loader:event().
-type event() :: locus_database:event().
-export_type([event/0]).
%% Copyright (c) 2017-2019 Guilherme Andrade
%%
%% Permission is hereby granted, free of charge, to any person obtaining a
%% copy of this software and associated documentation files (the "Software"),
%% to deal in the Software without restriction, including without limitation
%% the rights to use, copy, modify, merge, publish, distribute, sublicense,
%% and/or sell copies of the Software, and to permit persons to whom the
%% Software is furnished to do so, subject to the following conditions:
%%
%% The above copyright notice and this permission notice shall be included in
%% all copies or substantial portions of the Software.
%%
%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
%% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
%% DEALINGS IN THE SOFTWARE.
%%
%% locus is an independent project and has not been authorized, sponsored,
%% or otherwise approved by MaxMind.
%%
%% locus includes code extracted from OTP source code, by Ericsson AB,
%% released under the Apache License 2.0.
-module(locus_filesystem_load).
-behaviour(gen_server).
-include_lib("kernel/include/file.hrl").
%% ------------------------------------------------------------------
%% API Function Exports
%% ------------------------------------------------------------------
-export(
[start_link/2
]).
-ignore_xref(
[start_link/2
]).
%% ------------------------------------------------------------------
%% gen_server Function Exports
%% ------------------------------------------------------------------
-export(
[init/1,
handle_call/3,
handle_cast/2,
handle_info/2,
terminate/2,
code_change/3
]).
%% ------------------------------------------------------------------
%% Record and Type Definitions
%% ------------------------------------------------------------------
-type msg() ::
{event, event()} |
{finished, {success,success()}} |
{finished, dismissed} |
{finished, {error,not_found}} |
{finished, {error,term()}}.
-export_type([msg/0]).
-type event() ::
event_load_attempt_started() |
event_load_attempt_dismissed().
-export_type([event/0]).
-type event_load_attempt_started() :: {load_attempt_started, source()}.
-export_type([event_load_attempt_started/0]).
-type event_load_attempt_dismissed() :: {load_attempt_dismissed, source()}.
-export_type([event_load_attempt_dismissed/0]).
-type source() :: {cache|filesystem, path()}.
-export_type([source/0]).
-ifdef(POST_OTP_18).
-type success() ::
#{ modified_on := calendar:datetime(),
content := binary()
}.
-else.
-type success() ::
#{ modified_on => calendar:datetime(),
content => binary()
}.
-endif.
-export_type([success/0]).
-type path() :: nonempty_string().
-export_type([path/0]).
-record(state, {
owner_pid :: pid(),
source :: source(),
previously_modified_on :: calendar:datetime() | undefined
}).
-type state() :: #state{}.