diff --git a/lib/sqlite_db_connection/app.ex b/lib/sqlite_db_connection/app.ex index 8de233f0..b6ea41a1 100644 --- a/lib/sqlite_db_connection/app.ex +++ b/lib/sqlite_db_connection/app.ex @@ -1,9 +1,14 @@ defmodule Sqlite.DbConnection.App do @moduledoc false use Application + + @registry Sqlite.DbConnection.Registry def start(_, _) do + children = [ + {Registry, keys: :duplicate, name: @registry, partitions: System.schedulers_online()} + ] opts = [strategy: :one_for_one, name: Sqlite.DbConnection.Supervisor] - Supervisor.start_link([], opts) + Supervisor.start_link(children, opts) end end diff --git a/lib/sqlite_db_connection/protocol.ex b/lib/sqlite_db_connection/protocol.ex index 737cc44c..248ed2ae 100644 --- a/lib/sqlite_db_connection/protocol.ex +++ b/lib/sqlite_db_connection/protocol.ex @@ -1,10 +1,10 @@ defmodule Sqlite.DbConnection.Protocol do @moduledoc false - alias Sqlite.DbConnection.Query + alias Sqlite.DbConnection.{Query, RegistryWorker} use DBConnection - defstruct [db: nil, path: nil, checked_out?: false] + defstruct [db: nil, path: nil, checked_out?: false, registry_worker: nil] @type state :: %__MODULE__{db: pid, path: String.t, checked_out?: boolean} @@ -16,8 +16,15 @@ defmodule Sqlite.DbConnection.Protocol do {:ok, db} = Sqlitex.Server.start_link(db_path, db_timeout: db_timeout) :ok = Sqlitex.Server.exec(db, "PRAGMA foreign_keys = ON") {:ok, [[foreign_keys: 1]]} = Sqlitex.Server.query(db, "PRAGMA foreign_keys") - - {:ok, %__MODULE__{db: db, path: db_path, checked_out?: false}} + state = %__MODULE__{db: db, path: db_path, checked_out?: false} + + if Keyword.get(opts, :notifications, false) do + {:ok, registry_worker} = RegistryWorker.start_link(opts) + :ok = Sqlitex.Server.set_update_hook(db, registry_worker) + {:ok, %{state | registry_worker: registry_worker}} + else + {:ok, state} + end end @spec disconnect(Exception.t, state) :: :ok diff --git a/lib/sqlite_db_connection/registry_worker.ex b/lib/sqlite_db_connection/registry_worker.ex new file mode 100644 index 00000000..919a93e7 --- /dev/null +++ b/lib/sqlite_db_connection/registry_worker.ex @@ -0,0 +1,21 @@ +defmodule Sqlite.DbConnection.RegistryWorker do + @moduledoc false + + use GenServer + @registry Sqlite.DbConnection.Registry + + def start_link(args) do + GenServer.start_link(__MODULE__, args) + end + + def init(opts) do + {:ok, opts} + end + + def handle_info({action, table, rowid}, state) do + Registry.dispatch(@registry, "notifications", fn entries -> + for {pid, _} <- entries, do: send(pid, {action, to_string(table), rowid}) + end) + {:noreply, state} + end +end \ No newline at end of file diff --git a/lib/sqlite_ecto/notifications.ex b/lib/sqlite_ecto/notifications.ex new file mode 100644 index 00000000..96b4aa6c --- /dev/null +++ b/lib/sqlite_ecto/notifications.ex @@ -0,0 +1,45 @@ +defmodule Sqlite.Ecto2.Notifications do + use GenServer + + @registry Sqlite.DbConnection.Registry + + def start_link(args) do + {server_opts, args} = Keyword.split(args, [:name]) + GenServer.start_link(__MODULE__, args, server_opts) + end + + def init(args) do + repo = Keyword.fetch!(args, :repo) + schemas = Keyword.fetch!(args, :schemas) + {:ok, _} = Registry.register(@registry, "notifications", []) + {:ok, %{repo: repo, schemas: schemas}} + end + + def handle_info({_, "schema_migrations", _}, state) do + {:noreply, state} + end + + def handle_info({_, "t_" <> _, _}, state) do + {:noreply, state} + end + + def handle_info({:delete, _table, _rowid}, state) do + # What to do with delete? + # can't lookup record since it's already deleted. + {:noreply, state} + end + + def handle_info({action, table, rowid}, state) do + repo = state.repo + %{columns: c, rows: [row]} = Ecto.Adapters.SQL.query!(repo, "SELECT * FROM '#{table}' where rowid = #{rowid};", []) + state.schemas + |> Enum.filter(fn({s_table, _schema}) -> s_table == table end) + |> Enum.map(fn({^table, schema}) -> repo.load(schema, {c, row}) end) + |> dispatch(state) + {:noreply, state} + end + + defp dispatch(message, _state) do + IO.inspect(message, label: "message") + end +end \ No newline at end of file diff --git a/mix.exs b/mix.exs index 34a06eed..905972f0 100644 --- a/mix.exs +++ b/mix.exs @@ -5,7 +5,7 @@ defmodule Sqlite.Ecto2.Mixfile do [app: :sqlite_ecto2, version: "2.3.1", name: "Sqlite.Ecto2", - elixir: "~> 1.4", + elixir: "~> 1.5", deps: deps(), elixirc_paths: elixirc_paths(Mix.env), @@ -46,7 +46,7 @@ defmodule Sqlite.Ecto2.Mixfile do {:poison, "~> 2.2 or ~> 3.0", optional: true}, {:postgrex, "~> 0.13", optional: true}, {:sbroker, "~> 1.0"}, - {:sqlitex, "~> 1.4"}] + {:sqlitex, "1.5.0"}] end defp description, do: "SQLite3 adapter for Ecto2"