The actions we will be able to take on these resources include:
- Opening a new ticket
- Closing a ticket
- Assigning a ticket to a representative
### Create a new project
We first create a new project with the `--sup` flag to add a supervision tree. This will be necessary for later steps.
```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
git commit -m "init"
```
Open the project in your text editor, and we'll get started.
If you are using ElixirLs (if you are using VScode, it is likely that you are), then add the following dependency to use Ash's custom autocomplete plugin.
The basic building blocks of an Ash application are resources. They are tied together by an API module (not to be confused with a web API), which will allow you to interact with those resources.
Lets start by creating our first resource along with our first API. We will create the following files:
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 `create!/1` function on our API module `Helpdesk.Support`.
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 making a custom action for opening a ticket, called `:open`.
Now lets add some logic to close a ticket. This time we'll add an `update` action.
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 {{link:ash:guide:Actions}} guides for more.
So far, there is no persistence happening. 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:
In order to add persistence, we need to add a data layer to our resources. Before we do that, however, lets 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 do no persistence. The way that we do this 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.
The examples shown here could be implemented easily using things like `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 AshPostgres, the above code would be exactly the same, except for the call to `set_data/2`.
### Adding basic persistence
Before we get into working with relationships, lets add some actual persistence to our resource. This will let us add relationships and try out querying data.
There is a built in data layer that is good for testing and prototyping, that uses [ETS](https://elixir-lang.org/getting-started/mix-otp/ets.html).
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. Keep in mind, ETS is in memory, meaning restarting your application/iex session will remove all of the data.
You may notice that if you don't add the resource to the registry, 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. We focus as often as possible on ensuring that your application is valid before it compiles.
There are a wide array of options when managing relationships, and going over all of them here wouldn't be reasonable. See the guide on {{link:ash:guide:Managing Relationships}} for a full explanation. For now, we'll show a simple example. Add the following action to allow us to assign a ticket to a representative.
What you've seen above constitutes some very simple usage of Ash, barely scratching the surface. 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 over other options. Where Ash shines is in all of the additional turn-key tools that are built in, the ability to extend the framework yourself, and the consistent design patterns that enable unparalleled velocity, power and flexibility as your application and needs grow.
Creating and using changesets can be verbose. Check out the {{link:ash:guide:Code Interface}} to derive things like `Helpdesk.Support.Ticket.assign!(representative.id)`
See {{link:ash_postgres:guide:Get Started With Postgres:AshPostgres}} to see how to back your resources with postgres. This is highly recommended, as the postgres data layer provides tons of advanced capabilities.