rbino
Publishing an event with the previous value of an attribute with PubSub
Hey everyone!
In my app I have this resource (some parts omitted):
defmodule MyApp.Vote do
use Ash.Resource,
data_layer: AshPostgres.DataLayer
actions do
create :cast do
argument :poll_id, :uuid, allow_nil?: false
argument :option_id, :uuid, allow_nil?: false
upsert? true
upsert_identity :unique_voter_per_poll
change manage_relationship(:poll_id, :poll, type: :append_and_remove)
change manage_relationship(:option_id, :option, type: :append_and_remove)
change relate_actor(:voter)
end
end
relationships do
belongs_to :poll, Poll do
primary_key? true
allow_nil? false
end
belongs_to :option, Option do
primary_key? true
allow_nil? false
end
belongs_to :voter, Voter do
primary_key? true
allow_nil? false
end
end
identities do
identity :unique_voter_per_poll, [:voter_id, :poll_id]
end
code_interface do
define_for MyApp.Voting
define :cast, args: [:poll_id, :option_id]
end
end
If a voter casts their vote for another option in the same poll, this is an upsert so the new option id overwrites the old one.
Now, I want to publish updates on a pubsub because I want to display (and update) the count of votes for each option in a Live View. This means that when I cast a vote, I want to receive a vote event for the option_id, but I also want to receive an unvote event for the previous option_id.
What is the recommended way to do this?
Marked As Solved
rbino
I think I’ve found a workaround: I’ve added a custom change that reads the current option.
defmodule Tabemono.Voting.Vote.Changes.StorePreviousOption do
use Ash.Resource.Change
require Ash.Query
def change(changeset, _opts, ctx) do
poll_id = Ash.Changeset.get_argument(changeset, :poll_id)
voter_id = ctx.actor.id
previous_vote =
Tabemono.Voting.Vote
|> Ash.Query.lock("FOR UPDATE NOWAIT")
|> Ash.Query.filter(poll_id: poll_id, voter_id: voter_id)
|> Tabemono.Voting.read_one!()
if previous_vote do
Ash.Changeset.put_context(changeset, :old_option_id, previous_vote.option_id)
else
changeset
end
end
end
To be extra careful, I’ve added a row lock. This way, in the (unlikely) event that the same user tries to update the vote from two different locations, one of them should fail, ensuring a single notification is sent for that old option id.
After that, it’s just a matter of reading the changeset context in the notification that gets sent. I would’ve preferred to put this information in the notification metadata instead, but I’m not sure how to control it.
Is this solutions reasonable or does it have any problem?
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance









