From eaa4b9b698b16254bd084f6c93c2c35ff625db27 Mon Sep 17 00:00:00 2001 From: German Velasco Date: Mon, 1 Mar 2021 08:31:50 -0500 Subject: [PATCH] Allow setting sequence starting point (#414) What changed? ============ We introduce `ExMachina.sequence/3` that allows for passing the `start_at` option to set a starting point for a sequence. This is meant to be used in a factory definition: ```elixir def money_factory do %{ cents: sequence(:cents, &"#{&1}", start_at: 60) } end ``` Then, the following would increment starting at 60: ```elixir build(:money) // => %{cents: "60"} build(:money) // => %{cents: "61"} ``` --- lib/ex_machina.ex | 25 +++++++++++++++++++++++-- lib/ex_machina/sequence.ex | 6 ++++-- test/ex_machina/sequence_test.exs | 7 ++++++- test/ex_machina_test.exs | 11 +++++++++++ 4 files changed, 44 insertions(+), 5 deletions(-) diff --git a/lib/ex_machina.ex b/lib/ex_machina.ex index 7edace3..3a8f7ff 100644 --- a/lib/ex_machina.ex +++ b/lib/ex_machina.ex @@ -37,7 +37,13 @@ defmodule ExMachina do @before_compile unquote(__MODULE__) import ExMachina, - only: [sequence: 1, sequence: 2, merge_attributes: 2, evaluate_lazy_attributes: 1] + only: [ + sequence: 1, + sequence: 2, + sequence: 3, + merge_attributes: 2, + evaluate_lazy_attributes: 1 + ] def build(factory_name, attrs \\ %{}) do ExMachina.build(__MODULE__, factory_name, attrs) @@ -142,10 +148,25 @@ defmodule ExMachina do } end """ - @spec sequence(any, (integer -> any) | nonempty_list) :: any def sequence(name, formatter), do: ExMachina.Sequence.next(name, formatter) + @doc """ + 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 + """ + @spec sequence(any, (integer -> any) | nonempty_list, start_at: non_neg_integer) :: any + def sequence(name, formatter, opts), do: ExMachina.Sequence.next(name, formatter, opts) + @doc """ Builds a single factory. diff --git a/lib/ex_machina/sequence.ex b/lib/ex_machina/sequence.ex index d5987b9..05a94ad 100644 --- a/lib/ex_machina/sequence.ex +++ b/lib/ex_machina/sequence.ex @@ -116,9 +116,11 @@ defmodule ExMachina.Sequence do end @doc false - def next(sequence_name, formatter) do + def next(sequence_name, formatter, opts \\ []) do + start_at = Keyword.get(opts, :start_at, 0) + Agent.get_and_update(__MODULE__, fn sequences -> - current_value = Map.get(sequences, sequence_name, 0) + current_value = Map.get(sequences, sequence_name, start_at) new_sequences = Map.put(sequences, sequence_name, current_value + 1) {formatter.(current_value), new_sequences} end) diff --git a/test/ex_machina/sequence_test.exs b/test/ex_machina/sequence_test.exs index b3797ed..ae59d1d 100644 --- a/test/ex_machina/sequence_test.exs +++ b/test/ex_machina/sequence_test.exs @@ -26,7 +26,12 @@ defmodule ExMachina.SequenceTest do assert 1 == Sequence.next(:month, & &1) end - test "let's you quickly create sequences" do + test "can optionally set starting integer" do + assert "100" == Sequence.next(:dollars_in_cents, &"#{&1}", start_at: 100) + assert "101" == Sequence.next(:dollars_in_cents, &"#{&1}") + end + + test "lets you quickly create sequences" do assert "Comment Body0" == Sequence.next("Comment Body") assert "Comment Body1" == Sequence.next("Comment Body") end diff --git a/test/ex_machina_test.exs b/test/ex_machina_test.exs index addec7f..aa37a07 100644 --- a/test/ex_machina_test.exs +++ b/test/ex_machina_test.exs @@ -70,6 +70,12 @@ defmodule ExMachinaTest do %{floor: floor_number} = attrs sequence(:room_number, &"#{floor_number}0#{&1}") end + + def money_factory do + %{ + cents: sequence(:cents, &"#{&1}", start_at: 600) + } + end end describe "sequence" do @@ -82,6 +88,11 @@ defmodule ExMachinaTest do assert "Post Title0" == Factory.build(:article).title assert "Post Title1" == Factory.build(:article).title end + + test "sequence/3 allows for setting a starting value" do + assert "600" == Factory.build(:money).cents + assert "601" == Factory.build(:money).cents + end end describe "build/2" do