Skip to content

Commit 2843cce

Browse files
committed
feat: add maintainer info and basic factory
1 parent 51c2b16 commit 2843cce

7 files changed

+196
-19
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# 0.1.0
2+
- Initial Release

LICENSE

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Copyright 2022 theblitzapp
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4+
5+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6+
7+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

README.md

+7-8
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,20 @@
1-
# ExFactory
1+
# FactoryEx
22

3-
**TODO: Add description**
3+
[![Test](https://github.com/theblitzapp/factory_ex/actions/workflows/test-actions.yml/badge.svg)](https://github.com/theblitzapp/factory_ex/actions/workflows/test-actions.yml)
4+
[![Hex version badge](https://img.shields.io/hexpm/v/factory_ex.svg)](https://hex.pm/packages/factory_ex)
45

56
## Installation
67

7-
If [available in Hex](https://hex.pm/docs/publish), the package can be installed
8-
by adding `ex_factory` to your list of dependencies in `mix.exs`:
8+
[Available in Hex](https://hex.pm/docs/publish), the package can be installed
9+
by adding `factory_ex` to your list of dependencies in `mix.exs`:
910

1011
```elixir
1112
def deps do
1213
[
13-
{:ex_factory, "~> 0.1.0"}
14+
{:factory_ex, "~> 0.1.0"}
1415
]
1516
end
1617
```
1718

18-
Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
19-
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
20-
be found at <https://hexdocs.pm/ex_factory>.
19+
Documentation can be found at <https://hexdocs.pm/factory_ex>.
2120

lib/factory_ex.ex

+136
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
defmodule FactoryEx do
2+
@moduledoc """
3+
This module defines an Ecto factory behaviour.
4+
5+
For defining your own factories just implement `schema/0`, `repo/0` and
6+
`build/0` callback e.g:
7+
8+
```elixir
9+
defmodule MyFactory do
10+
@behaviour FactoryEx
11+
12+
def schema, do: MySchema
13+
14+
def repo, do: MyRepo
15+
16+
def build(params \\ %{}) do
17+
default = %{
18+
foo: 21,
19+
bar: 42
20+
}
21+
22+
Map.merge(default, params)
23+
end
24+
end
25+
```
26+
27+
And then using it in your tests as:
28+
29+
```elixir
30+
# For getting a default parameter map.
31+
FactoryEx.build(MyFactory)
32+
33+
# For getting a default parameter map with a modification.
34+
FactoryEx.build(MyFactory, foo: 42)
35+
36+
# For inserting a default schema.
37+
FactoryEx.insert!(MyFactory)
38+
39+
# For inserting a default schema with a modification.
40+
FactoryEx.insert!(MyFactory, foo: 42)
41+
```
42+
"""
43+
44+
@doc """
45+
Callback that returns the schema module.
46+
"""
47+
@callback schema() :: module()
48+
49+
@doc """
50+
Callback that returns the schema's repo module.
51+
"""
52+
@callback repo() :: module()
53+
54+
@doc """
55+
Callback that returns a map with valid defaults for the schema.
56+
"""
57+
@callback build(map()) :: map()
58+
59+
@doc """
60+
Callback that returns a struct with valid defaults for the schema.
61+
"""
62+
@callback build_struct(map()) :: struct()
63+
64+
@optional_callbacks [build_struct: 1]
65+
66+
@doc """
67+
Builds the parameters for a schema `changeset/2` function given the factory
68+
`module` and an optional list/map of `params`.
69+
"""
70+
@spec build_params(module()) :: map()
71+
@spec build_params(module(), keyword() | map()) :: map()
72+
def build_params(module, params \\ %{})
73+
74+
def build_params(module, params) when is_list(params) do
75+
build_params(module, Map.new(params))
76+
end
77+
78+
def build_params(module, params) do
79+
params
80+
|> module.build()
81+
|> SharedUtils.Map.deep_struct_to_map()
82+
end
83+
84+
@doc """
85+
Builds a schema given the factory `module` and an optional
86+
list/map of `params`.
87+
"""
88+
@spec build(module()) :: Ecto.Schema.t()
89+
@spec build(module(), keyword() | map()) :: Ecto.Schema.t()
90+
def build(module, params \\ %{})
91+
92+
def build(module, params) when is_list(params) do
93+
build(module, Map.new(params))
94+
end
95+
96+
def build(module, params) do
97+
struct!(module.schema(), module.build(params))
98+
end
99+
100+
@doc """
101+
Inserts a schema given the factory `module` and an optional list/map of
102+
`params`. Fails on error.
103+
"""
104+
@spec insert!(module()) :: Ecto.Schema.t() | no_return()
105+
@spec insert!(module(), keyword() | map(), Keyword.t()) :: Ecto.Schema.t() | no_return()
106+
def insert!(module, params \\ %{}, options \\ [])
107+
108+
def insert!(module, params, options) when is_list(params) do
109+
insert!(module, Map.new(params), options)
110+
end
111+
112+
def insert!(module, params, options) do
113+
module
114+
|> build(params)
115+
|> module.repo().insert!(options)
116+
end
117+
118+
@doc """
119+
Insert as many as `count` schemas given the factory `module` and an optional
120+
list/map of `params`.
121+
"""
122+
@spec insert_many!(pos_integer(), module()) :: [Ecto.Schema.t()]
123+
@spec insert_many!(pos_integer(), module(), keyword() | map()) :: [Ecto.Schema.t()]
124+
def insert_many!(count, module, params \\ %{}, options \\ []) when count > 0 do
125+
Enum.map(1..count, fn _ -> insert!(module, params, options) end)
126+
end
127+
128+
@doc """
129+
Removes all the instances of a schema from the database given its factory
130+
`module`.
131+
"""
132+
@spec cleanup(module) :: {integer(), nil | [term()]}
133+
def cleanup(module, options \\ []) do
134+
module.repo().delete_all(module.schema(), options)
135+
end
136+
end

mix.exs

+36-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
1-
defmodule ExFactory.MixProject do
1+
defmodule FactoryEx.MixProject do
22
use Mix.Project
33

44
def project do
55
[
6-
app: :ex_factory,
6+
app: :factory_ex,
77
version: "0.1.0",
88
elixir: "~> 1.13",
9+
description: "Factories for elixir to help create data models at random, this works for any type of ecto structs",
10+
elixirc_paths: elixirc_paths(Mix.env()),
911
start_permanent: Mix.env() == :prod,
10-
deps: deps()
12+
deps: deps(),
13+
docs: docs(),
14+
package: package()
1115
]
1216
end
1317

@@ -25,4 +29,33 @@ defmodule ExFactory.MixProject do
2529
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
2630
]
2731
end
32+
33+
defp package do
34+
[
35+
maintainers: ["Mika Kalathil"],
36+
licenses: ["MIT"],
37+
links: %{"GitHub" => "https://github.com/theblitzapp/factory_ex"},
38+
files: ~w(mix.exs README.md CHANGELOG.md LICENSE lib config)
39+
]
40+
end
41+
42+
defp docs do
43+
[
44+
main: "FactoryEx",
45+
source_url: "https://github.com/theblitzapp/factory_ex",
46+
47+
groups_for_modules: [
48+
"General": [
49+
FactoryEx
50+
],
51+
52+
"Adapters": [
53+
FactoryEx.Adapter
54+
]
55+
]
56+
]
57+
end
58+
59+
defp elixirc_paths(:test), do: ["lib", "test/support"]
60+
defp elixirc_paths(_), do: ["lib"]
2861
end

test/ex_factory_test.exs

-8
This file was deleted.

test/factory_ex_test.exs

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
defmodule FactoryExTest do
2+
use ExUnit.Case
3+
doctest FactoryEx
4+
5+
test "greets the world" do
6+
assert FactoryEx.hello() == :world
7+
end
8+
end

0 commit comments

Comments
 (0)