add ash_tutorial
This commit is contained in:
230
ash_tutorial/code_interfaces.livemd
Normal file
230
ash_tutorial/code_interfaces.livemd
Normal file
@@ -0,0 +1,230 @@
|
||||
# Ash: 7 - Code Interfaces
|
||||
|
||||
```elixir
|
||||
Application.put_env(:ash, :validate_domain_resource_inclusion?, false)
|
||||
Application.put_env(:ash, :validate_domain_config_inclusion?, false)
|
||||
Mix.install([{:ash, "~> 3.0"}], consolidate_protocols: false)
|
||||
```
|
||||
|
||||
## Code Interfaces
|
||||
|
||||
<div class="flex items-center w-full flex-start justify-between rounded-xl p-4" style="background-color: #f0f5f9; color: #61758a;">
|
||||
<div class="flex">
|
||||
<i class="ri-arrow-left-fill"></i>
|
||||
<a class="flex ml-2" style="color: #61758a;" href="relationships.livemd">Relationships</a>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<i class="ri-home-fill"></i>
|
||||
<a class="flex ml-2" style="color: #61758a;" href="overview.livemd">Home</a>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<a class="flex mr-2" style="color: #61758a;" href="aggregates.livemd">Aggregates</a>
|
||||
<i class="ri-arrow-right-fill"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
### In this tutorial you will add a Code Interface
|
||||
|
||||
In previous tutorials you always used `Changesets` directly to CRUD on resources.
|
||||
While this is a perfectly valid way of handling CRUD operations, Ash provides [Code Interfaces](https://hexdocs.pm/ash/code-interfaces.html) to make this more ergonomic for you.
|
||||
The code interface can be defined on the resources *or* the domain. In this example, we will define it on the domain.
|
||||
|
||||
You will add 3 code interfaces for:
|
||||
|
||||
* Creating a representative
|
||||
* Opening a ticket
|
||||
* Assigning a representative
|
||||
|
||||
For each resource you want to add a code interface to, inside that resource definition on the domain, you will place "definitions".
|
||||
For example:
|
||||
|
||||
<!-- livebook:{"force_markdown":true} -->
|
||||
|
||||
```elixir
|
||||
resource MyApp.Tweet do
|
||||
define :create_tweet, args: [:content]
|
||||
end
|
||||
```
|
||||
|
||||
<!-- livebook:{"break_markdown":true} -->
|
||||
|
||||
Then add the 3 interfaces by defining the following inside the `Ticket` resource's block:
|
||||
|
||||
* `define :assign_ticket, args: [:representative_id], action: :assign`
|
||||
* `define :open_ticket, args: [:subject, :description], action: :open`
|
||||
|
||||
And the following inside the `Representative` resource's block.
|
||||
|
||||
* `define :create_representative, args: [:name], action: :create`
|
||||
|
||||
<details class="rounded-lg my-4" style="background-color: #96ef86; color: #040604;">
|
||||
<summary class="cursor-pointer font-bold p-4"></i>Show Solution</summary>
|
||||
<div class="p-4">
|
||||
|
||||
```elixir
|
||||
resource Tutorial.Support.Ticket do
|
||||
define :assign_ticket, args: [:representative_id], action: :assign
|
||||
define :open_ticket, args: [:subject, :description], action: :open
|
||||
end
|
||||
|
||||
resource Tutorial.Support.Representative do
|
||||
define :create_representative, args: [:name], action: :create
|
||||
end
|
||||
```
|
||||
|
||||
</div>
|
||||
</details>
|
||||
|
||||
### Enter your solution
|
||||
|
||||
```elixir
|
||||
defmodule Tutorial.Support.Ticket do
|
||||
use Ash.Resource,
|
||||
domain: Tutorial.Support,
|
||||
data_layer: Ash.DataLayer.Ets
|
||||
|
||||
actions do
|
||||
defaults [:read]
|
||||
|
||||
create :open do
|
||||
accept [:subject, :description]
|
||||
end
|
||||
|
||||
update :close do
|
||||
change set_attribute(:status, :closed)
|
||||
end
|
||||
|
||||
update :assign do
|
||||
accept [:representative_id]
|
||||
end
|
||||
end
|
||||
|
||||
attributes do
|
||||
uuid_primary_key :id
|
||||
attribute :subject, :string, allow_nil?: false
|
||||
attribute :description, :string, allow_nil?: true
|
||||
|
||||
attribute :status, :atom do
|
||||
constraints one_of: [:open, :closed]
|
||||
default :open
|
||||
allow_nil? false
|
||||
end
|
||||
|
||||
create_timestamp :created_at
|
||||
update_timestamp :updated_at
|
||||
end
|
||||
|
||||
relationships do
|
||||
belongs_to :representative, Tutorial.Support.Representative
|
||||
end
|
||||
end
|
||||
|
||||
defmodule Tutorial.Support.Representative do
|
||||
use Ash.Resource,
|
||||
domain: Tutorial.Support,
|
||||
data_layer: Ash.DataLayer.Ets
|
||||
|
||||
actions do
|
||||
defaults [:read]
|
||||
|
||||
create :create do
|
||||
accept [:name]
|
||||
end
|
||||
end
|
||||
|
||||
attributes do
|
||||
uuid_primary_key :id
|
||||
attribute :name, :string
|
||||
end
|
||||
end
|
||||
|
||||
defmodule Tutorial.Support do
|
||||
use Ash.Domain
|
||||
|
||||
resources do
|
||||
resource Tutorial.Support.Ticket do
|
||||
# <-- Add ticket code interface here
|
||||
end
|
||||
|
||||
resource Tutorial.Support.Representative do
|
||||
# <-- Add representative code interface here
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
## Using the Code Interfaces
|
||||
|
||||
Create a Representative, but instead of using a changeset, use the code interface you created.
|
||||
|
||||
You can use a code interface by calling `Tutorial.Support.create_representative!/1` with the desired name.
|
||||
|
||||
Set the name to `Joe Armstrong` and store it in a `joe` variable.
|
||||
|
||||
<details class="rounded-lg my-4" style="background-color: #96ef86; color: #040604;">
|
||||
<summary class="cursor-pointer font-bold p-4"></i>Show Solution</summary>
|
||||
<div class="p-4">
|
||||
|
||||
```elixir
|
||||
joe = Tutorial.Support.create_representative!("Joe Armstrong")
|
||||
```
|
||||
|
||||
</div>
|
||||
</details>
|
||||
|
||||
```elixir
|
||||
|
||||
```
|
||||
|
||||
Create a Ticket with the created code interface.
|
||||
|
||||
Call `Tutorial.Support.open_ticket!/2` with a `subject` and `description` and store the result in a `ticket` variable.
|
||||
|
||||
<details class="rounded-lg my-4" style="background-color: #96ef86; color: #040604;">
|
||||
<summary class="cursor-pointer font-bold p-4"></i>Show Solution</summary>
|
||||
<div class="p-4">
|
||||
|
||||
```elixir
|
||||
ticket = Tutorial.Support.open_ticket!("I can't find my hand!", "I think someone stole it.")
|
||||
```
|
||||
|
||||
</div>
|
||||
</details>
|
||||
|
||||
```elixir
|
||||
|
||||
```
|
||||
|
||||
Assign the Representative `joe` to the Ticket using the code Interface.
|
||||
|
||||
Use `Tutorial.Support.assign_ticket!/2`.
|
||||
|
||||
<details class="rounded-lg my-4" style="background-color: #96ef86; color: #040604;">
|
||||
<summary class="cursor-pointer font-bold p-4"></i>Show Solution</summary>
|
||||
<div class="p-4">
|
||||
|
||||
```elixir
|
||||
Tutorial.Support.assign_ticket!(ticket, joe.id)
|
||||
```
|
||||
|
||||
</div>
|
||||
</details>
|
||||
|
||||
```elixir
|
||||
|
||||
```
|
||||
|
||||
<div class="flex items-center w-full flex-start justify-between rounded-xl p-4" style="background-color: #f0f5f9; color: #61758a;">
|
||||
<div class="flex">
|
||||
<i class="ri-arrow-left-fill"></i>
|
||||
<a class="flex ml-2" style="color: #61758a;" href="relationships.livemd">Managing Relationships</a>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<i class="ri-home-fill"></i>
|
||||
<a class="flex ml-2" style="color: #61758a;" href="overview.livemd">Home</a>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<a class="flex mr-2" style="color: #61758a;" href="aggregates.livemd">Aggregates</a>
|
||||
<i class="ri-arrow-right-fill"></i>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user