# `ExMachina`
[🔗](https://github.com/beam-community/ex_machina/blob/main/lib/ex_machina.ex#L1)

Defines functions for generating data

In depth examples are in the [README](readme.html)

# `build`

```elixir
@callback build(factory_name :: atom()) :: any()
```

# `build`

```elixir
@callback build(factory_name :: atom(), attrs :: keyword() | map()) :: any()
```

# `build_list`

```elixir
@callback build_list(number_of_records :: integer(), factory_name :: atom()) :: list()
```

# `build_list`

```elixir
@callback build_list(
  number_of_records :: integer(),
  factory_name :: atom(),
  attrs :: keyword() | map()
) ::
  list()
```

# `build_pair`

```elixir
@callback build_pair(factory_name :: atom()) :: list()
```

# `build_pair`

```elixir
@callback build_pair(factory_name :: atom(), attrs :: keyword() | map()) :: list()
```

# `build`

Builds a single factory.

This will defer to the `[factory_name]_factory/0` callback defined in the
factory module in which it is `use`d.

### Example

    def user_factory do
      %{name: "John Doe", admin: false}
    end

    # Returns %{name: "John Doe", admin: false}
    build(:user)

    # Returns %{name: "John Doe", admin: true}
    build(:user, admin: true)

## Full control of a factory's attributes

If you want full control over the factory attributes, you can define the
factory with `[factory_name]_factory/1`, taking in the attributes as the first
argument.

Caveats:

- ExMachina will no longer merge the attributes for your factory. If you want
to do that, you can merge the attributes with the `merge_attributes/2` helper.

- ExMachina will no longer evaluate lazy attributes. If you want to do that,
you can evaluate the lazy attributes with the `evaluate_lazy_attributes/1`
helper.

### Example

    def article_factory(attrs) do
      title = Map.get(attrs, :title, "default title")
      slug = Article.title_to_slug(title)

      article = %Article{title: title, slug: slug}

      article
      # merge attributes on your own
      |> merge_attributes(attrs)
      # evaluate any lazy attributes
      |> evaluate_lazy_attributes()
    end

    # Returns %Article{title: "default title", slug: "default-title"}
    build(:article)

    # Returns %Article{title: "hello world", slug: "hello-world"}
    build(:article, title: "hello world")

# `build_list`

Builds any number of factories.

## Example

    # Returns a list of 3 users
    build_list(3, :user)

# `build_pair`

Builds two factories.

This is just an alias for `build_list(2, factory_name, attrs)`.

## Example

    # Returns a list of 2 users
    build_pair(:user)

# `evaluate_lazy_attributes`

```elixir
@spec evaluate_lazy_attributes(struct() | map()) :: struct() | map()
```

Helper function to evaluate lazy attributes that are passed into a factory.

## Example

    # custom factory
    def article_factory(attrs) do
      %{title: "title"}
      |> merge_attributes(attrs)
      |> evaluate_lazy_attributes()
    end

    def author_factory do
      %{name: sequence("gandalf")}
    end

    # => returns [
    #  %{title: "title", author: %{name: "gandalf0"},
    #  %{title: "title", author: %{name: "gandalf0"}
    # ]
    build_pair(:article, author: build(:author))

    # => returns [
    #  %{title: "title", author: %{name: "gandalf0"},
    #  %{title: "title", author: %{name: "gandalf1"}
    # ]
    build_pair(:article, author: fn -> build(:author) end)

# `merge_attributes`

```elixir
@spec merge_attributes(struct() | map(), map()) :: struct() | map() | no_return()
```

Helper function to merge attributes into a factory that could be either a map
or a struct.

## Example

    # custom factory
    def article_factory(attrs) do
      title = Map.get(attrs, :title, "default title")

      article = %Article{
        title: title
      }

      merge_attributes(article, attrs)
    end

Note that when trying to merge attributes into a struct, this function will
raise if one of the attributes is not defined in the struct.

# `sequence`

```elixir
@spec sequence(String.t()) :: String.t()
```

Shortcut for creating unique string values.

This is automatically imported into a model factory when you `use ExMachina`.

This is equivalent to `sequence(name, &"#{name}#{&1}")`. If you need to
customize the returned string, see `sequence/2`.

Note that sequences keep growing and are *not* reset by ExMachina. Most of the
time you won't need to reset the sequence, but when you do need to reset them,
you can use `ExMachina.Sequence.reset/0`.

## Examples

    def user_factory do
      %User{
        # Will generate "username0" then "username1", etc.
        username: sequence("username")
      }
    end

    def article_factory do
      %Article{
        # Will generate "Article Title0" then "Article Title1", etc.
        title: sequence("Article Title")
      }
    end

# `sequence`

```elixir
@spec sequence(any(), (integer() -&gt; any()) | [...]) :: any()
```

Create sequences for generating unique values.

This is automatically imported into a model factory when you `use ExMachina`.

The `name` can be any term, although it is typically an atom describing the
sequence. Each time a sequence is called with the same `name`, its number is
incremented by one.

The `formatter` function takes the sequence number, and returns a sequential
representation of that number – typically a formatted string.

## Examples

    def user_factory do
      %{
        # Will generate "me-0@foo.com" then "me-1@foo.com", etc.
        email: sequence(:email, &"me-#{&1}@foo.com"),
        # Will generate "admin" then "user", "other", "admin" etc.
        role: sequence(:role, ["admin", "user", "other"])
      }
    end

# `sequence`

```elixir
@spec sequence(any(), (integer() -&gt; any()) | [...], [{:start_at, non_neg_integer()}]) ::
  any()
```

Similar to `sequence/2` but it allows for passing a `start_at` option
to the sequence generation.

## Examples

    def user_factory do
      %{
        # Will generate "me-100@foo.com" then "me-101@foo.com", etc.
        email: sequence(:email, &"me-#{&1}@foo.com", start_at: 100),
      }
    end

---

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