lardcanoe
How to pattern match on a union type field before and after it has been saved
When saving the contents of a union type, you can simply pass in a map of the data, such as:
x = %MainResource{union_field: %{foo: :bar}}
# Before saving, you can do
x.union_field.foo
# and pattern match on
%{union_field: %{foo: val}}
But when you get the same one back from the db after saving, it is in the form:
x.union_field.value.foo
# and pattern match happens on
%{union_field: %{value: %{foo: val}}}
I have code that needs to pattern match on the unsaved and saved version, and wondering if there is a way to do this without needing 2 functions to handle both versions. I’m using map_with_tag for storage.
Thanks!
Marked As Solved
zachdaniel
Hmm…something worth trying is to first attempt to cast the value as the underlying union type. If successful it will be in the second format only. You could write a general purpose helper that takes a value and a type and does the castin, maybe with a function of what to do with the caster value as well perhaps?
Also Liked
sodapopcan
Taking Zach’s answer into consideration, I did want to ask how you are using these accessors and why they are deemded necessary, but very hard without more context (and especially since my Ash knowledge is unforunately limited). At first I was thinking that it’s always literally :union_field, but now based on your answer (and the bit of docs I read) assuming that is dynamic while :value is literally :value. Assuming this, you can make a 2-arity accessor:
def get_value(resource, union_field) do
case resource do
%{^union_field => %{value: value}} ->
value
%{^union_field => value} ->
value
end
end
Then:
get_value(%MainResource{some_field: %{foo: :bar}}, :some_field)
# %{foo: :bar}
get_value(%MainResource{some_other_field: %{value: %{foo: :bar}}}, :some_other_field)
# %{foo: :bar}
(apologies, I'm noticing the typo of `bar` vs `:bar` in my original post)
Again, not super sure this is what you’re after but this is my next best attempt ![]()
sodapopcan
I don’t totally follow what you mean by two functions, like two differently named functions? You can certainly make this work with one function with either two heads or a case statememtn. I’m not sure of the exact API you want but something like this:
def get_value(%MainResource{union_field: %{value: value}}), do: value
def get_value(%MainResource{union_field: value}), do: value
Then you can do:
get_value(%MainResource{union_field: %{foo: bar}})
# %{foo: :bar}
get_value(%MainResource{union_field: %{value: %{foo: bar}}}
# %{foo: :bar}
Is that what you mean or are you talking about the . syntax?
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









