1
0
Fork 0
mirror of https://github.com/thoughtbot/factory_bot.git synced 2022-11-09 11:43:51 -05:00
thoughtbot--factory_bot/lib/factory_girl/definition.rb
Joshua Clayton 779eafccbd Fix various trait bugs so traits can be used within each other
The previous implementation of trait handling within the Definition
didn't account for when implicit traits were used within other traits.
This is useful if you have two different traits, but one depends on
another; for example, a refunded order and a completed order could both
have the attribute `completed_at` set, but refunded would additionally
have `refunded_at` set:

    FactoryGirl.define do
      factory :order do
        trait :completed do
          completed_at { 3.days.ago }
        end

        trait :refunded do
          completed
          refunded_at { 1.day.ago }
        end
      end
    end

This also tests to make sure that callbacks, custom constructors, and
creation overrides work correctly when implicit traits are used within
other traits.

Fixes #360
2012-06-15 20:08:42 -04:00

126 lines
2.8 KiB
Ruby

module FactoryGirl
# @api private
class Definition
attr_reader :defined_traits, :declarations
def initialize(name = nil, base_traits = [])
@declarations = DeclarationList.new(name)
@callbacks = []
@defined_traits = []
@to_create = nil
@base_traits = base_traits
@additional_traits = []
@constructor = nil
@attributes = nil
@compiled = false
end
delegate :declare_attribute, to: :declarations
def attributes
@attributes ||= AttributeList.new.tap do |attribute_list|
attribute_lists = aggregate_from_traits_and_self(:attributes) { declarations.attributes }
attribute_lists.each do |attributes|
attribute_list.apply_attributes attributes
end
end
end
def to_create(&block)
if block_given?
@to_create = block
else
aggregate_from_traits_and_self(:to_create) { @to_create }.last
end
end
def constructor
aggregate_from_traits_and_self(:constructor) { @constructor }.last
end
def callbacks
aggregate_from_traits_and_self(:callbacks) { @callbacks }
end
def compile
unless @compiled
declarations.attributes
defined_traits.each do |defined_trait|
base_traits.each {|bt| bt.define_trait defined_trait }
additional_traits.each {|bt| bt.define_trait defined_trait }
end
@compiled = true
end
end
def overridable
declarations.overridable
self
end
def inherit_traits(new_traits)
@base_traits += new_traits
end
def append_traits(new_traits)
@additional_traits += new_traits
end
def add_callback(callback)
@callbacks << callback
end
def skip_create
@to_create = ->(instance) { }
end
def define_trait(trait)
@defined_traits << trait
end
def define_constructor(&block)
@constructor = block
end
private
def base_traits
@base_traits.map { |name| trait_by_name(name) }
end
def additional_traits
@additional_traits.map { |name| trait_by_name(name) }
end
def trait_by_name(name)
trait_for(name) || FactoryGirl.trait_by_name(name)
end
def trait_for(name)
defined_traits.detect {|trait| trait.name == name }
end
def initialize_copy(source)
super
@attributes = nil
@compiled = false
end
def aggregate_from_traits_and_self(method_name, &block)
compile
[].tap do |list|
base_traits.each do |trait|
list << trait.send(method_name)
end
list << instance_exec(&block)
additional_traits.each do |trait|
list << trait.send(method_name)
end
end.flatten.compact
end
end
end