fceruti
A tiny macro to clean LiveView code
Sup ppl, I just wanted to share a little macro I created to simplify LiveView code. It allows you to convert
socket.assings.variable
into
~a|variable|
For instance here’s a function from Chris McCord’s live_trek
def handle_event("delete", %{"id" => id}, socket) do
todo = Todos.get_todo!(socket.assigns.scope, id)
{:ok, _} = Todos.delete_todo(socket.assigns.scope, todo)
{:noreply, socket}
end
Would be
def handle_event("delete", %{"id" => id}, socket) do
todo = Todos.get_todo!(~a|scope|, id)
{:ok, _} = Todos.delete_todo(~a|scope|, todo)
{:noreply, socket}
end
Necessary? Not really.
But I thought it looks much better, specially when you have lot’s of socket.assigns and want to reduce verbosit, avoid unpacking assigns and reduce formatter line breaks.
Here’s v0.1
defmacro sigil_a(expr, _modifiers) do
socket = Macro.var(:socket, nil)
quote do
Map.get(unquote(socket).assigns, String.to_existing_atom(unquote(expr)))
end
end
And here’s a vim command to search and replace:
:%s/socket\.assigns\.\([a-zA-Z0-9_]*\)/\~a|\1|
Most Liked
zachallaun
If you wanted to be really evil, you could overload the @ operator if the caller environment contains socket ![]()
defmodule Evil do
defmacro __using__(_) do
quote do
import Kernel, except: [@: 1]
import Evil
end
end
import Kernel, except: [@: 1]
defmacro @name do
var = Macro.var(:socket, nil)
if assigns_call?(__CALLER__, name) do
{name, _, _} = name
quote(do: Map.fetch!(unquote(var).assigns, unquote(name)))
else
quote(do: Kernel.@(unquote(name)))
end
end
defp assigns_call?(env, {name, _, nil}) when is_atom(name) do
Macro.Env.has_var?(env, {:socket, nil})
end
defp assigns_call?(_, _), do: false
end
defmodule Hmm do
use Evil
@okay :foo
def maybe(socket) do
@okay
end
def wow do
@okay
end
end
dbg(Hmm.maybe(%{assigns: %{okay: :NEAT!}}))
dbg(Hmm.wow())
(don’t do this)
tfwright
Nice idea. Curious, why Map.get rather than fetch? I think the behavior being replaced raises if the expected key is missing, whereas this will return nil I think
sodapopcan
Innnnnteresting. I was trying to figure out how to do something like this and couldn’t figure it out. I was trying to use var!/2 for this to no avail. I never understood that you can check the caller env.
I was looking to create a single guard that would work on both socket and assigns. I have a lot of single module CRUD LiveViews so I wanted something like:
def handle_event("update", %{"user" => user_params}, socket) when in_action([:edit]) do
#...
end
def render(assigns) when in_action([:edit]) do
~H"""
"""
end
This would check whichever existed of socket.assigns.live_action or assigns.live_action was in [:edit].
Even though I don’t think that is particularly evil (that is, what I wanted to do—what you’re suggesting is absolutely pure evil
) , I ultimately decided I’d rather not do anything out the ordinary like this so I never bothered to seek help figuring it out, but it’s nice to know how it could be done, so thanks!
(that is, if that would even solve the problem… I haven’t actually tried, just responded immediately, lol, but still informative for me)
Popular in Guides/Tuts
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
- #hex









