ruby-on-railsruby-on-rails-4.2fakerfabrication-gem

How to use Fabricate-gem to generate objects?


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...

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


Solution

  • 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.