From b30bdc29e97998369d7869a76b251bd1305377b9 Mon Sep 17 00:00:00 2001 From: Brian Underwood Date: Fri, 2 Aug 2024 15:47:53 +0200 Subject: [PATCH] change: Don't require specifying update type for watchers with labels --- CHANGELOG.md | 10 ++ README.md | 12 +- lib/ecto_watch.ex | 153 +++++++++++++++++++---- lib/ecto_watch/watcher_server.ex | 60 ++++++---- mix.exs | 2 +- test/ecto_watch_test.exs | 200 +++++++++++++++++-------------- 6 files changed, 293 insertions(+), 144 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f892047..da219a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.8.0] - 2023-08-01 + +### Changed + +- BREAKING: Don't require specifying update type for watchers with labels (#19) + +### Added + +- Allow watchers without an ecto schema (thanks @frerich / #18) + ## [0.7.0] - 2023-07-31 ### Changed diff --git a/README.md b/README.md index 9bf1b92..f49729c 100644 --- a/README.md +++ b/README.md @@ -110,12 +110,12 @@ You can also setup the database to trigger only on specific column changes on `: ]} # subscribing - EctoWatch.subscribe(:user_contact_info_updated, :updated) + EctoWatch.subscribe(:user_contact_info_updated) # or... - EctoWatch.subscribe(:user_contact_info_updated, :updated, package.id) + EctoWatch.subscribe(:user_contact_info_updated, package.id) # handling messages - def handle_info({:updated, :user_contact_info_updated, %{id: id}}, socket) do + def handle_info({:user_contact_info_updated, %{id: id}}, socket) do ``` A label is required for two reasons: @@ -137,12 +137,12 @@ You can also use labels in general without tracking specific columns: ]} # subscribing - EctoWatch.subscribe(:user_update, :updated) + EctoWatch.subscribe(:user_update) # or... - EctoWatch.subscribe(:user_update, :updated, package.id) + EctoWatch.subscribe(:user_update, package.id) # handling messages - def handle_info({:updated, :user_update, %{id: id}}, socket) do + def handle_info({:user_update, %{id: id}}, socket) do ``` ## Getting additional values diff --git a/lib/ecto_watch.ex b/lib/ecto_watch.ex index 0059056..0fe26fc 100644 --- a/lib/ecto_watch.ex +++ b/lib/ecto_watch.ex @@ -2,17 +2,118 @@ defmodule EctoWatch do @moduledoc false alias EctoWatch.WatcherServer + alias EctoWatch.Helpers use Supervisor - def subscribe(schema_mod_or_label, update_type, id \\ nil) do - if !Process.whereis(__MODULE__) do - raise "EctoWatch is not running. Please start it by adding it to your supervision tree or using EctoWatch.start_link/1" + def subscribe(schema_mod_or_label, update_type, id) when is_atom(schema_mod_or_label) do + if Helpers.ecto_schema_mod?(schema_mod_or_label) do + raise ArgumentError, + """ + This way of subscribing was removed in version 0.8.0. Instead call: + + subscribe({#{inspect(schema_mod_or_label)}, #{inspect(update_type)}}, #{id}) + + IMPORTANT NOTE: The messages that you receive have also changed! + + Before: + + # labels: + def handle_info({:updated, User, %{id: id}}, socket) do + # schemas: + def handle_info({:user_contact_info_updated, :updated, %{id: id}}, socket) do + + Now: + + # labels: + def handle_info({{User, :updated}, %{id: id}}, socket) do + # schemas: + def handle_info({:user_contact_info_updated, %{id: id}}, socket) do + + See the updated documentation for subscribing." + """ + else + raise ArgumentError, + """ + This way of subscribing was removed in version 0.8.0. Instead call: + + subscribe(#{inspect(schema_mod_or_label)}, #{id}) + + Before: + + # labels: + def handle_info({:updated, User, %{id: id}}, socket) do + # schemas: + def handle_info({:user_contact_info_updated, :updated, %{id: id}}, socket) do + + Now: + + # labels: + def handle_info({{User, :updated}, %{id: id}}, socket) do + # schemas: + def handle_info({:user_contact_info_updated, %{id: id}}, socket) do + + See the updated documentation for subscribing." + """ + end + end + + def subscribe(identifier, id \\ nil) do + if is_atom(identifier) && id in ~w[inserted updated deleted]a do + if Helpers.ecto_schema_mod?(identifier) do + raise ArgumentError, + """ + This way of subscribing was removed in version 0.8.0. Instead call: + + subscribe({#{inspect(identifier)}, #{inspect(id)}}) + + Before: + + # labels: + def handle_info({:updated, User, %{id: id}}, socket) do + # schemas: + def handle_info({:user_contact_info_updated, :updated, %{id: id}}, socket) do + + Now: + + # labels: + def handle_info({{User, :updated}, %{id: id}}, socket) do + # schemas: + def handle_info({:user_contact_info_updated, %{id: id}}, socket) do + + See the updated documentation for subscribing." + """ + else + raise ArgumentError, + """ + This way of subscribing was removed in version 0.8.0. Instead call: + + subscribe(#{inspect(identifier)}) + + Before: + + # labels: + def handle_info({:updated, User, %{id: id}}, socket) do + # schemas: + def handle_info({:user_contact_info_updated, :updated, %{id: id}}, socket) do + + Now: + + # labels: + def handle_info({{User, :updated}, %{id: id}}, socket) do + # schemas: + def handle_info({:user_contact_info_updated, %{id: id}}, socket) do + + See the updated documentation for subscribing." + """ + end end - with :ok <- check_update_args(update_type, id), + validate_ecto_watch_running!() + + with :ok <- validate_identifier(identifier), {:ok, {pub_sub_mod, channel_name}} <- - WatcherServer.pub_sub_subscription_details(schema_mod_or_label, update_type, id) do + WatcherServer.pub_sub_subscription_details(identifier, id) do Phoenix.PubSub.subscribe(pub_sub_mod, channel_name) else {:error, error} -> @@ -20,20 +121,34 @@ defmodule EctoWatch do end end - def check_update_args(update_type, id) do - case {update_type, id} do - {:inserted, _} -> - :ok + defp validate_identifier({schema_mod, update_type}) + when is_atom(schema_mod) and is_atom(update_type) do + cond do + !EctoWatch.Helpers.ecto_schema_mod?(schema_mod) -> + raise ArgumentError, + "Expected atom to be an Ecto schema module. Got: #{inspect(schema_mod)}" - {:updated, _} -> - :ok + update_type not in ~w[inserted updated deleted]a -> + raise ArgumentError, + "Unexpected update_type: #{inspect(update_type)}. Expected :inserted, :updated, or :deleted" - {:deleted, _} -> + true -> :ok + end + end - {other, _} -> - raise ArgumentError, - "Unexpected update_type: #{inspect(other)}. Expected :inserted, :updated, or :deleted" + defp validate_identifier(label) when is_atom(label) do + :ok + end + + defp validate_identifier(other) do + raise ArgumentError, + "Invalid subscription (expected either `{schema_module, :inserted | :updated | :deleted}` or a label): #{inspect(other)}" + end + + defp validate_ecto_watch_running! do + if !Process.whereis(__MODULE__) do + raise "EctoWatch is not running. Please start it by adding it to your supervision tree or using EctoWatch.start_link/1" end end @@ -63,14 +178,6 @@ defmodule EctoWatch do {EctoWatch.WatcherSupervisor, options} ] - # children = children ++ - # Enum.map(options.watchers, fn watcher_options -> - # %{ - # id: WatcherServer.name(watcher_options), - # start: {WatcherServer, :start_link, [{options.repo_mod, options.pub_sub_mod, watcher_options}]} - # } - # end) - Supervisor.init(children, strategy: :rest_for_one) end end diff --git a/lib/ecto_watch/watcher_server.ex b/lib/ecto_watch/watcher_server.ex index 181bcf1..5193254 100644 --- a/lib/ecto_watch/watcher_server.ex +++ b/lib/ecto_watch/watcher_server.ex @@ -8,16 +8,18 @@ defmodule EctoWatch.WatcherServer do use GenServer - def pub_sub_subscription_details(schema_mod_or_label, update_type, identifier_value) do - name = unique_label(schema_mod_or_label, update_type) - - if Process.whereis(name) do - GenServer.call( - name, - {:pub_sub_subscription_details, schema_mod_or_label, update_type, identifier_value} - ) - else - {:error, "No watcher found for #{inspect(schema_mod_or_label)} / #{inspect(update_type)}"} + def pub_sub_subscription_details(identifier, identifier_value) do + with {:ok, pid} <- find(identifier) do + GenServer.call(pid, {:pub_sub_subscription_details, identifier, identifier_value}) + end + end + + defp find(identifier) do + name = unique_label(identifier) + + case Process.whereis(name) do + nil -> {:error, "No watcher found for #{inspect(identifier)}"} + pid -> {:ok, pid} end end @@ -108,7 +110,7 @@ defmodule EctoWatch.WatcherServer do end def handle_call( - {:pub_sub_subscription_details, schema_mod_or_label, update_type, identifier_value}, + {:pub_sub_subscription_details, identifier, identifier_value}, _from, state ) do @@ -125,14 +127,12 @@ defmodule EctoWatch.WatcherServer do end result = - with :ok <- validate_subscription(state, update_type, column) do - unique_label = unique_label(schema_mod_or_label, update_type) - + with :ok <- validate_subscription(state, identifier, column) do channel_name = if column && value do - "#{unique_label}|#{column}|#{value}" + "#{state.unique_label}|#{column}|#{value}" else - "#{unique_label}" + "#{state.unique_label}" end {:ok, {state.pub_sub_mod, channel_name}} @@ -141,9 +141,9 @@ defmodule EctoWatch.WatcherServer do {:reply, result, state} end - defp validate_subscription(state, update_type, column) do + defp validate_subscription(state, identifier, column) do cond do - update_type == :inserted && column == state.options.schema_definition.primary_key -> + match?({_, :inserted}, identifier) && column == state.options.schema_definition.primary_key -> {:error, "Cannot subscribe to primary_key for inserted records"} column && not MapSet.member?(state.identifier_columns, column) -> @@ -170,7 +170,13 @@ defmodule EctoWatch.WatcherServer do type = String.to_existing_atom(type) message = - {type, state.options.label || state.options.schema_definition.label, returned_values} + case state.options.label do + nil -> + {{state.options.label || state.options.schema_definition.label, type}, returned_values} + + label -> + {label, returned_values} + end for topic <- topics( @@ -202,11 +208,19 @@ defmodule EctoWatch.WatcherServer do # To make things simple: generate a single string which is unique for each watcher # that can be used as the watcher process name, trigger name, trigger function name, # and Phoenix.PubSub channel name. - def unique_label(%WatcherOptions{} = options) do - :"ew_#{options.update_type}_for_#{Helpers.label(options.label || options.schema_definition.label)}" + defp unique_label(%WatcherOptions{} = options) do + if options.label do + unique_label(options.label) + else + unique_label({options.schema_definition.label, options.update_type}) + end + end + + defp unique_label({schema_mod, update_type}) do + :"ew_#{update_type}_for_#{Helpers.label(schema_mod)}" end - defp unique_label(schema_mod_or_label, update_type) do - :"ew_#{update_type}_for_#{Helpers.label(schema_mod_or_label)}" + defp unique_label(label) do + :"ew_for_#{Helpers.label(label)}" end end diff --git a/mix.exs b/mix.exs index 6722941..e701603 100644 --- a/mix.exs +++ b/mix.exs @@ -4,7 +4,7 @@ defmodule EctoWatch.MixProject do def project do [ app: :ecto_watch, - version: "0.7.0", + version: "0.8.0", elixir: "~> 1.10", description: "EctoWatch allows you to easily get Phoenix.PubSub notifications directly from postgresql.", diff --git a/test/ecto_watch_test.exs b/test/ecto_watch_test.exs index f8b86b8..6115d36 100644 --- a/test/ecto_watch_test.exs +++ b/test/ecto_watch_test.exs @@ -436,19 +436,45 @@ defmodule EctoWatchTest do end end + test "warnings about old behavior for subscribing" do + assert_raise ArgumentError, + ~r/This way of subscribing was removed in version 0.8.0. Instead call:\n+subscribe\(\{EctoWatchTest.Thing, :updated\}\)/, + fn -> + EctoWatch.subscribe(Thing, :updated) + end + + assert_raise ArgumentError, + ~r/This way of subscribing was removed in version 0.8.0. Instead call:\n+subscribe\(\{EctoWatchTest.Thing, :updated\}, 123\)/, + fn -> + EctoWatch.subscribe(Thing, :updated, 123) + end + + assert_raise ArgumentError, + ~r/This way of subscribing was removed in version 0.8.0. Instead call:\n+subscribe\(:a_label\)/, + fn -> + EctoWatch.subscribe(:a_label, :updated) + end + + assert_raise ArgumentError, + ~r/This way of subscribing was removed in version 0.8.0. Instead call:\n+subscribe\(:a_label, 123\)/, + fn -> + EctoWatch.subscribe(:a_label, :updated, 123) + end + end + test "subscribe returns error if EctoWatch hasn't been started", %{ already_existing_id1: already_existing_id1 } do assert_raise RuntimeError, ~r/EctoWatch is not running/, fn -> - EctoWatch.subscribe(Thing, :updated) + EctoWatch.subscribe({Thing, :updated}) end assert_raise RuntimeError, ~r/EctoWatch is not running/, fn -> - EctoWatch.subscribe(Thing, :updated, already_existing_id1) + EctoWatch.subscribe({Thing, :updated}, already_existing_id1) end assert_raise RuntimeError, ~r/EctoWatch is not running/, fn -> - EctoWatch.subscribe(Thing, :updated, {:parent_thing_id, already_existing_id1}) + EctoWatch.subscribe({Thing, :updated}, {:parent_thing_id, already_existing_id1}) end end @@ -469,23 +495,22 @@ defmodule EctoWatchTest do ) assert_raise ArgumentError, - ~r/No watcher found for NotASchema \/ :updated/, + ~r/Expected atom to be an Ecto schema module. Got: NotASchema/, fn -> - EctoWatch.subscribe(NotASchema, :updated) + EctoWatch.subscribe({NotASchema, :updated}) end assert_raise ArgumentError, - ~r/No watcher found for NotASchema \/ :updated/, + ~r/Expected atom to be an Ecto schema module. Got: NotASchema/, fn -> - EctoWatch.subscribe(NotASchema, :updated, already_existing_id1) + EctoWatch.subscribe({NotASchema, :updated}, already_existing_id1) end assert_raise ArgumentError, - ~r/No watcher found for NotASchema \/ :updated/, + ~r/Expected atom to be an Ecto schema module. Got: NotASchema/, fn -> EctoWatch.subscribe( - NotASchema, - :updated, + {NotASchema, :updated}, {:parent_thing_id, already_existing_id1} ) end @@ -506,41 +531,40 @@ defmodule EctoWatchTest do assert_raise ArgumentError, "Unexpected update_type: :something_else. Expected :inserted, :updated, or :deleted", fn -> - EctoWatch.subscribe(Thing, :something_else) + EctoWatch.subscribe({Thing, :something_else}) end assert_raise ArgumentError, - "Unexpected update_type: 1234. Expected :inserted, :updated, or :deleted", + "Invalid subscription (expected either `{schema_module, :inserted | :updated | :deleted}` or a label): {EctoWatchTest.Thing, 1234}", fn -> - EctoWatch.subscribe(Thing, 1234) + EctoWatch.subscribe({Thing, 1234}) end assert_raise ArgumentError, "Unexpected update_type: :something_else. Expected :inserted, :updated, or :deleted", fn -> - EctoWatch.subscribe(Thing, :something_else, already_existing_id1) + EctoWatch.subscribe({Thing, :something_else}, already_existing_id1) end assert_raise ArgumentError, - "Unexpected update_type: 1234. Expected :inserted, :updated, or :deleted", + "Invalid subscription (expected either `{schema_module, :inserted | :updated | :deleted}` or a label): {EctoWatchTest.Thing, 1234}", fn -> - EctoWatch.subscribe(Thing, 1234, already_existing_id1) + EctoWatch.subscribe({Thing, 1234}, already_existing_id1) end assert_raise ArgumentError, "Unexpected update_type: :something_else. Expected :inserted, :updated, or :deleted", fn -> EctoWatch.subscribe( - Thing, - :something_else, + {Thing, :something_else}, {:parent_thing_id, already_existing_id1} ) end assert_raise ArgumentError, - "Unexpected update_type: 1234. Expected :inserted, :updated, or :deleted", + "Invalid subscription (expected either `{schema_module, :inserted | :updated | :deleted}` or a label): {EctoWatchTest.Thing, 1234}", fn -> - EctoWatch.subscribe(Thing, 1234, {:parent_thing_id, already_existing_id1}) + EctoWatch.subscribe({Thing, 1234}, {:parent_thing_id, already_existing_id1}) end end end @@ -559,10 +583,10 @@ defmodule EctoWatchTest do label: :other_inserted} ]}) - EctoWatch.subscribe(Thing, :inserted) - EctoWatch.subscribe(Other, :inserted) - EctoWatch.subscribe(:things_inserted, :inserted) - EctoWatch.subscribe(:other_inserted, :inserted) + EctoWatch.subscribe({Thing, :inserted}) + EctoWatch.subscribe({Other, :inserted}) + EctoWatch.subscribe(:things_inserted) + EctoWatch.subscribe(:other_inserted) Ecto.Adapters.SQL.query!( TestRepo, @@ -576,10 +600,10 @@ defmodule EctoWatchTest do [] ) - assert_receive {:inserted, Thing, %{id: 3}} - assert_receive {:inserted, :things_inserted, %{id: 3}} - assert_receive {:inserted, Other, %{weird_id: 1234}} - assert_receive {:inserted, :other_inserted, %{weird_id: 1234}} + assert_receive {{Thing, :inserted}, %{id: 3}} + assert_receive {:things_inserted, %{id: 3}} + assert_receive {{Other, :inserted}, %{weird_id: 1234}} + assert_receive {:other_inserted, %{weird_id: 1234}} end test "inserts for an association column", %{already_existing_id2: already_existing_id2} do @@ -597,11 +621,10 @@ defmodule EctoWatchTest do ]} ) - EctoWatch.subscribe(Thing, :inserted, {:parent_thing_id, already_existing_id2}) + EctoWatch.subscribe({Thing, :inserted}, {:parent_thing_id, already_existing_id2}) EctoWatch.subscribe( :things_parent_id_inserted, - :inserted, {:parent_thing_id, already_existing_id2} ) @@ -611,9 +634,9 @@ defmodule EctoWatchTest do [] ) - assert_receive {:inserted, Thing, %{id: 3, parent_thing_id: ^already_existing_id2}} + assert_receive {{Thing, :inserted}, %{id: 3, parent_thing_id: ^already_existing_id2}} - assert_receive {:inserted, :things_parent_id_inserted, + assert_receive {:things_parent_id_inserted, %{id: 3, parent_thing_id: ^already_existing_id2}} end @@ -631,8 +654,7 @@ defmodule EctoWatchTest do ~r/Column other_parent_thing_id is not in the list of extra columns/, fn -> EctoWatch.subscribe( - Thing, - :inserted, + {Thing, :inserted}, {:other_parent_thing_id, already_existing_id2} ) end @@ -651,7 +673,7 @@ defmodule EctoWatchTest do assert_raise ArgumentError, ~r/Column the_string is not an association column/, fn -> - EctoWatch.subscribe(Thing, :inserted, {:the_string, "test"}) + EctoWatch.subscribe({Thing, :inserted}, {:the_string, "test"}) end end @@ -678,8 +700,8 @@ defmodule EctoWatchTest do [] ) - refute_receive {:inserted, Thing, %{}} - refute_receive {:inserted, Other, %{}} + refute_receive {{Thing, :inserted}, %{}} + refute_receive {{Other, :inserted}, %{}} end end @@ -697,16 +719,16 @@ defmodule EctoWatchTest do {%{table_name: :things}, :updated, label: :things_updated} ]}) - EctoWatch.subscribe(Thing, :updated) - EctoWatch.subscribe(:things_updated, :updated) + EctoWatch.subscribe({Thing, :updated}) + EctoWatch.subscribe(:things_updated) Ecto.Adapters.SQL.query!(TestRepo, "UPDATE things SET the_string = 'the new value'", []) - assert_receive {:updated, Thing, %{id: ^already_existing_id1}} - assert_receive {:updated, :things_updated, %{id: ^already_existing_id1}} + assert_receive {{Thing, :updated}, %{id: ^already_existing_id1}} + assert_receive {:things_updated, %{id: ^already_existing_id1}} - assert_receive {:updated, Thing, %{id: ^already_existing_id2}} - assert_receive {:updated, :things_updated, %{id: ^already_existing_id2}} + assert_receive {{Thing, :updated}, %{id: ^already_existing_id2}} + assert_receive {:things_updated, %{id: ^already_existing_id2}} end test "updates for the primary key", %{ @@ -722,13 +744,13 @@ defmodule EctoWatchTest do ]} ) - EctoWatch.subscribe(Thing, :updated, already_existing_id1) + EctoWatch.subscribe({Thing, :updated}, already_existing_id1) Ecto.Adapters.SQL.query!(TestRepo, "UPDATE things SET the_string = 'the new value'", []) - assert_receive {:updated, Thing, %{id: ^already_existing_id1}} + assert_receive {{Thing, :updated}, %{id: ^already_existing_id1}} - refute_receive {:updated, Thing, %{id: ^already_existing_id2}} + refute_receive {{Thing, :updated}, %{id: ^already_existing_id2}} end test "updates for an association column", %{ @@ -749,23 +771,22 @@ defmodule EctoWatchTest do ]} ) - EctoWatch.subscribe(Thing, :updated, {:parent_thing_id, already_existing_id1}) + EctoWatch.subscribe({Thing, :updated}, {:parent_thing_id, already_existing_id1}) EctoWatch.subscribe( :things_parent_id_updated, - :updated, {:parent_thing_id, already_existing_id1} ) Ecto.Adapters.SQL.query!(TestRepo, "UPDATE things SET the_string = 'the new value'", []) - refute_receive {:updated, Thing, %{id: ^already_existing_id1}} - refute_receive {:things_parent_id_updated, Thing, %{id: ^already_existing_id1}} + refute_receive {{Thing, :updated}, %{id: ^already_existing_id1}} + refute_receive {:things_parent_id_updated, %{id: ^already_existing_id1}} - assert_receive {:updated, Thing, + assert_receive {{Thing, :updated}, %{id: ^already_existing_id2, parent_thing_id: ^already_existing_id1}} - assert_receive {:updated, :things_parent_id_updated, + assert_receive {:things_parent_id_updated, %{id: ^already_existing_id2, parent_thing_id: ^already_existing_id1}} end @@ -783,8 +804,7 @@ defmodule EctoWatchTest do ~r/Column other_parent_thing_id is not in the list of extra columns/, fn -> EctoWatch.subscribe( - Thing, - :updated, + {Thing, :updated}, {:other_parent_thing_id, already_existing_id2} ) end @@ -803,7 +823,7 @@ defmodule EctoWatchTest do assert_raise ArgumentError, ~r/Column the_string is not an association column/, fn -> - EctoWatch.subscribe(Thing, :updated, {:the_string, "test"}) + EctoWatch.subscribe({Thing, :updated}, {:the_string, "test"}) end end @@ -821,22 +841,22 @@ defmodule EctoWatchTest do ]} ) - EctoWatch.subscribe(:thing_custom_event, :updated, already_existing_id1) + EctoWatch.subscribe(:thing_custom_event, already_existing_id1) Ecto.Adapters.SQL.query!(TestRepo, "UPDATE things SET the_string = 'the new value'", []) - refute_receive {:updated, _, %{id: ^already_existing_id1}} - refute_receive {:updated, _, %{id: ^already_existing_id2}} + refute_receive {_, %{id: ^already_existing_id1}} + refute_receive {_, %{id: ^already_existing_id2}} Ecto.Adapters.SQL.query!(TestRepo, "UPDATE things SET the_integer = 9999", []) - assert_receive {:updated, :thing_custom_event, %{id: ^already_existing_id1}} - refute_receive {:updated, _, %{id: ^already_existing_id2}} + assert_receive {:thing_custom_event, %{id: ^already_existing_id1}} + refute_receive {_, %{id: ^already_existing_id2}} Ecto.Adapters.SQL.query!(TestRepo, "UPDATE things SET the_float = 99.999", []) - assert_receive {:updated, :thing_custom_event, %{id: ^already_existing_id1}} - refute_receive {:updated, _, %{id: ^already_existing_id2}} + assert_receive {:thing_custom_event, %{id: ^already_existing_id1}} + refute_receive {_, %{id: ^already_existing_id2}} end test "extra_columns option", %{ @@ -852,7 +872,7 @@ defmodule EctoWatchTest do ]} ) - EctoWatch.subscribe(Thing, :updated, already_existing_id1) + EctoWatch.subscribe({Thing, :updated}, already_existing_id1) Ecto.Adapters.SQL.query!( TestRepo, @@ -860,28 +880,28 @@ defmodule EctoWatchTest do [already_existing_id1] ) - assert_receive {:updated, Thing, + assert_receive {{Thing, :updated}, %{id: ^already_existing_id1, the_integer: 4455, the_float: 84.52}} - refute_receive {:updated, _, %{id: ^already_existing_id2}} + refute_receive {{_, :updated}, %{id: ^already_existing_id2}} Ecto.Adapters.SQL.query!(TestRepo, "UPDATE things SET the_integer = 9999 WHERE id = $1", [ already_existing_id1 ]) - assert_receive {:updated, Thing, + assert_receive {{Thing, :updated}, %{id: ^already_existing_id1, the_integer: 9999, the_float: 84.52}} - refute_receive {:updated, _, %{id: ^already_existing_id2}} + refute_receive {{_, :updated}, %{id: ^already_existing_id2}} Ecto.Adapters.SQL.query!(TestRepo, "UPDATE things SET the_float = 99.999 WHERE id = $1", [ already_existing_id1 ]) - assert_receive {:updated, Thing, + assert_receive {{Thing, :updated}, %{id: ^already_existing_id1, the_integer: 9999, the_float: 99.999}} - refute_receive {:updated, _, %{id: ^already_existing_id2}} + refute_receive {{_, :updated}, %{id: ^already_existing_id2}} end test "no notifications without subscribe", %{ @@ -890,9 +910,9 @@ defmodule EctoWatchTest do } do Ecto.Adapters.SQL.query!(TestRepo, "UPDATE things SET the_string = 'the new value'", []) - refute_receive {:updated, Thing, %{id: ^already_existing_id1}} + refute_receive {{Thing, :updated}, %{id: ^already_existing_id1}} - refute_receive {:updated, Thing, %{id: ^already_existing_id2}} + refute_receive {{Thing, :updated}, %{id: ^already_existing_id2}} end end @@ -910,16 +930,16 @@ defmodule EctoWatchTest do {%{table_name: :things}, :deleted, label: :things_deleted} ]}) - EctoWatch.subscribe(Thing, :deleted) - EctoWatch.subscribe(:things_deleted, :deleted) + EctoWatch.subscribe({Thing, :deleted}) + EctoWatch.subscribe(:things_deleted) Ecto.Adapters.SQL.query!(TestRepo, "DELETE FROM things", []) - assert_receive {:deleted, Thing, %{id: ^already_existing_id1}} - assert_receive {:deleted, :things_deleted, %{id: ^already_existing_id1}} + assert_receive {{Thing, :deleted}, %{id: ^already_existing_id1}} + assert_receive {:things_deleted, %{id: ^already_existing_id1}} - assert_receive {:deleted, Thing, %{id: ^already_existing_id2}} - assert_receive {:deleted, :things_deleted, %{id: ^already_existing_id2}} + assert_receive {{Thing, :deleted}, %{id: ^already_existing_id2}} + assert_receive {:things_deleted, %{id: ^already_existing_id2}} end test "deletes for the primary key", %{ @@ -935,13 +955,13 @@ defmodule EctoWatchTest do ]} ) - EctoWatch.subscribe(Thing, :deleted, already_existing_id1) + EctoWatch.subscribe({Thing, :deleted}, already_existing_id1) Ecto.Adapters.SQL.query!(TestRepo, "DELETE FROM things", []) - assert_receive {:deleted, Thing, %{id: ^already_existing_id1}} + assert_receive {{Thing, :deleted}, %{id: ^already_existing_id1}} - refute_receive {:deleted, Thing, %{id: ^already_existing_id2}} + refute_receive {{Thing, :deleted}, %{id: ^already_existing_id2}} end test "deletes for an association column", %{ @@ -962,23 +982,22 @@ defmodule EctoWatchTest do ]} ) - EctoWatch.subscribe(Thing, :deleted, {:parent_thing_id, already_existing_id1}) + EctoWatch.subscribe({Thing, :deleted}, {:parent_thing_id, already_existing_id1}) EctoWatch.subscribe( :things_parent_id_deleted, - :deleted, {:parent_thing_id, already_existing_id1} ) Ecto.Adapters.SQL.query!(TestRepo, "DELETE FROM things", []) - refute_receive {:deleted, Thing, %{id: ^already_existing_id1}} - refute_receive {:deleted, :things_parent_id_deleted, %{id: ^already_existing_id1}} + refute_receive {{Thing, :deleted}, %{id: ^already_existing_id1}} + refute_receive {:things_parent_id_deleted, %{id: ^already_existing_id1}} - assert_receive {:deleted, Thing, + assert_receive {{Thing, :deleted}, %{id: ^already_existing_id2, parent_thing_id: ^already_existing_id1}} - assert_receive {:deleted, :things_parent_id_deleted, + assert_receive {:things_parent_id_deleted, %{id: ^already_existing_id2, parent_thing_id: ^already_existing_id1}} end @@ -998,8 +1017,7 @@ defmodule EctoWatchTest do ~r/Column other_parent_thing_id is not in the list of extra columns/, fn -> EctoWatch.subscribe( - Thing, - :deleted, + {Thing, :deleted}, {:other_parent_thing_id, already_existing_id2} ) end @@ -1018,7 +1036,7 @@ defmodule EctoWatchTest do assert_raise ArgumentError, ~r/Column the_string is not an association column/, fn -> - EctoWatch.subscribe(Thing, :deleted, {:the_string, "test"}) + EctoWatch.subscribe({Thing, :deleted}, {:the_string, "test"}) end end @@ -1028,9 +1046,9 @@ defmodule EctoWatchTest do } do Ecto.Adapters.SQL.query!(TestRepo, "DELETE FROM things", []) - refute_receive {:deleted, Thing, %{id: ^already_existing_id1}} + refute_receive {{Thing, :deleted}, %{id: ^already_existing_id1}} - refute_receive {:deleted, Thing, %{id: ^already_existing_id2}} + refute_receive {{Thing, :deleted}, %{id: ^already_existing_id2}} end end end