Setting up data with ex_machina

Here is my Factory: (using the ex_machina package)

def question_factory do
    %Question{
      active: true,
      type: "NBA",
      description: "player points",
      reserved: %{
        information: %{
          game_id: Ecto.UUID.generate,
          player_id: Ecto.UUID.generate,
          player_name: "Lebron James"
        },
        inputs: [
          %{
            type: "text",
            label: "Player Points",
          }
        ]
      }
    }
end

Question Schema

schema "questions" do
  field(:active, :boolean)
  field(:type, :string)
  field(:description, :string)
  embeds_one(:reserved, Statcasters.Questions.Reserved)

  timestamps()
end

Reserved Schema

embedded_schema do
  field(:information, :map)
  field(:inputs, {:array, :map})
end

Here is my test setup:

question = insert(:question, reserved: %{ information: %{player_name: "steve"}})

In this example I want to only update the player_name for the reserved map. But when I use the above setup the question struct looks like this:

%MyApp.Question{
  __meta__: #Ecto.Schema.Metadata<:loaded, "questions">,
  active: true,
  description: "player points",
  id: 125,
  inserted_at: ~N[2018-08-27 14:47:42.075169],
  reserved: %MyApp.Questions.Reserved{
    information: %{player_name: "steve"},
    inputs: nil
  },
  type: "NBA",
  updated_at: ~N[2018-08-27 14:47:42.075182]
}

I want it to look like this:

%MyApp.Question{
  __meta__: #Ecto.Schema.Metadata<:loaded, "questions">,
  active: false,
  description: "player points",
  id: 122,
  inserted_at: ~N[2018-08-27 14:39:52.672051],
  reserved: %MyApp.Questions.Reserved{
    information: %{
      game_id: "1ab95979-329a-488c-9d5f-22bed4f2b985",
      player_id: "07cc1588-68eb-43b6-afa6-483fa3005cb2",
      player_name: "Steve"
    },
    inputs: [%{label: "Player Points", type: "text"}]
  },
  type: "NBA",
  updated_at: ~N[2018-08-27 14:39:52.672058]
}

Again, the problem is that it’s replacing all of the values within reserved I want it just to change player_name how can I achieve this using elixir and ex_machina? Thanks for any help.

ExMachina really only provides a mechanism to override elements at one level. It’s not merging. The simplest solution to your problem would be, in your test:

question = 
  :question
  |> build()
  |> put_in([:reserved, :information, :player_name], "steve")
  |> Repo.insert!()

You can move that put_in to the factory and give it a name like, with_player_name/2.

2 Likes

Cool, Thanks for the help.

Part of me wants to know what it would take to add that functionality to ex_machina?