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
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
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
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
asweet-confluent
I recently noticed that Elixir’s Logger defaults its primary log level to :debug when no :logger, :level application configuration is pre...
New

Other Trending Topics Top

JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
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
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
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

Latest on Elixir Forum

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews