Undefined function error (fetch)

Hi there! I am getting this error:

 ** (UndefinedFunctionError) function BookList.UserSpace.Invitation.fetch/2 
is undefined (BookList.UserSpace.Invitation does not implement the Access behaviour)

I must be missing use or alias or something. The error comes after the line IO.inspect below.

defmodule BookListWeb.InvitationView do

  use BookListWeb, :view
  alias BookListWeb.InvitationView

  def render("index.json", %{invitations: invitations}) do
    IO.inspect invitations, label: "INV (XX)"
    %{data: render_many(invitations, InvitationView, "invitation.json")}
  end

....
1 Like

What exactly is printed by the inspect?

1 Like

This error happens when you try to do x[:y] where x is a struct, e.g.:

iex> Version.parse!("1.0.0")[:major]
** (UndefinedFunctionError) function Version.fetch/2 is undefined (Version does not implement the Access behaviour)
    (elixir) Version.fetch(#Version<1.0.0>, :major)
    (elixir) lib/access.ex:267: Access.get/3

Use x.y or Map.get/Map.fetch! instead.

2 Likes

He doesn’t even use square brackets in the code, so it’s not that easy…

Can you provide the full stack trace?

Here is the stack trace:

ex(7)> [info] GET /api/invitations
[debug] Processing with BookListWeb.InvitationController.index/2
  Parameters: %{}
  Pipelines: []
INVITATIONS YAY
INVITATIONS: %{}
[debug] QUERY OK source="invitations" db=0.2ms
SELECT i0."id", i0."invitee", i0."inviter", i0."group_name", i0."group_id", i0."status", i0."inserted_at", i0."updated_at" FROM "invitations" AS i0 []
INVITATIONS (*): [
  %BookList.UserSpace.Invitation{
    __meta__: #Ecto.Schema.Metadata<:loaded, "invitations">,
    group_id: 1,
    group_name: "Family",
    id: 1,
    inserted_at: ~N[2019-03-22 17:36:37.655163],
    invitee: "nguillemet",
    inviter: "jxxcarlson",
    status: "Waiting",
    updated_at: ~N[2019-03-22 17:36:37.655185]
  }
]
INV (XX): [
  %BookList.UserSpace.Invitation{
    __meta__: #Ecto.Schema.Metadata<:loaded, "invitations">,
    group_id: 1,
    group_name: "Family",
    id: 1,
    inserted_at: ~N[2019-03-22 17:36:37.655163],
    invitee: "nguillemet",
    inviter: "jxxcarlson",
    status: "Waiting",
    updated_at: ~N[2019-03-22 17:36:37.655185]
  }
]
[info] Sent 500 in 8ms
[error] #PID<0.802.0> running BookListWeb.Endpoint (cowboy_protocol) terminated
Server: localhost:4000 (http)
Request: GET /api/invitations
** (exit) an exception was raised:
    ** (UndefinedFunctionError) function BookList.UserSpace.Invitation.fetch/2 is undefined (BookList.UserSpace.Invitation does not implement the Access behaviour)
        (book_list) BookList.UserSpace.Invitation.fetch(%BookList.UserSpace.Invitation{__meta__: #Ecto.Schema.Metadata<:loaded, "invitations">, group_id: 1, group_name: "Family", id: 1, inserted_at: ~N[2019-03-22 17:36:37.655163], invitee: "nguillemet", inviter: "jxxcarlson", status: "Waiting", updated_at: ~N[2019-03-22 17:36:37.655185]}, "invitee")
        (elixir) lib/access.ex:322: Access.get/3
        (book_list) lib/book_list_web/views/invitation_view.ex:18: BookListWeb.InvitationView.render/2
        (elixir) lib/enum.ex:1327: Enum."-map/2-lists^map/1-0-"/2
        (book_list) lib/book_list_web/views/invitation_view.ex:8: BookListWeb.InvitationView.render/2
        (phoenix) lib/phoenix/view.ex:332: Phoenix.View.render_to_iodata/3
        (phoenix) lib/phoenix/controller.ex:740: Phoenix.Controller.do_render/4
        (book_list) lib/book_list_web/controllers/invitation_controller.ex:1: BookListWeb.InvitationController.action/2
        (book_list) lib/book_list_web/controllers/invitation_controller.ex:1: BookListWeb.InvitationController.phoenix_controller_pipeline/2
        (book_list) lib/book_list_web/endpoint.ex:1: BookListWeb.Endpoint.instrument/4
        (phoenix) lib/phoenix/router.ex:278: Phoenix.Router.__call__/1
        (book_list) lib/book_list_web/endpoint.ex:1: BookListWeb.Endpoint.plug_builder_call/2
        (book_list) lib/plug/debugger.ex:102: BookListWeb.Endpoint."call (overridable 3)"/2
        (book_list) lib/book_list_web/endpoint.ex:1: BookListWeb.Endpoint.call/2
        (plug) lib/plug/adapters/cowboy/handler.ex:16: Plug.Adapters.Cowboy.Handler.upgrade/4
        (cowboy) /Users/carlson/dev/app/booklistBackend/deps/cowboy/src/cowboy_protocol.erl:442: :cowboy_protocol.execute/4

And here is BookListWeb.InvitationView

defmodule BookListWeb.InvitationView do

  use BookListWeb, :view
  alias BookListWeb.InvitationView

  def render("index.json", %{invitations: invitations}) do
    IO.inspect invitations, label: "INV (XX)"
    %{data: render_many(invitations, InvitationView, "invitation.json")}
  end

  def render("invitation.json", %{invitation: invitation}) do
    %{invitee: invitation["invitee"],
      inviter: invitation["inviter"] || "",
      group_name: invitation["group_name"] || "",
      status: invitation["status"] || "",
      group_id: invitation["group_id"] || -1
    }
  end

  def render("error.json", %{error: str}) do
    %{error: str}
  end

end

Below is the controller.

defmodule BookListWeb.InvitationController do
  use BookListWeb, :controller

  alias BookList.UserSpace
  alias BookList.Repo
  alias BookList.UserSpace.Invitation

  action_fallback BookListWeb.FallbackController

  def index(conn, params) do
    invitations  = Repo.all(Invitation)
    render(conn, "index.json", invitations: invitations)
  end

end

I added the controller function below to BookListWeb.InvitationController. It works as expected.

  def invite(conn,  invitation) do
    IO.inspect invitation, label: "INVITATION"

    cs = Invitation.changeset(%Invitation{}, invitation)
    case cs.valid? do
      true ->
        Repo.insert(cs)
        render(conn, "invitation.json", %{invitation: invitation})
      false ->
        render(conn, "error.json", %{error: "Could not make invitation"})
    end
  end

Below is the error message again:

function BookList.UserSpace.Invitation.fetch/2 is undefined (BookList.UserSpace.Invitation does not implement the Access behaviour)

It refers to BookList.UserSpace.Invitation —don’t see what’s wrong, but the problem must be here.

defmodule BookList.UserSpace.Invitation do

  use Ecto.Schema

  import Ecto.Changeset
  import Ecto.Query

  alias BookList.Repo
  alias BookList.UserSpace.Invitation

  schema "invitations" do
    field :invitee, :string
    field :inviter, :string
    field :group_name, :string
    field :group_id, :integer
    field :status, :string

    timestamps()
  end

  def changeset(struct, params \\ %{}) do
    struct
    |> cast(params, [:invitee, :inviter, :group_name, :group_id, :status ])
    |> validate_required([:invitee, :inviter])
  end

end

that’s logger output, could you paste the full stacktrace from the exception? My guess is, the exception happened in a template, the controller code looks ok, and if so please attach that too.

actually nevermind, I see where the issue is:

def render("invitation.json", %{invitation: invitation}) do
    %{invitee: invitation["invitee"],

You’re doing invitation["invitee"] which is not gonna work for the reason I mentioned in previous message.

4 Likes

You are quite right – the code below now works. Thank you very much!!

  def render("invitation.json", %{invitation: invitation}) do
    %{invitee: invitation.invitee,
      inviter: invitation.inviter || "",
      group_name: invitation.group_name || "",
      status: invitation.status || "",
      group_id: invitation.group_id || -1
    }
  end
1 Like