diff --git a/CHANGELOG.md b/CHANGELOG.md index 25be7a6..f67e70f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## v0.8.0 (2018-05-10) +* (Enhancement) Use `NaiveDateTime` instead of `Ecto.DateTime`. +* (Dependency Update) Require `ecto ~> 2.1` + ## v0.7.0 (2017-03-10) * (Enhancement) Add `delete` override to the ArcEcto module. * (Dependency Update) Require `arc ~> 0.8.0` diff --git a/README.md b/README.md index 57598af..d003dea 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Add the latest stable release to your `mix.exs` file: ```elixir defp deps do [ - {:arc_ecto, "~> 0.7.0"} + {:arc_ecto, "~> 0.8.0"} ] end diff --git a/lib/arc_ecto/definition.ex b/lib/arc_ecto/definition.ex index ee1c149..c47d478 100644 --- a/lib/arc_ecto/definition.ex +++ b/lib/arc_ecto/definition.ex @@ -18,8 +18,8 @@ defmodule Arc.Ecto.Definition do url else case updated_at do - %Ecto.DateTime{} -> - stamp = :calendar.datetime_to_gregorian_seconds(Ecto.DateTime.to_erl(updated_at)) + %NaiveDateTime{} -> + stamp = :calendar.datetime_to_gregorian_seconds(NaiveDateTime.to_erl(updated_at)) case URI.parse(url).query do nil -> url <> "?v=#{stamp}" _ -> url <> "&v=#{stamp}" diff --git a/lib/arc_ecto/type.ex b/lib/arc_ecto/type.ex index 52cdd7a..12f0982 100644 --- a/lib/arc_ecto/type.ex +++ b/lib/arc_ecto/type.ex @@ -1,19 +1,25 @@ defmodule Arc.Ecto.Type do + @moduledoc false + require Logger + def type, do: :string @filename_with_timestamp ~r{^(.*)\?(\d+)$} - - # Support embeds_one/embeds_many - def cast(_definition, %{"file_name" => file, "updated_at" => updated_at}) do - {:ok, %{file_name: file, updated_at: updated_at}} - end + def cast(definition, %{file_name: file, updated_at: updated_at}) do cast(definition, %{"file_name" => file, "updated_at" => updated_at}) end + + def cast(_definition, %{"file_name" => file, "updated_at" => updated_at}) do + {:ok, %{file_name: file, updated_at: updated_at}} + end + def cast(definition, args) do case definition.store(args) do - {:ok, file} -> {:ok, %{file_name: file, updated_at: Ecto.DateTime.utc}} - _ -> :error + {:ok, file} -> {:ok, %{file_name: file, updated_at: NaiveDateTime.utc_now}} + error -> + Logger.error(inspect(error)) + :error end end @@ -29,9 +35,10 @@ defmodule Arc.Ecto.Type do updated_at = case gsec do gsec when is_binary(gsec) -> gsec - |> String.to_integer() - |> :calendar.gregorian_seconds_to_datetime() - |> Ecto.DateTime.from_erl() + |> String.to_integer + |> :calendar.gregorian_seconds_to_datetime + |> NaiveDateTime.from_erl! + _ -> nil end @@ -44,7 +51,7 @@ defmodule Arc.Ecto.Type do end def dump(_definition, %{file_name: file_name, updated_at: updated_at}) do - gsec = :calendar.datetime_to_gregorian_seconds(Ecto.DateTime.to_erl(updated_at)) + gsec = :calendar.datetime_to_gregorian_seconds(NaiveDateTime.to_erl(updated_at)) {:ok, "#{file_name}?#{gsec}"} end diff --git a/mix.exs b/mix.exs index 45484d7..494c724 100644 --- a/mix.exs +++ b/mix.exs @@ -1,7 +1,7 @@ defmodule Arc.Ecto.Mixfile do use Mix.Project - @version "0.7.0" + @version "0.8.0" def project do [app: :arc_ecto, @@ -46,7 +46,7 @@ defmodule Arc.Ecto.Mixfile do defp deps do [ {:arc, "~> 0.8.0"}, - {:ecto, "~> 2.0"}, + {:ecto, "~> 2.1"}, {:mock, "~> 0.1.1", only: :test}, {:ex_doc, ">= 0.0.0", only: :dev} ] diff --git a/mix.lock b/mix.lock index b577023..acd78f8 100644 --- a/mix.lock +++ b/mix.lock @@ -1,19 +1,19 @@ -%{"arc": {:hex, :arc, "0.8.0", "bb7cf8ea50f30f9c2bb357270c074def42a7ec6a3b4605be731cc5faf8fde6fd", [:mix], [{:ex_aws, "~> 1.1", [hex: :ex_aws, optional: true]}, {:httpoison, "~> 0.11", [hex: :httpoison, optional: false]}, {:poison, "~> 2.2 or ~> 3.1", [hex: :poison, optional: true]}, {:sweet_xml, "~> 0.6", [hex: :sweet_xml, optional: true]}]}, - "certifi": {:hex, :certifi, "1.0.0", "1c787a85b1855ba354f0b8920392c19aa1d06b0ee1362f9141279620a5be2039", [:rebar3], []}, - "decimal": {:hex, :decimal, "1.1.2", "79a769d4657b2d537b51ef3c02d29ab7141d2b486b516c109642d453ee08e00c", [:mix], []}, - "earmark": {:hex, :earmark, "1.0.1", "2c2cd903bfdc3de3f189bd9a8d4569a075b88a8981ded9a0d95672f6e2b63141", [:mix], []}, - "ecto": {:hex, :ecto, "2.0.5", "7f4c79ac41ffba1a4c032b69d7045489f0069c256de606523c65d9f8188e502d", [:mix], [{:db_connection, "~> 1.0-rc.4", [hex: :db_connection, optional: true]}, {:decimal, "~> 1.1.2 or ~> 1.2", [hex: :decimal, optional: false]}, {:mariaex, "~> 0.7.7", [hex: :mariaex, optional: true]}, {:poison, "~> 1.5 or ~> 2.0", [hex: :poison, optional: true]}, {:poolboy, "~> 1.5", [hex: :poolboy, optional: false]}, {:postgrex, "~> 0.12.0", [hex: :postgrex, optional: true]}, {:sbroker, "~> 1.0-beta", [hex: :sbroker, optional: true]}]}, +%{"arc": {:hex, :arc, "0.8.0", "bb7cf8ea50f30f9c2bb357270c074def42a7ec6a3b4605be731cc5faf8fde6fd", [:mix], [{:ex_aws, "~> 1.1", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:httpoison, "~> 0.11", [hex: :httpoison, repo: "hexpm", optional: false]}, {:poison, "~> 2.2 or ~> 3.1", [hex: :poison, repo: "hexpm", optional: true]}, {:sweet_xml, "~> 0.6", [hex: :sweet_xml, repo: "hexpm", optional: true]}], "hexpm"}, + "certifi": {:hex, :certifi, "1.0.0", "1c787a85b1855ba354f0b8920392c19aa1d06b0ee1362f9141279620a5be2039", [:rebar3], [], "hexpm"}, + "decimal": {:hex, :decimal, "1.4.0", "fac965ce71a46aab53d3a6ce45662806bdd708a4a95a65cde8a12eb0124a1333", [:mix], [], "hexpm"}, + "earmark": {:hex, :earmark, "1.0.1", "2c2cd903bfdc3de3f189bd9a8d4569a075b88a8981ded9a0d95672f6e2b63141", [:mix], [], "hexpm"}, + "ecto": {:hex, :ecto, "2.1.4", "d1ba932813ec0e0d9db481ef2c17777f1cefb11fc90fa7c142ff354972dfba7e", [:mix], [{:db_connection, "~> 1.1", [hex: :db_connection, repo: "hexpm", optional: true]}, {:decimal, "~> 1.2", [hex: :decimal, repo: "hexpm", optional: false]}, {:mariaex, "~> 0.8.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.13.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"}, "erlcloud": {:hex, :erlcloud, "0.9.2"}, - "ex_doc": {:hex, :ex_doc, "0.13.1", "658dbfc8cc5b0fac192f0f3254efe66ee6294200804a291549e0aeb052053bba", [:mix], [{:earmark, "~> 1.0", [hex: :earmark, optional: false]}]}, - "hackney": {:hex, :hackney, "1.7.1", "e238c52c5df3c3b16ce613d3a51c7220a784d734879b1e231c9babd433ac1cb4", [:rebar3], [{:certifi, "1.0.0", [hex: :certifi, optional: false]}, {:idna, "4.0.0", [hex: :idna, optional: false]}, {:metrics, "1.0.1", [hex: :metrics, optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, optional: false]}, {:ssl_verify_fun, "1.1.1", [hex: :ssl_verify_fun, optional: false]}]}, - "httpoison": {:hex, :httpoison, "0.11.1", "d06c571274c0e77b6cc50e548db3fd7779f611fbed6681fd60a331f66c143a0b", [:mix], [{:hackney, "~> 1.7.0", [hex: :hackney, optional: false]}]}, - "idna": {:hex, :idna, "4.0.0", "10aaa9f79d0b12cf0def53038547855b91144f1bfcc0ec73494f38bb7b9c4961", [:rebar3], []}, + "ex_doc": {:hex, :ex_doc, "0.13.1", "658dbfc8cc5b0fac192f0f3254efe66ee6294200804a291549e0aeb052053bba", [:mix], [{:earmark, "~> 1.0", [hex: :earmark, repo: "hexpm", optional: false]}], "hexpm"}, + "hackney": {:hex, :hackney, "1.7.1", "e238c52c5df3c3b16ce613d3a51c7220a784d734879b1e231c9babd433ac1cb4", [:rebar3], [{:certifi, "1.0.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "4.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"}, + "httpoison": {:hex, :httpoison, "0.11.1", "d06c571274c0e77b6cc50e548db3fd7779f611fbed6681fd60a331f66c143a0b", [:mix], [{:hackney, "~> 1.7.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"}, + "idna": {:hex, :idna, "4.0.0", "10aaa9f79d0b12cf0def53038547855b91144f1bfcc0ec73494f38bb7b9c4961", [:rebar3], [], "hexpm"}, "jsx": {:hex, :jsx, "2.1.1"}, "lhttpc": {:hex, :lhttpc, "1.3.0"}, - "meck": {:hex, :meck, "0.8.3", "4628a1334c69610c5bd558b04dc78d723d8ec5445c123856de34c77f462b5ee5", [:rebar], []}, - "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], []}, - "mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], []}, - "mock": {:hex, :mock, "0.1.1", "e21469ca27ba32aa7b18b61699db26f7a778171b21c0e5deb6f1218a53278574", [:mix], [{:meck, "~> 0.8.2", [hex: :meck, optional: false]}]}, + "meck": {:hex, :meck, "0.8.3", "4628a1334c69610c5bd558b04dc78d723d8ec5445c123856de34c77f462b5ee5", [:rebar], [], "hexpm"}, + "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"}, + "mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], [], "hexpm"}, + "mock": {:hex, :mock, "0.1.1", "e21469ca27ba32aa7b18b61699db26f7a778171b21c0e5deb6f1218a53278574", [:mix], [{:meck, "~> 0.8.2", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm"}, "mogrify": {:hex, :mogrify, "0.1.0"}, - "poolboy": {:hex, :poolboy, "1.5.1", "6b46163901cfd0a1b43d692657ed9d7e599853b3b21b95ae5ae0a777cf9b6ca8", [:rebar], []}, - "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.1", "28a4d65b7f59893bc2c7de786dec1e1555bd742d336043fe644ae956c3497fbe", [:make, :rebar], []}} + "poolboy": {:hex, :poolboy, "1.5.1", "6b46163901cfd0a1b43d692657ed9d7e599853b3b21b95ae5ae0a777cf9b6ca8", [:rebar], [], "hexpm"}, + "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.1", "28a4d65b7f59893bc2c7de786dec1e1555bd742d336043fe644ae956c3497fbe", [:make, :rebar], [], "hexpm"}} diff --git a/test/definition_test.exs b/test/definition_test.exs index e2930e8..b72b743 100644 --- a/test/definition_test.exs +++ b/test/definition_test.exs @@ -20,13 +20,13 @@ defmodule ArcTest.Ecto.Definition do end test "url appends timestamp to url with no query parameters" do - updated_at = Ecto.DateTime.from_erl({{2015, 1, 1}, {1, 1, 1}}) + updated_at = NaiveDateTime.from_erl!({{2015, 1, 1}, {1, 1, 1}}) url = DummyDefinition.url({%{file_name: "test.png", updated_at: updated_at}, :scope}, :original, []) assert url == "fallback?v=63587293261" end test "url appends timestamp to url with query parameters" do - updated_at = Ecto.DateTime.from_erl({{2015, 1, 1}, {1, 1, 1}}) + updated_at = NaiveDateTime.from_erl!({{2015, 1, 1}, {1, 1, 1}}) url = DummyDefinition.url({%{file_name: "test.png", updated_at: updated_at}, :scope}, :signed, []) assert url == "fallback?a=1&b=2&v=63587293261" end diff --git a/test/schema_test.exs b/test/schema_test.exs index 86f71a1..6c6bee4 100644 --- a/test/schema_test.exs +++ b/test/schema_test.exs @@ -48,50 +48,50 @@ defmodule ArcTest.Ecto.Schema do end def build_upload(path) do - %{__struct__: Plug.Upload, path: path, file_name: Path.basename(path)} + %{__struct__: Plug.Upload, path: path, filename: Path.basename(path)} end test "supports :invalid changeset" do cs = TestUser.changeset(%TestUser{}) assert cs.valid? == false assert cs.changes == %{} - assert cs.errors == [avatar: {"can't be blank", []}] + assert cs.errors == [avatar: {"can't be blank", [validation: :required]}] end - test_with_mock "cascades storage success into a valid change", DummyDefinition, [store: fn({%{__struct__: Plug.Upload, path: "/path/to/my/file.png", file_name: "file.png"}, %TestUser{}}) -> {:ok, "file.png"} end] do + test_with_mock "cascades storage success into a valid change", DummyDefinition, [store: fn({%{__struct__: Plug.Upload, path: "/path/to/my/file.png", filename: "file.png"}, %TestUser{}}) -> {:ok, "file.png"} end] do upload = build_upload("/path/to/my/file.png") cs = TestUser.changeset(%TestUser{}, %{"avatar" => upload}) assert cs.valid? %{file_name: "file.png", updated_at: _} = cs.changes.avatar end - test_with_mock "cascades storage error into an error", DummyDefinition, [store: fn({%{__struct__: Plug.Upload, path: "/path/to/my/file.png", file_name: "file.png"}, %TestUser{}}) -> {:error, :invalid_file} end] do + test_with_mock "cascades storage error into an error", DummyDefinition, [store: fn({%{__struct__: Plug.Upload, path: "/path/to/my/file.png", filename: "file.png"}, %TestUser{}}) -> {:error, :invalid_file} end] do upload = build_upload("/path/to/my/file.png") cs = TestUser.changeset(%TestUser{}, %{"avatar" => upload}) assert called DummyDefinition.store({upload, %TestUser{}}) assert cs.valid? == false - assert cs.errors == [avatar: {"is invalid", [type: ArcTest.Ecto.Schema.DummyDefinition.Type]}] + assert cs.errors == [avatar: {"is invalid", [type: ArcTest.Ecto.Schema.DummyDefinition.Type, validation: :cast]}] end - test_with_mock "converts changeset into schema", DummyDefinition, [store: fn({%{__struct__: Plug.Upload, path: "/path/to/my/file.png", file_name: "file.png"}, %TestUser{}}) -> {:error, :invalid_file} end] do + test_with_mock "converts changeset into schema", DummyDefinition, [store: fn({%{__struct__: Plug.Upload, path: "/path/to/my/file.png", filename: "file.png"}, %TestUser{}}) -> {:error, :invalid_file} end] do upload = build_upload("/path/to/my/file.png") TestUser.changeset(%TestUser{}, %{"avatar" => upload}) assert called DummyDefinition.store({upload, %TestUser{}}) end - test_with_mock "applies changes to schema", DummyDefinition, [store: fn({%{__struct__: Plug.Upload, path: "/path/to/my/file.png", file_name: "file.png"}, %TestUser{}}) -> {:error, :invalid_file} end] do + test_with_mock "applies changes to schema", DummyDefinition, [store: fn({%{__struct__: Plug.Upload, path: "/path/to/my/file.png", filename: "file.png"}, %TestUser{}}) -> {:error, :invalid_file} end] do upload = build_upload("/path/to/my/file.png") TestUser.changeset(%TestUser{}, %{"avatar" => upload, "first_name" => "test"}) assert called DummyDefinition.store({upload, %TestUser{first_name: "test"}}) end - test_with_mock "converts atom keys", DummyDefinition, [store: fn({%{__struct__: Plug.Upload, path: "/path/to/my/file.png", file_name: "file.png"}, %TestUser{}}) -> {:error, :invalid_file} end] do + test_with_mock "converts atom keys", DummyDefinition, [store: fn({%{__struct__: Plug.Upload, path: "/path/to/my/file.png", filename: "file.png"}, %TestUser{}}) -> {:error, :invalid_file} end] do upload = build_upload("/path/to/my/file.png") TestUser.changeset(%TestUser{}, %{avatar: upload}) assert called DummyDefinition.store({upload, %TestUser{}}) end - test_with_mock "casting nil attachments", DummyDefinition, [store: fn({%{__struct__: Plug.Upload, path: "/path/to/my/file.png", file_name: "file.png"}, %TestUser{}}) -> {:ok, "file.png"} end] do + test_with_mock "casting nil attachments", DummyDefinition, [store: fn({%{__struct__: Plug.Upload, path: "/path/to/my/file.png", filename: "file.png"}, %TestUser{}}) -> {:ok, "file.png"} end] do changeset = TestUser.changeset(%TestUser{}, %{"avatar" => build_upload("/path/to/my/file.png")}) changeset = TestUser.changeset2(changeset, %{"avatar" => nil}) assert nil == Ecto.Changeset.get_field(changeset, :avatar) diff --git a/test/type_test.exs b/test/type_test.exs index 4f5a7ef..a880231 100644 --- a/test/type_test.exs +++ b/test/type_test.exs @@ -7,20 +7,20 @@ defmodule ArcTest.Ecto.Type do end test "dumps filenames with timestamp" do - timestamp = Ecto.DateTime.cast!({{1970, 1, 1}, {0, 0, 0}}) + timestamp = NaiveDateTime.from_erl!({{1970, 1, 1}, {0, 0, 0}}) {:ok, value} = DummyDefinition.Type.dump(%{file_name: "file.png", updated_at: timestamp}) assert value == "file.png?62167219200" end test "loads filenames with timestamp" do - timestamp = Ecto.DateTime.cast!({{1970, 1, 1}, {0, 0, 0}}) + timestamp = NaiveDateTime.from_erl!({{1970, 1, 1}, {0, 0, 0}}) {:ok, value} = DummyDefinition.Type.load("file.png?62167219200") assert value == %{file_name: "file.png", updated_at: timestamp} end test "loads pathological filenames" do - timestamp = Ecto.DateTime.cast!({{1970, 1, 1}, {0, 0, 0}}) + timestamp = NaiveDateTime.from_erl!({{1970, 1, 1}, {0, 0, 0}}) {:ok, value} = DummyDefinition.Type.load("image.php?62167219200") assert value == %{file_name: "image.php", updated_at: timestamp} end @@ -31,7 +31,7 @@ defmodule ArcTest.Ecto.Type do end test "dumps with updated_at" do - timestamp = Ecto.DateTime.cast!({{1970, 1, 1}, {0, 0, 0}}) + timestamp = NaiveDateTime.from_erl!({{1970, 1, 1}, {0, 0, 0}}) value = %{file_name: "file.png", updated_at: timestamp} {:ok, dumped_type} = DummyDefinition.Type.dump(value) assert dumped_type == "file.png?62167219200"