Protocol Enumerable not implemented for ProjectName.Files.File of type Atom

The after_save function isn’t getting called because its arguments don’t match. Let’s walk through how that happens.

We’ll start by regrouping a little; a common idiom in Elixir is to make a function that does something to every element of a list by looping with a function that does something to one element:

  def create_files(files \\ [], after_save \\ &{:ok, &1}) do
    allFiles =
      for file <- files do
        create_file(file, after_save)
      end

    IO.inspect(allFiles)

    {:ok, files}
  end

  defp create_file(file, after_save) do
    %File{}
    |> File.changeset(file)
    |> after_save(after_save)
    |> Repo.insert()
  end

Now split apart the pipe in create_file:

defp create_file(file, after_save) do
  changeset = File.changeset(%File{}, file)

  changeset_after_callback = after_save(changeset, after_save)

  Repo.insert(changeset_after_callback)
end

Shapes:

  • changeset is an Ecto.Changeset struct

  • the first head of after_save does not match, so the second runs - thus changeset_after_callback is the same Ecto.Changeset struct as changeset.

  • Repo.insert can return either {:ok, %File{}} or {:error, %Ecto.Changeset{}}


Based on the shape that after_save expects in that first head, I suspect you want to move the after_save call to… after the save, done by Repo.insert:

defp create_file(file, after_save) do
  changeset = File.changeset(%File{}, file)

  insert_result = Repo.insert(changeset)

  after_save(insert_result, after_save)
end

Shapes in this version:

  • changeset is an Ecto.Changeset struct

  • Repo.insert can return either {:ok, %File{}} or {:error, %Ecto.Changeset{}} for insert_result

  • if the insert succeeded, the first head of after_save will match and call the after_save callback. Otherwise, the error will be returned as-is via the second head of after_save.

And then the code can be smooshed back together into a pipe:

defp create_file(file, after_save) do
  %File{}
  |> File.changeset(file)
  |> Repo.insert()
  |> after_save(after_save)
end

Edit: by the way, did you mean allFiles in the return value of create_files?

    {:ok, files}
  end
2 Likes