From 12d0085661a45cc8782a41cab096e15b60cf8572 Mon Sep 17 00:00:00 2001 From: Boris Kuznetsov Date: Tue, 6 Feb 2024 21:53:41 +0100 Subject: [PATCH] Add support for extensions to custom transformations --- lib/waffle/definition/versioning.ex | 15 ++++++++++--- lib/waffle/processor.ex | 23 ++++++++++++-------- test/actions/store_test.exs | 28 ++++++++++++++++++++++++ test/processor_test.exs | 33 ++++++++++++++++++++++++++++- 4 files changed, 86 insertions(+), 13 deletions(-) diff --git a/lib/waffle/definition/versioning.ex b/lib/waffle/definition/versioning.ex index 06ee5e6..fc1eac0 100644 --- a/lib/waffle/definition/versioning.ex +++ b/lib/waffle/definition/versioning.ex @@ -36,9 +36,18 @@ defmodule Waffle.Definition.Versioning do conversion = definition.transform(version, {file, scope}) case conversion do - :skip -> nil - {_, _, ext} -> "#{name}.#{ext}" - _ -> "#{name}#{Path.extname(file.file_name)}" + :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 diff --git a/lib/waffle/processor.ex b/lib/waffle/processor.ex index 129cf1c..47befaa 100644 --- a/lib/waffle/processor.ex +++ b/lib/waffle/processor.ex @@ -39,6 +39,9 @@ defmodule Waffle.Processor do transformation as elixir function, [read more about custom transformations](custom_transformation.livemd) + * `{transform/2, fn version, file -> :png end}` - A custom + transformation converting a file into a different extension + ## ImageMagick transformations As images are one of the most commonly uploaded filetypes, Waffle @@ -149,11 +152,22 @@ defmodule Waffle.Processor do apply_transformation(file, transform, version) end + @spec apply_transformation( + Waffle.File.t(), + (Waffle.File.t() -> {:ok, Waffle.File.t()} | {:error, String.t()}), + atom() + ) :: {:ok, Waffle.File.t()} | {:error, String.t()} + defp apply_transformation(_, :skip, _), do: {:ok, nil} defp apply_transformation(file, :noaction, _), do: {:ok, file} # Deprecated defp apply_transformation(file, {:noaction}, _), do: {:ok, file} + defp apply_transformation(file, func, version) when is_function(func), do: func.(version, file) + + defp apply_transformation(file, {func, _}, version) when is_function(func), + do: func.(version, file) + defp apply_transformation(file, {cmd, conversion}, _) do Convert.apply(cmd, file, conversion) end @@ -161,13 +175,4 @@ defmodule Waffle.Processor do defp apply_transformation(file, {cmd, conversion, extension}, _) do Convert.apply(cmd, file, conversion, extension) end - - @spec apply_transformation( - Waffle.File.t(), - (Waffle.File.t() -> {:ok, Waffle.File.t()} | {:error, String.t()}), - atom() - ) :: {:ok, Waffle.File.t()} | {:error, String.t()} - defp apply_transformation(file, func, version) when is_function(func) do - func.(version, file) - end end diff --git a/test/actions/store_test.exs b/test/actions/store_test.exs index 439c8ab..416276d 100644 --- a/test/actions/store_test.exs +++ b/test/actions/store_test.exs @@ -18,6 +18,25 @@ defmodule WaffleTest.Actions.Store do def __versions, do: [:original, :thumb, :skipped] end + defmodule DummyDefinitionWithExtension do + use Waffle.Actions.Store + use Waffle.Definition.Storage + + def validate({file, _}), do: String.ends_with?(file.file_name, ".png") + + def transform(:convert_to_jpg, _), + do: {:convert, "-format jpg", :jpg} + + def transform(:custom_to_jpg, {file, _}) do + { + fn _, _ -> {:ok, file} end, + fn _, _ -> :jpg end + } + end + + def __versions, do: [:convert_to_jpg, :custom_to_jpg] + end + defmodule DummyDefinitionWithHeaders do use Waffle.Actions.Store use Waffle.Definition.Storage @@ -36,6 +55,15 @@ defmodule WaffleTest.Actions.Store do def __versions, do: [:original, :thumb, :skipped] end + test "custom transformations change a file extension" do + with_mock Waffle.Storage.S3, + put: fn DummyDefinitionWithExtension, _, {%{file_name: "image.jpg", path: _}, nil} -> + {:ok, "resp"} + end do + assert DummyDefinitionWithExtension.store(@img) == {:ok, "image.png"} + end + end + test "checks file existence" do assert DummyDefinition.store("non-existent-file.png") == {:error, :invalid_file_path} end diff --git a/test/processor_test.exs b/test/processor_test.exs index a35a6df..3c58421 100644 --- a/test/processor_test.exs +++ b/test/processor_test.exs @@ -31,8 +31,23 @@ defmodule WaffleTest.Processor do end end + def transform(:custom_with_ext, _) do + {&transform_custom/2, &transform_custom_ext/2} + end + def transform(:skipped, _), do: :skip + defp transform_custom(version, file) do + Convert.apply( + :convert, + file, + fn input, output -> [input, "-strip", "-thumbnail", "1x1", output] end, + transform_custom_ext(version, file) + ) + end + + defp transform_custom_ext(_, _), do: :jpg + def __versions, do: [:original, :thumb] end @@ -119,7 +134,7 @@ defmodule WaffleTest.Processor do cleanup(new_file.path) end - test "transforms with custom function" do + test "transforms with a custom function" do {:ok, new_file} = Waffle.Processor.process( DummyDefinition, @@ -134,6 +149,22 @@ defmodule WaffleTest.Processor do cleanup(new_file.path) end + test "transforms with custom functions" do + {:ok, new_file} = + Waffle.Processor.process( + DummyDefinition, + :custom_with_ext, + {Waffle.File.new(@img, DummyDefinition), nil} + ) + + assert new_file.path != @img + # original file untouched + assert "128x128" == geometry(@img) + assert "1x1" == geometry(new_file.path) + assert Path.extname(new_file.path) == ".jpg" + cleanup(new_file.path) + end + test "transforms a file given as a binary" do img_binary = File.read!(@img)