diff --git a/.formatter.exs b/.formatter.exs index 7aad6276..c857bc55 100644 --- a/.formatter.exs +++ b/.formatter.exs @@ -103,7 +103,6 @@ locals_without_parens = [ prepare: 1, prepare: 2, primary?: 1, - primary_actions?: 1, primary_key?: 1, private?: 1, publish: 2, diff --git a/documentation/info/faq.md b/documentation/concepts/resources.md similarity index 100% rename from documentation/info/faq.md rename to documentation/concepts/resources.md diff --git a/documentation/guides/getting-started.md b/documentation/guides/getting-started.md index 115bcce2..164412b9 100644 --- a/documentation/guides/getting-started.md +++ b/documentation/guides/getting-started.md @@ -2,18 +2,21 @@ The first step is to decide if you're building a phoenix application or not. Phoenix is an extremely high quality web framework, and is the suggested pairing with Ash if you expect to be building a front end, or an API. For this guide, we assume that elixir has already been installed. -## Installing Phoenix +## Installing With Phoenix -First, install the phoenix installation archive. +Install the Phoenix installation archive and then create a new Phoenix application. Be sure to look over the options available with `mix help phx.new`, and visit the phoenix [Phoenix Documentation](https://www.phoenixframework.org/) for more information. ```bash mix archive.install hex phx_new +mix phx.new --live ``` -Then, create a new phx application. Generally, we suggest installing liveview as well, via the `--live` flag. See the phoenix [Phoenix Documentation](https://www.phoenixframework.org/) fore more information. +## Installing Without Phoenix + +Create a new application. Be sure to look aver the options available with `mix help new`. ```bash -mix phx.new --live +mix new ``` ## Adding Ash @@ -21,7 +24,7 @@ mix phx.new --live 1. First off, add Ash as a dependency. In `mix.exs`, add `{:ash, "~> 1.52.0-rc.0"}` to your dependencies. -2. Next, add an API. To start, a simple choice for naming your first API is a good name for the "core" of your application. For example, a help-desk application called "AwesomeDesk" might start with an API module called `AwesomeDesk.Tickets`. Create `lib/my_app/my_api/my_api.ex`, with the following contents: +2. Next, add an API. To start, a simple choice for naming your first API is a good name for the "core" of your application. For example, a help-desk application called "AwesomeDesk" might start with an API module called `AwesomeDesk.Tickets`. Create an `lib/my_app/my_api/my_api.ex`, with the following contents: ```elixir defmodule MyApp.MyApi do @@ -81,7 +84,7 @@ mix phx.new --live end ``` -6. Add your resource to the registry that you created +6. Add your [resource](../concepts/resource.md) to the registry that you created ```elixir @@ -90,7 +93,9 @@ mix phx.new --live end ``` -7. You can't do anything without adding [actions](../concepts/actions.md) -8. Add some actions to your resource -9. Try it out. Currently, your resource won't do much. The `defaults` option creates +7. Resources are static descriptions of behavior, and don't do anything on their own. To give them functionality, we must first add [actions](../concepts/actions.md). +8. +9. can't do anything **without** adding [actions](../concepts/actions.md). So to pro +10. Add some actions to your resource +11. Try it out. Currently, your resource won't do much. The `defaults` option creates four available "actions" \ No newline at end of file diff --git a/documentation/info/f_a_q.md b/documentation/info/f_a_q.md new file mode 100644 index 00000000..e69de29b diff --git a/documentation/info/glossary.md b/documentation/info/glossary.md new file mode 100644 index 00000000..e69de29b diff --git a/documentation/info/upgrading.md b/documentation/info/upgrading.md index e276ed04..a63434e9 100644 --- a/documentation/info/upgrading.md +++ b/documentation/info/upgrading.md @@ -14,4 +14,4 @@ end ### Primary Actions -Primary actions have been simplified for 2.0.0. If there was a single action of a given type before, it would have been marked as `primary?` automatically. Now, `primary?` actions are fully optional, although you may still want to configure them. Certain things like [managing relationships](managing_relationships.md) can be much simpler when paired with primary actions. For a fully explicit experience everywhere, however, you may want to skip primary actions altogether. To make sure your application behaves the same, go to each of your resources and check to see if they only have one action of each type. If they do, mark that single action as `primary?`. \ No newline at end of file +Primary actions have been simplified for 2.0.0. If there was a single action of a given type before, it would have been marked as `primary?` automatically. Now, `primary?` actions are fully optional, although you may still want to configure them. Certain things like [managing relationships](managing_relationships.md) can be much simpler when paired with primary actions. For a fully explicit experience everywhere, however, you may want to skip primary actions altogether. To make sure your application behaves the same, go to each of your resources and check to see if they only have one action of each type. If they do, mark that single action as `primary?`. Additionally, the `primary_actions?` option has been removed now that all primary actions are explicit. \ No newline at end of file diff --git a/lib/ash/doc_index/doc_index.ex b/lib/ash/doc_index/doc_index.ex index 4221d1ea..2333f230 100644 --- a/lib/ash/doc_index/doc_index.ex +++ b/lib/ash/doc_index/doc_index.ex @@ -46,7 +46,7 @@ defmodule Ash.DocIndex do end) @impl Ash.DocIndex - def guides() do + def guides do @files end end @@ -56,8 +56,7 @@ defmodule Ash.DocIndex do def to_name(string) do string |> String.split(~r/[-_]/, trim: true) - |> Enum.map(&String.capitalize/1) - |> Enum.join(" ") + |> Enum.map_join(" ", &String.capitalize/1) end def to_path(string) do diff --git a/lib/ash/resource/dsl.ex b/lib/ash/resource/dsl.ex index 6434af77..68f139e8 100644 --- a/lib/ash/resource/dsl.ex +++ b/lib/ash/resource/dsl.ex @@ -553,29 +553,6 @@ defmodule Ash.Resource.Dsl do type, as long as they have different names. This is the primary mechanism for customizing your resources to conform to your business logic. It is normal and expected to have multiple actions of each type in a large application. - - ## Primary actions - - If you have multiple actions of the same type, one of them must be designated as the - primary action for that type, via the `primary?` option. This tells the ash what to do - if an action of that type is requested, but no specific action name is given. This is how - many relationship changes will happen, by utilizing the primary actions. For this reason, - ** when defining actions, you usually want to ensure that the primary action takes no required - arguments **. Without that, relationship changes to your resources might fail due to missing - arguments. This does, however, allow you to customize exactly how related entities are read/ - created. - - - ## Turning primary actions off - - If you want an extremely explicit experience with actions, you can specify the following option: - ```elixir - primary_actions? false - ``` - - This will prevent Ash from adding a default implementation of each action type, as well as cause any calls - to `Ash.Resource.Info.primary_action/2` to raise an error. This is experimental, but any errors raised in appropriate - places can be addressed, just report them as issues. """, imports: [ Ash.Resource.Change.Builtins, @@ -584,16 +561,6 @@ defmodule Ash.Resource.Dsl do Ash.Filter.TemplateHelpers ], schema: [ - primary_actions?: [ - type: {:in, [true, false, :only_read]}, - default: true, - doc: """ - Causes any calls to `Ash.Resource.Info.primary_action/2` to raise an error. This is experimental, but any errors raised in appropriate - places can be addressed, just report them as issues. Use `:read_only` to allow primary actions only for reads. - This can be useful to avoid constantly having to provide the basic `read` action of a given resource, when you need - to do things like loading data. - """ - ], defaults: [ type: {:list, {:in, [:create, :read, :update, :destroy]}}, doc: """ diff --git a/lib/ash/resource/info.ex b/lib/ash/resource/info.ex index 5c866d53..ae45c06a 100644 --- a/lib/ash/resource/info.ex +++ b/lib/ash/resource/info.ex @@ -423,37 +423,15 @@ defmodule Ash.Resource.Info do @spec primary_action(Ash.Resource.t(), Ash.Resource.Actions.action_type()) :: Ash.Resource.Actions.action() | nil def primary_action(resource, type) do - primary_actions? = primary_actions?(resource) - - cond do - primary_actions? == :only_read && type != :read -> - raise "Cannot use primary #{type} actions with resource: #{inspect(resource)}, because `primary_actions?` is set to `:only_read`" - - !primary_actions? -> - raise "Cannot use primary actions with resource: #{inspect(resource)}, because `primary_actions?` is set to `false`" - - true -> - resource - |> actions() - |> Enum.filter(&(&1.type == type)) - |> case do - [action] -> action - actions -> Enum.find(actions, & &1.primary?) - end + resource + |> actions() + |> Enum.filter(&(&1.type == type)) + |> case do + [action] -> action + actions -> Enum.find(actions, & &1.primary?) end end - @doc "Returns the value of the primary_actions? configuration" - @spec primary_actions?(Ash.Resource.t()) :: boolean | :only_read - def primary_actions?(resource) do - Extension.get_opt( - resource, - [:actions], - :primary_actions?, - true - ) - end - @doc "Returns the configured default actions" @spec default_actions(Ash.Resource.t()) :: list(:create | :read | :update | :destroy) def default_actions(resource) do diff --git a/lib/ash/resource/transformers/set_primary_actions.ex b/lib/ash/resource/transformers/set_primary_actions.ex index 2ece8e59..026c269d 100644 --- a/lib/ash/resource/transformers/set_primary_actions.ex +++ b/lib/ash/resource/transformers/set_primary_actions.ex @@ -12,8 +12,6 @@ defmodule Ash.Resource.Transformers.ValidatePrimaryActions do @extension Ash.Resource.Dsl def transform(resource, dsl_state) do - primary_actions? = Ash.Resource.Info.primary_actions?(resource) - dsl_state = add_defaults(dsl_state, resource) dsl_state @@ -40,23 +38,19 @@ defmodule Ash.Resource.Transformers.ValidatePrimaryActions do )}} {type, actions}, {:ok, dsl_state} -> - if primary_actions? && !(primary_actions? == :only_read && type != :read) do - case Enum.count_until(actions, & &1.primary?, 2) do - 2 -> - {:halt, - {:error, - DslError.exception( - module: resource, - message: - "Multiple actions of type #{type} configured as `primary?: true`, but only one action per type can be the primary", - path: [:actions, type] - )}} + case Enum.count_until(actions, & &1.primary?, 2) do + 2 -> + {:halt, + {:error, + DslError.exception( + module: resource, + message: + "Multiple actions of type #{type} configured as `primary?: true`, but only one action per type can be the primary", + path: [:actions, type] + )}} - _ -> - {:cont, {:ok, dsl_state}} - end - else - {:cont, {:ok, dsl_state}} + _ -> + {:cont, {:ok, dsl_state}} end end) end diff --git a/test/elixir_sense/plugin_test.exs b/test/elixir_sense/plugin_test.exs index 092f2813..a38a1d9f 100644 --- a/test/elixir_sense/plugin_test.exs +++ b/test/elixir_sense/plugin_test.exs @@ -238,7 +238,7 @@ defmodule Ash.ElixirSense.PluginTest do result = suggestions(buffer, cursor) labels = result |> Enum.map(& &1.label) |> Enum.sort() - assert labels == ["create", "defaults", "destroy", "primary_actions?", "read", "update"] + assert labels == ["create", "defaults", "destroy", "read", "update"] end test "suggesting available options and entities" do