# `Ash.Generator`
[🔗](https://github.com/ash-project/ash/blob/v3.23.1/lib/ash/generator/generator.ex#L5)

Tools for generating input to Ash resource actions and for generating seed data.

## Using Ash.Generator

To define generators for your tests, `use Ash.Generator`, and define
functions that use `changeset_generator/3` and/or `seed_generator/2`.

```elixir
defmodule YourApp.Generator do
  use Ash.Generator

  # using `seed_generator`, bypasses the action and saves directly to the data layer
  def blog_post(opts \\ []) do
    seed_generator(
      %MyApp.Blog.Post{
        name: sequence(:title, &"My Blog Post #{&1}")
        text: StreamData.repeatedly(fn -> Faker.Lorem.paragraph() end)
      },
      overrides: opts
    )
  end

  # using `changeset_generator`, calls the action when passed to `generate`
  def blog_post_comment(opts \\ []) do
    blog_post_id = opts[:blog_post_id] || once(:default_blog_post_id, fn -> generate(blog_post()).id end)

    changeset_generator(
      MyApp.Blog.Comment,
      :create,
      defaults: [
        blog_post_id: blog_post_id
      ],
      overrides: opts
    )
  end
end
```

Then, in your tests, you can `import YourApp.Generator`, and use `generate/1` and `generate_many/1` to generate data.
For example:

```elixir
import YourApp.Generator

test "`comment_count` on blog_post shows the count of comments" do
  blog_post = generate(blog_post())
  assert Ash.load!(blog_post, :comment_count).comment_count == 0

  generate_many(blog_post_comment(blog_post_id: blog_post.id), 10)

  assert Ash.load!(blog_post, :comment_count).comment_count == 10
end
```

## About Generators

These generators are backed by `StreamData`, and are ready for use with property testing via `ExUnitProperties`

Many functions in this module support "overrides", which allow passing down either constant values
or your own `StreamData` generators.

For example:

```elixir
# All generated posts will have text as `"text"`. Equivalent to providing `StreamData.constant("text")`.
Ash.Generator.seed_input(Post, %{text: "text"})
```

# `overrides`

```elixir
@type overrides() ::
  %{required(term()) =&gt; stream_data() | term()}
  | Keyword.t(stream_data() | term())
```

A map or keyword of data generators or constant values to use in place of defaults.

Many functions in `Ash.Generator` support `overrides`, allowing to customize the default
generated values.

# `stream_data`

```elixir
@type stream_data() :: Enumerable.t()
```

An instance of `StreamData`, gotten from one of the functions in that module.

# `action_input`

```elixir
@spec action_input(
  Ash.Resource.t() | Ash.Resource.record(),
  action_name :: atom(),
  generators :: overrides()
) :: map()
```

Generate input meant to be passed into a resource action.

Arguments that are passed to a `manage_relationship` are not generated by default, and you will
have to generate them yourself by passing your own generators/values down. See the module documentation for more.

# `changeset`

```elixir
@spec changeset(
  Ash.Resource.t(),
  action :: atom(),
  overrides(),
  changeset_options :: Keyword.t()
) :: Ash.Changeset.t()
```

Creates the input for the provided action with `action_input/3`, and creates a changeset for that action with that input.

See `action_input/3` and the module documentation for more.

# `changeset_generator`

A generator of changesets which call their specific actions when passed to `generate/1` or `generate_many/2`.

See `seed_generator/2` for the equivalent construct for cases when you want to seed directly to the data layer as opposed to calling resource
actions.

## Examples

```elixir
iex> changeset_generator(MyApp.Blog.Post, :create, defaults: [title: sequence(:blog_post_title, &"My Blog Post #{&1}")]) |> generate()
%Ash.Changeset{...}
```

## Usage in tests

This can be used to define generators in tests. A useful pattern is defining a function like so:

```elixir
def blog_post(opts \ []) do
  changeset_generator(
    MyApp.Blog.Post,
    :create,
    defaults: [
      name: sequence(:blog_post_title, &"My Blog Post #{&1}")
      text: StreamData.repeatedly(fn -> Faker.Lorem.paragraph() end)
    ],
    overrides: opts
  )
end
```

When you only allow child resource to be created through a managed relationship, e.g. an update action on a parent resource,
this pattern could be expanded, yielding a resource with a new child resource:

```elixir
def post_for(author, opts \ []) do
  changeset_generator(
    author,
    :new_post,
    uses: [
      post_input:
        action_input(
          MyApp.Blog.Post,
          :create,
          title: sequence(:title, &"Post #{&1}")
        )
    ],
    defaults: fn inputs ->
      [posts: [inputs.post_input]]
    end,
    overrides: opts
  )
end
```

See the `Ash.Generator` moduledocs for more information.

## Options

* `:defaults` - A keyword list of values or generators, used as inputs. Can also be a function
  when using the `:uses` option.
* `:overrides` - A keyword list or map of `t:overrides()`
* `:actor` - Passed through to the changeset
* `:tenant` - Passed through to the changeset
* `:scope` - Passed through to the changeset
* `:uses` - A map of generators that are passed into your `defaults`. `defaults` must be a
  function. This is useful when multiple things in your `defaults` need to use the same generated
  value.
* `:authorize?` - Passed through to the changeset
* `:context` - Passed through to the changeset
* `:after_action` - A one argument function that takes the result and returns
  a new result to run after the record is created.
* `:private_arguments` - A map of private arguments, whose values can also be generators. Can also
  be a function when using the `:uses` option.

## The `uses` option

```elixir
def blog_post(opts \ []) do
  changeset_generator(
    MyApp.Blog.Post,
    :create,
    uses: [
      author: author() # A function using `changeset_generator` just like this one.
    ],
    defaults: fn %{author: author} ->
      author = generate(author)

      [
        name: sequence(:blog_post_title, &"My Blog Post #{&1}")
        author_name: author.name,
        text: StreamData.repeatedly(fn -> Faker.Lorem.paragraph() end)
      ]
    end
    overrides: opts
  )
end
```

# `generate`

```elixir
@spec generate(stream_data() | Ash.Changeset.t() | Ash.Resource.record()) ::
  Ash.Resource.record()
```

Takes one value from a changeset or seed generator and calls `Ash.create!` or `Ash.update!` on it.

Passes through resource structs without doing anything.
Creates a changeset if given

# `generate_many`

Takes `count` values from a changeset or seed generator and passes their inputs into `Ash.bulk_create!` or `Ash.Seed.seed!` respectively.

# `initialize_once`

```elixir
@spec initialize_once(term()) :: pid()
```

Starts and links an agent for a `once/2`, or returns the existing agent pid if it already exists.

See `once/2` for more.

# `initialize_sequence`

```elixir
@spec initialize_sequence(atom()) :: pid()
```

Starts and links an agent for a sequence, or returns the existing agent pid if it already exists.

See `sequence/3` for more.

# `many_changesets`

```elixir
@spec many_changesets(
  Ash.Resource.t(),
  action :: atom(),
  count :: pos_integer(),
  overrides(),
  changeset_options :: Keyword.t()
) :: [Ash.Changeset.t()]
```

Generate `count` changesets and return them as a list.

# `many_queries`

```elixir
@spec many_queries(
  Ash.Resource.t(),
  action :: atom(),
  count :: pos_integer(),
  overrides(),
  changeset_options :: Keyword.t()
) :: [Ash.Query.t()]
```

Generate `count` queries and return them as a list.

# `mixed_map`

```elixir
@spec mixed_map(map(), [term()]) :: stream_data()
```

Creates a generator of maps where all keys are required except the list provided

## Example

```elixir
iex> mixed_map(%{a: StreamData.constant(1), b: StreamData.constant(2)}, [:b]) |> Enum.take(2)
[%{a: 1}, %{a: 1, b: 2}]
```

# `next_in_sequence`

Gets the next value for a given sequence identifier.

See `sequence/3` for more.

This is equivalent to `identifier |> Ash.Generator.sequence(fun, sequencer) |> Enum.at(0)`

# `once`

```elixir
@spec once(pid() | term(), (-&gt; value) | Enumerable.t(value)) :: StreamData.t(value)
when value: term()
```

Run the provided function or enumerable (i.e generator) only once.

This is useful for ensuring that some piece of data is generated a single time during a test.

The lifecycle of this generator is tied to the process that initially starts it. In general,
that will be the test. In the rare case where you are running async processes that need to share a sequence
that is not created in the test process, you can initialize a sequence in the test using `initialize_once/1`.

Example:

    iex> Ash.Generator.once(:user, fn ->
           register_user(...)
         end) |> Enum.at(0)
    %User{id: 1} # created the user

    iex> Ash.Generator.once(:user, fn ->
           register_user(...)
         end) |> Enum.at(0)
    %User{id: 1} # reused the last user

# `query`

```elixir
@spec query(
  Ash.Resource.t(),
  action :: atom(),
  overrides(),
  query_options :: Keyword.t()
) :: Ash.Query.t()
```

Creates the input for the provided action with `action_input/3`, and returns a query for that action with that input.

See `action_input/3` and the module documentation for more.

# `seed!`

Gets input using `seed_input/2` and passes it to `Ash.Seed.seed!/2`, returning the result

# `seed_generator`

```elixir
@spec seed_generator(
  Ash.Resource.record()
  | {Ash.Resource.t(), map()}
  | (map() -&gt; Ash.Resource.record() | {Ash.Resource.t(), %{}}),
  opts :: Keyword.t()
) :: stream_data()
```

A generator of seedable records, to be passed to `generate/1` or `generate_many/1`

See `changeset_generator/3` for the equivalent construct for cases when you want to call resource
actions as opposed to seed directly to the data layer.

When a struct is given, only exactly the given values/generators will be used. If you
pass a tuple, i.e `{Resource, %{field: :value}}`, all values not provided will be generated
automatically.

## Examples

```elixir
iex> seed_generator(%MyApp.Blog.Post{name: sequence(:blog_post_title, &"My Blog Post #{&1}")}) |> generate()
%Tunez.Music.Artist{name: "Artist 1"}

iex> seed_generator({MyApp.Blog.Post, %{}}) |> generate()
%Tunez.Music.Artist{name: "A random name"}
```

## Usage in tests

This can be used to define seed generators in tests. A useful pattern is defining a function like so:

```elixir
def blog_post(opts \ []) do
  seed_generator(
    %MyApp.Blog.Post{
      name: sequence(:blog_post_title, &"My Blog Post #{&1}")
      text: StreamData.repeatedly(fn -> Faker.Lorem.paragraph() end)
    },
    overrides: opts
  )
end
```

See the `Ash.Generator` moduledocs for more information.

## Options

* `:overrides` - A keyword list or map of `t:overrides()`
* `:actor` - Passed through to the changeset
* `:tenant` - Passed through to the changeset
* `:scope` - Passed through to the changeset
* `:uses` - A map of generators that are passed into the first argument, if it is a function.
* `:authorize?` - Passed through to the changeset
* `:context` - Passed through to the changeset
* `:after_action` - A one argument function that takes the result and returns
  a new result to run after the record is created.

# `seed_input`

```elixir
@spec seed_input(Ash.Resource.t(), map()) :: StreamData.t(map())
```

Generate input meant to be passed into `Ash.Seed.seed!/2`.

A map of custom `StreamData` generators can be provided to add to or overwrite the generated input,
for example: `Ash.Generator.seed_input(Post, %{text: StreamData.constant("Post")})`

# `seed_many!`

Generates an input `n` times, and passes them all to seed, returning the list of seeded items.

# `sequence`

```elixir
@spec sequence(pid() | atom(), (iterator | nil -&gt; value), (iterator | nil -&gt; iterator)) ::
  StreamData.t(value)
when iterator: term(), value: term()
```

Generate globally unique values.

This is useful for generating values that are unique within a given test or processes that it spawns, such as email addresses,
or for generating values that are unique across a single resource, such as identifiers. The values will be unique
for anything using the same sequence name, **within the same test**.

> ### Not Globally Unique {: .warning}
> The lifecycle of this generator is tied to the process that initially starts it. In general,
> that will be the test. In the rare case where you are running async processes that need to share a sequence
> that is not created in the test process, you can initialize a sequence in the test using `initialize_sequence/1`.
>
> If you need a globally unique value, use a value like `System.unique_integer([:positive])` in your values instead.
>
> For example:
>
> ```elixir
> StreamData.repeatedly(fn -> "email#{System.unique_integer([:positive])}@example.com" end)
> ```

Example:

    Ash.Generator.sequence(:unique_email, fn i -> "user#{i}@example.com" end) |> Enum.take(3)
    iex> ["user0@example.com", "user1@example.com", "user2@example.com"]

## Using a different sequencer

By default we use an incrementing integer starting at 0. However, if you want to use something else, you can provide
your own sequencer. The initial value will be `nil`, which you can use to detect that you are the start of the sequence.

Example:

    Ash.Generator.sequence(:unique_email, fn i -> "user#{i}@example.com" end, fn num -> (num || 1) - 1 end) |> Enum.take(3)
    iex> ["user0@example.com", "user-1@example.com", "user-2@example.com"]

# `stop_once`

Stops the agent for a `once/2`.

See `once/2` for more.

# `stop_sequence`

Stops the agent for a sequence.

See `sequence/3` for more.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
