How to write fixtures for diamond-like schemas in ExMachina?

Hi, I am having trouble using ExMachina and designing factories because my data has a “diamond” shape.

                    Resource 1
                   /          \
              Resource 2    Resource 3
                   \          /
                    Resource 4

Ideally, we would like to write:


resource_1 = build(
  :resource_1, 
  resource_2: build(:resource_2, resource_4: build(:resource_4),
  resource_3: build(:resource_3, resource_4: build(:resource_4),
  )

But ExMachina has to know that the two resource 4s.

Since it doesn’t, we resorted to the following solution, where we first insert the shared resource.

resource_4 = insert(:resource_4)

resource_1 = build(
  :resource_1, 
  resource_2: build(:resource_2, resource_4: resource_4,
  resource_3: build(:resource_3, resource_4: resource_4,
  )

Is this alright?

  • Is there an idiomatic solution for this case?
  • Does this point to a problem in our data model?

In general, how to write fixtures with ExMachina when we encounter “diamonds”?

1 Like

I could be way off base, but this kind of looks like you want to pipe the results of functions together and they aren’t playing nice with the |> syntax. One strategy is to use with:

with resource_4 <- build(:resource_4),
       resource_3 <- build(:resource_3, resource_4: resource_4),
       resource_2 <- build(:resource_2, resource_4: resource_4) do
  build(:resource_1, resource_2: resource_2, resource_3: resource_3, resource_4: resource_4)
end

This indeed looks nice, but I am looking for more specific advice: are diamonds a mistake in our data model? Is there an idiomatic way to use ExMachina in this case? How did people with similar problems solve it? Thanks!

It could be a sign of a data-model issue, but it’s not unusual. For instance, imagine a simple “blog” schema:

  • Post has_many Comments
  • Comment belongs to a Post and a User
  • User has_many Comments

A factory for a post with two comments from the same user would create the diamond pattern you’ve described.

IMO the issue here is that ExMachina’s built-in approach of “insert everything at once” isn’t always a good match for how the application actually works; in the blog example, Post is inserted entirely separately and User might be as well.

2 Likes

Yeah. As a more general note, I just don’t see the need for libraries like ExMachina. We replaced it entirely years and years ago with regular context function calls, and some modules in test/support that help reduce boilerplate when we want to set up more complex scenarios.

4 Likes