Packages

Combine the power of Ecto with the Bolt protocol + an elegant DSL for Neo4J databases.

Current section

Files

Jump to
ex4j lib application.ex
Raw

lib/application.ex

defmodule Ex4j.Application do
@moduledoc false
require Logger
use Application
def start(_type, _args) do
patch_boltx()
config = Application.get_env(:ex4j, Boltx)
children = build_children(config)
opts = [strategy: :one_for_one, name: Ex4j.Supervisor]
Supervisor.start_link(children, opts)
end
# Boltx 0.0.6 patches applied at runtime so they take effect regardless
# of BEAM code-path ordering (critical when ex4j is used as a dependency).
# Remove once Boltx ships fixes for these issues.
defp patch_boltx do
patch_boltx_error()
ensure_patched_connection()
end
# Boltx.Error.message/1 calls module.format_error(code) but format_error/1
# is not defined in Boltx.Connection or Boltx.Client.
defp patch_boltx_error do
:code.purge(Boltx.Error)
:code.delete(Boltx.Error)
previous = Code.compiler_options(ignore_module_conflict: true)
Module.create(
Boltx.Error,
quote do
@error_map %{
"Neo.ClientError.Security.Unauthorized" => :unauthorized,
"Neo.ClientError.Request.Invalid" => :request_invalid,
"Neo.ClientError.Statement.SemanticError" => :semantic_error,
"Neo.ClientError.Statement.SyntaxError" => :syntax_error
}
defexception [:module, :code, :bolt, :packstream]
def wrap(module, code) when is_atom(code),
do: %__MODULE__{module: module, code: code}
def wrap(module, code) when is_binary(code),
do: wrap(module, to_atom(code))
def wrap(module, bolt_error) when is_map(bolt_error),
do: %__MODULE__{
module: module,
code: bolt_error.code |> to_atom(),
bolt: bolt_error
}
def wrap(module, code, packstream),
do: %__MODULE__{module: module, code: code, packstream: packstream}
def message(%__MODULE__{code: code, module: module, bolt: bolt}) do
cond do
is_map(bolt) and Map.has_key?(bolt, :message) ->
bolt.message
function_exported?(module, :format_error, 1) ->
module.format_error(code)
true ->
"#{inspect(module)} error: #{inspect(code)}"
end
end
def to_atom(error_message) do
Map.get(@error_map, error_message, :unknown)
end
end,
Macro.Env.location(__ENV__)
)
Code.compiler_options(previous)
end
# Our Boltx.Connection override (lib/ex4j/boltx/connection.ex) compiles into
# ex4j's ebin but BEAM may load the original from boltx's ebin first.
# Force-load our version so the patched ping/1 (with db support) is active.
defp ensure_patched_connection do
unless function_exported?(Boltx.Connection, :format_error, 1) do
ebin = Application.app_dir(:ex4j, "ebin")
beam = Path.join(ebin, "Elixir.Boltx.Connection") |> String.to_charlist()
:code.purge(Boltx.Connection)
:code.delete(Boltx.Connection)
:code.load_abs(beam)
end
end
defp build_children(nil) do
Logger.warning(
"Ex4j: No Boltx connection configuration found. Set config :ex4j, Boltx, [...]"
)
[]
end
defp build_children(config) do
{database, config} = Keyword.pop(config, :database)
if database do
Application.put_env(:ex4j, :database, database)
end
config =
config
|> Keyword.put(:name, Ex4j.Bolt.pool_name())
|> rename_key(:url, :uri)
|> rename_key(:basic_auth, :auth)
[{Boltx, config}]
end
defp rename_key(keyword, old_key, new_key) do
case Keyword.pop(keyword, old_key) do
{nil, keyword} -> keyword
{value, keyword} -> Keyword.put_new(keyword, new_key, value)
end
end
end