# `BoxTurtle.Schema.Query`
[🔗](https://github.com/twajjo/box_turtle/blob/v0.1.0/lib/schema/query.ex#L1)

Generates the standard read functions of `BoxTurtle.Schema.Query.API` for an Ecto schema.

Given an Ecto schema `MyApp.Blog.Post` (with `title`, `status`, timestamps, and
`belongs_to :author, MyApp.Blog.Author`), define a sibling module:

    defmodule MyApp.Blog.Post.Query do
      use BoxTurtle.Schema.Query,
        repo: MyApp.Repo,
        default_sort: [desc: :inserted_at, desc: :id],
        default_preloads: [:author]
    end

and call it from your context:

    defmodule MyApp.Blog do
      alias MyApp.Blog.Post

      # Second page of published posts, newest first, each with its author (one query).
      def published_posts(page), do: Post.Query.list!(filters: [status: "published"], page: page)

      # [%{status: "draft", count: 3}, %{status: "published", count: 12}]
      def post_counts_by_status, do: Post.Query.count!(group_by: [:status])

      # {:ok, %Post{}} or {:error, :not_found}
      def fetch_post(id), do: Post.Query.get(id)
    end

See `BoxTurtle.Schema.Query.API` for the generated functions and their call-time options, and the
Schema guide for a walkthrough.

## Options

  * `:repo` - the `Ecto.Repo` to query. Required.
  * `:schema` - the schema module. Defaults to the parent of the using module (`MyApp.Blog.Post` above).
  * `:primary_key` - defaults to `:id`.
  * `:default_sort` - sort applied to lists unless overridden. Defaults to `[primary_key]`.
  * `:default_preloads` - associations to preload. Defaults to `[]`.
  * `:default_distinct` - columns for `DISTINCT ON` (PostgreSQL only: with other adapters the module
    fails to compile). Defaults to `[]`.
  * `:filter_module` - a `BoxTurtle.Schema.Filters.API` implementation. Defaults to `BoxTurtle.Schema.Filters`.
  * `:sorting_module` - a `BoxTurtle.Schema.Sort.API` implementation. Defaults to `BoxTurtle.Schema.Sort`.
  * `:page_size` - rows per page when `:page` is a number. Defaults to 40.
  * `:delegates` - `[{DelegateModule, %Pattern{}}]`, see `delegate/1`.
  * `:ownership_scope` - module passed to delegates as `:row_module`. Defaults to each delegate module.

## Extensions

`extend/2` and `delegate/1` generate the optional `*_by(pattern, opts \\ [])` functions. Each one gets:

  1. a clause per `:delegates` entry whose module implements the function, forwarding `%Pattern{}` arguments
     to it with this module's settings injected into the options, so the delegate queries this schema;
  2. for `count_by`, `list_by` and `query_by!` (and their variants), a `nil` clause that falls back to the
     unscoped `count`, `list` or `query!`;
  3. with `extend/2`, your own clauses.

A delegate holds a scope shared by several schemas, here "rows belonging to an author". It is an
ordinary Query module; when called through a delegating module, `list/1` and `count/1` run
against the delegating module's schema:

    defmodule MyApp.Blog.ByAuthor do
      use BoxTurtle.Schema.Query, repo: MyApp.Repo, schema: MyApp.Blog.Author

      extend :list_by do
        def list_by(%MyApp.Blog.Author{id: id}, opts), do: list(add_filters(opts, author_id: id))
      end

      extend :count_by do
        def count_by(%MyApp.Blog.Author{id: id}, opts), do: count(add_filters(opts, author_id: id))
      end
    end

    defmodule MyApp.Blog.Post.Query do
      use BoxTurtle.Schema.Query,
        repo: MyApp.Repo,
        delegates: [{MyApp.Blog.ByAuthor, %MyApp.Blog.Author{}}]

      # list_by/2 and count_by/2 for %Author{} patterns (and nil), from the delegate.
      delegate [:list_by, :count_by]

      # Your own clauses (posts here have a :slug column).
      extend :get_by! do
        def get_by!(slug, opts) when is_binary(slug), do: get!(%{slug: slug}, opts)
      end
    end

    author = MyApp.Repo.get!(MyApp.Blog.Author, author_id)
    {:ok, posts} = MyApp.Blog.Post.Query.list_by(author, filters: [status: "published"])
    {:ok, all_posts} = MyApp.Blog.Post.Query.list_by(nil)
    post = MyApp.Blog.Post.Query.get_by!("hello-world")

# `delegate`
*macro* 

Defines each of the given extension functions with only their delegate and `nil` clauses.

# `extend`
*macro* 

Defines the extension function `extension` (one of `[:count_by, :count_by!, :get_by, :get_by!, :list_by, :list_by!, :query_by!]`) with its delegate
and `nil` clauses, followed by the clauses in `block`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
