Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,20 @@ This is not meant to generate data for your tests, if this is what
you need, checkout [ecto_fixtures](https://github.com/dockyard/ecto_fixtures) or
[ex_machina](https://github.com/thoughtbot/ex_machina) instead.

## Compatible versions

Version `0.3.0` or lower:

* Elixir ~> `1.11.x` or lower.

Version `0.4.0`:

* Elixir ~> `1.13.x`.

Version `0.5.0` or higher:

* Elixir ~> `1.14.x` or higher.

## Installation

Add `seedex` to your list of dependencies in `mix.exs`:
Expand Down
3 changes: 2 additions & 1 deletion config/config.exs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use Mix.Config
import Config

config :seedex,
ecto_repos: [Seedex.Repo],
Expand All @@ -8,6 +8,7 @@ config :seedex,
config :seedex, Seedex.Repo,
database: "seedex_test",
username: "postgres",
password: "postgres",
hostname: "localhost",
pool: Ecto.Adapters.SQL.Sandbox

Expand Down
17 changes: 10 additions & 7 deletions lib/mix/tasks/seedex.seed.ex
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ defmodule Mix.Tasks.Seedex.Seed do

@doc false
def run(args) do
{opts, _, _} = OptionParser.parse(args, switches: [env: :string, seeds_path: :string, debug: :boolean, quiet: :boolean])
{opts, _, _} =
OptionParser.parse(args,
switches: [env: :string, seeds_path: :string, debug: :boolean, quiet: :boolean]
)

if opts[:debug] do
Logger.configure(level: :debug)
Expand All @@ -35,20 +38,20 @@ defmodule Mix.Tasks.Seedex.Seed do
Mix.Task.run("app.start", [])

seeds_path = Keyword.get(opts, :seeds_path, default_path())
env = Keyword.get(opts, :env, to_string(Mix.env))
env = Keyword.get(opts, :env, to_string(Mix.env()))

unless File.dir?(seeds_path) do
Mix.raise """
Mix.raise("""
seeeds_path is not a directory, create priv/repo/seeds or configure in :seedex configuration
"""
""")
end

seeds_path
|> seeds_files(env)
|> Enum.each(&Code.load_file(&1))
|> Enum.each(&Code.compile_file(&1))

unless opts[:quiet] do
Mix.shell.info("Database has been populated with seeds from #{seeds_path}")
Mix.shell().info("Database has been populated with seeds from #{seeds_path}")
end
end

Expand All @@ -60,7 +63,7 @@ defmodule Mix.Tasks.Seedex.Seed do
defp exs_files(path) do
path
|> Path.join("*.exs")
|> Path.wildcard
|> Path.wildcard()
end

defp default_path do
Expand Down
41 changes: 31 additions & 10 deletions lib/seedex.ex
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,21 @@ defmodule Seedex do
]
```
"""
@spec seed(module :: atom, constraints :: [atom], data :: [map], process :: (struct -> struct)) :: :ok
@spec seed(module :: atom, constraints :: [atom], data :: [map], process :: (struct -> struct)) ::
:ok
def seed(module, constraints \\ [:id], data \\ [], process \\ nil) do
dispatch_seed(module, constraints, data, process, update: true)
end

@doc """
Same as `seed/3` but does not update the record if it already exists
"""
@spec seed_once(module :: atom, constraints :: [atom], data :: (struct -> struct) | [map], process :: (struct -> struct)) :: :ok
@spec seed_once(
module :: atom,
constraints :: [atom],
data :: (struct -> struct) | [map],
process :: (struct -> struct)
) :: :ok
def seed_once(module, constraints \\ [:id], data \\ [], process \\ nil) do
dispatch_seed(module, constraints, data, process, update: false)
end
Expand All @@ -52,39 +58,48 @@ defmodule Seedex do
# arguments were all pased
defp dispatch_seed(module, constraints, data, func, opts) when is_function(func, 1),
do: do_seed(module, constraints, data, func, opts)

# 3 arguments passed
defp dispatch_seed(module, [h | t], data, nil, opts) when is_atom(h) and is_list(data),
do: do_seed(module, [h | t], data, &identity/1, opts)

defp dispatch_seed(module, [h | t], func, nil, opts) when is_atom(h) and is_function(func, 1),
do: do_seed(module, [h | t], [], func, opts)

defp dispatch_seed(module, [h | t], func, nil, opts) when is_map(h) and is_function(func, 1),
do: do_seed(module, [:id], [h | t], func, opts)

# 2 arguments passed
defp dispatch_seed(module, func, [], nil, opts) when is_function(func, 1),
do: do_seed(module, [:id], [], func, opts)

defp dispatch_seed(module, [h | t], [], nil, opts) when is_map(h),
do: do_seed(module, [:id], [h | t], &identity/1, opts)

defp dispatch_seed(_module, _constraints, _data, _func, _opts),
do: raise ArgumentError, "invalid arguments to seed"
do: raise(ArgumentError, "invalid arguments to seed")

defp do_seed(module, constraints, [], process, opts), do:
do_seed(module, constraints, [%{}], process, opts)
defp do_seed(module, constraints, [], process, opts),
do: do_seed(module, constraints, [%{}], process, opts)

defp do_seed(module, constraints, data, process, opts) do
Enum.each data, fn record ->
Enum.each(data, fn record ->
record = struct(module, record) |> process.()
insert_seed(module, record, constraints, opts)
end
end)
end

defp insert_seed(module, record, constraints, opts) do
existing = fetch_record(module, record, constraints)

cond do
existing && opts[:update] ->
update_record(record, existing)

!existing ->
Logger.debug("Inserting record #{inspect(record)}")
repo().insert(record)

true ->
:ok
end
Expand All @@ -94,14 +109,15 @@ defmodule Seedex do
case make_query(record, constraints) do
[] ->
nil

query ->
repo().get_by(module, query)
end
end

defp make_query(record, constraints) do
constraints
|> Enum.map(& {&1, Map.fetch!(record, &1)})
|> Enum.map(&{&1, Map.fetch!(record, &1)})
|> Enum.reject(fn {_k, v} -> is_nil(v) end)
end

Expand All @@ -113,18 +129,23 @@ defmodule Seedex do

defp make_changeset(record, existing) do
{changeset, changes} = {Ecto.Changeset.change(existing), Map.from_struct(record)}
Enum.reduce changes, changeset, fn

Enum.reduce(changes, changeset, fn
{_key, %Ecto.Association.NotLoaded{}}, changeset ->
changeset

{_key, nil}, changeset ->
changeset

{key, _value}, changeset when key in ["__meta__", :__meta__] ->
changeset

{key, %Ecto.Association.BelongsTo{} = assoc}, changeset ->
Ecto.Changeset.put_assoc(changeset, key, assoc)

{key, value}, changeset ->
Ecto.Changeset.put_change(changeset, key, value)
end
end)
end

defp repo do
Expand Down
49 changes: 28 additions & 21 deletions mix.exs
Original file line number Diff line number Diff line change
@@ -1,39 +1,46 @@
defmodule Seedex.Mixfile do
use Mix.Project

@version "0.3.0"
@version "0.3.1"

def project do
[app: :seedex,
version: @version,
elixir: "~> 1.1",
description: "Seed data generation for Ecto",
source_url: "https://github.com/danhper/seedex",
elixirc_paths: elixirc_paths(Mix.env),
build_embedded: Mix.env == :prod,
start_permanent: Mix.env == :prod,
package: package(),
deps: deps(),
docs: [source_ref: "#{@version}", extras: ["README.md"], main: "readme"]]
[
app: :seedex,
version: @version,
elixir: "~> 1.13",
description: "Seed data generation for Ecto",
source_url: "https://github.com/danhper/seedex",
elixirc_paths: elixirc_paths(Mix.env()),
build_embedded: Mix.env() == :prod,
start_permanent: Mix.env() == :prod,
package: package(),
deps: deps(),
docs: [source_ref: "#{@version}", extras: ["README.md"], main: "readme"]
]
end

def application do
[applications: applications(Mix.env),
description: 'Seed data generation for Ecto']
[
extra_applications: [:ecto],
applications: applications(Mix.env()),
description: 'Seed data generation for Ecto'
]
end

defp applications(:test), do: applications(:all) ++ [:ecto, :postgrex]
defp applications(_all), do: [:logger]
defp applications(_all), do: [:logger]

defp elixirc_paths(:test), do: ["lib", "test/support"]
defp elixirc_paths(_all), do: ["lib"]
defp elixirc_paths(_all), do: ["lib"]

defp deps do
[{:ecto, "~> 1.1 or ~> 2.1 or ~> 3.0"},
{:ecto_sql, "~> 3.0"},
{:postgrex, "~> 0.13", only: [:test]},
{:earmark, "~> 1.0", only: :docs},
{:ex_doc, "~> 0.14", only: :docs}]
[
{:ecto, "~> 3.9"},
{:ecto_sql, "~> 3.9"},
{:postgrex, "~> 0.16.1", only: [:test]},
{:earmark, "~> 1.4.20", only: :docs},
{:ex_doc, "~> 0.14", only: :docs}
]
end

defp package do
Expand Down
25 changes: 13 additions & 12 deletions mix.lock
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
%{
"connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm"},
"db_connection": {:hex, :db_connection, "2.0.6", "bde2f85d047969c5b5800cb8f4b3ed6316c8cb11487afedac4aa5f93fd39abfa", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm"},
"decimal": {:hex, :decimal, "1.7.0", "30d6b52c88541f9a66637359ddf85016df9eb266170d53105f02e4a67e00c5aa", [:mix], [], "hexpm"},
"earmark": {:hex, :earmark, "1.3.2", "b840562ea3d67795ffbb5bd88940b1bed0ed9fa32834915125ea7d02e35888a5", [:mix], [], "hexpm"},
"ecto": {:hex, :ecto, "3.1.1", "d6677f95f1e0bd39bc3db3db6b23a59977cb154ed2cceec69a56becd805128be", [:mix], [{:decimal, "~> 1.6", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"},
"ecto_sql": {:hex, :ecto_sql, "3.1.1", "af2458e7a467d75a6389e1d4ebfb57c328ccc684d6ee52145f7b34e94efb5fc4", [:mix], [{:db_connection, "~> 2.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.1.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:mariaex, "~> 0.9.1", [hex: :mariaex, repo: "hexpm", optional: true]}, {:myxql, "~> 0.2.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.14.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"},
"ex_doc": {:hex, :ex_doc, "0.20.2", "1bd0dfb0304bade58beb77f20f21ee3558cc3c753743ae0ddbb0fd7ba2912331", [:mix], [{:earmark, "~> 1.3", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.10", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"},
"makeup": {:hex, :makeup, "0.8.0", "9cf32aea71c7fe0a4b2e9246c2c4978f9070257e5c9ce6d4a28ec450a839b55f", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"},
"makeup_elixir": {:hex, :makeup_elixir, "0.13.0", "be7a477997dcac2e48a9d695ec730b2d22418292675c75aa2d34ba0909dcdeda", [:mix], [{:makeup, "~> 0.8", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"},
"nimble_parsec": {:hex, :nimble_parsec, "0.5.0", "90e2eca3d0266e5c53f8fbe0079694740b9c91b6747f2b7e3c5d21966bba8300", [:mix], [], "hexpm"},
"connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"},
"db_connection": {:hex, :db_connection, "2.4.3", "3b9aac9f27347ec65b271847e6baeb4443d8474289bd18c1d6f4de655b70c94d", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c127c15b0fa6cfb32eed07465e05da6c815b032508d4ed7c116122871df73c12"},
"decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"},
"earmark": {:hex, :earmark, "1.4.20", "d5097b1c7417a03c73a2985fcf01c3f72192c427b8a498719737dca5273938cb", [:mix], [{:earmark_parser, "== 1.4.18", [hex: :earmark_parser, repo: "hexpm", optional: false]}], "hexpm", "7be744242dbde74c858279f4a65d9d31f37d163190d739340015c30038c1edb3"},
"earmark_parser": {:hex, :earmark_parser, "1.4.18", "e1b2be73eb08a49fb032a0208bf647380682374a725dfb5b9e510def8397f6f2", [:mix], [], "hexpm", "114a0e85ec3cf9e04b811009e73c206394ffecfcc313e0b346de0d557774ee97"},
"ecto": {:hex, :ecto, "3.9.2", "017db3bc786ff64271108522c01a5d3f6ba0aea5c84912cfb0dd73bf13684108", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "21466d5177e09e55289ac7eade579a642578242c7a3a9f91ad5c6583337a9d15"},
"ecto_sql": {:hex, :ecto_sql, "3.9.1", "9bd5894eecc53d5b39d0c95180d4466aff00e10679e13a5cfa725f6f85c03c22", [:mix], [{:db_connection, "~> 2.5 or ~> 2.4.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.9.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5fd470a4fff2e829bbf9dcceb7f3f9f6d1e49b4241e802f614de6b8b67c51118"},
"ex_doc": {:hex, :ex_doc, "0.20.2", "1bd0dfb0304bade58beb77f20f21ee3558cc3c753743ae0ddbb0fd7ba2912331", [:mix], [{:earmark, "~> 1.3", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.10", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm", "8e24fc8ff9a50b9f557ff020d6c91a03cded7e59ac3e0eec8a27e771430c7d27"},
"makeup": {:hex, :makeup, "0.8.0", "9cf32aea71c7fe0a4b2e9246c2c4978f9070257e5c9ce6d4a28ec450a839b55f", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "5fbc8e549aa9afeea2847c0769e3970537ed302f93a23ac612602e805d9d1e7f"},
"makeup_elixir": {:hex, :makeup_elixir, "0.13.0", "be7a477997dcac2e48a9d695ec730b2d22418292675c75aa2d34ba0909dcdeda", [:mix], [{:makeup, "~> 0.8", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "adf0218695e22caeda2820eaba703fa46c91820d53813a2223413da3ef4ba515"},
"nimble_parsec": {:hex, :nimble_parsec, "0.5.0", "90e2eca3d0266e5c53f8fbe0079694740b9c91b6747f2b7e3c5d21966bba8300", [:mix], [], "hexpm", "5c040b8469c1ff1b10093d3186e2e10dbe483cd73d79ec017993fb3985b8a9b3"},
"poolboy": {:hex, :poolboy, "1.5.1", "6b46163901cfd0a1b43d692657ed9d7e599853b3b21b95ae5ae0a777cf9b6ca8", [:rebar], [], "hexpm"},
"postgrex": {:hex, :postgrex, "0.14.2", "6680591bbce28d92f043249205e8b01b36cab9ef2a7911abc43649242e1a3b78", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"},
"telemetry": {:hex, :telemetry, "0.4.0", "8339bee3fa8b91cb84d14c2935f8ecf399ccd87301ad6da6b71c09553834b2ab", [:rebar3], [], "hexpm"},
"postgrex": {:hex, :postgrex, "0.16.5", "fcc4035cc90e23933c5d69a9cd686e329469446ef7abba2cf70f08e2c4b69810", [:mix], [{:connection, "~> 1.1", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "edead639dc6e882618c01d8fc891214c481ab9a3788dfe38dd5e37fd1d5fb2e8"},
"telemetry": {:hex, :telemetry, "1.1.0", "a589817034a27eab11144ad24d5c0f9fab1f58173274b1e9bae7074af9cbee51", [:rebar3], [], "hexpm", "b727b2a1f75614774cff2d7565b64d0dfa5bd52ba517f16543e6fc7efcc0df48"},
}