diff --git a/README.md b/README.md index 5de68e5..b667cb9 100644 --- a/README.md +++ b/README.md @@ -46,12 +46,17 @@ end ### Runtime Configuration ```elixir -config :exqlite, default_chunk_size: 100 +config :exqlite, + default_chunk_size: 100, + type_extensions: [MyApp.TypeExtension] ``` * `default_chunk_size` - The chunk size that is used when multi-stepping when not specifying the chunk size explicitly. - +* `type_extensions`: An optional list of modules that implement the + Exqlite.TypeExtension behaviour, allowing types beyond the default set that + can be stored and retrieved from the database. + ### Compile-time Configuration In `config/config.exs`, diff --git a/lib/exqlite/sqlite3.ex b/lib/exqlite/sqlite3.ex index 64acd12..906b6c4 100644 --- a/lib/exqlite/sqlite3.ex +++ b/lib/exqlite/sqlite3.ex @@ -481,5 +481,28 @@ defmodule Exqlite.Sqlite3 do raise ArgumentError, "#{inspect(datetime)} is not in UTC" end - defp convert(val), do: val + defp convert(val) do + convert_with_type_extensions(type_extensions(), val) + end + + defp convert_with_type_extensions(nil, val), do: val + defp convert_with_type_extensions([], val), do: val + + defp convert_with_type_extensions([extension | other_extensions], val) do + case extension.convert(val) do + nil -> + convert_with_type_extensions(other_extensions, val) + + {:ok, converted} -> + converted + + {:error, reason} -> + raise ArgumentError, + "Failed conversion by TypeExtension #{extension}: #{inspect(val)}. Reason: #{inspect(reason)}." + end + end + + defp type_extensions do + Application.get_env(:exqlite, :type_extensions) + end end diff --git a/lib/exqlite/type_extension.ex b/lib/exqlite/type_extension.ex new file mode 100644 index 0000000..09e9d12 --- /dev/null +++ b/lib/exqlite/type_extension.ex @@ -0,0 +1,14 @@ +defmodule Exqlite.TypeExtension do + @moduledoc """ + A behaviour that defines the API for extensions providing custom data loaders and dumpers + for Ecto schemas. + """ + + @doc """ + Takes a value and convers it to data suitable for storage in the database. + + Returns a tagged :ok/:error tuple. If the value is not convertable by this + extension, returns nil. + """ + @callback convert(value :: term) :: {:ok, term} | {:error, reason :: term} | nil +end