Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question: Is it possible to reference the "current" attribute value when overriding it? #1140

Closed
Epigene opened this issue Jun 11, 2018 · 3 comments

Comments

@Epigene
Copy link

Epigene commented Jun 11, 2018

Hi,

I've been using this gem for a couple years, so I think I know my way around, but this one has got me stumped. I know I can reference other attributes when setting some attribute, but what about referencing the "current" value of the field itself?

With JSON fields becoming more prominent, I have found the need to merge some default hash with additional options more prominent. Consider:

FactoryBot.define do
  factory :payload do
    transient do
      options { {} }
    end

    data { {default: "true"}.merge(options) }

    trait :custom do
       options { {default: "false"} }
    end
  end
end

Now I can do either:

create(:payload, :custom) or create(:payload, options: {default: "false"}), to get a "{default => "false"}" payload.

However, I can not do create(:payload, :custom, options: {additional: "yup"}) to get a {default: "false", additional: "yup"}} payload.

It would all be solved if I could access options to merge with the value e.g.:

FactoryBot.define do
  factory :payload do
    transient do
      options { {} }
    end

    data { {default: "true"}.merge(options) }

    trait :custom do
       options { options.merge(default: "false") } # options can not reference itself :(
    end
  end
end

Obviously, the example above causes an infinite loop and a stack error.

Is what I want possible? Maybe I am approaching this all wrong..

@mikegee
Copy link

mikegee commented Jun 11, 2018

Maybe merge the options into the data from an after(:build) callback?

@Epigene
Copy link
Author

Epigene commented Jun 11, 2018

While after(:build) does provide a more flexible assignment alternative, the issue is not merging data with options, but options with more options.

@composerinteralia
Copy link
Collaborator

composerinteralia commented Jun 17, 2018

You could use two different transient attributes for this:

FactoryBot.define do
  factory :payload do
    transient do
      default_options { { default: "true" } }
      options { {} }
    end

    data { default_options.merge(options) }

    trait :custom do
      default_options { {default: "false"} }
    end
  end
end

It makes sense that options { options.merge(default: "false") } wouldn't work. Internally this defines an options method on an evaluator object. When we call that method, it executes the block in the context of the evaluator object (using instance_eval). So options { options } ends up being a recursive call to the same method.

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

No branches or pull requests

3 participants