diff --git a/README.md b/README.md index cf928bf..c632cb2 100644 --- a/README.md +++ b/README.md @@ -1,55 +1,38 @@ +![Logo](https://github.com/ash-project/ash/blob/main/logos/cropped-for-header-black-text.png?raw=true#gh-light-mode-only) +![Logo](https://github.com/ash-project/ash/blob/main/logos/cropped-for-header-white-text.png?raw=true#gh-dark-mojde-only) + +[![CI](https://github.com/ash-project/ash_sqlite/actions/workflows/elixir.yml/badge.svg)](https://github.com/ash-project/ash_sqlite/actions/workflows/elixir.yml) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) +[![Hex version badge](https://img.shields.io/hexpm/v/ash_sqlite.svg)](https://hex.pm/packages/ash_sqlite) +[![Hexdocs badge](https://img.shields.io/badge/docs-hexdocs-purple)](https://hexdocs.pm/ash_sqlite) + # AshSqlite -![Elixir CI](https://github.com/ash-project/ash_sqlite/workflows/Elixir%20CI/badge.svg) -[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) -[![Coverage Status](https://coveralls.io/repos/github/ash-project/ash_sqlite/badge.svg?branch=main)](https://coveralls.io/github/ash-project/ash_sqlite?branch=main) -[![Hex version badge](https://img.shields.io/hexpm/v/ash_sqlite.svg)](https://hex.pm/packages/ash_sqlite) +Welcome! `AshSqlite` is the SQLite data layer for [Ash Framework](https://hexdocs.pm/ash). -## Notice: Beta +## Tutorials -This is a newly released library. You can expect some hiccups here and there. Please report any issues you find! +- [Get Started](documentation/tutorials/getting-started-with-ash-sqlite.md) -## DSL +## Topics -See the DSL documentation in `AshSqlite.DataLayer` for DSL documentation +- [What is AshSqlite?](documentation/topics/about-ash-sqlite/what-is-ash-sqlite.md) -## Usage +### Resources -Add `ash_sqlite` to your `mix.exs` file. +- [References](documentation/topics/resources/references.md) +- [Polymorphic Resources](documentation/topics/resources/polymorphic-resources.md) -```elixir -{:ash_sqlite, "~> 0.1.2-rc.0"} -``` +### Development -To use this data layer, you need to chage your Ecto Repo's from `use Ecto.Repo`, to `use Sqlite.Repo`. because AshSqlite adds functionality to Ecto Repos. +- [Migrations and tasks](documentation/topics/development/migrations-and-tasks.md) +- [Testing](documentation/topics/development/testing.md) -Then, configure each of your `Ash.Resource` resources by adding `use Ash.Resource, data_layer: AshSqlite.DataLayer` like so: +### Advanced -```elixir -defmodule MyApp.SomeResource do - use Ash.Resource, domain: MyDomain, data_layer: AshSqlite.DataLayer +- [Expressions](documentation/topics/advanced/expressions.md) +- [Manual Relationships](documentation/topics/advanced/manual-relationships.md) - sqlite do - repo MyApp.Repo - table "table_name" - end +## Reference - attributes do - # ... Attribute definitions - end -end -``` - -## Generating Migrations - -See the documentation for `Mix.Tasks.AshSqlite.GenerateMigrations` for how to generate migrations from your resources - -# Contributors - -Ash is made possible by its excellent community! - - - - - -[Become a contributor](https://ash-hq.org/docs/guides/ash/latest/how_to/contribute.md) +- [AshSqlite.DataLayer DSL](documentation/dsls/DSL:-AshSqlite.DataLayer.md) diff --git a/config/config.exs b/config/config.exs index 9765e4f..7fd1cae 100644 --- a/config/config.exs +++ b/config/config.exs @@ -10,7 +10,10 @@ if Mix.env() == :dev do manage_mix_version?: true, # Instructs the tool to manage the version in your README.md # Pass in `true` to use `"README.md"` or a string to customize - manage_readme_version: ["README.md", "documentation/tutorials/get-started-with-sqlite.md"], + manage_readme_version: [ + "README.md", + "documentation/tutorials/getting-started-with-ash-sqlite.md" + ], version_tag_prefix: "v" end diff --git a/documentation/dsls/DSL:-AshSqlite.DataLayer.md b/documentation/dsls/DSL:-AshSqlite.DataLayer.md index 8236e7c..0d68638 100644 --- a/documentation/dsls/DSL:-AshSqlite.DataLayer.md +++ b/documentation/dsls/DSL:-AshSqlite.DataLayer.md @@ -47,7 +47,7 @@ end | [`foreign_key_names`](#sqlite-foreign_key_names){: #sqlite-foreign_key_names } | `list({atom, String.t} \| {String.t, String.t})` | `[]` | A list of foreign keys that could raise errors, or an mfa to a function that takes a changeset and returns a list. In the format: `{:key, "name_of_constraint"}` or `{:key, "name_of_constraint", "custom error message"}` | | [`migration_ignore_attributes`](#sqlite-migration_ignore_attributes){: #sqlite-migration_ignore_attributes } | `list(atom)` | `[]` | A list of attributes that will be ignored when generating migrations. | | [`table`](#sqlite-table){: #sqlite-table } | `String.t` | | The table to store and read the resource from. If this is changed, the migration generator will not remove the old table. | -| [`polymorphic?`](#sqlite-polymorphic?){: #sqlite-polymorphic? } | `boolean` | `false` | Declares this resource as polymorphic. See the [polymorphic resources guide](/documentation/topics/polymorphic_resources.md) for more. | +| [`polymorphic?`](#sqlite-polymorphic?){: #sqlite-polymorphic? } | `boolean` | `false` | Declares this resource as polymorphic. See the [polymorphic resources guide](/documentation/topics/resources/polymorphic-resources.md) for more. | ## sqlite.custom_indexes diff --git a/documentation/how_to/using-fragments.md b/documentation/how_to/using-fragments.md deleted file mode 100644 index f03d881..0000000 --- a/documentation/how_to/using-fragments.md +++ /dev/null @@ -1,25 +0,0 @@ -# Using Fragments - -Fragments allow you to use arbitrary sqlite expressions in your queries. Fragments can often be an escape hatch to allow you to do things that don't have something officially supported with Ash. - -## Examples - -Use simple expressions - -```elixir -fragment("? / ?", points, count) -``` - -Call functions - -```elixir -fragment("repeat('hello', 4)") -``` - -Use entire queries - -```elixir -fragment("points > (SELECT SUM(points) FROM games WHERE user_id = ? AND id != ?)", user_id, id) -``` - -Using entire queries like the above is a last resort, but can often help us avoid having to add extra structure unnecessarily. diff --git a/documentation/topics/about-ash-sqlite/what-is-ash-sqlite.md b/documentation/topics/about-ash-sqlite/what-is-ash-sqlite.md new file mode 100644 index 0000000..b77be02 --- /dev/null +++ b/documentation/topics/about-ash-sqlite/what-is-ash-sqlite.md @@ -0,0 +1,34 @@ +# What is AshSqlite? + +AshSqlite is the SQLite `Ash.DataLayer` for [Ash Framework](https://hexdocs.pm/ash). This doesn't have all of the features of [AshPostgres](https://hexdocs.pm/ash_postgres), but it does support most of the features of Ash data layers. The main feature missing is Aggregate support. + +Use this to persist records in a SQLite table. For example, the resource below would be persisted in a table called `tweets`: + +```elixir +defmodule MyApp.Tweet do + use Ash.Resource, + data_layer: AshSQLite.DataLayer + + attributes do + integer_primary_key :id + attribute :text, :string + end + + relationships do + belongs_to :author, MyApp.User + end + + sqlite do + table "tweets" + repo MyApp.Repo + end +end +``` + +The table might look like this: + +| id | text | author_id | +| --- | --------------- | --------- | +| 1 | "Hello, world!" | 1 | + +Creating records would add to the table, destroying records would remove from the table, and updating records would update the table. diff --git a/documentation/topics/advanced/expressions.md b/documentation/topics/advanced/expressions.md new file mode 100644 index 0000000..f377eed --- /dev/null +++ b/documentation/topics/advanced/expressions.md @@ -0,0 +1,61 @@ +# Expressions + +In addition to the expressions listed in the [Ash expressions guide](https://hexdocs.pm/ash/expressions.html), AshSqlite provides the following expressions + +# Fragments + +Fragments allow you to use arbitrary sqlite expressions in your queries. Fragments can often be an escape hatch to allow you to do things that don't have something officially supported with Ash. + +### Examples + +#### Simple expressions + +```elixir +fragment("? / ?", points, count) +``` + +#### Calling functions + +```elixir +fragment("repeat('hello', 4)") +``` + +#### Using entire queries + +```elixir +fragment("points > (SELECT SUM(points) FROM games WHERE user_id = ? AND id != ?)", user_id, id) +``` + +> ### a last resport {: .warning} +> +> Using entire queries as shown above is a last resort, but can sometimes be the best way to accomplish a given task. + +#### In calculations + +```elixir +calculations do + calculate :lower_name, :string, expr( + fragment("LOWER(?)", name) + ) +end +``` + +#### In migrations + +```elixir +create table(:managers, primary_key: false) do + add :id, :uuid, null: false, default: fragment("UUID_GENERATE_V4()"), primary_key: true +end +``` + +## Like + +These wrap the sqlite builtin like operator + +Please be aware, these match _patterns_ not raw text. Use `contains/1` if you want to match text without supporting patterns, i.e `%` and `_` have semantic meaning! + +For example: + +```elixir +Ash.Query.filter(User, like(name, "%obo%")) # name contains obo anywhere in the string, case sensitively +``` diff --git a/documentation/how_to/join-manual-relationships.md b/documentation/topics/advanced/manual-relationships.md similarity index 100% rename from documentation/how_to/join-manual-relationships.md rename to documentation/topics/advanced/manual-relationships.md diff --git a/documentation/topics/migrations_and_tasks.md b/documentation/topics/development/migrations-and-tasks.md similarity index 100% rename from documentation/topics/migrations_and_tasks.md rename to documentation/topics/development/migrations-and-tasks.md diff --git a/documentation/how_to/test-with-sqlite.md b/documentation/topics/development/testing.md similarity index 100% rename from documentation/how_to/test-with-sqlite.md rename to documentation/topics/development/testing.md diff --git a/documentation/topics/polymorphic_resources.md b/documentation/topics/resources/polymorphic-resources.md similarity index 100% rename from documentation/topics/polymorphic_resources.md rename to documentation/topics/resources/polymorphic-resources.md diff --git a/documentation/topics/references.md b/documentation/topics/resources/references.md similarity index 100% rename from documentation/topics/references.md rename to documentation/topics/resources/references.md diff --git a/documentation/topics/sqlite-expressions.md b/documentation/topics/sqlite-expressions.md deleted file mode 100644 index 8f900da..0000000 --- a/documentation/topics/sqlite-expressions.md +++ /dev/null @@ -1,24 +0,0 @@ -# Sqlite Expressions - -In addition to the expressions listed in the [Ash expressions guide](https://hexdocs.pm/ash/expressions.html), AshSqlite provides the following expressions - -## Fragments -`fragment` allows you to embed raw sql into the query. Use question marks to interpolate values from the outer expression. - -For example: - -```elixir -Ash.Query.filter(User, fragment("? IS NOT NULL", first_name)) -``` - -# Like - -This wraps the builtin sqlite `LIKE` operator. - -Please be aware, these match *patterns* not raw text. Use `contains/1` if you want to match text without supporting patterns, i.e `%` and `_` have semantic meaning! - -For example: - -```elixir -Ash.Query.filter(User, like(name, "%obo%")) # name contains obo anywhere in the string, case sensitively -``` diff --git a/documentation/tutorials/get-started-with-sqlite.md b/documentation/tutorials/getting-started-with-ash-sqlite.md similarity index 99% rename from documentation/tutorials/get-started-with-sqlite.md rename to documentation/tutorials/getting-started-with-ash-sqlite.md index 21226d7..8a883a1 100644 --- a/documentation/tutorials/get-started-with-sqlite.md +++ b/documentation/tutorials/getting-started-with-ash-sqlite.md @@ -1,4 +1,4 @@ -# Get Started With Sqlite +# Getting Started With AshSqlite ## Goals diff --git a/lib/ash_sqlite.ex b/lib/ash_sqlite.ex index 59404f2..e09956f 100644 --- a/lib/ash_sqlite.ex +++ b/lib/ash_sqlite.ex @@ -2,6 +2,6 @@ defmodule AshSqlite do @moduledoc """ The AshSqlite extension gives you tools to map a resource to a sqlite database table. - For more, check out the [getting started guide](/documentation/tutorials/get-started-with-sqlite.md) + For more, check out the [getting started guide](/documentation/tutorials/getting-started-with-ash-sqlite.md) """ end diff --git a/lib/data_layer.ex b/lib/data_layer.ex index 195a322..5183c62 100644 --- a/lib/data_layer.ex +++ b/lib/data_layer.ex @@ -276,7 +276,7 @@ defmodule AshSqlite.DataLayer do type: :boolean, default: false, doc: """ - Declares this resource as polymorphic. See the [polymorphic resources guide](/documentation/topics/polymorphic_resources.md) for more. + Declares this resource as polymorphic. See the [polymorphic resources guide](/documentation/topics/resources/polymorphic-resources.md) for more. """ ] ] diff --git a/mix.exs b/mix.exs index e2a251d..f990b2a 100644 --- a/mix.exs +++ b/mix.exs @@ -2,8 +2,7 @@ defmodule AshSqlite.MixProject do use Mix.Project @description """ - A sqlite data layer for `Ash` resources. Leverages Ecto's sqlite - support, and delegates to a configured repo. + The SQLite data layer for Ash Framework. """ @version "0.1.2-rc.0" @@ -63,78 +62,32 @@ defmodule AshSqlite.MixProject do ] end - defp extras() do - "documentation/**/*.{md,livemd,cheatmd}" - |> Path.wildcard() - |> Enum.map(fn path -> - title = - path - |> Path.basename(".md") - |> Path.basename(".livemd") - |> Path.basename(".cheatmd") - |> String.split(~r/[-_]/) - |> Enum.map_join(" ", &capitalize/1) - |> case do - "F A Q" -> - "FAQ" - - other -> - other - end - - {String.to_atom(path), - [ - title: title - ]} - end) - end - - defp capitalize(string) do - string - |> String.split(" ") - |> Enum.map(fn string -> - [hd | tail] = String.graphemes(string) - String.capitalize(hd) <> Enum.join(tail) - end) - end - - defp groups_for_extras() do - [ - Tutorials: [ - ~r'documentation/tutorials' - ], - "How To": ~r'documentation/how_to', - Topics: ~r'documentation/topics', - DSLs: ~r'documentation/dsls' - ] - end - defp docs do [ - main: "get-started-with-sqlite", + main: "readme", source_ref: "v#{@version}", logo: "logos/small-logo.png", - extras: extras(), - spark: [ - mix_tasks: [ - SQLite: [ - Mix.Tasks.AshSqlite.GenerateMigrations, - Mix.Tasks.AshSqlite.Create, - Mix.Tasks.AshSqlite.Drop, - Mix.Tasks.AshSqlite.Migrate, - Mix.Tasks.AshSqlite.Rollback - ] - ], - extensions: [ - %{ - module: AshSqlite.DataLayer, - name: "AshSqlite", - target: "Ash.Resource", - type: "DataLayer" - } - ] + extras: [ + {"README.md", title: "Home"}, + "documentation/tutorials/getting-started-with-ash-sqlite.md", + "documentation/topics/about-ash-sqlite/what-is-ash-sqlite.md", + "documentation/topics/resources/references.md", + "documentation/topics/resources/polymorphic-resources.md", + "documentation/topics/development/migrations-and-tasks.md", + "documentation/topics/development/testing.md", + "documentation/topics/advanced/expressions.md", + "documentation/topics/advanced/manual-relationships.md", + "documentation/dsls/DSL:-AshSqlite.DataLayer.md", + "CHANGELOG.md" + ], + groups_for_extras: [ + Tutorials: [ + ~r'documentation/tutorials' + ], + "How To": ~r'documentation/how_to', + Topics: ~r'documentation/topics', + DSLs: ~r'documentation/dsls' ], - groups_for_extras: groups_for_extras(), groups_for_modules: [ AshSqlite: [ AshSqlite,