Skip to content

Instantly share code, notes, and snippets.

@Wigny
Last active August 29, 2023 00:21
Show Gist options
  • Save Wigny/0674ee19128ff1778ae8c1ddac7ab2ce to your computer and use it in GitHub Desktop.
Save Wigny/0674ee19128ff1778ae8c1ddac7ab2ce to your computer and use it in GitHub Desktop.
Ecto Duration ParameterizedType
Mix.install([
{:ecto_sql, "~> 3.10"},
{:timex, "~> 3.7"}
])
defmodule MyApp.Type.Duration do
use Ecto.ParameterizedType
alias Timex.Duration
defguardp is_duration(value) when is_struct(value, Duration)
@impl true
def type(_params), do: :float
@impl true
def init(opts) do
%{unit: opts[:unit]}
end
@impl true
def cast(value, _params) when is_nil(value), do: {:ok, nil}
def cast(value, _params) when is_duration(value), do: {:ok, value}
def cast(value, params) when is_float(value), do: {:ok, from_float(value, params.unit)}
@impl true
def load(value, _loader, _params) when is_nil(value), do: {:ok, nil}
def load(value, _loader, params) when is_float(value), do: {:ok, from_float(value, params.unit)}
@impl true
def dump(value, _dumper, _params) when is_nil(value), do: {:ok, nil}
def dump(value, _dumper, params) when is_duration(value), do: {:ok, to_float(value, params.unit)}
defp from_float(value, :microseconds), do: Duration.from_microseconds(value)
defp from_float(value, :milliseconds), do: Duration.from_milliseconds(value)
defp from_float(value, :seconds), do: Duration.from_seconds(value)
defp from_float(value, :minutes), do: Duration.from_minutes(value)
defp from_float(value, :hours), do: Duration.from_hours(value)
defp to_float(value, :microseconds), do: Duration.to_microseconds(value)
defp to_float(value, :milliseconds), do: Duration.to_milliseconds(value)
defp to_float(value, :seconds), do: Duration.to_seconds(value)
defp to_float(value, :minutes), do: Duration.to_minutes(value)
defp to_float(value, :hours), do: Duration.to_hours(value)
end
defmodule MyApp.Movies.Movie do
use Ecto.Schema
schema "movies" do
field :title, :string
field :running_time, MyApp.Type.Duration, unit: :minutes, source: :running_minutes
end
end
defmodule MyApp.Movies do
alias MyApp.Movies.Movie
def create_movie(attrs) do
%Movie{}
|> Ecto.Changeset.change(attrs)
|> Ecto.Changeset.apply_action(:insert)
end
end
IO.inspect MyApp.Movies.create_movie(%{title: "Barbie", running_time: Timex.Duration.from_hours(1.9)})
IO.inspect MyApp.Movies.create_movie(%{title: "Oppenheimer", running_time: Timex.Duration.from_minutes(180.15)})
@Wigny
Copy link
Author

Wigny commented Aug 23, 2023

Inspired by the Absinthe Duration Scalar.

@Wigny
Copy link
Author

Wigny commented Aug 23, 2023

Should be easily modified to load/cast the Postgres :interval data type instead of a float duration unit by just updating the type to this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment