Skip to content

Instantly share code, notes, and snippets.

@marka2g
Last active April 12, 2023 03:23
Show Gist options
  • Save marka2g/8cd2dbfd3584827c0542497fc07067e3 to your computer and use it in GitHub Desktop.
Save marka2g/8cd2dbfd3584827c0542497fc07067e3 to your computer and use it in GitHub Desktop.
Elixir Genserver ShoppingCart with Registry and TimeToLive Example
# genserver with registry
defmodule MyApp.Workers.ShoppingCartCache do
use GenServer
require Logger
# one month
@ttl System.get_env("SHOPPING_CART_CACHE_PUBLIC_TTL") |> String.to_integer()
def start_link(shopping_cart_id) do
GenServer.start_link(__MODULE__, [shopping_cart_id], name: via_tuple(shopping_cart_id))
end
@impl true
def init(opts) do
Logger.info("Starting ShoppingCartCache with opts #{opts}")
Process.send_after(self(), :timeout, @ttl)
{:ok, []}
end
def items(shopping_bag_id) do
GenServer.call(via_tuple(shopping_cart_id), :items)
end
def add_item(shopping_cart_id, item) do
GenServer.cast(via_tuple(shopping_cart_id), {:add_item, item})
end
def remove_item(shopping_cart_id, item) do
GenServer.cast(via_tuple(shopping_cart_id), {:remove_item, item})
end
def trash_item(shopping_cart_id, item) do
GenServer.cast(via_tuple(shopping_cart_id), {:trash_item, item})
end
def empty_cart(shopping_cart_id) do
GenServer.call(via_tuple(shopping_cart_id), :empty_cart)
end
@impl true
def handle_info(:timeout, state) do
{:stop, :normal, state}
end
@impl true
def handle_call(:items, _from, state) do
{:reply, Enum.to_list(state), state}
end
@impl true
def handle_cast({:add_item, item}, state) do
{:noreply, [item | state]}
end
@impl true
def handle_cast({:remove_item, item}, state) do
new_cart = state |> List.delete(item)
{:noreply, new_cart}
end
@impl true
def handle_cast({:trash_item, item}, state) do
new_cart =
state
|> Enum.filter(fn v -> v != item end)
{:noreply, Enum.to_list(new_cart)}
end
@impl true
def handle_call(:empty_cart, _from, _state) do
{:reply, [], []}
end
defp via_tuple(shopping_cart_id) do
{:via, Registry, {:shopping_cart_registry, shopping_cart_id}}
end
end
# dynamic supervisor
defmodule MyApp.Workers.ShoppingCartSupervisor do
use DynamicSupervisor
def start_link(init_arg) do
DynamicSupervisor.start_link(__MODULE__, init_arg, name: __MODULE__)
end
def init(_init_arg) do
DynamicSupervisor.init(strategy: :one_for_one)
end
def start_child(shopping_cart_attrs) do
DynamicSupervisor.start_child(
__MODULE__,
{MyApp.Workers.ShoppingCartCache, shopping_cart_attrs}
)
end
end
# apllication.ex
defmodule MyApp.Application do
@moduledoc false
use Application
@impl true
def start(_type, _args) do
children = [
# Start the Telemetry supervisor
MyApp.Telemetry,
# Start the Ecto repository
MyApp.Repo,
# Start the PubSub system
{Phoenix.PubSub, name: MyApp.PubSub},
# Start Finch
{Finch, name: MyApp.Finch},
# Start the Endpoint (http/https)
MyAppWeb.Endpoint,
MyApp.Workers.ShoppingCartSupervisor,
{Registry, [keys: :unique, name: :shopping_cart_registry]}
]
opts = [strategy: :one_for_one, name: MyApp.Supervisor]
Supervisor.start_link(children, opts)
end
@impl true
def config_change(changed, _new, removed) do
MyAppsWeb.Endpoint.config_change(changed, removed)
:ok
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment