Manual Installation

Copy Markdown View Source

This guide will walk you through the process of manually installing Ash into your project. If you are starting from scratch, you can use mix new or mix igniter.new and follow these instructions. These installation instructions apply both to new projects and existing ones.

Install & Setup Dependencies

See the readmes for spark and reactor for more information on their installation. We've included their changes here for your convenience.

Create .formatter.exs:

# Used by "mix format"
[
  inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"],
  plugins: [Spark.Formatter]
]

Create config/config.exs:

import Config
config :spark, formatter: [remove_parens?: true]

Update mix.exs:

...
    defp deps do
      [
+       {:sourceror, "~> 1.8", only: [:dev, :test]}
        # {:dep_from_hexpm, "~> 0.3.0"},
        # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
...

Update .formatter.exs:

...
  [
    inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"],
-   plugins: [Spark.Formatter]
+   plugins: [Spark.Formatter],
+   import_deps: [:reactor]
  ]

Skip protocol consolidation

To avoid warnings about protocol consolidation when recompiling in dev, we set protocol consolidation to happen only in non-dev environments.

Setup The Formatter

Configure the DSL auto-formatter. This tells the formatter to remove excess parentheses and how to sort sections in your Ash.Resource & Ash.Domain modules for consistency.

Update .formatter.exs:

...
    inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"],
    plugins: [Spark.Formatter],
-   import_deps: [:reactor]
+   import_deps: [:ash, :reactor]
  ]

Update config/config.exs:

  import Config
- config :spark, formatter: [remove_parens?: true]
  
+ config :spark,
+   formatter: [
+     remove_parens?: true,
+     "Ash.Resource": [
+       section_order: [
+         :resource,
+         :code_interface,
+         :actions,
+         :policies,
+         :pub_sub,
+         :preparations,
+         :changes,
+         :validations,
+         :multitenancy,
+         :attributes,
+         :relationships,
+         :calculations,
+         :aggregates,
+         :identities
+       ]
+     ],
+     "Ash.Domain": [section_order: [:resources, :policies, :authorization, :domain, :execution]]
+   ]
+

Configure Dev/Test environments

Configure your dev and test environments Update config/config.exs:

...
    ]
  
+ import_config "#{config_env()}.exs"
+

Create config/dev.exs:

import Config
config :ash, policies: [show_policy_breakdowns?: true]

Create config/prod.exs:

import Config

Create config/test.exs:

import Config
config :ash, policies: [show_policy_breakdowns?: true]

Setup Backwards Compatibility Configurations

Configure backwards compatibility settings. See the backwards compatibility guide for an explanation of each of the configurations.

Update config/config.exs:

  import Config
  
+ # These enable behaviors that will become the default in the next major
+ # version of Ash. Setting them now opts your application into the new
+ # behavior and ensures a seamless upgrade. See the backwards compatibility
+ # guide for an explanation of each setting:
+ # https://hexdocs.pm/ash/backwards-compatibility-config.html
+ config :ash,
+   allow_forbidden_field_for_relationships_by_default: true,
+   include_embedded_source_by_default?: false,
+   show_keysets_for_all_actions?: false,
+   default_page_type: :keyset,
+   policies: [no_filter_static_forbidden_reads?: false],
+   keep_read_action_loads_when_loading?: false,
+   default_actions_require_atomic?: true,
+   read_action_after_action_hooks_in_order?: true,
+   bulk_actions_default_to_errors?: true,
+   transaction_rollback_on_error?: true,
+   redact_sensitive_values_in_errors?: true,
+   default_string_length_count: :codepoints,
+   infer_generic_action_reactors?: false,
+   many_to_many_destroy_destination_on_match?: true
+ 
  config :spark,
    formatter: [
...

Sign Pagination Keysets

Sign the keysets used for keyset pagination, so that Ash only accepts keysets that it generated. Each environment gets its own secret. In production, the secret is read from the ASH_KEYSET_SIGNING_SECRET environment variable in config/runtime.exs. You can generate a value with mix phx.gen.secret or elixir -e ':crypto.strong_rand_bytes(32) |> Base.encode64() |> IO.puts()'.

If your application uses Phoenix, the production secret is instead derived from Phoenix's SECRET_KEY_BASE environment variable, so there is only one secret to manage.

Update config/dev.exs:

  import Config
- config :ash, policies: [show_policy_breakdowns?: true]
  
+ config :ash,
+   policies: [show_policy_breakdowns?: true],
+   keyset_signing_secret: "<a long random string>"
+

Create config/runtime.exs:

import Config

if config_env() == :prod do
  config :ash,
    keyset_signing_secret:
      System.get_env("ASH_KEYSET_SIGNING_SECRET") ||
        raise("environment variable ASH_KEYSET_SIGNING_SECRET is missing")
end

Update config/test.exs:

  import Config
- config :ash, policies: [show_policy_breakdowns?: true]
  
+ config :ash,
+   policies: [show_policy_breakdowns?: true],
+   keyset_signing_secret: "<a long random string>"
+

Register Your Domains

Ash relies on your application's :ash_domains configuration to find domain modules for mix tasks (diagrams, livebooks, policy charts) and to validate that domains/resources are registered at compile time.

Add your domain modules to config/config.exs:

config :my_app, :ash_domains, [
  MyApp.Accounts,
  MyApp.Helpdesk
]

For more detail, see the Domains guide.