View Source LibEcto (lib_ecto v0.3.9)

LibEcto is a simple wrapper for ecto, make it much easier for daily use.

Why LibEcto

Ecto is a great library, but it's a little bit verbose for daily use.

For example, imaging you have a schema like this:

defmodule Sample.Schema do
  use Ecto.Schema
  import Ecto.Changeset

  @primary_key {:id, LibEcto.KsuidType, autogenerate: true}
  schema "test" do
    field :name, :string
    field :value, :string

    timestamps()
  end

  def changeset(m, params) do
    m
    |> cast(params, [:name, :value])
  end
end

For most common use case, you need to write a lot of boilerplate code to do simple CRUD operation:

defmodule Sample.DB do

  alias Sample.Schema
  alias Sample.Repo
  import Ecto.Changeset

  def insert_one(params) do
    Schema.changeset(%Schema{}, params)
    |> Repo.insert()
  end

  def update_one(m, params) do
    m
    |> change(params)
    |> Repo.update()
  end

  def delete_one(m) do
    Repo.delete(m)
  end


  def get_by_id(id) do
    Repo.get(Schema, id, select: [:id, :name, :value])
  end

  def get_by_id_array(id_array) do
    Repo.all(from m in Schema, where: m.id in ^id_array, select: [:id, :name, :value])
  end

  def get_by_name(name) do
    Repo.get_by(Schema, name: name, select: [:id, :name, :value])
  end

  #... more boilerplate code
  # - get by name array
  # - get by page
  # - get by prefix

end

But!!!!!! With LibEcto, you can code like this:

defmodule Sample.DB do
    use LibEcto,
      repo: Sample.Repo,
      schema: Sample.Schema,
      columns: [
        :id,
        :name,
        :value
      ],
      filters: [
        :id,
        :name
      ]

    def filter(:id, dynamic, %{"id" => value}) when is_bitstring(value),
      do: {:ok, dynamic([m], ^dynamic and m.id == ^value)}

    def filter(:id, dynamic, %{"id" => value}) when is_list(value),
      do: {:ok, dynamic([m], ^dynamic and m.id in ^value)}

    def filter(:name, dynamic, %{"name" => value}) when is_bitstring(value),
      do: {:ok, dynamic([m], ^dynamic and m.name == ^value)}

    def filter(:name, dynamic, %{"name" => {"like", value}}) when is_bitstring(value),
      do: {:ok, dynamic([m], ^dynamic and like(m.name, ^value))}

    def filter(:name, dynamic, %{"name" => value}) when is_list(value),
      do: {:ok, dynamic([m], ^dynamic and m.name in ^value)}

    def filter(_, dynamic, _), do: {:ok, dynamic}


    def init_filter, do: dynamic([m], true)


    # you can use ecto's ability to build complicate query or update or transaction if GenericDB can't satisfy your need
    def other_complicated_query_or_update() do
      # do something
    end
end

LibEcto will generate all the boilerplate code for you, and you can focus on your business logic:

iex> Sample.DB.create_one(%{name: "test", value: "testv"})
{:ok, %Simple.Schema{id: "2JIebKci1ZgKenvhllJa3PMbydB", name: "test", value: "testv"}}

iex> Sample.DB.get_one(%{"name" => "test"})
{:ok, %Simple.Schema{id: "2JIebKci1ZgKenvhllJa3PMbydB", name: "test", value: "testv"}}

iex> Sample.DB.get_one(%{"name" => "not-exists"})
{:ok, nil}

iex> Sample.DB.get_one!(%{"name" => "not-exists"})
{:error, 404}

iex> {:ok, m} = Sample.DB.get_one(%{"name" => "test"})
iex> Sample.DB.update_one(m, name: "test2")
{:ok, %Simple.Schema{id: "2JIebKci1ZgKenvhllJa3PMbydB", name: "test2", value: "testv"}}

All supported functions:

  • create_one/1
  • get_one/1
  • get_one!/1
  • get_all/2
  • get_limit/4
  • count/1
  • exists?/1
  • get_by_page/5
  • update_one/2
  • delete_one/1
  • delete_all/1

For more usage details, please check the test cases, which covers all the supported functions.

V2

V2 is a complete rewrite of LibEcto, it's much more powerful and flexible. It simply breaks down the original complex macros into more reasonable ones. The only difference with LibEcto is the DB layer, Here's how to use it:

  defmodule Test.DB do
    @moduledoc false
    use LibEctoV2

    @repo Repo
    @schema Test.Schema
    @columns [:id, :name, :value]
    @filters [:id, :name]

    def filter(:id, dynamic, %{"id" => value}) when is_bitstring(value),
      do: {:ok, dynamic([m], ^dynamic and m.id == ^value)}

    def filter(:id, dynamic, %{"id" => value}) when is_list(value), do: {:ok, dynamic([m], ^dynamic and m.id in ^value)}

    def filter(:name, dynamic, %{"name" => value}) when is_bitstring(value),
      do: {:ok, dynamic([m], ^dynamic and m.name == ^value)}

    def filter(:name, dynamic, %{"name" => {"like", value}}) when is_bitstring(value),
      do: {:ok, dynamic([m], ^dynamic and like(m.name, ^value))}

    def filter(:name, dynamic, %{"name" => value}) when is_list(value),
      do: {:ok, dynamic([m], ^dynamic and m.name in ^value)}

    def filter(_, dynamic, _), do: {:ok, dynamic}

    def init_filter, do: dynamic([m], m.removed_at == 0)
  end

Installation

The package can be installed by adding lib_ecto to your list of dependencies in mix.exs:

def deps do
  [
    {:lib_ecto, "~> 0.3"}
  ]
end

Test

Test cases use ecto_sqlite3 as database.

Finished in 1.5 seconds (0.00s async, 1.5s sync)
17 tests, 0 failures

Randomized with seed 336538

Generating cover results ...

Percentage | Module
-----------|--------------------------
    40.00% | LibEcto.KsuidType
    78.95% | LibEcto.Ksuid
   100.00% | LibEcto
   100.00% | LibEcto.Base62
-----------|--------------------------
    76.19% | Total

Coverage test failed, threshold not met:

    Coverage:   76.19%
    Threshold:  90.00%

Generated HTML coverage results in "cover" directory