Giter Club home page Giter Club logo

geolix's Introduction

Geolix

IP location lookup provider.

Note: If you are reading this on GitHub then the information in this file may be out of sync with the Hex package. If you are using this library through Hex please refer to the appropriate documentation on HexDocs (link available on Hex).

Package Setup

To use Geolix with your projects, edit your mix.exs file and add the project as a dependency:

defp deps do
  [
    # ...
    {:geolix, "~> 0.18"},
    # ...
  ]
end

If you want to use a manual supervision approach (without starting the application) please look at the inline documentation of Geolix.Supervisor.

Application Configuration

To get started you need to define one or more :databases to use for lookups. Each database definition is a map with at least two fields:

  • :id - an identifier for this database, usable to limit lookups to a single database if you have defined more than one
  • :adapter - the adapter module used to handle lookup requests. See the part "Adapters" in this document for additional information

Depending on the adapter you may need to provide additional values.

Configuration (static)

One option for configuration is using a static configuration, i.e. for two databases handled by the adapter MyAdapter:

config :geolix,
  databases: [
    %{
      id: :city,
      adapter: MyAdapter,
      source: "/absolute/path/to/city.db"
    },
    %{
      id: :country,
      adapter: MyAdapter,
      source: "/absolute/path/to/country.db"
    }
  ]

Configuration (dynamic)

If there are any reasons you cannot use a pre-defined configuration you can also configure an initializer module to be called before starting the top-level suprevisor or alternatively for each individual database.

This may be the most suitable configuration if you have the database located in the :priv_dir of your application.

# {mod, fun}
config :geolix,
  init: {MyInitModule, :my_init_mf_toplevel}

config :geolix,
  databases: [
    %{
      id: :dynamic_country,
      adapter: MyAdapter,
      init: {MyInitModule, :my_init_mf_database}
    }
  ]

# {mod, fun, args}
config :geolix,
  init: {MyInitModule, :my_init_mfa_toplevel, [:foo, :bar]}

config :geolix,
  databases: [
    %{
      id: :dynamic_country,
      adapter: MyAdapter,
      init: {MyInitModule, :my_init_mfa_database, [:foo, :bar]}
    }
  ]

# initializer module
defmodule MyInitModule do
  @spec my_init_mf_toplevel() :: :ok
  def my_init_mf_toplevel(), do: my_init_mfa_toplevel(:foo, :bar)

  @spec my_init_mfa_toplevel(atom, atom) :: :ok
  def my_init_mfa_toplevel(:foo, :bar) do
    priv_dir = Application.app_dir(:my_app, "priv")

    databases = [
      %{
        id: :dynamic_city,
        adapter: MyAdapter,
        source: Path.join([priv_dir, "city.db"])
      }
      | Application.get_env(:geolix, :databases, [])
    ]

    Application.put_env(:geolix, :databases, databases)
  end

  @spec my_init_mf_database(map) :: map
  def my_init_mf_database(database) do
    my_init_mfa_database(database, :foo, :bar)
  end

  @spec my_init_mfa_database(map, atom, atom) :: map
  def my_init_mfa_database(%{id: :dynamic_country} = database, :foo, :bar) do
    priv_dir = Application.app_dir(:my_app, "priv")

    %{database | source: Path.join([priv_dir, "country.db"])}
  end
end

Above example illustrates both types of dynamic initialization.

The top-level initializer is called as defined ({mod, fun} or {mod, fun, args}) and expected to always return :ok. At the database level the current database configuration is passed as the first parameter with optional {m, f, a} parameters following. It is expected that this function return the new, complete configuration.

If you choose to use the dynamic database initialization the only requirement for your config file is a plain %{init: {MyInitModule, :my_init_fun}} entry. Every additional field in the example is only used for illustration and only required for the complete return value.

Configuration (runtime)

If you do not want to use a pre-defined or dynamically initialized configuration you can also define adapters at runtime. This may be useful in a testing environment.

iex(1)> Geolix.load_database(%{
...(1)>   id: :runtime_city,
...(1)>   adapter: MyAdapter,
...(1)>   source: "/absolute/path/to/city.db"
...(1)> })
:ok

Please be aware that these databases will not be reloaded if, for any reason, the supervisor/application is restarted.

Running load_database/1 on an already configured database (matched by :id) will reload/replace it without persisting the configuration. On success a result of :ok will be returned otherwise a tuple in the style of {:error, message}. The individual errors are defined by the adapter.

Adapters

All the work done by geolix is handled using adapters. These adapters can use a database, a webservice or any other means available to handle your lookup requests.

Known adapters:

For detailed information how to configure the adapter of your choice please read the adapter's configuration.

Fake Adapter

Pre-packaged is a fake/static adapter (Geolix.Adapter.Fake) working on a plain Agent holding your IP lookup responses. An example of how you might use this adapter:

config :geolix,
  databases: [
    %{
      id: :country,
      adapter: Geolix.Adapter.Fake,
      data:
        %{}
        |> Map.put({1, 1, 1, 1}, %{country: %{iso_code: "US"}})
        |> Map.put({2, 2, 2, 2}, %{country: %{iso_code: "GB"}})
    }
  ]

Custom Adapters

If you need a different database or have other special needs for lookups you can write your own adapter. The only requirement is the usage of the Geolix.Adapter behaviour.

As a starting point you can take a close look at the aforementioned Geolix.Adapter.Fake implementation.

Database Loading

Loading Errors

If the configuration is erroneous a message will be sent to Logger with the level :error. Any other error during the load process is expected to be defined and logged by the adapter itself.

State Retrieval

All databases are loaded, unless you called Geolix.load_database/1, asynchronously. This includes configured databases loaded upon application start.

The database loader allows you to access the current state of loading:

iex(1)> Geolix.Database.Loader.loaded_databases()
[:city]

iex(2)> Geolix.Database.Loader.registered_databases()
[:city, :country]

Above example demonstrates a state where the database :country is known but not completely loaded yet. Please be aware that both lists are unsorted.

Reloading

To trigger a forceful reload of all databases configured in the application environment you can use Geolix.reload_databases/0 to do so. This uses an internal GenServer.cast/2 so a slight delay will occur.

Unloading

Calling Geolix.unload_database/1 with a database id will unload this database. As this is done in a lazy fashion it will still be kept in memory while not being reloaded or used for lookups. If the database is configured via application environment it will still be reloaded as usual in case of a supervisor or application restart.

Usage

Lookups are done using Geolix.lookup/1,2:

iex(1)> Geolix.lookup("127.0.0.1")
%{
  city: %{...},
  country: %{...}
}

iex(2)> Geolix.lookup({127, 0, 0, 1}, where: :city)
%{...}

Using Geolix.lookup/2 with only one parameter (the IP) will lookup the information in all registered databases as a map with the database name as the key. The individual database result types are defined by the adapter used.

Lookup options:

  • :timeout - GenServer call timeout for the lookup. Defaults to 5_000.
  • :where - Lookup information in a single registered database

All options are passed unmodified to the adapter's lookup/2 implementation.

License

Apache License, Version 2.0

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.