2022-08-23 13:56:06 +12:00
# Get Started
2022-08-19 13:25:45 +12:00
2023-09-02 07:40:32 +12:00
## Learn with Livebook
We have a basic step by step tutorial in Livebook that introduces you to Ash. No prior Ash knowledge is required.
2023-09-02 12:12:57 +12:00
The Livebook tutorial is self contained and separate from the documentation below.
2023-09-02 07:40:32 +12:00
[![Run in Livebook ](https://livebook.dev/badge/v1/pink.svg )](https://livebook.dev/run?url=https%3A%2F%2Fgithub.com%2Fash-project%2Fash_tutorial%2Fblob%2Fmaster%2Foverview.livemd)
2022-08-19 13:25:45 +12:00
## Goals
In this guide we will:
2022-08-19 13:36:58 +12:00
1. Create a new Elixir application and add Ash as a dependency
2024-04-05 09:24:55 +13:00
2. Create a simple set of resources and see how they can be used
3. Go over some core concepts f Ash
4. Find out what material might be good to visit next
2022-08-19 13:25:45 +12:00
## Requirements
If you want to follow along yourself, you will need the following things:
2024-04-05 09:24:55 +13:00
1. [Elixir and Erlang installed ](https://elixir-lang.org/install.html )
2022-08-19 13:25:45 +12:00
2. A text editor to make the changes that we make
2022-11-03 20:03:43 +13:00
3. A terminal to run the examples using `iex`
2022-08-19 13:25:45 +12:00
## Steps
For this tutorial, we'll use examples based around creating a help desk.
We will make the following resources:
2022-09-14 04:35:29 +12:00
- `Helpdesk.Support.Ticket`
- `Helpdesk.Support.Representative`
2022-08-19 13:25:45 +12:00
The actions we will be able to take on these resources include:
2022-11-03 20:03:43 +13:00
- Opening a new Ticket
- Closing a Ticket
- Assigning a Ticket to a representative
2022-08-19 13:25:45 +12:00
### Create a new project
2024-04-05 09:24:55 +13:00
We first create a new project with the `--sup` flag to add a supervision tree. This will be necessary for other follow-up tutorials.
2022-08-19 13:25:45 +12:00
```bash
# In your terminal
mix new --sup helpdesk & & cd helpdesk
```
It is a good idea to make it a git repository and commit the initial project. You'll be able to see what changes we made, and can save your changes once we're done.
```bash
# Run in your terminal
git init
git add -A
2022-10-24 17:07:51 +13:00
git commit -m "first commit"
git branch -M main
2022-08-19 13:25:45 +12:00
```
Open the project in your text editor, and we'll get started.
### Add Ash to your application
2024-04-05 09:24:55 +13:00
Add the `ash` and `picosat_elixir` dependencies to your `mix.exs`
2022-08-19 13:25:45 +12:00
2022-10-24 17:07:51 +13:00
```elixir
defp deps do
[
2024-04-05 09:24:55 +13:00
{:ash, "~> 3.0"},
{:picosat_elixir, "~> 0.2"}
2022-10-24 17:07:51 +13:00
]
end
```
2022-08-19 13:25:45 +12:00
2024-04-05 09:24:55 +13:00
And then run `mix deps.get && mix deps.compile` to install the dependencies
2024-03-28 09:06:40 +13:00
2024-04-05 09:24:55 +13:00
> ### Picosat installation issues? {: .info}
>
> Replace `{:picosat_elixir, "~> 0.2"}` with `{:simple_sat, "~> 0.1"}` to use a simpler (but mildly slower) solver. You can always switch back to `picosat_elixir` later once you're done with the tutorial.
2024-03-28 09:06:40 +13:00
### Formatting
2023-12-05 19:32:29 +13:00
To ensure that your code stays formatted like the examples here, you can add `:ash` as an import dependency in your `.formatter.exs` :
2022-08-19 13:25:45 +12:00
```elixir
[
2023-12-05 19:32:29 +13:00
# ...
import_deps: [..., :ash],
# ...
2022-08-19 13:25:45 +12:00
]
```
2023-12-05 19:32:29 +13:00
> #### Note {: .neutral}
>
2023-12-08 14:27:00 +13:00
> For more auto-formatting options, see the [Auto-Format Code guide](/documentation/how_to/auto-format-code.md).
2023-12-05 19:32:29 +13:00
And run `mix deps.get` , to install the dependency.
2022-08-19 13:25:45 +12:00
2024-03-28 09:06:40 +13:00
### Building your first Ash Domain
2022-08-19 13:25:45 +12:00
2024-03-28 09:06:40 +13:00
The basic building blocks of an Ash application are Ash resources. They are tied together by a domain module, which will allow you to interact with those resources.
2022-11-03 20:03:43 +13:00
### Creating our first resource
2024-03-28 09:06:40 +13:00
Let's start by creating our first resource along with our first domain. We will create the following files:
2022-08-19 13:25:45 +12:00
2024-04-05 09:24:55 +13:00
- The domain `Helpdesk.Support` , in `lib/helpdesk/support.ex`
- Our Ticket resource `Helpdesk.Support.Ticket` , in `lib/helpdesk/support/ticket.ex` .
2022-08-19 13:25:45 +12:00
2022-10-24 17:07:51 +13:00
To create the required folders and files, you can use the following command in your terminal:
2022-08-19 13:25:45 +12:00
```bash
2023-12-21 03:36:00 +13:00
mkdir -p lib/helpdesk/support & & touch $_/ticket.ex
2022-09-28 19:38:38 +13:00
touch lib/helpdesk/support.ex
2022-09-14 04:35:29 +12:00
```
2024-04-05 09:24:55 +13:00
Your project structure should now include the following files:
2022-09-14 04:35:29 +12:00
```
lib/
├─ helpdesk/
│ ├─ support/
2023-12-21 03:36:00 +13:00
│ │ ├─ ticket.ex
2022-09-14 04:35:29 +12:00
│ ├─ support.ex
2022-08-19 13:25:45 +12:00
```
Add the following to the files we created
2024-03-28 09:06:40 +13:00
```elixir
# lib/helpdesk/support.ex
defmodule Helpdesk.Support do
use Ash.Domain
resources do
resource Helpdesk.Support.Ticket
end
end
```
2022-08-19 13:25:45 +12:00
```elixir
2023-12-21 03:36:00 +13:00
# lib/helpdesk/support/ticket.ex
2022-08-19 13:25:45 +12:00
2022-09-14 04:35:29 +12:00
defmodule Helpdesk.Support.Ticket do
2022-08-19 13:25:45 +12:00
# This turns this module into a resource
2024-03-28 09:06:40 +13:00
use Ash.Resource, domain: Helpdesk.Support
2022-08-19 13:25:45 +12:00
actions do
2024-04-05 09:24:55 +13:00
# Use the default implementation of the :read action
defaults [:read]
2024-04-07 07:20:22 +12:00
# and a create action, which we'll customize later
2024-04-05 09:24:55 +13:00
create :create
2022-08-19 13:25:45 +12:00
end
# Attributes are the simple pieces of data that exist on your resource
attributes do
# Add an autogenerated UUID primary key called `:id` .
uuid_primary_key :id
# Add a string type attribute called `:subject`
attribute :subject, :string
end
end
```
2024-03-28 09:06:40 +13:00
Next, add your domain to your `config.exs`
2023-01-26 04:16:16 +13:00
Run the following to create your `config.exs` if it doesn't already exist
```elixir
mkdir -p config
touch config/config.exs
```
2024-04-05 09:24:55 +13:00
and add the following contents to it.
2023-01-26 04:16:16 +13:00
```elixir
# in config/config.exs
import Config
2024-03-28 09:06:40 +13:00
config :helpdesk, :ash_domains, [Helpdesk.Support]
2023-01-26 04:16:16 +13:00
```
2022-08-19 13:25:45 +12:00
### Try our first resource out
2024-04-05 09:24:55 +13:00
Run `iex -S mix` in your project's root directory and try out the following.
2022-08-19 13:25:45 +12:00
2024-03-28 09:06:40 +13:00
To create a ticket, we first make an `Ash.Changeset` for the `:create` action of the `Helpdesk.Support.Ticket` resource. Then we pass it to the `Ash.create!/1` function.
2022-08-19 13:25:45 +12:00
```elixir
2022-09-14 04:35:29 +12:00
Helpdesk.Support.Ticket
2022-08-19 13:25:45 +12:00
|> Ash.Changeset.for_create(:create)
2024-03-28 09:06:40 +13:00
|> Ash.create!()
2022-08-19 13:25:45 +12:00
```
This returns what we call a `record` which is an instance of a resource.
```elixir
2022-10-08 04:20:33 +13:00
#Helpdesk.Support.Ticket<
2022-08-19 13:25:45 +12:00
...,
id: "c0f8dc32-a018-4eb4-8656-d5810118f4ea",
subject: nil,
...
2022-10-08 04:20:33 +13:00
>
2022-08-19 13:25:45 +12:00
```
### Customizing our Actions
2024-04-05 09:24:55 +13:00
One thing you may have noticed earlier is that we created a ticket without providing any input, and as a result our ticket had a `subject` of `nil` . Additionally, we don't have any other data on the ticket. Lets add a `status` attribute, ensure that `subject` can't be `nil` , and provide a better interface by giving the `:create` action a better name, and accepting `:subject` as part of the action.
2022-08-19 13:25:45 +12:00
We'll start with the attribute changes:
```elixir
2023-12-21 03:36:00 +13:00
# lib/helpdesk/support/ticket.ex
2022-09-14 04:35:29 +12:00
2022-08-19 13:25:45 +12:00
attributes do
...
attribute :subject, :string do
# Don't allow `nil` values
allow_nil? false
2024-03-30 04:12:19 +13:00
# Allow this attribute to be public. By default, all attributes are private.
public? true
2022-08-19 13:25:45 +12:00
end
# status is either `open` or `closed` . We can add more statuses later
attribute :status, :atom do
# Constraints allow you to provide extra rules for the value.
# The available constraints depend on the type
# See the documentation for each type to know what constraints are available
# Since atoms are generally only used when we know all of the values
# it provides a `one_of` constraint, that only allows those values
constraints [one_of: [:open, :closed]]
# The status defaulting to open makes sense
default :open
# We also don't want status to ever be `nil`
allow_nil? false
end
end
```
2024-04-05 09:24:55 +13:00
And then replace the `:create` action with `:open` , and accept `:ubject` as input.
2022-08-19 13:25:45 +12:00
```elixir
2023-12-21 03:36:00 +13:00
# lib/helpdesk/support/ticket.ex
2022-09-14 04:35:29 +12:00
2022-08-19 13:25:45 +12:00
actions do
...
create :open do
accept [:subject]
end
end
```
2022-11-03 20:03:43 +13:00
Let's try these changes in `iex` :
2022-08-19 13:25:45 +12:00
We use `create!` with an exclamation point here because that will raise the error which gives a nicer view of the error in `iex`
```elixir
# Use this to pick up changes you've made to your code, or restart your session
2022-09-14 04:35:29 +12:00
recompile()
2022-08-19 13:25:45 +12:00
2022-09-14 04:35:29 +12:00
Helpdesk.Support.Ticket
2022-08-19 13:25:45 +12:00
|> Ash.Changeset.for_create(:open, %{subject: "My mouse won't click!"})
2024-04-05 09:24:55 +13:00
|> Ash.create!()
2022-08-19 13:25:45 +12:00
```
And we can see our newly created ticket with a subject and a status.
```elixir
2022-09-14 04:35:29 +12:00
#Helpdesk.Support.Ticket<
2022-08-19 13:25:45 +12:00
...
id: "3c94d310-7b5e-41f0-9104-5b193b831a5d",
status: :open,
subject: "My mouse won't click!",
...
>
```
2022-11-03 20:03:43 +13:00
If we didn't include a subject, or left off the arguments completely, we would see an error instead
2022-08-19 13:25:45 +12:00
```text
2024-03-28 09:06:40 +13:00
** (Ash.Error.Invalid) Invalid Error
2022-08-19 13:25:45 +12:00
* attribute subject is required
```
2022-08-20 10:35:22 +12:00
### Updates and validations
2022-11-03 20:03:43 +13:00
Now let's add some logic to close a ticket. This time we'll add an `update` action.
2022-08-20 10:35:22 +12:00
2023-01-18 18:34:20 +13:00
Here we will use a `change` . Changes allow you to customize how an action executes with very fine-grained control. There are built-in changes that are automatically available as functions, but you can define your own and pass it in as shown below. You can add multiple, and they will be run in order. See the [Actions guide ](/documentation/topics/actions.md ) for more.
2022-08-20 10:35:22 +12:00
```elixir
2023-12-21 03:36:00 +13:00
# lib/helpdesk/support/ticket.ex
2022-09-14 04:35:29 +12:00
2022-08-20 10:35:22 +12:00
actions do
...
update :close do
# We don't want to accept any input here
2022-09-14 04:35:29 +12:00
accept []
2022-08-20 10:35:22 +12:00
2024-04-05 09:24:55 +13:00
validate attribute_does_not_equal(:status, :closed) do
message "Ticket is already closed"
end
2022-08-20 10:35:22 +12:00
change set_attribute(:status, :closed)
# A custom change could be added like so:
#
# change MyCustomChange
# change {MyCustomChange, opt: :val}
end
end
```
2022-11-03 20:03:43 +13:00
Try out opening and closing a ticket in `iex` :
2022-08-20 10:35:22 +12:00
```elixir
2022-10-24 17:07:51 +13:00
# Use this to pick up changes you've made to your code, or restart your session
recompile()
2022-08-20 10:35:22 +12:00
# parenthesis so you can paste into iex
ticket = (
2022-09-14 04:35:29 +12:00
Helpdesk.Support.Ticket
2022-08-20 10:35:22 +12:00
|> Ash.Changeset.for_create(:open, %{subject: "My mouse won't click!"})
2024-04-05 09:24:55 +13:00
|> Ash.create!()
2022-08-20 10:35:22 +12:00
)
ticket
|> Ash.Changeset.for_update(:close)
2024-04-05 09:24:55 +13:00
|> Ash.update!()
2022-08-20 10:35:22 +12:00
2022-09-14 04:35:29 +12:00
#Helpdesk.Support.Ticket<
2022-08-20 10:35:22 +12:00
...
status: :closed,
subject: "My mouse won't click!",
...
>
```
### Querying without persistence
2022-11-03 20:03:43 +13:00
So far we haven't used a data layer that does any persistence, like storing records in a database. All that this simple resource does is return the record back to us. You can see this lack of persistence by attempting to use a `read` action:
2022-08-20 10:35:22 +12:00
```elixir
2024-04-05 09:24:55 +13:00
Ash.read!(Helpdesk.Support.Ticket)
2022-08-20 10:35:22 +12:00
```
Which will raise an error explaining that there is no data to be read for that resource.
2022-11-03 20:03:43 +13:00
In order to save our data somewhere, we need to add a data layer to our resources. Before we do that, however, let's go over how Ash allows us to work against many different data layers (or even no data layer at all).
Resources without a data layer will implicitly be using `Ash.DataLayer.Simple` , which will just return structs and won't actually store anything. The way that we make our queries return some data is by leveraging `context` , a free-form map available on queries and changesets. The simple data layer looks for `query.context[:data_layer][:data][resource]` . It provides a utility, `Ash.DataLayer.Simple.set_data/2` to set it.
2022-08-20 10:35:22 +12:00
2022-11-03 20:03:43 +13:00
Try the following in `iex` . We will open some tickets, and close some of them, and then use `Ash.DataLayer.Simple.set_data/2` to use those tickets.
2022-08-20 10:35:22 +12:00
```elixir
# Ash.Query is a macro, so it must be required
require Ash.Query
2022-09-14 04:35:29 +12:00
tickets =
2022-08-20 10:35:22 +12:00
for i < - 0 . . 5 do
2022-09-14 04:35:29 +12:00
ticket =
Helpdesk.Support.Ticket
2022-08-20 10:35:22 +12:00
|> Ash.Changeset.for_create(:open, %{subject: "Issue #{i}"})
2024-04-05 09:24:55 +13:00
|> Ash.create!()
2022-09-14 04:35:29 +12:00
2022-08-20 10:35:22 +12:00
if rem(i, 2) == 0 do
ticket
|> Ash.Changeset.for_update(:close)
2024-04-05 09:24:55 +13:00
|> Ash.update!()
2022-08-20 10:35:22 +12:00
else
ticket
end
end
2022-11-03 20:03:43 +13:00
```
2022-08-20 10:35:22 +12:00
2022-11-03 20:03:43 +13:00
Find the tickets where the subject contains `"2"` . Note that the we're setting the ticket data that we're querying using `set_data` .
2022-08-20 10:35:22 +12:00
2022-11-03 20:03:43 +13:00
```elixir
2022-09-14 04:35:29 +12:00
Helpdesk.Support.Ticket
2022-08-20 10:35:22 +12:00
|> Ash.Query.filter(contains(subject, "2"))
|> Ash.DataLayer.Simple.set_data(tickets)
2024-04-05 09:24:55 +13:00
|> Ash.read!()
2022-11-03 20:03:43 +13:00
```
2022-08-20 10:35:22 +12:00
2022-11-03 20:03:43 +13:00
Find the tickets that are _closed_ and their subject does _not_ contain `"4"`
```elixir
2022-09-14 04:35:29 +12:00
Helpdesk.Support.Ticket
2022-08-25 03:53:48 +12:00
|> Ash.Query.filter(status == :closed and not(contains(subject, "4")))
2022-08-20 10:35:22 +12:00
|> Ash.DataLayer.Simple.set_data(tickets)
2024-04-05 09:24:55 +13:00
|> Ash.read!()
2022-08-20 10:35:22 +12:00
```
2023-01-18 18:34:20 +13:00
The examples above could be easily implemented with `Enum.filter` , but the real power here is to allow you to use the same tools when working with any data layer. If you were using the `AshPostgres.DataLayer` data layer.
2022-11-03 20:03:43 +13:00
Even though it doesn't persist data in any way, `Ash.DataLayer.Simple` can be useful to model static data, or be used for resources where all the actions are manual and inject data from other sources.
2022-08-20 10:35:22 +12:00
### Adding basic persistence
2022-11-03 20:03:43 +13:00
Before we get into working with relationships, let's add some real persistence to our resource. This will let us add relationships and try out querying data.
2022-08-20 10:35:22 +12:00
2022-11-03 20:03:43 +13:00
There is a built in data layer that is useful for testing and prototyping, that uses [ETS ](https://elixir-lang.org/getting-started/mix-otp/ets.html ). ETS (Erlang Term Storage) is OTP's in-memory database, so the data won't actually stick around beyond the lifespan of your program, but it's a simple way to try things out.
2022-08-20 10:35:22 +12:00
2022-10-16 04:16:38 +13:00
To add it to your resource, modify it like so:
2022-08-20 10:35:22 +12:00
```elixir
2023-12-21 03:36:00 +13:00
# lib/helpdesk/support/ticket.ex
2022-10-24 17:07:51 +13:00
2022-08-20 10:35:22 +12:00
use Ash.Resource,
2024-04-05 09:24:55 +13:00
domain: Helpdesk.Support,
2022-08-20 10:35:22 +12:00
data_layer: Ash.DataLayer.Ets
```
2022-11-03 20:03:43 +13:00
Now we can slightly modify our code above, by removing the `Ash.DataLayer.Simple.set_data/2` calls, and we can see our persistence in action. Remember, ETS is in-memory, meaning restarting your application/iex session will remove all of the data.
2022-08-20 10:35:22 +12:00
```elixir
2022-10-24 17:07:51 +13:00
# Use this to pick up changes you've made to your code, or restart your session
recompile()
2022-08-20 10:35:22 +12:00
require Ash.Query
for i < - 0 . . 5 do
2022-09-14 04:35:29 +12:00
ticket =
Helpdesk.Support.Ticket
2022-08-20 10:35:22 +12:00
|> Ash.Changeset.for_create(:open, %{subject: "Issue #{i}"})
2024-04-05 09:24:55 +13:00
|> Ash.create!()
2022-09-14 04:35:29 +12:00
2022-08-20 10:35:22 +12:00
if rem(i, 2) == 0 do
ticket
|> Ash.Changeset.for_update(:close)
2024-04-05 09:24:55 +13:00
|> Ash.update!()
2022-08-20 10:35:22 +12:00
end
end
# Show the tickets where the subject contains "2"
2022-09-14 04:35:29 +12:00
Helpdesk.Support.Ticket
2022-08-20 10:35:22 +12:00
|> Ash.Query.filter(contains(subject, "2"))
2024-04-05 09:24:55 +13:00
|> Ash.read!()
2022-08-20 10:35:22 +12:00
# Show the tickets that are closed and their subject does not contain "4"
2022-09-14 04:35:29 +12:00
Helpdesk.Support.Ticket
2022-08-25 03:53:48 +12:00
|> Ash.Query.filter(status == :closed and not(contains(subject, "4")))
2024-04-05 09:24:55 +13:00
|> Ash.read!()
2022-08-20 10:35:22 +12:00
```
### Adding relationships
2022-11-03 20:03:43 +13:00
Now we want to be able to assign a Ticket to a Representative. First, let's create the Representative resource:
2022-08-20 10:35:22 +12:00
```elixir
2023-12-21 03:36:00 +13:00
# lib/helpdesk/support/representative.ex
2022-08-20 10:35:22 +12:00
2022-09-14 04:35:29 +12:00
defmodule Helpdesk.Support.Representative do
2022-11-03 20:03:43 +13:00
# This turns this module into a resource using the in memory ETS data layer
2022-08-20 10:35:22 +12:00
use Ash.Resource,
2024-03-30 04:12:19 +13:00
domain: Helpdesk.Support,
2022-08-20 10:35:22 +12:00
data_layer: Ash.DataLayer.Ets
actions do
# Add the default simple actions
2024-04-05 09:24:55 +13:00
defaults [:read]
create :create do
accept [:name]
end
2022-08-20 10:35:22 +12:00
end
# Attributes are the simple pieces of data that exist on your resource
attributes do
# Add an autogenerated UUID primary key called `:id` .
uuid_primary_key :id
# Add a string type attribute called `:name`
2024-03-30 04:12:19 +13:00
attribute :name, :string do
# Make the attribute public in order to give a name when calling functions from `Ash.Changeset` .
public? true
end
2022-08-20 10:35:22 +12:00
end
relationships do
2022-11-03 20:03:43 +13:00
# `has_many` means that the destination attribute is not unique, therefore many related records could exist.
2022-08-20 10:35:22 +12:00
# We assume that the destination attribute is `representative_id` based
# on the module name of this resource and that the source attribute is `id` .
2022-09-14 04:35:29 +12:00
has_many :tickets, Helpdesk.Support.Ticket
2022-08-20 10:35:22 +12:00
end
end
```
2022-11-03 20:03:43 +13:00
Now let's modify our Ticket resource to have the inverse relationship to the Representative.
2022-08-20 10:35:22 +12:00
```elixir
2023-12-21 03:36:00 +13:00
# lib/helpdesk/support/ticket.ex
2022-08-20 10:35:22 +12:00
relationships do
# belongs_to means that the destination attribute is unique, meaning only one related record could exist.
# We assume that the destination attribute is `representative_id` based
# on the name of this relationship and that the source attribute is `representative_id` .
# We create `representative_id` automatically.
2022-09-14 04:35:29 +12:00
belongs_to :representative, Helpdesk.Support.Representative
2022-08-20 10:35:22 +12:00
end
```
2024-03-28 09:06:40 +13:00
Finally, let's add our new Representative resource to our domain module
2022-08-20 10:35:22 +12:00
```elixir
2023-12-21 03:36:00 +13:00
# lib/helpdesk/support.ex
2022-08-20 10:35:22 +12:00
2023-12-21 03:36:00 +13:00
resources do
2022-08-20 10:35:22 +12:00
...
2023-12-21 03:36:00 +13:00
resource Helpdesk.Support.Representative
2022-08-20 10:35:22 +12:00
end
```
2024-03-28 09:06:40 +13:00
You may notice that if you don't add the resource to your domain, or if you don't add the `belongs_to` relationship, that you'll get helpful errors at compile time. Helpful compile time validations are a core concept of Ash as we really want to ensure that your application is valid.
2022-08-20 10:35:22 +12:00
## Working with relationships
2023-01-18 18:34:20 +13:00
There are a wide array of options when managing relationships, and we won't cover all of them here. See the guide on [Managing Relationships ](/documentation/topics/managing-relationships.md ) for a full explanation.
2022-11-03 20:03:43 +13:00
In this example we'll demonstrate the use of action arguments, the method by which you can accept additional input to an action.
2022-08-20 10:35:22 +12:00
2022-11-03 20:03:43 +13:00
Add the `assign` action to allow us to assign a Ticket to a Representative.
2022-08-20 10:35:22 +12:00
```elixir
2023-12-21 03:36:00 +13:00
# lib/helpdesk/support/ticket.ex
2022-09-14 04:35:29 +12:00
2022-08-20 10:35:22 +12:00
update :assign do
# No attributes should be accepted
accept []
# We accept a representative's id as input here
argument :representative_id, :uuid do
# This action requires representative_id
allow_nil? false
end
2022-11-03 20:03:43 +13:00
# We use a change here to replace the related Representative
# If there is a different representative for this Ticket, it will be changed to the new one
# The Representative itself is not modified in any way
2022-09-20 07:44:06 +12:00
change manage_relationship(:representative_id, :representative, type: :append_and_remove)
2022-08-20 10:35:22 +12:00
end
```
2022-11-03 20:03:43 +13:00
Let's try it out in our `iex` console!
Use `recompile` to pick up changes you've made to your code, or just restart your session.
2022-08-20 10:35:22 +12:00
```elixir
2022-10-24 17:07:51 +13:00
recompile()
2022-11-03 20:03:43 +13:00
```
### Open a Ticket
2022-10-24 17:07:51 +13:00
2022-11-03 20:03:43 +13:00
```elixir
2022-08-20 10:35:22 +12:00
ticket = (
2022-09-14 04:35:29 +12:00
Helpdesk.Support.Ticket
2022-08-20 10:35:22 +12:00
|> Ash.Changeset.for_create(:open, %{subject: "I can't find my hand!"})
2024-03-30 04:12:19 +13:00
|> Ash.create!()
2022-08-20 10:35:22 +12:00
)
2022-11-03 20:03:43 +13:00
```
2022-08-20 10:35:22 +12:00
2022-11-03 20:03:43 +13:00
### Create a Representative
```elixir
2022-08-20 10:35:22 +12:00
representative = (
2022-09-14 04:35:29 +12:00
Helpdesk.Support.Representative
2022-08-20 10:35:22 +12:00
|> Ash.Changeset.for_create(:create, %{name: "Joe Armstrong"})
2024-03-30 04:12:19 +13:00
|> Ash.create!()
2022-08-20 10:35:22 +12:00
)
2022-11-03 20:03:43 +13:00
```
2022-08-20 10:35:22 +12:00
2022-11-03 20:03:43 +13:00
### Assign that Representative to the Ticket
```elixir
ticket
|> Ash.Changeset.for_update(:assign, %{representative_id: representative.id})
2024-03-30 04:12:19 +13:00
|> Ash.update!()
2022-08-20 10:35:22 +12:00
```
### What next?
2022-11-03 20:03:43 +13:00
What you've seen above barely scratches the surface of what Ash can do. In a lot of ways, it will look very similar to other tools that you've seen. If all that you ever used was the above, then realistically you won't see much benefit to using Ash.
2024-04-05 09:24:55 +13:00
Where Ash shines however, is all of the tools that can work *with* your resources. You have the ability to extend the framework yourself, and apply consistent design patterns that enable unparalleled efficiency, power and flexibility as your application grows.
2022-08-20 10:35:22 +12:00
2023-09-30 04:54:26 +13:00
#### Get Help
2024-04-07 07:20:22 +12:00
- Check out [ElixirForum ](https://elixirforum.com/c/ash-framework-forum )
2023-09-30 04:54:26 +13:00
- Join our [Discord server ](https://discord.gg/D7FNG2q )
- Open a [GitHub issue ](https://github.com/ash-project/ash/issues/new/choose )
2022-08-20 10:35:22 +12:00
#### Persist your data
2024-04-07 07:20:22 +12:00
See [The AshPostgres getting started guide ](https://hexdocs.pm/ash_postgres ) to see how to back your resources with Postgres. This is highly recommended, as the Postgres data layer provides tons of advanced capabilities.
2022-08-20 10:35:22 +12:00
2024-03-28 09:06:40 +13:00
#### Add a web API
2022-08-20 10:35:22 +12:00
2024-04-07 07:20:22 +12:00
Check out [AshJsonApi ](https://hexdocs.pm/ash_json_api ) and [AshGraphql ](https://hexdocs.pm/ash_graphql ) extensions to build APIs around your resource
2022-08-20 10:35:22 +12:00
#### Authorize access and work with users
2024-04-07 09:40:05 +12:00
See the [Policies guide ](/documentation/topics/security/policies.md ) for information on how to authorize access to your resources using actors and policies.
2024-04-07 07:20:22 +12:00
#### Clean up your code that uses Ash?
Creating and using changesets manually can be verbose, and they all look very similar. Luckily, Ash has your back and can help you build high quality interfaces for you!
Check out the [Code Interface Guide ](/documentation/topics/code-interface.md ) to derive things like `Helpdesk.Support.Ticket.assign!(representative.id)`