From 03c41f64470423a168f91d40edcd91eb242c3c61 Mon Sep 17 00:00:00 2001 From: Josh Steiner Date: Tue, 22 Sep 2015 15:18:04 -0400 Subject: [PATCH] Move factory definitions to a macro --- README.md | 16 ++++++++++------ lib/ex_machina.ex | 36 +++++++++++++++++++---------------- lib/ex_machina/ecto.ex | 20 +++++++++++-------- test/ex_machina/ecto_test.exs | 12 ++++++------ test/ex_machina_test.exs | 12 +++++------- 5 files changed, 53 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index e1dd46b..8c3068c 100644 --- a/README.md +++ b/README.md @@ -34,22 +34,22 @@ defmodule MyApp.Factories do # It will automatically be used when calling `create` use ExMachina.Ecto, repo: MyApp.Repo - def factory(:config) do + factory :config do # Factories can be plain maps %{url: "http://example.com"} end - def factory(:article) do + factory :article do %Article{ title: "My Awesome Article" } end - def factory(:comment, attrs) do + factory :comment do %Comment{ body: "This is great!", author_email: sequence(:email, &"email-#{&1}@example.com"), - article_id: assoc(attrs, :article).id + article_id: assoc(:article).id } end end @@ -85,7 +85,9 @@ defining `save_function/1` in your module. defmodule MyApp.JsonFactories do use ExMachina - def factory(:user), do: %User{name: "John"} + factory :user do + %User{name: "John"} + end def save_record(record) do # Poison is a library for working with JSON @@ -105,7 +107,9 @@ or `create_json` to return encoded JSON objects. defmodule MyApp.Factories do use ExMachina.Ecto, repo: MyApp.Repo - def factory(:user), do: %User{name: "John"} + factory :user do + %User{name: "John"} + end # builds the object and then encodes it as JSON def build_json(factory_name, attrs) do diff --git a/lib/ex_machina.ex b/lib/ex_machina.ex index a4d567b..f5780cf 100644 --- a/lib/ex_machina.ex +++ b/lib/ex_machina.ex @@ -41,7 +41,7 @@ defmodule ExMachina do quote do @before_compile unquote(__MODULE__) - import ExMachina, only: [sequence: 2] + import ExMachina, only: [sequence: 2, factory: 2] def build(factory_name, attrs \\ %{}) do ExMachina.build(__MODULE__, factory_name, attrs) @@ -69,12 +69,21 @@ defmodule ExMachina do end end + defmacro factory(factory_name, do: block) do + quote do + def factory(unquote(factory_name), var!(attrs)) do + !var!(attrs) # Removes unused variable warning if attrs wasn't used + unquote(block) + end + end + end + @doc """ Create sequences for generating unique values ## Examples - def factory(:user) do + factory :user do %{ # Will generate "me-0@example.com" then "me-1@example.com", etc. email: sequence(:email, &"me-\#{&1}@foo.com") @@ -88,7 +97,7 @@ defmodule ExMachina do ## Example - def factory(:user) do + factory :user do %{name: "John Doe", admin: false} end @@ -137,7 +146,7 @@ defmodule ExMachina do ## Example - def factory(:user) do + factory :user do %{name: "John Doe", admin: false} end @@ -176,19 +185,10 @@ defmodule ExMachina do defmacro __before_compile__(_env) do quote do - @doc """ - Calls factory/1 with the passed in factory name - - This allows you to define factories without the `attrs` param. - """ - def factory(factory_name, _attrs) do - __MODULE__.factory(factory_name) - end - @doc """ Raises a helpful error if no factory is defined. """ - def factory(factory_name) do + def factory(factory_name, _) do raise UndefinedFactory, factory_name end @@ -206,7 +206,9 @@ defmodule ExMachina do defmodule MyApp.Factories do use ExMachina.Ecto, repo: MyApp.Repo - def factory(:user), do: %User{name: "John"} + factory :user do + %User{name: "John"} + end end # Will build and save the record to the MyApp.Repo @@ -216,7 +218,9 @@ defmodule ExMachina do # Note, we are not using ExMachina.Ecto use ExMachina - def factory(:user), do: %User{name: "John"} + factory :user do + %User{name: "John"} + end def save_function(record) do # Poison is a library for working with JSON diff --git a/lib/ex_machina/ecto.ex b/lib/ex_machina/ecto.ex index 9d70eb9..21290bf 100644 --- a/lib/ex_machina/ecto.ex +++ b/lib/ex_machina/ecto.ex @@ -3,22 +3,26 @@ defmodule ExMachina.Ecto do quote do use ExMachina + import ExMachina.Ecto, only: [assoc: 1, assoc: 2] + @repo Dict.fetch!(unquote(opts), :repo) def fields_for(factory_name, attrs \\ %{}) do ExMachina.Ecto.fields_for(__MODULE__, factory_name, attrs) end - defp assoc(attrs, factory_name, opts \\ []) do - ExMachina.Ecto.assoc(__MODULE__, attrs, factory_name, opts) - end - def save_record(record) do ExMachina.Ecto.save_record(__MODULE__, @repo, record) end end end + defmacro assoc(factory_name, opts \\ []) do + quote do + ExMachina.Ecto.assoc(__MODULE__, var!(attrs), unquote(factory_name), unquote(opts)) + end + end + @doc """ Builds a factory with the passed in factory_name and returns its fields @@ -29,7 +33,7 @@ defmodule ExMachina.Ecto do ## Example - def factory(:user) do + factory :user do %MyApp.User{name: "John Doe", admin: false} end @@ -58,15 +62,15 @@ defmodule ExMachina.Ecto do attrs = %{user: %{name: "Someone"}} # Returns attrs.user - assoc(attrs, :user) + assoc(:user) attrs = %{} # Creates and returns new instance based on :user factory - assoc(attrs, :user) + assoc(:user) attrs = %{} # Creates and returns new instance based on :user factory - assoc(attrs, :author, factory: :user) + assoc(:author, factory: :user) """ def assoc(module, attrs, factory_name, opts \\ []) do case Map.get(attrs, factory_name) do diff --git a/test/ex_machina/ecto_test.exs b/test/ex_machina/ecto_test.exs index e9100f6..f9a9e41 100644 --- a/test/ex_machina/ecto_test.exs +++ b/test/ex_machina/ecto_test.exs @@ -19,13 +19,13 @@ defmodule ExMachina.EctoTest do defmodule MyApp.EctoFactories do use ExMachina.Ecto, repo: TestRepo - def factory(:book) do + factory :book do %MyApp.Book{ title: "Foo" } end - def factory(:user) do + factory :user do %{ id: 3, name: "John Doe", @@ -33,18 +33,18 @@ defmodule ExMachina.EctoTest do } end - def factory(:article, attrs) do + factory :article do %{ id: 1, title: "My Awesome Article", - author_id: assoc(attrs, :author, factory: :user).id + author_id: assoc(:author, factory: :user).id } end - def factory(:comment, attrs) do + factory :comment do %{ body: "This is great!", - article_id: assoc(attrs, :article).id + article_id: assoc(:article).id } end end diff --git a/test/ex_machina_test.exs b/test/ex_machina_test.exs index 35bc912..281455a 100644 --- a/test/ex_machina_test.exs +++ b/test/ex_machina_test.exs @@ -4,7 +4,7 @@ defmodule ExMachinaTest do defmodule MyApp.Factories do use ExMachina - def factory(:user) do + factory :user do %{ id: 3, name: "John Doe", @@ -12,7 +12,7 @@ defmodule ExMachinaTest do } end - def factory(:email) do + factory :email do %{ email: sequence(:email, &"me-#{&1}@foo.com") } @@ -27,7 +27,9 @@ defmodule ExMachinaTest do defmodule MyApp.NoSaveFunction do use ExMachina - def factory(:foo), do: %{foo: :bar} + factory(:foo) do + %{foo: :bar} + end end test "sequence/2 sequences a value" do @@ -35,10 +37,6 @@ defmodule ExMachinaTest do assert "me-1@foo.com" == MyApp.Factories.build(:email).email end - test "factories can be defined without the attrs param" do - assert MyApp.Factories.build(:user) == MyApp.Factories.factory(:user) - end - test "raises a helpful error if the factory is not defined" do assert_raise ExMachina.UndefinedFactory, "No factory defined for :foo", fn -> MyApp.Factories.build(:foo)