jkbbwr

jkbbwr

Conditionally construct map

So I have found a pothole and I am not sure what the solution is.

I have a pretty standard JSON api in phoenix, I am using ecto, and I am mapping those to maps inside the ThingJSON this is all pretty standard.

Now depending on the route, the controller might decide it doesn’t want to preload some data. For example

%Aos.Schema.Shipyard{
  __meta__: #Ecto.Schema.Metadata<:loaded, "shipyard">,
  id: "103b0451-7396-4f11-9cf8-7081c1829f5e",
  port_id: "a748b9d2-40ff-4f14-8c8c-258b343145eb",
  port: %Aos.Schema.Port{
    __meta__: #Ecto.Schema.Metadata<:loaded, "port">,
    id: "a748b9d2-40ff-4f14-8c8c-258b343145eb",
    name: "London",
    shortcode: "lond",
    destinations: #Ecto.Association.NotLoaded<association :destinations is not loaded>,
    ships: #Ecto.Association.NotLoaded<association :ships is not loaded>,
    shipyard: #Ecto.Association.NotLoaded<association :shipyard is not loaded>,
    agents: #Ecto.Association.NotLoaded<association :agents is not loaded>,
    inserted_at: ~U[2024-07-01 02:02:57Z],
    updated_at: ~U[2024-07-01 02:02:57Z]
  },
  ships: [
    %Aos.Schema.ShipyardStock{
      __meta__: #Ecto.Schema.Metadata<:loaded, "shipyard_stock">,
      id: "8117b318-b0ef-4aa7-be31-88437acbe8fc",
      shipyard_id: "103b0451-7396-4f11-9cf8-7081c1829f5e",
      shipyard: #Ecto.Association.NotLoaded<association :shipyard is not loaded>,
      ship_id: "0a238468-1cf0-47b4-b891-d5936d6554a8",
      ship: #Ecto.Association.NotLoaded<association :ship is not loaded>,
      cost: 100,
      inserted_at: ~U[2024-07-01 02:02:57Z],
      updated_at: ~U[2024-07-01 02:02:57Z]
    }
  ],
  inserted_at: ~U[2024-07-01 02:02:57Z],
  updated_at: ~U[2024-07-01 02:02:57Z]
}

On one route it might want to preload the :ships but on another it might not want to.

I have some pretty standard view code that looks like this


  def render("shipyards.json", %{page: page}) do
    %{
      data: for(yard <- page.entries, do: shipyard(yard)),
      meta: %{
        page_number: page.page_number,
        page_size: page.page_size,
        total_entries: page.total_entries,
        total_pages: page.total_pages
      }
    }
  end

  def shipyard(shipyard) do
    %{
      id: shipyard.id,
      port: PortJSON.port(shipyard.port)
    }
  end

Now I want to modify the shipyard function to also display the ships but only if the assoc is actually preloaded.

Okay so I think

  def shipyard(shipyard) do
    %{
      id: shipyard.id,
      port: PortJSON.port(shipyard.port),
      ships:
        if Ecto.assoc_loaded?(shipyard.ships) do
          for(entry <- shipyard.ships, do: stock(entry))
        else
          # ???
        end
    }
  end

Problem is, if ships is not loaded it will export json of ships: null which is misleading IMO. I’d rather it just omitted the key.

I could do something like this

  def render_if(map, key, assoc, render) do
    if Ecto.assoc_loaded?(assoc) do
      Map.put(map, key, render.(assoc))
    else
      map
    end
  end

  def shipyard(shipyard) do
    %{
      id: shipyard.id,
      port: PortJSON.port(shipyard.port)
    }
    |> render_if(:ships, shipyard.ships, &stock/1)
  end

But it feels kinda wrong.

Also why is Ecto.assoc_loaded? not available as a guard.

Anyone got any ideas?

Most Liked Responses

tfwright

tfwright

I don’t think there’s anything wrong with the way you’re handling it. I have done similar although I had a util that constructed the json with variable guards, among them filtering out unloaded associations–something like this:

def serialize(data) do
  Map.flat_map(data, fn 
    {field, %Ecto.AssocNotLoaded{}} -> []
    {field, assoc = %{}} -> [{field, serialize(assoc)}]
    {field, val} -> [{field, val}]
  end)
  # other selecting/filtering...
end
dimitarvp

dimitarvp

You can pattern match on Ecto.AssocNotLoaded and then have the map construction not include the key in that clause, or you can put nil and then have Enum.reject that deletes any map pairs with a nil value. Either one would be just fine.

Where Next?

Popular in Questions Top

sergio
In Ruby, I can go: User.find_by(email: "foobar@email.com").update(email: "hello@email.com") How can I do something similar in Elixir? ...
New
Kurisu
For example for a current url like http://localhost:4000/cosmetic/products?_utf8=✓&amp;query=perfume&amp;page=2, I would like to get: ...
New
fireproofsocks
I’m working on defining a simple Ecto schema for a table (in PostGres), but I don’t see where I can define a column as NOT NULL. Conside...
New
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
JulienCorb
I am trying to implement my new.html.eex file to create new posts on my website. new.html.eex: &lt;h1&gt;Create Post&lt;/h1&gt; &lt;%= ...
New
LegitStack
I’m trying to make a websocket server in Phoenix or raw Elixir. I heard about gun, I think I could use cowboy, but since I’m not that sma...
New
lucidguppy
I have a super simple question about elixir - how would I take a file like this foo bar baz and output a new file that enumerates th...
New
nobody
Hi! In PHP: $_SERVER[‘SERVER_ADDR’] - in Elixir? Searched the docs for ip address and the web, no good results. Thanks!
New
dotdotdotPaul
Okay, I’m having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I’m sure I’...
New
marick
I had some trouble figuring out how to make many-to-many associations work. Once I got it working, I wrote a blog post. Because I’m a nov...
New

Other popular topics Top

TunkShif
This post is an instruction guide to help you setup your Neovim for Elixir development from scratch. It includes general information on h...
274 41539 114
New
lessless
I believe there are people here who are dealing with CSV files import on the daily basis, and since Excel is a really popular tool there ...
New
JakeBecker
TL;DR: I’ve just released an implementation of Microsoft’s IDE-independent Language Server Protocol for Elixir. It adds language support ...
1144 53690 245
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers’ Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list. ...
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
Qqwy
Update: How to use the Blogs &amp; Podcasts section You can post links to your blog posts or podcasts either in one of the Official Blog...
3271 126479 1222
New

We're in Beta

About us Mission Statement