When I upload a new video it returns a "no function clause matching" error

Viewed 211

I am using Elixir for a new website with video uploading. The videos save to the db but after uploading them it returns

no function clause matching in EngpinWeb.VideoController.persist_file/2

I am using Elixir 1.11.2 and Phoenix v1.5.7

This is the code in my controller that it says is returning the error.

 defp persist_file(video, %{path: temp_path}) do
   video_path = build_video_path(video)
    unless File.exists?(video_path) do
     video_path |> Path.dirname() |> File.mkdir_p()
     File.copy!(temp_path, video_path)
    end

But I haven't been able to find anything to deal with this persistence issue.

This is the rest of the whole controller

defmodule EngpinWeb.VideoController do
  2   use EngpinWeb, :controller
  3 
  4   import PhoenixVideoStream.Util, only: [build_video_path: 1]
  5 
  6   alias Engpin.Teachers
  7   alias Engpin.Teachers.Video
  8 
  9 
 10   def index(conn, _params) do
 11     videos = Teachers.list_videos()
 12     render(conn, "index.html", videos: videos)
 13   end
 14 
 15   def new(conn, _params) do
 16     changeset = Teachers.change_video(%Video{})
 17     render(conn, "new.html", changeset: changeset)
 18   end
 19 
 20   def create(conn, %{"video" => video_params}) do
 21     IO.inspect video_params
 22   changeset = Video.changeset(%Video{}, video_params)
 23   case Teachers.create_video(video_params) do
 24     {:ok, video} ->
 25       persist_file(video, video_params["video_file_id"])
 26 
 27       conn
 28       |> put_flash(:info, "Video created successfully.")
 29       |> redirect(to: Routes.video_path(conn, :show, :video))
 30     {:error, changeset} ->
 31       render(conn, "new.html", changeset: changeset)
 32     end
 33   end
 34 #def create(conn, %{"video" => video_params}) do
 35     #  case Teachers.create_video(video_params) do
 36     #    {:ok, video} ->
 37     #      conn
 38    #      |> put_flash(:info, "Video created successfully.")
 39    #      |> redirect(to: Routes.video_path(conn, :show, video))
 40   
 41     #    {:error, %Ecto.Changeset{} = changeset} ->
 42    #      render(conn, "new.html", changeset: changeset)
 43    #  end
 44   #end
 45 
 46   def show(conn, %{"id" => id}) do
 47     video = Teachers.get_video!(id)
 48     render(conn, "show.html", video: video)
 49   end
 50 
 51   def edit(conn, %{"id" => id}) do
 52     video = Teachers.get_video!(id)
 53     changeset = Teachers.change_video(video)
 54     render(conn, "edit.html", video: video, changeset: changeset)
 55   end
 56 
 57   def update(conn, %{"id" => id, "video" => video_params}) do
 58     video = Teachers.get_video!(id)
 59 
 60     case Teachers.update_video(video, video_params) do
 61       {:ok, video} ->
 62         conn
 63         |> put_flash(:info, "Video updated successfully.")
 64         |> redirect(to: Routes.video_path(conn, :show, video))
 65 
 66       {:error, %Ecto.Changeset{} = changeset} ->
 67         render(conn, "edit.html", video: video, changeset: changeset)
 68     end
 69   end
 70 
 71   def delete(conn, %{"id" => id}) do
 72     video = Teachers.get_video!(id)
 73     {:ok, _video} = Teachers.delete_video(video)
 74 
 75     conn
 76     |> put_flash(:info, "Video deleted successfully.")
 77     |> redirect(to: Routes.video_path(conn, :index))
 78   end
 79 
 80   defp persist_file(video, %{path: temp_path}) do
 81    video_path = build_video_path(video)
 82     unless File.exists?(video_path) do
 83      video_path |> Path.dirname() |> File.mkdir_p()
 84      File.copy!(temp_path, video_path)
 85     end
 86   end
 87 end

1 Answers

if your function defp persist_file(video, %{path: temp_path}) doesn't have a matching clause for the parameters you called it with, then find the call site for the function and look at the actual parameters that were passed in. In this case I think what's happening is that the second parameter is a map with string keys instead of symbols, so it doesn't match the path pattern in the function definition.

Here's an example:

iex(1)> %{foo: :bar} = %{:foo => :bar} 
%{foo: :bar}
iex(2)> %{foo: :bar} = %{"foo" => :bar}
** (MatchError) no match of right hand side value: %{"foo" => :bar}

In the specific case of your persist_file function, try it like this:

defp persist_file(video, %{"path" => temp_path}) do
  video_path = build_video_path(video)
  unless File.exists?(video_path) do
    video_path |> Path.dirname() |> File.mkdir_p()
    File.copy!(temp_path, video_path)
  end
end

notice that the map pattern has string keys, like the map pattern in the def create from your line 20.

Related