Skip to content

Commit 2fa7fb2

Browse files
authored
Introduce Ecto.Adapters.SQLite3.Extension (#167)
This allows type extensions to be registered at runtime via the `:ecto_sqlite3, extensions:` configuration key, which takes a list of modules that implement the `Extension` behaviour.
1 parent c80241f commit 2fa7fb2

File tree

3 files changed

+79
-4
lines changed

3 files changed

+79
-4
lines changed

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,20 @@ config :my_app, MyApp.Repo,
4545
database: "path/to/my/database.db"
4646
```
4747

48+
## Type Extensions
49+
50+
Type extensions allow custom data types to be stored and retrieved from an SQLite3 database.
51+
52+
This is done by implementing a module with the `Ecto.Adapters.SQLite3.TypeExtension` behaviour which maps types to encoder and decoder functions. Type extensions are activated by adding them to the `ecto_sqlite3` configuration as a list of type extention modules assigned to the `type_extensions` key:
53+
54+
```elixir
55+
config :exqlite:
56+
type_extensions: [MyApp.TypeExtension]
57+
58+
config :ecto_sqlite3,
59+
type_extensions: [MyApp.TypeExtension]
60+
```
61+
4862
## Database Encryption
4963

5064
As of version 0.9, `exqlite` supports loading database engines at runtime rather than compiling `sqlite3.c` itself.

lib/ecto/adapters/sqlite3.ex

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -444,8 +444,8 @@ defmodule Ecto.Adapters.SQLite3 do
444444
end
445445

446446
@impl Ecto.Adapter
447-
def loaders(_, type) do
448-
[type]
447+
def loaders(primitive_type, ecto_type) do
448+
loader_from_extension(primitive_type, ecto_type)
449449
end
450450

451451
##
@@ -528,8 +528,8 @@ defmodule Ecto.Adapters.SQLite3 do
528528
end
529529

530530
@impl Ecto.Adapter
531-
def dumpers(_primitive, type) do
532-
[type]
531+
def dumpers(primitive_type, ecto_type) do
532+
dumper_from_extension(primitive_type, ecto_type)
533533
end
534534

535535
##
@@ -569,4 +569,34 @@ defmodule Ecto.Adapters.SQLite3 do
569569

570570
System.cmd(cmd, args, cmd_opts)
571571
end
572+
573+
defp extensions do
574+
Application.get_env(:ecto_sqlite3, :type_extensions, [])
575+
end
576+
577+
defp loader_from_extension(primitive_type, ecto_type) do
578+
loader_from_extension(extensions(), primitive_type, ecto_type)
579+
end
580+
581+
defp loader_from_extension([], _primitive_type, ecto_type), do: [ecto_type]
582+
583+
defp loader_from_extension([extension | other_extensions], primitive_type, ecto_type) do
584+
case extension.loaders(primitive_type, ecto_type) do
585+
nil -> loader_from_extension(other_extensions, primitive_type, ecto_type)
586+
loader -> loader
587+
end
588+
end
589+
590+
defp dumper_from_extension(primitive_type, ecto_type) do
591+
dumper_from_extension(extensions(), primitive_type, ecto_type)
592+
end
593+
594+
defp dumper_from_extension([], _primitive_type, ecto_type), do: [ecto_type]
595+
596+
defp dumper_from_extension([extension | other_extensions], primitive_type, ecto_type) do
597+
case extension.dumpers(primitive_type, ecto_type) do
598+
nil -> dumper_from_extension(other_extensions, primitive_type, ecto_type)
599+
dumper -> dumper
600+
end
601+
end
572602
end
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
defmodule Ecto.Adapters.SQLite3.TypeExtension do
2+
@moduledoc """
3+
A behaviour that defines the API for extensions providing custom data loaders and dumpers
4+
for Ecto schemas.
5+
"""
6+
7+
@type primitive_type :: atom | {:map, type :: atom}
8+
@type ecto_type :: atom
9+
10+
@doc """
11+
Takes a primitive type and, if it knows how to decode it into an
12+
appropriate Elixir data structure, reutrns a two-element list in
13+
the form `[(db_data :: any -> term), elixir_type :: atom]`.
14+
15+
The function that is the first element will be called whenever
16+
the `primitive_type` appears in a schema and data is fetched from
17+
the database for it.
18+
"""
19+
@callback loaders(primitive_type, ecto_type) :: list | nil
20+
21+
@doc """
22+
Takes a primitive type and, if it knows how to encode it for storage
23+
in the database, returns a two-element list in
24+
the form `[elixir_type :: atom, (term -> db_data :: any)]`.
25+
26+
The function that is the second element will be called whenever
27+
the `primitive_type` appears in a schema and data is about to be
28+
sent to the database for storage.
29+
"""
30+
@callback dumpers(ecto_type, primitive_type) :: list | nil
31+
end

0 commit comments

Comments
 (0)