View Source Getting Started
goals
Goals
In this guide we will:
- Create a new Elixir application and add Ash as a dependency
- Create a simple set of resources and show they can be used
- Illustrate some core concepts of Ash
- Point you to good next resources so you can explore Ash further
things-you-may-want-to-read-first
Things you may want to read first
- Install Elixir
- {{link:ash:guide:Philosophy}}
requirements
Requirements
If you want to follow along yourself, you will need the following things:
- Elixir and Erlang installed
- A text editor to make the changes that we make
- A terminal to run the commands we show using
iex
steps
Steps
For this tutorial, we'll use examples based around creating a help desk.
We will make the following resources:
Helpdesk.Tickets.Ticket
Helpdesk.Tickets.Representative
Helpdesk.Tickets.Customer
Helpdesk.Tickets.Comment
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
- Commenting on a ticket
create-a-new-project
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.
# 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.
# 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.
add-ash-to-your-application
Add Ash to your application
Add the ash dependency to your mix.exs
{{mix_dep:ash}}
Add ash to your .formatter.exs file
[
# import the formatter rules from ash
import_deps: [:ash],
inputs: [...]
]
And run mix deps.get
creating-our-first-resources
Creating our first resources
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:
- The API -
lib/helpdesk/tickets.ex
- A registry to list our resources -
lib/helpdesk/tickets/registry.ex
- Our tickets resource -
lib/helpdesk/tickets/resources/ticket.ex
.
We also create an accompanying registry, in , which is where we will list the resources for our Api.
To create the required folders and files, you can use the following command:
# Run in your terminal
touch lib/helpdesk/tickets.ex
mkdir -p lib/helpdesk/tickets/resources && touch $_/ticket.ex
touch lib/**helpdesk**/tickets/registry.ex
Add the following to the files we created
# lib/helpdesk/tickets/resources/ticket.ex
defmodule Helpdesk.Tickets.Ticket do
# This turns this module into a resource
use Ash.Resource
actions do
# Add a set of simple actions. You'll customize these later.
defaults [:create, :read, :update, :destroy]
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
# lib/helpdesk/tickets/registry.ex
defmodule Helpdesk.Tickets.Registry do
use Ash.Registry,
extensions: [
# This extension adds helpful compile time validations
Ash.Registry.ResourceValidations
]
entries do
entry Helpdesk.Tickets.Ticket
end
end
# lib/helpdesk/tickets.ex
defmodule Helpdesk.Tickets do
use Ash.Api
resources do
# This defines the set of resources that can be used with this API
registry Helpdesk.Tickets.Registry
end
end
try-our-first-resource-out
Try our first resource out
Run iex -S mix
in your project and try it out
To create a ticket, we first make an Ash.Changeset
for the :create
action of the Helpdesk.Tickets.Ticket
resource. Then we pass it to the create!/1
function on our API module Helpdesk.Tickets
.
Helpdesk.Tickets.Ticket
|> Ash.Changeset.for_create(:create)
|> Helpdesk.Tickets.create!()
This returns what we call a record
which is an instance of a resource.
{:ok, #Helpdesk.Tickets.Ticket<
...,
id: "c0f8dc32-a018-4eb4-8656-d5810118f4ea",
subject: nil,
...
>}
In this form, 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:
Helpdesk.Tickets.read(Helpdesk.Tickets.Ticket)
Which will result in nothing being returned.
{:ok, []}
Later on, we will discuss adding a {{link:ash:guide:Data Layers:Data Layer}} to your resources to achieve persistence. For now, however, we will focus on prototyping our resources and what we can add to them.
customizing-our-actions
Customizing our Actions
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
.
We'll start with the attribute changes:
attributes do
...
attribute :subject, :string do
# Don't allow `nil` values
allow_nil? false
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
And then add our customized action:
actions do
...
create :open do
# By default you can provide all public attributes to an action
# This action should only accept the subject
accept [:subject]
end
end
Now we can play with these changes in iex:
We use create!
with an exclamation point here because that will raise the error which gives a nicer view of the error in iex
# Use this to pick up changes you've made to your code, or restart your session
recompile()
Helpdesk.Tickets.Ticket
|> Ash.Changeset.for_create(:open, %{subject: "My mouse won't click!"})
|> Helpdesk.Tickets.create!()
And we can see our newly created ticket with a subject and a status.
#Helpdesk.Tickets.Ticket<
...
id: "3c94d310-7b5e-41f0-9104-5b193b831a5d",
status: :open,
subject: "My mouse won't click!",
...
>
If we didn't include a subject, or left out the input, we would see an error instead
** (Ash.Error.Invalid) Input Invalid
* attribute subject is required