lapinkoira

lapinkoira

Hi all, I am reading the Programming Phoenix book and following the steps to implement the Rumbl webapp.

Then at some steps I try to add something custom to learn a bit more.

So in that application, I have a controller which renders a template like this:

Listing videos

<%= for video <- @videos do %>
  <td class="text-right">
    <%= link "Show", to: video_path(@conn, :show, video), class: "btn btn-default btn-xs" %>
    <%= link "Edit", to: video_path(@conn, :edit, video), class: "btn btn-default btn-xs" %>
    <%= link "Delete", to: video_path(@conn, :delete, video), method: :delete, data: [confirm: "Are you sure?"], class: "btn btn-danger btn-xs" %>
  </td>
</tr>

<% end %>

User Url Title Description Category
<%= video.user_id %> <%= video.url %> <%= video.title %> <%= video.description %> <%= if category = video.category, do: category.name %>

<%= link “New video”, to: video_path(@conn, :new) %>

I added this line to render the categories <%= if category = video.category, do: category.name %>

This is my controller
def index(conn, _params, user) do
videos = Repo.all(user_videos(user)) |> Repo.preload(:category)
render(conn, “index.html”, videos: videos)
end

defp user_videos(user) do
# query user videos
assoc(user, :videos)
end

And when I run a test I wrote, this one:

test “renders index.html”, %{conn: conn} do
videos = [%Rumbl.Video{id: “1”, title: “dogs”},
%Rumbl.Video{id: “2”, title: “cats”}]
content = render_to_string(Rumbl.VideoView, “index.html”,
conn: conn, videos: videos)

assert String.contains?(content, "Listing videos")
for video <- videos do
  assert String.contains?(content, video.title)
end

end

It complains with categories not being preloaded

** (KeyError) key :name not found in: #Ecto.Association.NotLoaded

I also tried this:

defp user_videos(user) do
query = (from u in user, select: u.videos, preload: [:category])
Repo.all(query)
end

But not working neither, so how can I preload them?

I thought I just needed to preload them at the pipe with |> Repo.preload(:category)

Also the strange thing is this started to crash with test, without the test it renders the index.html template without any issue

Showing Posts 1 to 10

dom

dom

It doesn’t matter what you do in the controller, since this test tests the view, not the controller.

videos = [%Rumbl.Video{id: "1", title: "dogs"}, %Rumbl.Video{id: "2", title: "cats"}]

You need to preload the category for these videos (in the test itself). Here that could mean just hardcoding it like:

videos = [%Rumbl.Video{id: "1", title: "dogs", category: %Rumbl.Category{name: “Animals”}}, ...]
lapinkoira

lapinkoira OP

Thanks that works, is there a way to make it more elegant handling it at the template? like “if video.category is nil dont ask for it”

jc00ke

jc00ke

I’m wondering this as well. It’s becoming a bear to preload associations. I’m close to writing a assert_without_assoc function :wink:

LostKobrakai

LostKobrakai

There’s Ecto.assoc_loaded?

jc00ke

jc00ke

Thanks, but that’s not quite what I’m looking for.

This is more of what I’m looking for (or at least what I think I want :wink:)

user = %User{id: "foo", widgets: [])
u = Users.get_user!(user.id) # this won't have widgets preloaded
# %User{id: "foo", widgets: Ecto.Association.NotLoaded< :widgets is not loaded>}
assert user == u

# or maybe I need to write
assert_without_assoc user == u
# where this will ignore fields where the assoc is not loaded.
LostKobrakai

LostKobrakai

Maybe this can shed some light:

jc00ke

jc00ke

Word, that makes sense, except that’s not how the test code from mix phx.gen.html|context is generated. In a pinch I compare ids, but that doesn’t feel right.

LostKobrakai

LostKobrakai

That code is meant as a starting point to get people going and not to be some holy grail of how things are to be done. Their focus lies quite a bit more on being accessable to people new to the framework.

That I talk about in the linked post: Compare IDs if you want to assert on the identity (Is it the same thing?). Compare fields to each other if you want to make sure creating / updating works correctly for them.

axelson

axelson

Scenic Core Team

I have a little helper function that might be useful to you:

  def clear_associations(%{__struct__: struct} = schema) do
    struct.__schema__(:associations)
    |> Enum.reduce(schema, fn association, schema ->
      %{schema | association => build_not_loaded(struct, association)}
    end)
  end

  defp build_not_loaded(struct, association) do
    %{
      cardinality: cardinality,
      field: field,
      owner: owner
    } = struct.__schema__(:association, association)

    %Ecto.Association.NotLoaded{
      __cardinality__: cardinality,
      __field__: field,
      __owner__: owner
    }
  end

With that you could write a assert_matches_without_associations function. Just call clear_associations/1 on each input and then assert equality.

Although (in line with what @LostKobrakai is saying) I actually use this assert_ids_match/2 helper more:

  @doc """
  Helper for checking that for two structs, or two lists of structs have the
  same id keys
  """
  def assert_ids_match(list1, list2) when is_list(list1) and is_list(list2) do
    list1_ids =
      list1
      |> Enum.map(& &1.id)
      |> Enum.sort()

    list2_ids =
      list2
      |> Enum.map(& &1.id)
      |> Enum.sort()

    assert list1_ids == list2_ids
  end

  def assert_ids_match(%{id: id1}, %{id: id2}) do
    assert id1 == id2
  end
jc00ke

jc00ke

Awesome, thanks for the code! I’ll probably head the assert_ids_match route soon; it’s good enough for me.

— All posts loaded —

Where Next? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
kszambelanczyk
Hello! Could someone please give me a help/sample code, how to delete a file from s3 using waffle/waffle_ecto from Phoenix app. I creat...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
nseaSeb
Hello, I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
FlyingNoodle
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge &amp; Solve. They are GUI (Emerge) and State management (S...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews