-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
b0e3776
commit 1411769
Showing
18 changed files
with
388 additions
and
180 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
As `EctoWatch` is pre-1.0.0, breaking changes may occur in minor versions. This guide is here to help you understand version releases with breaking changes and how to change your code to work with the new version. | ||
|
||
## Upgrading to 0.6.0 | ||
|
||
In this version the broadcast messages from `EctoWatch` changed from a 4-element tuple to a 3-element tuple. Before the primary key value and the `extra_columns` data were two separate elements in the tuple: | ||
|
||
```elixir | ||
def handle_info({:inserted, Comment, id, %{post_id: post_id}}, socket) do | ||
# ... | ||
``` | ||
|
||
With version 0.6.0, they are combined into a single map: | ||
|
||
```elixir | ||
def handle_info({:inserted, Comment, %{id: id, post_id: post_id}}, socket) do | ||
# ... | ||
``` | ||
|
||
## Upgrading to 0.8.0 | ||
|
||
In this version became is no longer required to specify the update type (`:inserted`/`:updated`/`:deleted`) for watchers with labels. Before you would have: | ||
|
||
```elixir | ||
# watcher config: | ||
watchers: [ | ||
{Comment, :updated, trigger_columns: [:title, :body], label: :title_or_body_updated}, | ||
|
||
# subscribe: | ||
EctoWatch.subscribe(:title_or_body_updated, :updated) | ||
# or | ||
EctoWatch.subscribe(:title_or_body_updated, :updated, comment_id) | ||
# or | ||
EctoWatch.subscribe(:title_or_body_updated, :updated, {:post_id, post_id}) | ||
|
||
# handler: | ||
def handle_info({:inserted, Comment, %{id: id}}, socket) do | ||
# ... | ||
``` | ||
|
||
With versios 0.8.0 the update type is implied by the label, so you can subscribe simply by doing: | ||
|
||
```elixir | ||
# subscribe: | ||
EctoWatch.subscribe(:title_or_body_updated) | ||
# or | ||
EctoWatch.subscribe(:title_or_body_updated, comment_id) | ||
# or | ||
EctoWatch.subscribe(:title_or_body_updated, {:post_id, post_id}) | ||
``` | ||
|
||
Also, to keep the subscribe function consistent, the normal case of subscribing and handling to a watcher that doesn't have a label requires a tuple of the ecto schema + update type: | ||
|
||
```elixir | ||
# watcher config: | ||
watchers: [ | ||
{Comment, :updated}, | ||
|
||
EctoWatch.subscribe({Comment, :updated}) | ||
|
||
# handler (NOTE the flipped order of schema and update type): | ||
def handle_info({{Comment, :inserted}, %{id: id}}, socket) do | ||
# ... | ||
``` | ||
|
||
You can think of the first argument of `subscribe` or the first element of the tuple as an lookup identifier for the watcher which is either `{ecto_schema(), update_type()}` or a label atom. So handling a label would just be: | ||
|
||
|
||
```elixir | ||
def handle_info({:title_or_body_updated, %{id: id}}, socket) do | ||
# ... | ||
``` | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
To use EctoWatch, you need to add it to your supervision tree and specify watchers for Ecto schemas and update types. It would look something like this in your `application.ex` file (after `MyApp.Repo` and `MyApp.PubSub`): | ||
|
||
```elixir | ||
alias MyApp.Accounts.User | ||
alias MyApp.Accounts.Package | ||
|
||
{EctoWatch, | ||
repo: MyApp.Repo, | ||
pub_sub: MyApp.PubSub, | ||
watchers: [ | ||
{User, :inserted}, | ||
{User, :updated}, | ||
{User, :deleted}, | ||
{Package, :inserted}, | ||
{Package, :updated} | ||
]} | ||
``` | ||
|
||
This will setup: | ||
|
||
* triggers in PostgreSQL during application startup | ||
* an Elixir process for each watcher which listens for notifications and broadcasts them via `Phoenix.PubSub` | ||
|
||
Then any process (e.g. a GenServer, a LiveView, a Phoenix channel, etc...) can subscribe to messages like so: | ||
|
||
```elixir | ||
EctoWatch.subscribe({User, :inserted}) | ||
EctoWatch.subscribe({User, :updated}) | ||
EctoWatch.subscribe({User, :deleted}) | ||
|
||
EctoWatch.subscribe({Package, :inserted}) | ||
EctoWatch.subscribe({Package, :updated}) | ||
``` | ||
|
||
(note that if you are subscribing in a LiveView `mount` callback you should subscribe inside of a `if connected?(socket) do` to avoid subscribing twice). | ||
|
||
You can also subscribe to individual records: | ||
|
||
```elixir | ||
EctoWatch.subscribe({User, :updated}, user.id) | ||
EctoWatch.subscribe({User, :deleted}, user.id) | ||
``` | ||
|
||
... OR you can subscribe to records by an association column (but the given column must be in the `extra_columns` list for the watcher! See below for more info on the `extra_columns` option): | ||
|
||
```elixir | ||
EctoWatch.subscribe({User, :updated}, {:role_id, role.id}) | ||
EctoWatch.subscribe({User, :deleted}, {:role_id, role.id}) | ||
``` | ||
|
||
Once subscribed, messages can be handled like so (LiveView example are given here but `handle_info` callbacks can be used elsewhere as well): | ||
|
||
```elixir | ||
def handle_info({{User, :inserted}, %{id: id}}, socket) do | ||
user = Accounts.get_user(id) | ||
socket = stream_insert(socket, :users, user) | ||
|
||
{:noreply, socket} | ||
end | ||
|
||
def handle_info({{User, :updated}, %{id: id}}, socket) do | ||
user = Accounts.get_user(id) | ||
socket = stream_insert(socket, :users, user) | ||
|
||
{:noreply, socket} | ||
end | ||
|
||
def handle_info({{User, :deleted}, %{id: id}}, socket) do | ||
socket = stream_delete_by_dom_id(socket, :songs, "users-#{id}") | ||
|
||
{:noreply, socket} | ||
end | ||
``` | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
|
||
If you would like to get more than just the `id` from the record, you can use the `extra_columns` option. | ||
|
||
> The `extra_columns` option should be used with care because: | ||
> | ||
> * The `pg_notify` function has a limit of 8000 characters and wasn't created to send full-records on updates. | ||
> * If many updates are done in quick succession to the same record, subscribers will need to process all of the old results before getting to the newest one. | ||
> | ||
> Thus using `extra_columns` with columns that change often may not be what you want. | ||
> | ||
> One use-case where using `extra_columns` may be particularly useful is if you want to receive updates about the deletion of a record and you need to know one of it's foreign keys. E.g. in a blog, if a `Comment` is deleted you might want to get the `post_id` to refresh any caches related to comments. | ||
```elixir | ||
# setup | ||
{EctoWatch, | ||
repo: MyApp.Repo, | ||
pub_sub: MyApp.PubSub, | ||
watchers: [ | ||
# ... | ||
{Comment, :deleted, extra_columns: [:post_id]}, | ||
# ... | ||
]} | ||
|
||
# subscribing | ||
EctoWatch.subscribe({Comment, :deleted}) | ||
|
||
# handling messages | ||
def handle_info({{Comment, :deleted}, %{id: id, post_id: post_id}}, socket) do | ||
Posts.refresh_cache(post_id) | ||
``` | ||
|
Oops, something went wrong.