improvement: deprecation!

A deprecation warning will be shown at compile time to illustrate
a change from listing all of your resources in an api to listing
them in a registry and connecting that registry to the api
This commit is contained in:
Zach Daniel 2021-10-07 02:41:02 -04:00
parent d644303c30
commit ff756b72a7
47 changed files with 614 additions and 323 deletions

View file

@ -41,6 +41,8 @@ locals_without_parens = [
destroy: 1,
destroy: 2,
dispatcher: 1,
entry: 1,
entry: 2,
error_handler: 1,
event: 1,
expensive?: 1,
@ -91,6 +93,7 @@ locals_without_parens = [
read: 1,
read: 2,
read_action: 1,
registry: 1,
reject: 1,
relationship_context: 1,
require_attributes: 1,

View file

@ -52,7 +52,7 @@ attribute(:id, :integer, allow_nil?: true)
## Create an Ash API
Create an API module. This will be your primary way to interact with your Ash resources. We recommend `lib/my_app/api.ex` for simple setups. For more information on organizing resources into contexts/domains, see the [Contexts and Domains](contexts_and_domains.html) guide.
Create an API module. This will be your primary way to interact with your Ash resources. We recommend `lib/my_app/api.ex` for simple setups.
```elixir
# lib/my_app/api.ex
@ -64,6 +64,33 @@ defmodule MyApp.Api do
end
```
## Create a registry
The registry is in charge of keeping track of the resources available to an api.
```elixir
# lib/my_app/registry.ex
defmodule MyApp.Registry do
use Ash.Registry
entries do
end
end
```
## Refer to that registry in your api
```elixir
# lib/my_app/api.ex
defmodule MyApp.Api do
use Ash.Api
resources do
registry MyApp.Registry
end
end
```
## Create a resource
A resource is the primary entity in Ash. Your API module ties your resources together and gives them an interface, but the vast majority of your configuration will live in resources.
@ -123,12 +150,12 @@ For full details on defining a resource, see: `Ash.Resource.Dsl`.
## Add resources to your API
Alter your API (`lib/my_app/api.ex`) to add the resources we created on the previous step:
Alter your Registry (`lib/my_app/registry.ex`) to add the resources we created on the previous step:
```elixir
resources do
resource MyApp.User
resource MyApp.Tweet
entries do
entry MyApp.User
entry MyApp.Tweet
end
```
@ -192,12 +219,12 @@ iex(6)> changeset = Ash.Changeset.new(MyApp.User, %{email: "@eng.com"})
To be able to store and later on read your resources, a _data layer_ is required. For more information, see the documentation for the data layer you would like to use. The currently supported data layers are listed below:
| Storage | Datalayer | Storage Documentation |
| --- | ---| --- |
| postgres | [AshPostgres.DataLayer](https://hexdocs.pm/ash_postgres) | [Postgres Documentation](https://www.postgresql.org/docs/) |
| csv | [AshCsv.DataLayer](https://hexdocs.pm/ash_csv) | [CSV Information](https://en.wikipedia.org/wiki/Comma-separated_values) |
| ets | `Ash.DataLayer.Ets` | [Erlang Term Storage Documentation](https://erlang.org/doc/man/ets.html) |
| mnesia | `Ash.DataLayer.Mnesia` | [Mnesia Documentation](https://erlang.org/doc/man/mnesia.html) |
| Storage | Datalayer | Storage Documentation |
| -------- | -------------------------------------------------------- | ------------------------------------------------------------------------ |
| postgres | [AshPostgres.DataLayer](https://hexdocs.pm/ash_postgres) | [Postgres Documentation](https://www.postgresql.org/docs/) |
| csv | [AshCsv.DataLayer](https://hexdocs.pm/ash_csv) | [CSV Information](https://en.wikipedia.org/wiki/Comma-separated_values) |
| ets | `Ash.DataLayer.Ets` | [Erlang Term Storage Documentation](https://erlang.org/doc/man/ets.html) |
| mnesia | `Ash.DataLayer.Mnesia` | [Mnesia Documentation](https://erlang.org/doc/man/mnesia.html) |
To add a data layer, we need to add it to the `use Ash.Resource` statement. In
this case we are going to use ETS which is a in-memory data layer that is built

View file

@ -1,14 +0,0 @@
# Contexts and Domains
It is suggested that you read a bit on Domain Driven Design before proceeding. If you are using phoenix or are familiar with phoenix contexts, then this will make sense to you.
In order to support domain driven design, Ash supports defining multiple APIs, each with their own set of resources. It is possible to share a resource between APIs, but this gets untenable very quickly because any resources related to the shared resource must _both_ appear in each API.
An experimental "Delegation" data layer was added to allow you to use other resources in other APIs as the data layer for a resource, but it created a significant amount of complexity in determining data layer behavior. Instead, simply use the same data layer and configuration in both resources.
Things missing to make this work well:
- Define the ecto schema as a separate module (prerequisite for hidden attributes)
- "hidden" attributes - attributes that are defined on the schema but not the Ash struct
- ability to filter on hidden fields in certain places (haven't determined where this needs to happen)
- ability to add a "base_filter" that can leverage hidden attributes

View file

@ -6,12 +6,20 @@ defmodule Ash.Api do
for all resources in that Api. You include them in an Api like so:
```elixir
defmodule MyApp.Registry do
use Ash.Registry
entries do
entry OneResource
entry SecondResource
end
end
defmodule MyApp.Api do
use Ash.Api
resources do
resource OneResource
resource SecondResource
registry MyApp.Registry
end
end
```
@ -477,20 +485,40 @@ defmodule Ash.Api do
end
def resource(api, resource) do
api
|> resource_references()
|> Enum.find(&(&1.resource == resource || &1.as == resource))
|> case do
nil -> {:error, NoSuchResource.exception(resource: resource)}
reference -> {:ok, reference.resource}
if Ash.Resource.Info.embedded?(resource) do
{:ok, resource}
else
api
|> resources()
|> Enum.find(&(&1 == resource))
|> case do
nil -> {:error, NoSuchResource.exception(resource: resource)}
resource -> {:ok, resource}
end
end
end
@spec resources(Ash.Api.t()) :: [Ash.Resource.t()]
@spec resources(Ash.Api.t()) :: list(Ash.Resource.t())
def resources(api) do
api
|> Extension.get_entities([:resources])
|> Enum.map(& &1.resource)
|> case do
[] ->
if registry = registry(api) do
Ash.Registry.entries(registry)
else
[]
end
other ->
other
end
end
@spec registry(atom) :: atom | nil
def registry(api) do
Extension.get_opt(api, [:resources], :registry, nil)
end
@spec define_interfaces?(atom) :: boolean
@ -498,11 +526,6 @@ defmodule Ash.Api do
Extension.get_opt(api, [:resources], :define_interfaces?, false)
end
@spec resource_references(Ash.Api.t()) :: [Ash.Api.ResourceReference.t()]
def resource_references(api) do
Extension.get_entities(api, [:resources])
end
@doc false
@spec get!(Ash.Api.t(), Ash.Resource.t(), term(), Keyword.t()) ::
Ash.Resource.record() | no_return

View file

@ -3,7 +3,6 @@ defmodule Ash.Api.Dsl do
name: :resource,
describe: "A reference to a resource",
target: Ash.Api.ResourceReference,
modules: [:resource],
args: [:resource],
examples: [
"resource MyApp.User"
@ -38,14 +37,46 @@ defmodule Ash.Api.Dsl do
Keep in mind that this can increase the compile times of your application.
"""
],
registry: [
type: :atom,
# {:ash_behaviour, Ash.Registry},
doc: """
Allows declaring that only the modules in a certain registry should be allowed to work with this Api.
This option is ignored if any explicit resources are included in the api, so everything is either in the registry
or in the api. See the docs on `Ash.Registry` for what the registry is used for.
"""
]
],
modules: [:registry],
deprecations: [
resource: """
Please define your resources in an `Ash.Registry`. For example:
# my_app/my_api/registry.ex
defmodule MyApp.MyApi.Registry do
use Ash.Registry
entries do
entry MyApp.Post
entry MyApp.Comment
end
end
# In your api module
resources do
registry MyApp.MyApi.Registry
end
"""
],
entities: [
@resource
]
}
@transformers [
Ash.Api.Transformers.EnsureResourcesCompiled,
Ash.Api.Transformers.ValidateRelatedResourceInclusion,
Ash.Api.Transformers.ValidateRelationshipAttributes,
Ash.Api.Transformers.ValidateManyToManyJoinAttributes

View file

@ -146,83 +146,6 @@ defmodule Ash.Api.Interface do
end
end
@doc false
def set_tenant(query_or_changeset, opts) do
case Keyword.fetch(opts, :tenant) do
{:ok, tenant} ->
case query_or_changeset do
%Ash.Query{} = query ->
Ash.Query.set_tenant(query, tenant)
%Ash.Changeset{} = changeset ->
Ash.Changeset.set_tenant(changeset, tenant)
other ->
other
end
:error ->
query_or_changeset
end
end
@doc false
def action_interface(api) do
api
|> Ash.Api.resource_references()
|> Enum.flat_map(fn %{resource: resource} = reference ->
resource_name = name(reference)
resource
|> Ash.Resource.Info.actions()
|> Enum.map(fn action ->
{resource, resource_name, action}
end)
end)
end
@doc false
def getters(api) do
api
|> Ash.Api.resource_references()
|> Enum.flat_map(fn %{resource: resource} = reference ->
if Ash.Resource.Info.primary_action(resource, :read) do
resource_name = name(reference)
resource
|> Ash.Resource.Info.identities()
|> Enum.map(fn identity ->
{resource, resource_name, identity}
end)
else
[]
end
end)
end
@doc false
def resources_with_names(api) do
api
|> Ash.Api.resource_references()
|> Enum.map(fn ref ->
{ref.resource, name(ref)}
end)
end
@doc false
def name(reference) do
reference
|> Map.get(:as)
|> Kernel.||(
reference.resource
|> Module.split()
|> List.last()
|> to_string()
|> Macro.underscore()
)
|> to_string()
end
defmacro enforce_query_or_resource!(query_or_resource) do
quote generated: true do
case Ash.Api.Interface.do_enforce_query_or_resource!(unquote(query_or_resource)) do

View file

@ -1,7 +1,7 @@
defmodule Ash.Api.ResourceReference do
@moduledoc "Represents a resource in an API"
defstruct [:resource, :as]
defstruct [:resource]
@type t :: %__MODULE__{}
end

View file

@ -6,19 +6,47 @@ defmodule Ash.Api.Transformers.EnsureResourcesCompiled do
"""
use Ash.Dsl.Transformer
alias Ash.Dsl.Transformer
require Logger
@impl true
def after_compile?, do: true
@impl true
def transform(_api, dsl) do
dsl
|> Transformer.get_entities([:resources])
|> Enum.map(& &1.resource)
|> Enum.each(fn resource ->
resource.ash_dsl_config()
def transform(api, dsl) do
api
|> Ash.Api.resources()
|> Enum.map(fn resource ->
try do
# This is to get the compiler to ensure that the resource is compiled
# For some very strange reason, `Code.ensure_compiled/1` isn't enough
resource.ash_dsl_config()
rescue
_ ->
:ok
end
case Code.ensure_compiled(resource) do
{:module, _module} ->
false
{:error, error} ->
# The module is being compiled but is in a deadlock that may or may not be resolved
{resource, error}
end
end)
|> Enum.filter(& &1)
|> case do
[] ->
{:ok, dsl}
rejected ->
for {resource, error} <- rejected do
Logger.error(
"Could not ensure that #{inspect(resource)} was compiled: #{inspect(error)}"
)
end
:halt
end
end
end

View file

@ -1,40 +0,0 @@
defmodule Ash.Api.Transformers.UniqueFunctionNames do
@moduledoc """
Ensures that all function names added to the API will be unique.
"""
use Ash.Dsl.Transformer
alias Ash.Dsl.Transformer
require Logger
# sobelow_skip ["DOS.BinToAtom"]
def transform(_module, dsl) do
dsl
|> Transformer.get_entities([:resources])
|> Enum.flat_map(fn %{resource: resource} = reference ->
resource
|> Ash.Resource.Info.actions()
|> Enum.map(fn action ->
action.as || :"#{Ash.Api.Interface.name(reference)}_#{action.name}"
end)
end)
|> Enum.reduce(%{}, fn name, acc ->
Map.update(acc, name, 0, &(&1 + 1))
end)
|> Enum.each(fn {name, count} ->
if count > 1 do
raise Ash.Error.Dsl.DslError.exception(
module: __MODULE__,
message: """
Multiple actions would share the Api helper name #{name}.
Specify the `as` option to update the helper name for those actions so they do not match.
Alternatively, specify the `as` option for those resources so they do not have the same short name.
""",
path: [:actions]
)
end
end)
{:ok, dsl}
end
end

View file

@ -4,8 +4,6 @@ defmodule Ash.Api.Transformers.ValidateManyToManyJoinAttributes do
"""
use Ash.Dsl.Transformer
alias Ash.Dsl.Transformer
@impl true
def after_compile?, do: true
@ -14,10 +12,9 @@ defmodule Ash.Api.Transformers.ValidateManyToManyJoinAttributes do
def after?(_), do: false
@impl true
def transform(_api, dsl) do
dsl
|> Transformer.get_entities([:resources])
|> Enum.map(& &1.resource)
def transform(api, dsl) do
api
|> Ash.Api.resources()
|> Enum.each(fn resource ->
resource
|> Ash.Resource.Info.relationships()

View file

@ -4,8 +4,6 @@ defmodule Ash.Api.Transformers.ValidateRelationshipAttributes do
"""
use Ash.Dsl.Transformer
alias Ash.Dsl.Transformer
@impl true
def after_compile?, do: true
@ -14,10 +12,9 @@ defmodule Ash.Api.Transformers.ValidateRelationshipAttributes do
def after?(_), do: false
@impl true
def transform(_api, dsl) do
dsl
|> Transformer.get_entities([:resources])
|> Enum.map(& &1.resource)
def transform(api, dsl) do
api
|> Ash.Api.resources()
|> Enum.each(fn resource ->
attribute_names =
resource

View file

@ -38,6 +38,7 @@ defmodule Ash.Dsl.Entity do
:transform,
examples: [],
entities: [],
deprecations: [],
describe: "",
snippet: "",
args: [],

View file

@ -106,9 +106,10 @@ defmodule Ash.Dsl.Extension do
@callback transformers() :: [module]
defp dsl!(resource) do
Code.ensure_compiled!(resource)
resource.ash_dsl_config()
rescue
UndefinedFunctionError ->
_ in [UndefinedFunctionError, ArgumentError] ->
try do
Module.get_attribute(resource, :ash_dsl_config) || %{}
rescue
@ -711,7 +712,7 @@ defmodule Ash.Dsl.Extension do
def do_build_section(mod, extension, section, path) do
entity_modules =
Enum.map(section.entities, fn entity ->
build_entity(mod, extension, path ++ [section.name], entity)
build_entity(mod, extension, path ++ [section.name], entity, section.deprecations)
end)
section_modules =
@ -768,6 +769,13 @@ defmodule Ash.Dsl.Extension do
extension = unquote(extension)
section = unquote(Macro.escape(section))
Ash.Dsl.Extension.maybe_deprecated(
field,
section.deprecations,
section_path,
__CALLER__
)
value =
if field in section.modules do
Ash.Dsl.Extension.expand_alias(value, __CALLER__)
@ -811,7 +819,7 @@ defmodule Ash.Dsl.Extension do
end
@doc false
def build_entity(mod, extension, section_path, entity, nested_entity_path \\ []) do
def build_entity(mod, extension, section_path, entity, deprecations, nested_entity_path \\ []) do
nested_entity_parts = Enum.map(nested_entity_path, &Macro.camelize(to_string(&1)))
mod_parts =
@ -831,6 +839,7 @@ defmodule Ash.Dsl.Extension do
extension,
section_path,
entity,
entity.deprecations,
nested_entity_path ++ [key]
)
end)
@ -840,6 +849,7 @@ defmodule Ash.Dsl.Extension do
options_mod_name,
entity.schema,
entity.modules,
entity.deprecations,
nested_entity_path
)
@ -854,7 +864,8 @@ defmodule Ash.Dsl.Extension do
section_path: Macro.escape(section_path),
options_mod_name: Macro.escape(options_mod_name),
nested_entity_mods: Macro.escape(nested_entity_mods),
nested_entity_path: Macro.escape(nested_entity_path)
nested_entity_path: Macro.escape(nested_entity_path),
deprecations: deprecations
] do
@moduledoc false
defmacro unquote(entity.name)(unquote_splicing(args), opts \\ []) do
@ -863,16 +874,32 @@ defmodule Ash.Dsl.Extension do
entity = unquote(Macro.escape(entity))
entity_name = unquote(Macro.escape(entity.name))
entity_args = unquote(Macro.escape(entity.args))
entity_deprecations = unquote(entity.deprecations)
options_mod_name = unquote(Macro.escape(options_mod_name))
source = unquote(__MODULE__)
extension = unquote(Macro.escape(extension))
nested_entity_mods = unquote(Macro.escape(nested_entity_mods))
nested_entity_path = unquote(Macro.escape(nested_entity_path))
deprecations = unquote(deprecations)
Ash.Dsl.Extension.maybe_deprecated(
entity.name,
deprecations,
section_path ++ nested_entity_path,
__CALLER__
)
arg_values =
entity_args
|> Enum.zip(unquote(args))
|> Enum.map(fn {key, value} ->
Ash.Dsl.Extension.maybe_deprecated(
key,
entity_deprecations,
nested_entity_path,
__CALLER__
)
if key in entity.modules do
Ash.Dsl.Extension.expand_alias(value, __CALLER__)
else
@ -882,6 +909,13 @@ defmodule Ash.Dsl.Extension do
opts =
Enum.map(opts, fn {key, value} ->
Ash.Dsl.Extension.maybe_deprecated(
key,
entity_deprecations,
nested_entity_path,
__CALLER__
)
if key in entity.modules do
{key, Ash.Dsl.Extension.expand_alias(value, __CALLER__)}
else
@ -1019,13 +1053,14 @@ defmodule Ash.Dsl.Extension do
end
@doc false
def build_entity_options(module_name, schema, modules, nested_entity_path) do
def build_entity_options(module_name, schema, modules, deprecations, nested_entity_path) do
Module.create(
module_name,
quote bind_quoted: [
schema: Macro.escape(schema),
nested_entity_path: nested_entity_path,
modules: modules
modules: modules,
deprecations: deprecations
] do
@moduledoc false
@ -1034,6 +1069,9 @@ defmodule Ash.Dsl.Extension do
key = unquote(key)
nested_entity_path = unquote(nested_entity_path)
modules = unquote(modules)
deprecations = unquote(deprecations)
Ash.Dsl.Extension.maybe_deprecated(key, deprecations, nested_entity_path, __CALLER__)
value =
if key in modules do
@ -1059,6 +1097,22 @@ defmodule Ash.Dsl.Extension do
module_name
end
@doc false
def maybe_deprecated(field, deprecations, path, env) do
if Keyword.has_key?(deprecations, field) do
prefix =
case Enum.join(path) do
"" -> ""
path -> "#{path}."
end
IO.warn(
"The #{prefix}#{field} key will be deprecated in an upcoming release!\n\n#{deprecations[field]}",
Macro.Env.stacktrace(env)
)
end
end
def expand_alias(ast, env) do
Macro.postwalk(ast, fn
{first, {:__aliases__, _, _} = node} ->

View file

@ -28,6 +28,7 @@ defmodule Ash.Dsl.Section do
snippet: "",
examples: [],
modules: [],
deprecations: [],
entities: [],
sections: [],
docs: ""

View file

@ -47,6 +47,11 @@ defmodule Ash.EmbeddableType do
]
]
defmodule ShadowApi do
@moduledoc false
use Ash.Api
end
@doc false
def embedded_resource_array_constraints, do: @embedded_resource_array_constraints
@ -135,7 +140,7 @@ defmodule Ash.EmbeddableType do
defmacro single_embed_implementation do
# credo:disable-for-next-line Credo.Check.Refactor.LongQuoteBlocks
quote location: :keep do
alias __MODULE__.ShadowApi
alias Ash.EmbeddableType.ShadowApi
def storage_type, do: :map
def cast_input(%{__struct__: __MODULE__} = input, _constraints), do: {:ok, input}
@ -375,7 +380,7 @@ defmodule Ash.EmbeddableType do
defmacro array_embed_implementation do
# credo:disable-for-next-line Credo.Check.Refactor.LongQuoteBlocks
quote location: :keep do
alias __MODULE__.ShadowApi
alias Ash.EmbeddableType.ShadowApi
def array_constraints, do: Ash.EmbeddableType.embedded_resource_array_constraints()
def apply_constraints_array([], _constraints), do: {:ok, []}
@ -555,19 +560,6 @@ defmodule Ash.EmbeddableType do
quote location: :keep do
use Ash.Type
parent = __MODULE__
defmodule ShadowApi do
@moduledoc false
use Ash.Api
@parent parent
resources do
resource @parent, []
end
end
Ash.EmbeddableType.single_embed_implementation()
Ash.EmbeddableType.array_embed_implementation()
end

View file

@ -1,56 +1,48 @@
# defmodule Ash.Registry.Dsl do
# @entry %Ash.Dsl.Entity{
# name: :entry,
# describe: "A reference to a a module",
# target: Ash.Registry.EntryReference,
# args: [:entry],
# examples: [
# "entry MyApp.Post"
# ],
# schema: [
# entry: [
# type: :atom,
# required: true,
# doc: "The module of the entry"
# ]
# ]
# }
defmodule Ash.Registry.Dsl do
@entry %Ash.Dsl.Entity{
name: :entry,
describe: "A reference to an ash module (typically a resource)",
target: Ash.Registry.Entry,
args: [:entry],
examples: [
"entry MyApp.User"
],
schema: [
entry: [
type: :atom,
required: true,
doc: "The referenced module"
]
]
}
# @entries %Ash.Dsl.Section{
# name: :entries,
# describe: "List the entries present in this registry",
# examples: [
# """
# entries do
# entry MyApp.User
# entry MyApp.Post
# entry MyApp.Comment
# end
# """
# ],
# entities: [
# @entry
# ]
# }
@entries %Ash.Dsl.Section{
name: :entries,
describe: "List the entries present in this registry",
examples: [
"""
entries do
entry MyApp.User
entry MyApp.Post
entry MyApp.Comment
end
"""
],
entities: [
@entry
]
}
# @sections [@resources]
@sections [@entries]
# @moduledoc """
# A small DSL for declaring APIs
@moduledoc """
A small DSL for declaring an `Ash.Registry`.
# Apis are the entrypoints for working with your resources.
# Table of Contents
#{Ash.Dsl.Extension.doc_index(@sections)}
# Apis may optionally include a list of resources, in which case they can be
# used as an `Ash.Registry` in various places. This is for backwards compatibility,
# but if at all possible you should define an `Ash.Registry` if you are using an extension
# that requires a list of resources. For example, most extensions look for two application
# environment variables called `:ash_apis` and `:ash_registries` to find any potential registries
#{Ash.Dsl.Extension.doc(@sections)}
"""
# # Table of Contents
# #{Ash.Dsl.Extension.doc_index(@sections)}
# #{Ash.Dsl.Extension.doc(@sections)}
# """
# use Ash.Dsl.Extension, sections: @sections, transformers: @transformers
# end
use Ash.Dsl.Extension, sections: @sections
end

View file

@ -0,0 +1,7 @@
defmodule Ash.Registry.Entry do
@moduledoc "Represents an entry in a registry"
defstruct [:entry]
@type t :: %__MODULE__{}
end

View file

@ -0,0 +1,39 @@
defmodule Ash.Registry do
@moduledoc """
A registry allows you to separate your resources from your `api` module, to reduce improve compile times and reduce compile time dependencies.
For example:
```elixir
defmodule MyApp.MyRegistry do
use Ash.Registry
entries do
entry MyApp.Resource
entry MyApp.OtherResource
end
end
```
"""
@type t :: module
use Ash.Dsl, default_extensions: [extensions: [Ash.Registry.Dsl]]
alias Ash.Dsl.Extension
@spec entries(t()) :: list(module)
def entries(registry) do
case registry |> Extension.get_entities([:entries]) |> Enum.map(& &1.entry) do
[] ->
registry |> Extension.get_entities([:resources]) |> Enum.map(& &1.resource)
other ->
other
end
end
@spec api_or_api_and_registry(Ash.Api.t() | {Ash.Api.t(), t()}) :: {t(), t()}
def api_or_api_and_registry({api, registry}), do: {api, registry}
def api_or_api_and_registry(api), do: {api, api}
end

View file

@ -83,9 +83,6 @@ defmodule Ash.MixProject do
"documentation/topics/embedded_resources.md": [
title: "Embedded Resources"
],
"documentation/topics/contexts_and_domains.md": [
title: "Context And Domains"
],
"documentation/topics/multitenancy.md": [
title: "Multitenancy"
]
@ -211,7 +208,7 @@ defmodule Ash.MixProject do
sobelow: "sobelow --skip",
credo: "credo --strict",
"ash.formatter":
"ash.formatter --extensions Ash.Resource.Dsl,Ash.Api.Dsl,Ash.DataLayer.Ets,Ash.DataLayer.Mnesia,Ash.Notifier.PubSub"
"ash.formatter --extensions Ash.Resource.Dsl,Ash.Api.Dsl,Ash.Registry.Dsl,Ash.DataLayer.Ets,Ash.DataLayer.Mnesia,Ash.Notifier.PubSub"
]
end
end

View file

@ -75,13 +75,22 @@ defmodule Ash.Test.Actions.BelongsToTest do
end
end
defmodule Registry do
@moduledoc false
use Ash.Registry
entries do
entry(Post)
entry(Reviewer)
end
end
defmodule Api do
@moduledoc false
use Ash.Api
resources do
resource(Post)
resource(Reviewer)
registry Registry
end
end

View file

@ -258,18 +258,27 @@ defmodule Ash.Test.Actions.CreateTest do
end
end
defmodule Registry do
@moduledoc false
use Ash.Registry
entries do
entry(Author)
entry(Post)
entry(Profile)
entry(ProfileWithBelongsTo)
entry(PostLink)
entry(Authorized)
entry(GeneratedPkey)
end
end
defmodule Api do
@moduledoc false
use Ash.Api
resources do
resource(Author)
resource(Post)
resource(Profile)
resource(ProfileWithBelongsTo)
resource(PostLink)
resource(Authorized)
resource(GeneratedPkey)
registry Registry
end
end

View file

@ -114,14 +114,23 @@ defmodule Ash.Test.Actions.DestroyTest do
end
end
defmodule Registry do
@moduledoc false
use Ash.Registry
entries do
entry(Author)
entry(Post)
entry(Profile)
end
end
defmodule Api do
@moduledoc false
use Ash.Api
resources do
resource(Author)
resource(Post)
resource(Profile)
registry Registry
end
end

View file

@ -114,15 +114,24 @@ defmodule Ash.Test.Actions.LoadTest do
end
end
defmodule Registry do
@moduledoc false
use Ash.Registry
entries do
entry(Author)
entry(Post)
entry(Category)
entry(PostCategory)
end
end
defmodule Api do
@moduledoc false
use Ash.Api
resources do
resource(Author)
resource(Post)
resource(Category)
resource(PostCategory)
registry Registry
end
end

View file

@ -95,13 +95,23 @@ defmodule Ash.Actions.MultitenancyTest do
end
end
defmodule Registry do
@moduledoc false
use Ash.Registry
entries do
entry(Comment)
entry(Post)
entry(User)
end
end
defmodule Api do
@moduledoc false
use Ash.Api
resources do
resource Comment
resource Post
resource User
registry Registry
end
end

View file

@ -63,11 +63,20 @@ defmodule Ash.Actions.PaginationTest do
end
end
defmodule Registry do
@moduledoc false
use Ash.Registry
entries do
entry(User)
end
end
defmodule Api do
use Ash.Api
resources do
resource User
registry Registry
end
end

View file

@ -69,13 +69,22 @@ defmodule Ash.Test.Actions.ReadTest do
end
end
defmodule Registry do
@moduledoc false
use Ash.Registry
entries do
entry(Post)
entry(Author)
end
end
defmodule Api do
@moduledoc false
use Ash.Api
resources do
resource Post
resource Author
registry Registry
end
end

View file

@ -192,16 +192,25 @@ defmodule Ash.Test.Actions.UpdateTest do
end
end
defmodule Registry do
@moduledoc false
use Ash.Registry
entries do
entry(Author)
entry(Post)
entry(Profile)
entry(PostLink)
entry(Authorized)
end
end
defmodule Api do
@moduledoc false
use Ash.Api
resources do
resource(Author)
resource(Post)
resource(Profile)
resource(PostLink)
resource(Authorized)
registry Registry
end
end

View file

@ -39,11 +39,20 @@ defmodule Ash.Test.Actions.ValidationTest do
end
end
defmodule Registry do
@moduledoc false
use Ash.Registry
entries do
entry Profile
end
end
defmodule Api do
use Ash.Api
resources do
resource Profile
registry Registry
end
end

View file

@ -12,15 +12,4 @@ defmodule Ash.Test.Resource.ApiTest do
end
end
end
defmacrop defapi(opts \\ [], do: body) do
quote do
defmodule Api do
@moduledoc false
use Ash.Api, unquote(opts)
unquote(body)
end
end
end
end

View file

@ -39,11 +39,20 @@ defmodule Ash.DataLayer.EtsTest do
end
end
defmodule Registry do
@moduledoc false
use Ash.Registry
entries do
entry EtsTestUser
end
end
defmodule EtsApiTest do
use Ash.Api
resources do
resource EtsTestUser
registry Registry
end
end

View file

@ -22,11 +22,20 @@ defmodule Ash.Test.Changeset.AuthorizerTest do
end
end
defmodule Registry do
@moduledoc false
use Ash.Registry
entries do
entry Post
end
end
defmodule Api do
use Ash.Api
resources do
resource Post
registry Registry
end
end

View file

@ -61,12 +61,21 @@ defmodule Ash.Test.CalculationTest do
end
end
defmodule Registry do
@moduledoc false
use Ash.Registry
entries do
entry(User)
end
end
defmodule Api do
@moduledoc false
use Ash.Api
resources do
resource(User)
registry Registry
end
end

View file

@ -184,17 +184,26 @@ defmodule Ash.Test.Changeset.ChangesetTest do
end
end
defmodule Registry do
@moduledoc false
use Ash.Registry
entries do
entry Category
entry Author
entry PostCategory
entry Post
entry CompositeKeyPost
entry UniqueNamePerAuthor
end
end
defmodule Api do
@moduledoc false
use Ash.Api
resources do
resource Category
resource Author
resource PostCategory
resource Post
resource CompositeKeyPost
resource UniqueNamePerAuthor
registry Registry
end
end

View file

@ -34,6 +34,15 @@ defmodule Ash.Test.CodeInterfaceTest do
end
end
defmodule Registry do
@moduledoc false
use Ash.Registry
entries do
entry(User)
end
end
defmodule Api do
@moduledoc false
use Ash.Api
@ -41,7 +50,7 @@ defmodule Ash.Test.CodeInterfaceTest do
resources do
define_interfaces?(true)
resource(User)
registry Registry
end
end

View file

@ -137,12 +137,21 @@ defmodule Ash.Test.Changeset.EmbeddedResourceTest do
end
end
defmodule Registry do
@moduledoc false
use Ash.Registry
entries do
entry(Author)
end
end
defmodule Api do
@moduledoc false
use Ash.Api
resources do
resource Author
registry Registry
end
end

View file

@ -122,15 +122,24 @@ defmodule Ash.Test.Filter.FilterInteractionTest do
end
end
defmodule Registry do
@moduledoc false
use Ash.Registry
entries do
entry(Post)
entry(User)
entry(Profile)
entry(PostLink)
end
end
defmodule Api do
@moduledoc false
use Ash.Api
resources do
resource(Post)
resource(User)
resource(Profile)
resource(PostLink)
registry Registry
end
end

View file

@ -166,16 +166,25 @@ defmodule Ash.Test.Filter.FilterTest do
end
end
defmodule Registry do
@moduledoc false
use Ash.Registry
entries do
entry(Post)
entry(SoftDeletePost)
entry(User)
entry(Profile)
entry(PostLink)
end
end
defmodule Api do
@moduledoc false
use Ash.Api
resources do
resource(Post)
resource(SoftDeletePost)
resource(User)
resource(Profile)
resource(PostLink)
registry Registry
end
end

View file

@ -103,13 +103,22 @@ defmodule Ash.Test.NotifierTest do
end
end
defmodule Registry do
@moduledoc false
use Ash.Registry
entries do
entry Post
entry PostLink
entry Comment
end
end
defmodule Api do
use Ash.Api
resources do
resource Post
resource PostLink
resource Comment
registry Registry
end
end

View file

@ -46,11 +46,20 @@ defmodule Ash.Test.Notifier.PubSubTest do
end
end
defmodule Registry do
@moduledoc false
use Ash.Registry
entries do
entry Post
end
end
defmodule Api do
use Ash.Api
resources do
resource Post
registry Registry
end
end

View file

@ -31,11 +31,20 @@ defmodule Ash.Test.QueryTest do
end
end
defmodule Registry do
@moduledoc false
use Ash.Registry
entries do
entry(User)
end
end
defmodule Api do
use Ash.Api
resources do
resource User
registry Registry
end
end

View file

@ -23,11 +23,20 @@ defmodule Ash.Test.Resource.Changes.LoadTest do
end
end
defmodule Registry do
@moduledoc false
use Ash.Registry
entries do
entry Post
end
end
defmodule Api do
use Ash.Api
resources do
resource Post
registry Registry
end
end

View file

@ -212,11 +212,20 @@ defmodule Ash.Test.Resource.Relationships.BelongsToTest do
end
end
defmodule Registry do
@moduledoc false
use Ash.Registry
entries do
entry Post
end
end
defmodule Api do
use Ash.Api
resources do
resource(Post)
registry Registry
end
end
end

View file

@ -28,12 +28,21 @@ defmodule Ash.Test.Sort.SortTest do
end
end
defmodule Registry do
@moduledoc false
use Ash.Registry
entries do
entry Post
end
end
defmodule Api do
@moduledoc false
use Ash.Api
resources do
resource(Post)
registry Registry
end
end

View file

@ -33,12 +33,21 @@ defmodule Ash.Test.Type.CiString do
end
end
defmodule Registry do
@moduledoc false
use Ash.Registry
entries do
entry Post
end
end
defmodule Api do
@moduledoc false
use Ash.Api
resources do
resource(Post)
registry Registry
end
end

View file

@ -24,12 +24,21 @@ defmodule Ash.Test.Type.EnumTest do
end
end
defmodule Registry do
@moduledoc false
use Ash.Registry
entries do
entry Post
end
end
defmodule Api do
@moduledoc false
use Ash.Api
resources do
resource(Post)
registry Registry
end
end

View file

@ -32,12 +32,21 @@ defmodule Ash.Test.Type.StringTest do
end
end
defmodule Registry do
@moduledoc false
use Ash.Registry
entries do
entry Post
end
end
defmodule Api do
@moduledoc false
use Ash.Api
resources do
resource(Post)
registry Registry
end
end

View file

@ -65,12 +65,21 @@ defmodule Ash.Test.Type.TypeTest do
end
end
defmodule Registry do
@moduledoc false
use Ash.Registry
entries do
entry Post
end
end
defmodule Api do
@moduledoc false
use Ash.Api
resources do
resource(Post)
registry Registry
end
end