I'm using Rails 4, Fabricate and Faker Gems. And I'm trying to seed my database with (100 or so) randomly created objects (Order that contains up to 3 Ice Creams). I followed This Answer that recommend using this approach.
models/order.rb
class Order < ActiveRecord::Base
...
has_many :ice_creams
...
end
models/ice_cream.rb
class IceCream < ActiveRecord::Base
...
has_and_belongs_to_many :flavors
has_many :added_extras
has_many :extras, :through => :added_extras
belongs_to :order
...
end
models/extra.rb
class Extra < ActiveRecord::Base
...
has_many :added_extras
has_many :extras, :through => :added_extras
...
end
test/fabricators/order_fabricator.rb
Fabricator(:order) do
user { User.offset(rand(User.count)).first } #fine
shift { Shift.offset(rand(Shift.count)).first } #fine
created_at { Faker::Date.backward(365) } #fine
ice_creams(rand: 3) { |attrs| Fabricate( :ice_cream, created_at: attrs[:created_at] ) } #fine
total { Faker::Number.between(5, 25) }
#add more logic depending of the total number of randomly created ice creams
discount { [0, 10, 15, 25].sample } #fine
total_after_discount { |order| order[:total] - ( (order[:total] * order[:discount]) / 100 ) }
paid { [50, 100, 200].sample } #fine
remaining { |order| order[:paid] - order[:total_after_discount] } #fine
end
test/fabricators/ice_cream_fabricator.rb
Fabricator(:ice_cream) do
size { Size.offset(rand(Size.count)).first } #fine
basis { Basis.offset(rand(Basis.count)).first } #fine
sauce { Sauce.offset(rand(Sauce.count)).first } #fine
topping { Topping.offset(rand(Topping.count)).first } #fine
flavors { [ Flavor.offset(rand(Flavor.count)).first ] }
#add additional ability to be one or two flavors randomly
extras { [ Extra.offset(rand(Extra.count)).first ] }
ice_cream_price { [15, 17, 18, 19, 20, 22].sample } #add logic
extras_price { [5, 10, 15, 20 ].sample } #add logic
total_price { |attrs| attrs[:ice_cream_price] + attrs[:extras_price] } #fine
created_at { Faker::Date.backward(365) }
end
It's working fine , I can now create fake Orders that contains upto 3 fake Ice Creams, But the thing is I'm struglling to figure out the logic to Fabricate more realistic Orders, As you may noticed in my fabricators code there are some attributes that I labeled fine -Which I'm fine with it's result- and some that I still not completely satisfied of, Like...
:total_price
to be passed to the Order as :total
I've Tried to do so by creating a Flavor Fabricator but It didn't work..
test/fabricators/flavor_fabricator.rb
Fabricator(:flavor) do
Flavor.offset(rand(Flavor.count)).first
end
I also tried to sum the :total_price
the activeRecord way, but it also didn't work
test/fabricators/order_fabricator.rb
Fabricator(:order) do
...
total { self.ice_creams.sum(:total_price) }
...
end
So my question is... - Are the things that I wish for possible or it's just too much? And if so how to achieve that?
I hope I made myself clear, And you can help me,. Thanks
It looks like you're trying to use fabrication to set calculated values on your models, IceCream#total_price
for example. You should be letting methods on your model do their thing, like calculate that total from the parts, instead of trying to force them with fabrication.
To answer your questions specifically:
1) I wish that the Fabricated Ice Cream can -randomly- have one or two flavors.
Fabricator(:ice_cream) do
flavors { Flavor.all.sample(rand(1..2)) }
end
2) Same as #1
3) You should have a method on Order
that calculates the total when it is created.