lib/waffle/definition/versioning.ex

defmodule Waffle.Definition.Versioning do
  @moduledoc ~S"""
  Define proper name for a version.

  It may be undesirable to retain original filenames (eg, it may
  contain personally identifiable information, vulgarity,
  vulnerabilities with Unicode characters, etc).

  You may specify the destination filename for uploaded versions
  through your definition module.

  A common pattern is to combine directories scoped to a particular
  model's primary key, along with static filenames. (eg:
  `user_avatars/1/thumb.png`).

      # To retain the original filename, but prefix the version and user id:
      def filename(version, {file, scope}) do
        file_name = Path.basename(file.file_name, Path.extname(file.file_name))
        "#{scope.id}_#{version}_#{file_name}"
      end

      # To make the destination file the same as the version:
      def filename(version, _), do: version

  """

  defmacro __using__(_) do
    quote do
      @versions [:original]
      @before_compile Waffle.Definition.Versioning
    end
  end

  def resolve_file_name(definition, version, {file, scope}) do
    name = definition.filename(version, {file, scope})
    conversion = definition.transform(version, {file, scope})

    case conversion do
      :skip ->
        nil

      {_, _, ext} ->
        [name, ext] |> Enum.join(".")

      {fn_transform, fn_extension}
      when is_function(fn_transform) and is_function(fn_extension) ->
        [name, fn_extension.(version, file)] |> Enum.join(".")

      _ ->
        [name, Path.extname(file.file_name)] |> Enum.join()
    end
  end

  defmacro __before_compile__(_env) do
    quote do
      def transform(_, _), do: :noaction
      def __versions, do: @versions
    end
  end
end