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

Access to constructor attributes hash within dynamic attribute block? + nested association #583

Closed
gamov opened this issue Nov 27, 2013 · 6 comments

Comments

@gamov
Copy link

gamov commented Nov 27, 2013

I tried asking this twice in the Google Group but both time my post disappeared…:

I would like to do different things if a certain attribute is passed to the constructor or not (using default), e.g.:

quantity { constructor[:quantity] ? constructor[:quantity] + 5 : 10 }  #lazy attribute

so the quantity will have different value if I specify the quantity or not in the constructor:

FactoryGirl.build :item, quantity: 4   #==> item.quantity == 9
FactoryGirl.build :item   #==> item.quantity == 10

Is this possible?

PS: In the getting started document, I think it would be good to specify that association attributes are processed before other attributes even though they could be defined after them in the define block.

@joshuaclayton
Copy link
Contributor

What you're looking for are transient attributes.

Best of luck!

@gamov
Copy link
Author

gamov commented Dec 2, 2013

I'm aware of the transient attributes, however, in my case, I don't want the user of the factory to have to remember to use the transient attribute.
My real case is of course more complicated than this one. Basically, I have a quantity dependency that I need to handle if user set a quantity attribute (by setting the association quantity > quantity).

No 'evaluator' I could tap in the block?

@joshuaclayton
Copy link
Contributor

@gamov can you provide the real example? I'm not sure changing quantity like that is actually ideal because it introduces a level of indirection (if you set a value, I'd assume that value be the value assigned and not change).

@gamov
Copy link
Author

gamov commented Dec 4, 2013

First of all, thanks for your help. I switched around the requirement and it leads to cleaner factories and only broke a few tests which were easily fixed. Now quantity of the nested association is always valid.

class ShippingItem < ActiveRecord::Base
   belongs_to :purchased_item
   validate { errors.add(:quantity, "cannot be higher than available quantity: #{purchased_item.quantity}") if purchased_item.quantity < quantity }
end

Shipping Item factory:

factory :shipping_item do
    quantity { rand 3..100 }
    purchased_item {association :purchased_item, quantity: (quantity..(quantity + 100)).to_a.sample}
end

This works fine if you set the quantity or not when invoking the shipping item factory (which was my original question).
Let say now that I want to set the delivery site for the purchased item from the shipping item factory. I would do:

factory :shipping_item do
    quantity { rand 3..100 }
    ignore {delivery_site nil}
    purchased_item {association :purchased_item, 
                                quantity: (quantity..(quantity + 100)).to_a.sample,
                                delivery_site: (delivery_site || FactoryGirl.build :delivery_site)
                            }
end

However, I can see it becoming a pattern and doesn't feel 'right' or DRY. Is it the usual way?

@joshuaclayton
Copy link
Contributor

@gamov okay, this is starting to make more sense now, and yes, I agree this isn't DRY.

In cases like this (especially because this is likely a simple case and there are MUCH more complicated ones out there), I actually create support classes for building complicated data (either because there's a LOT of associated data or certain aspects change and it impacts creation of other factories, like this). The example I point to in cases like this is here: #230 (comment)

At the end of the day, factory_girl can't (and won't!) handle every permutation of every way objects get associated and built. It's just not possible. In cases like yours where there's little things that change, I always recommend solving things with pure Ruby. It's flexible, extensible, and technically testable if you want.

Hopefully this helps - I know "factory_girl can't handle this well" doesn't sound ideal, but it really is, since it keeps factory_girl's interface clean and understandable and removes the chance for odd edge cases.

@gamov
Copy link
Author

gamov commented Jul 14, 2015

I still wish we could access the constructor supplied hash to check what has been supplied to make some choice in the lazy attribute block…

http://stackoverflow.com/questions/31205859/factory-girl-can-we-check-if-an-attribute-has-been-overridden-in-its-lazy-attri

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

2 participants