Packages
ecto_watch
0.14.0
1.1.0
1.0.0
0.15.1
0.15.0
0.14.0
0.13.3
0.13.2
0.13.1
0.13.0
0.12.6
0.12.5
0.12.4
0.12.3
0.12.2
0.12.1
0.12.0
0.11.2
0.11.1
0.11.0
0.10.0
0.9.11
0.9.10
0.9.9
0.9.8
0.9.7
0.9.6
0.9.5
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.1
0.8.0
0.7.0
0.6.0
0.6.0-rc2
0.6.0-rc1
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.0
0.3.0
0.2.0
0.1.0
EctoWatch allows you to easily get Phoenix.PubSub notifications directly from postgresql.
Current section
Files
Jump to
Current section
Files
lib/ecto_watch/options/watcher_options.ex
defmodule EctoWatch.Options.WatcherOptions do
@moduledoc false
alias EctoWatch.Helpers
defstruct [
:schema_definition,
:update_type,
:label,
:trigger_columns,
:extra_columns,
:debug?
]
def validate_list(list) do
Helpers.validate_list(list, &validate/1)
end
defmodule SchemaDefinition do
@moduledoc false
defstruct [
:schema_prefix,
:table_name,
:primary_key,
:column_map,
:columns,
:association_columns,
:label
]
def new(schema_mod) when is_atom(schema_mod) do
schema_prefix =
case schema_mod.__schema__(:prefix) do
nil -> "public"
prefix -> prefix
end
table_name = "#{schema_mod.__schema__(:source)}"
[primary_key] = schema_mod.__schema__(:primary_key)
fields = schema_mod.__schema__(:fields)
column_map = Map.new(fields, &{&1, schema_mod.__schema__(:field_source, &1)})
association_columns =
schema_mod.__schema__(:associations)
|> Enum.map(&schema_mod.__schema__(:association, &1))
|> Enum.map(& &1.owner_key)
%__MODULE__{
schema_prefix: schema_prefix,
table_name: table_name,
primary_key: primary_key,
column_map: column_map,
columns: fields,
association_columns: association_columns,
label: schema_mod
}
end
def new(%__MODULE__{}) do
raise "There is a bug! SchemaDefinition struct was passed to new/1"
end
def new(opts) when is_map(opts) do
schema_prefix = opts[:schema_prefix] || "public"
%__MODULE__{
schema_prefix: to_string(schema_prefix),
table_name: to_string(opts.table_name),
primary_key: opts.primary_key,
column_map: opts[:column_map] || %{},
columns: opts.columns,
association_columns: opts[:association_columns] || [],
label: "#{schema_prefix}|#{opts.table_name}"
}
end
end
def validate({schema_definition, update_type}) do
validate({schema_definition, update_type, []})
end
def validate({schema_definition, update_type, opts}) do
with {:ok, schema_definition} <- validate_schema_definition(schema_definition, opts[:label]),
{:ok, update_type} <- validate_update_type(update_type),
{:ok, opts} <- validate_opts(opts, schema_definition, update_type) do
{:ok, {schema_definition, update_type, opts}}
end
end
def validate(other) do
{:error,
"should be either `{schema_definition, update_type}` or `{schema_definition, update_type, opts}`. Got: #{inspect(other)}"}
end
def validate_schema_definition(schema_mod, _label_opt) when is_atom(schema_mod) do
if EctoWatch.Helpers.ecto_schema_mod?(schema_mod) do
{:ok, schema_mod}
else
{:error, "Expected atom to be an Ecto schema module. Got: #{inspect(schema_mod)}"}
end
end
def validate_schema_definition(opts, label_opt) when is_map(opts) do
schema = [
schema_prefix: [
type: {:or, ~w[string atom]a},
required: false,
default: :public
],
table_name: [
type: {:or, ~w[string atom]a},
required: true
],
primary_key: [
type: :atom,
required: false,
default: :id
],
columns: [
type: {:list, :atom},
required: false,
default: []
],
association_columns: [
type: {:list, :atom},
required: false,
default: []
],
column_map: [
type: {:map, :atom, :atom},
required: false,
default: %{}
]
]
if label_opt do
with {:error, error} <- NimbleOptions.validate(opts, NimbleOptions.new!(schema)) do
{:error, Exception.message(error)}
end
else
{:error, "Label must be used when passing in a map for schema_definition"}
end
end
def validate_schema_definition(_, _), do: {:error, "should be an ecto schema module name"}
def validate_update_type(update_type) do
if update_type in ~w[inserted updated deleted]a do
{:ok, update_type}
else
{:error, "update_type was not one of :inserted, :updated, or :deleted"}
end
end
def validate_opts(opts, schema_definition, update_type) do
schema_definition = SchemaDefinition.new(schema_definition)
schema = [
label: [
type: :atom,
required: false
],
trigger_columns: [
type:
{:custom, __MODULE__, :validate_trigger_columns,
[opts[:label], schema_definition, update_type]},
required: false,
default: []
],
extra_columns: [
type: {:custom, __MODULE__, :validate_columns, [schema_definition]},
required: false,
default: []
],
debug?: [
type: :boolean,
required: false,
default: false
]
]
with {:error, error} <- NimbleOptions.validate(opts, schema) do
{:error, Exception.message(error)}
end
end
def validate_trigger_columns([], _, _, _) do
{:ok, []}
end
def validate_trigger_columns(columns, label, schema_definition, update_type) do
cond do
update_type != :updated ->
{:error,
"Cannot listen to trigger_columns for `#{update_type}` events (only for `#{:updated}` events."}
label == nil ->
{:error, "Label must be used when trigger_columns are specified."}
true ->
validate_columns(columns, schema_definition)
end
end
def validate_columns(columns, schema_definition) do
Helpers.validate_list(columns, fn
column when is_atom(column) ->
if column in schema_definition.columns do
{:ok, column}
else
{:error,
"Invalid column: #{inspect(column)} (expected to be in #{inspect(schema_definition.columns)})"}
end
column ->
{:error, "Invalid column: #{inspect(column)} (expected to be an atom)"}
end)
end
def new({schema_definition, update_type}, debug?) do
new({schema_definition, update_type, []}, debug?)
end
def new({schema_definition, update_type, opts}, debug?) do
schema_definition = SchemaDefinition.new(schema_definition)
%__MODULE__{
schema_definition: schema_definition,
update_type: update_type,
label: opts[:label],
trigger_columns: opts[:trigger_columns] || [],
extra_columns: opts[:extra_columns] || [],
debug?: debug? || opts[:debug?]
}
end
end