Skip to content
This repository has been archived by the owner on Mar 15, 2021. It is now read-only.

Handling nested attributes in Fabricate.attributes_for. #215

Open
thomasfedb opened this issue Jul 28, 2014 · 8 comments
Open

Handling nested attributes in Fabricate.attributes_for. #215

thomasfedb opened this issue Jul 28, 2014 · 8 comments

Comments

@thomasfedb
Copy link

I have a fabricator defined as so:

Fabricator(:record) do
  # Study Allocation
  study_id { sequence(:study_id) }

  # Violations
  is_violation { [true, false].sample }
  violations {|attrs| attrs[:is_violation] ? Fabricate.build_times(2, :violation) : [] }

  # Demographics
  demo_date_of_birth { (rand(6) + 6).years.ago + rand(365).days }
  demo_weight { (20 + rand(40) + rand).round(2) }
  demo_height { (60 + rand(90)).to_f / 100 }
end

The issue I'm facing is when I use Fabricate.attributes_for(:record). Currently the following output results:

{
  "study_id" => 2,
  "is_violation" => true,
  "violations" => [
    #<Violation id: nil, violation_type_id: nil, record_id: nil, comment: nil, created_at: nil, updated_at: nil>,
    #<Violation id: nil, violation_type_id: nil, record_id: nil, comment: nil, created_at: nil, updated_at: nil>
  ],
  "demo_date_of_birth" => Mon, 26 Mar 2007 05:12:57 UTC +00:00,
  "demo_weight" => 21.14,
  "demo_height" => 1.4
}

Whereas I was hoping for output that I could feed into a controller's parameters, and would thus be more like:

{
  "study_id" => 2,
  "is_violation" => true,
  "violations_attributes" => [
    {
      "violation_type_id" => 17,
      "comment" => "Lorem ipsum."
    }
  ],
  "demo_date_of_birth" => Mon, 26 Mar 2007 05:12:57 UTC +00:00,
  "demo_weight" => 21.14,
  "demo_height" => 1.4
}
@thomasfedb
Copy link
Author

Ideally, as there are many case where generating attributes involves nesting attributes for some records, and committing others directly to the database, there would be callbacks available for attributes_for.

@paulelliott
Copy link
Owner

I agree that this is how it should work. There are some architectural decisions in the library that make this change extremely difficult though. I have it on my feature list for 3.0 though.

@thomasfedb
Copy link
Author

How did you go with this end the end @paulelliott?

@paulelliott
Copy link
Owner

As it stands there is not a solution for this. I closed out all old lingering issues last week but since you're still interested I'll reopen it 😄

This is a challenging issue to address but now that I'm thinking through it again I have some ideas. I'll see what I can do.

@paulelliott paulelliott reopened this Nov 3, 2016
@paulelliott paulelliott removed the stale label Nov 7, 2016
@paulelliott
Copy link
Owner

So the issue we have is in reliably knowing when something is an association and when it isn't. Right now it is inferred when you use the count or fabricator option on an attribute, which is totally fine in today's world.

Fabricator(:thingy) do
  widgets(count: 5) // works
  wocket(fabricator: :discombobulator) // works
  huzzahs do // doesn't work :(
    (1..10).map { Fabricate.build(:huzzah) }
  end
end

In the case above we could know that widgets and wocket are associations but we wouldn't be able to tell that huzzahs is. We need to indicate this is an association and there is no way to have that without defining a new syntax construct. It could be something as simple as an association flag or something more descriptive. Here are some options:

Fabricator(:thingy) do
  huzzahs(association: true) { ... }

  huzzahs(hasMany: true) { ... }
  hasMany :huzzahs { ... }

  wocket(belongsTo: true) { ... }
  belongsTo :wocket { ... }
end

I'm kind of leaning towards introducing the hasMany and belongsTo constructs. They could work like this:

Fabricator(:thingy) do
  hasMany :huzzahs, count: 2 // builds 2 `huzzah` objects and assigns them
  hasMany :huzzahs do // assigns whatever the block returns
    (1..10).map { Fabricate.build(:something) }
  end
end

To be clear, what I am proposing here is a major change and huge chunk of work to take on. I can't promise I would be able to implement it in the near term.

@thomasfedb
Copy link
Author

Hey @paulelliott, is it not possible to determine associations by inspecting the model? Certainly the information is there, can it be used in Fabrication?

@paulelliott
Copy link
Owner

Yeah, it could be. The problem with that is fabrication works with a lot of different systems that all do that differently. To fully support that paradigm would be just as much work as introducing the new construct.

@thomasfedb
Copy link
Author

I like your proposed solution, but could you use has_many rather than hasMany?

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants