# Ash: 4 - Attributes ```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) ``` ## Attributes
Querying
Home
Customizing Actions
### In this tutorial you will add and configure Attributes to a Ticket resource The Ticket resource will represent a helpdesk ticket. It will have 3 main attributes. * A `subject` or title * A `description` * A `status` which can be either `:open` or `:closed` It will also have 2 attributes for keeping track of when the ticket was *created*, and when it was *last updated*. #### Subject You need to make sure the `subject` is always set, it's not possible to create a ticket without a subject. You can do this by setting `allow_nil?` to `false`. Like so: `attribute :subject, :string, allow_nil?: false` #### Description The `:description` is simple, it's a `:string` and it is allowed to be empty. #### Status `:status` is more complicated. * It is of the type `:atom` * It can only be the value `:open` or `:closed` * By default it is `:open` * And it can't be `nil` Attributes are set in a `do end` block like so: ```elixir attribute :status, :atom do # ... allow_nil? false end ``` To set a constraint of values, you can use the `constraints` option, like so: `constraints [one_of: [:open, :closed]]` To set the default value, you use `default :open`. #### Keeping track of Created and Updated Ash provides [create_timestamp](https://hexdocs.pm/ash/dsl-ash-resource.html#attributes-create_timestamp) and [update_timestamp](https://hexdocs.pm/ash/dsl-ash-resource.html#attributes-update_timestamp) to keep track of when the Resource was first created, and when it was last updated. Add the following to the attributes block: ```elixir create_timestamp :created_at update_timestamp :updated_at ```
Show Solution
```elixir defmodule Tutorial.Support.Ticket do use Ash.Resource, domain: Tutorial.Support, data_layer: Ash.DataLayer.Ets actions do defaults [:read] create :create do accept [:subject, :description, :status] end end attributes do uuid_primary_key :id attribute :subject, :string, allow_nil?: false attribute :description, :string # status is either `open` or `closed`. 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]] default :open allow_nil? false end create_timestamp :created_at update_timestamp :updated_at end end defmodule Tutorial.Support do use Ash.Domain resources do resource Tutorial.Support.Ticket end end ```
### 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 :create do accept [:subject, :description, :status] end end attributes do uuid_primary_key :id # Add the attributes here --> # - Subject # - Description # - Status # - Create Timestamp # - Update Timestamp end end defmodule Tutorial.Support do use Ash.Domain resources do resource Tutorial.Support.Ticket end end ``` ## Creating a Ticket Create a `Ticket` without any attributes. Remember, when creating a resource use a changeset (`Ash.Changeset.for_create/3`), which gets passed to `Ash.create!/1`. The output should look something like this: ``` ** (Ash.Error.Invalid) Input Invalid * attribute subject is required ```
Show Solution
```elixir Tutorial.Support.Ticket |> Ash.Changeset.for_create(:create, %{}) |> Ash.create!() ```
```elixir ``` Now create a Ticket with a `subject`
Show Solution
```elixir Tutorial.Support.Ticket |> Ash.Changeset.for_create(:create, %{subject: "This is the subject"}) |> Ash.create!() ```
```elixir ``` Now create a ticket with the `status` set to `:closed`
Show Solution
```elixir Tutorial.Support.Ticket |> Ash.Changeset.for_create(:create, %{subject: "This is the subject", status: :closed}) |> Ash.create!() ```
```elixir ``` Now try creating a ticket with the status set to `:pending`. This should give an error because `:pending` is not a valid `status`.
Show Solution
```elixir Tutorial.Support.Ticket |> Ash.Changeset.for_create(:create, %{subject: "This is the subject", status: :pending}) |> Ash.create!() ```
```elixir ``` ## Latest created Ticket Since you added a creation date, you can now query on the latest created ticket. First, sort using the `Ash.Query.sort/2` function by `Ash.Query.sort(created_at: :desc)` Then limit the amount of records with the `Ash.Query.limit/2` function by doing `Ash.Query.limit(1)` Finally call `Ash.read_one!()` on the query. *Hint: Use a pipeline*
Show Solution
```elixir Tutorial.Support.Ticket |> Ash.Query.sort(created_at: :desc) |> Ash.Query.limit(1) |> Ash.read_one!() ```
```elixir ```
Querying
Home
Customizing Actions