# EctoDripper
[](https://hex.pm/packages/ecto_dripper) [](https://travis-ci.com/Ninigi/ecto_dripper)
Provides an easy way to create composable ecto queries following a convention of `query_x(queryable, %{x: "asdf"})`, or `query_all(queryable, %{x: "asdf"})`.
## Installation
The package can be installed by adding `ecto_dripper` to your list of dependencies in `mix.exs`:
```elixir
def deps do
  [
    {:ecto_dripper, "~> 0.1.0"}
  ]
end
```
## Why not just write my queries in the already extremely readable ecto DSL??
Excellent question and I am glad you are not a mindless zombie :ok_hand:
If you don't know how to write queries in ecto, then this lib doesn't do anything for you but hide a few basic queries away. Don't use `EctoDripper` if you are not at least a little familiar with ecto queries yet, at some point you will have to use it anyways :fire:
I wrote this lib as part of another project when I realized I am following the same pattern over and over again, and that's all this lib does for you: **Streamline some repetetive tasks and unclutter your query modules.**
## Composable Queries
Composable in this case means "pipable", as in create a bunch of queries you can pipe together, or use the convenience function `query_all/2` to create queries from arguments.
Because of elixirs awesome pattern matching, we can just blindly pipe query functions together and only apply them if a certain key is in the arguments.
```elixir
args = %{this_arg: "asdf"}
args2 = %{that_arg: "asdf"}
# Does only query for :this_arg
MyApp.MySchema
|> MyApp.MyQuery.query_all(args)
# Does only query for :that_arg
MyApp.MySchema
|> MyApp.MyQuery.query_all(args2)
# Or use a specific query
MyApp.MySchema
|> MyApp.MyQuery.query_this_arg(args)
```
## Basic Usage
```elixir
defmodule MyApp.SomeQuery do
  use EctoDripper,
    composable_queries: [
      [:status, :==, :status],
      [:max_height, :>, :height],
      [:status_down, :status_down]
    ],
    standalone_queries: [
      [:small_with_status_up]
    ]
  def status_down(query, args)
  def status_down(query, %{status_down: true}) do
    from(
      i in query,
      where: i.status == ^"down"
    )
  end
  def status_down(query, %{status_down: _}) do
    from(
      i in query,
      where: i.status != ^"down"
    )
  end
  def small_with_status_up(query, _args) do
    from(
      i in query,
      where: i.status == ^"up", i.height <= 10
    )
  end
end
MyThing
|> MyApp.SomeQuery.query_all(%{status: "somewhere", max_height: 30})
# #Ecto.Query<from i in MyThing, where: i.status == ^"somewhere", i.height > ^30>
# and use it with your Repo
MyThing
|> MyApp.SomeQuery.query_all(%{status: "up", max_height: 30})
|> Repo.all()
# [%MyThing{}, ..]
```
## What's going on here? I'd like to know how this works before `__using__`
```elixir
# What my modules usually looks like without EctoDripper
defmodule MyApp.MyQuery do
  def query_all(query, args) do
    query
    |> query_status(args)
    |> query_name_or_identifier(args)
  end
  def query_status(query, args)
  def query_status(query, %{status: status}) do
    # Create query when status key is in args
    from(
      thing in query,
      where: thing.status == ^status
    )
  end
  def query_status(query, _args) do
    # Let the query "fall through" if no status key in args
    query
  end
  def query_name_or_identifier(query, args)
  def query_name_or_identifier(query, %{query_name_or_identifier: query_name_or_identifier}) do
    # Create query when query_name_or_identifier key is in args
    from(
      thing in query,
      where: thing.name == ^query_name_or_identifier or thing.identifier == ^query_name_or_identifier
    )
  end
  def query_name_or_identifier(query, _args) do
    # Let the query "fall through" if no query_name_or_identifier key in args
    query
  end
end
# When using EctoDripper
defmodule MyApp.MyQuery do
  use EctoDripper,
    composable_queries:[
      [:status, :==],
      [:name_or_identifier, :do_query_name_or_identifier]
    ]
  def do_query_name_or_identifier(query, args) do
    from(
      thing in query,
      where: thing.name == ^query_name_or_identifier or thing.identifier == ^query_name_or_identifier
    )
  end
end
```
## Slightly more advanced usage - aka "I am lazy, give me more convenience"
There are 2 different options, `composable_queries` and `standalone_queries`, both take a list of lists to create some
basic functions for you.
A function option can consist of 2 or 3 keywords. When read from left to right, they describe the query function.
For example with:
```elixir
use EctoDripper,
  composable_queries: [
    [:status, :==]
  ]
```
`[:status, :==]` will create a query, comparing the status field of your queryable with the value for the status key in the args:
```elixir
def query_status(query, %{status: _} = args) do
  for(
    thing in query,
    where: thing.status == ^args.status
  )
end
```
If you want your query argument different from the field name, you can do so by adding a third field to the option:
`[:my_status_thing, :==, :status]`
```elixir
def query_status(query, %{my_status_thing: _} = args) do
  for(
    thing in query,
    where: thing.status == ^args.my_status_thing
  )
end
```
`[:name_or_identifier, :my_handler]` calls your custom handler function when passed `name_or_identifier` in args.
```elixir
def name_or_identifier(query, %{name_or_identifier: _} = args) do
  MyModule.my_handler(query, args)
end
```
## Read more
[A quick tutorial for usage in phoenix](https://medium.com/@fabian.zitter/phoenix-with-ectodripper-a-tutorial-i-guess-1578e4152f62)
## Repo.insert(%Contribution{code: "def awesome"})
**Contributions are highly welcome**
1. Fork it
2. Create your feature branch (git checkout -b my-new-feature)
3. Commit your changes (git commit -am 'Add some feature')
4. Push to the branch (git push origin my-new-feature)
5. Create new Pull Request