Refactor: Optional params to a controller action

I have a create action that has the possibility of tagging on an extra param called index. If the index is passed I want to slightly change the behavior. Right now I’m essentially doing a copy and paste, but I’d like to use pattern matching and condense this code to just one action:

def create(conn, %{"image" => image_params, "row_id" => row_id, "index" => index}) do
    user = Auth.current_user(conn)

    with {:ok, row} <- Dreamhouse.Permissions.find_row(user, row_id),
         :ok <- Dreamhouse.Permissions.can?(user, :update, row)
    do
      resp = ImageService.upload_to_s3(image_params)

      changeset = Image.changeset(%Image{}, %{path: resp.path, row_id: row_id})
      create_image(conn, changeset, fn(_x) -> nil; end, index)
    end
  end

  def create(conn, %{"image" => image_params, "row_id" => row_id}) do
    user = Auth.current_user(conn)

    with {:ok, row} <- Dreamhouse.Permissions.find_row(user, row_id),
         :ok <- Dreamhouse.Permissions.can?(user, :update, row)
    do
      resp = ImageService.upload_to_s3(image_params)

      changeset = Image.changeset(%Image{}, %{path: resp.path, row_id: row_id})
      create_image(conn, changeset, fn(_x) -> nil; end, nil)
    end
  end

As you can see, the only difference between these two actions is index is being passed as the parameter to one of them. I’m curious if there is a way to default index to nil if it isn’t present in the request? Or is that not the right way to tackle this type of thing?

Pattern matching and optionality contradict each other. Either something matches a pattern, or it doesn’t. The solution here it to not use pattern matching.

def create(conn, %{"image" => image_params, "row_id" => row_id} = params) do
    index = Map.get(params, "index", nil)
    user = Auth.current_user(conn)

    with {:ok, row} <- Dreamhouse.Permissions.find_row(user, row_id),
         :ok <- Dreamhouse.Permissions.can?(user, :update, row)
    do
      resp = ImageService.upload_to_s3(image_params)

      changeset = Image.changeset(%Image{}, %{path: resp.path, row_id: row_id})
      create_image(conn, changeset, fn(_x) -> nil; end, index)
    end
  end
1 Like

Ah, Thanks! That makes sense.