class FactoryBot::DefinitionProxy

Constants

UNPROXIED_METHODS

Attributes

child_factories[R]

Public Class Methods

new(definition, ignore = false) click to toggle source
# File lib/factory_bot/definition_proxy.rb, line 26
def initialize(definition, ignore = false)
  @definition = definition
  @ignore = ignore
  @child_factories = []
end

Public Instance Methods

add_attribute(name, &block) click to toggle source

Adds an attribute to the factory. The attribute value will be generated “lazily” by calling the block whenever an instance is generated. The block will not be called if the attribute is overridden for a specific instance.

Arguments:

  • name: Symbol or String The name of this attribute. This will be assigned using “name=” for generated instances.

# File lib/factory_bot/definition_proxy.rb, line 47
def add_attribute(name, &block)
  declaration = Declaration::Dynamic.new(name, @ignore, block)
  @definition.declare_attribute(declaration)
end
association(name, *options) click to toggle source

Adds an attribute that builds an association. The associated instance will be built using the same build strategy as the parent instance.

Example:

factory :user do
  name 'Joey'
end

factory :post do
  association :author, factory: :user
end

Arguments:

  • name: Symbol The name of this attribute.

  • options: Hash

Options:

  • factory: Symbol or String

    The name of the factory to use when building the associated instance.
    If no name is given, the name of the attribute is assumed to be the
    name of the factory. For example, a "user" association will by
    default use the "user" factory.
# File lib/factory_bot/definition_proxy.rb, line 151
def association(name, *options)
  if block_given?
    raise AssociationDefinitionError.new(
      "Unexpected block passed to '#{name}' association "\
      "in '#{@definition.name}' factory"
    )
  else
    declaration = Declaration::Association.new(name, *options)
    @definition.declare_attribute(declaration)
  end
end
factory(name, options = {}, &block) click to toggle source
# File lib/factory_bot/definition_proxy.rb, line 171
def factory(name, options = {}, &block)
  @child_factories << [name, options, block]
end
initialize_with(&block) click to toggle source
# File lib/factory_bot/definition_proxy.rb, line 237
def initialize_with(&block)
  @definition.define_constructor(&block)
end
method_missing(name, *args, &block) click to toggle source

Calls add_attribute using the missing method name as the name of the attribute, so that:

factory :user do
  name { 'Billy Idol' }
end

and:

factory :user do
  add_attribute(:name) { 'Billy Idol' }
end

are equivalent.

If no argument or block is given, factory_bot will first look for an association, then for a sequence, and finally for a trait with the same name. This means that given an “admin” trait, an “email” sequence, and an “account” factory:

factory :user, traits: [:admin] do
  email { generate(:email) }
  association :account
end

and:

factory :user do
  admin
  email
  account
end

are equivalent.

# File lib/factory_bot/definition_proxy.rb, line 91
    def method_missing(name, *args, &block) # rubocop:disable Style/MissingRespondToMissing
      association_options = args.first

      if association_options.nil?
        __declare_attribute__(name, block)
      elsif __valid_association_options?(association_options)
        association(name, association_options)
      else
        raise NoMethodError.new(<<~MSG)
          undefined method '#{name}' in '#{@definition.name}' factory
          Did you mean? '#{name} { #{association_options.inspect} }'
        MSG
      end
    end
sequence(name, *args, &block) click to toggle source

Adds an attribute that will have unique values generated by a sequence with a specified format.

The result of:

factory :user do
  sequence(:email) { |n| "person#{n}@example.com" }
end

Is equal to:

sequence(:email) { |n| "person#{n}@example.com" }

factory :user do
  email { FactoryBot.generate(:email) }
end

Except that no globally available sequence will be defined.

# File lib/factory_bot/definition_proxy.rb, line 122
def sequence(name, *args, &block)
  sequence = Sequence.new(name, *args, &block)
  FactoryBot::Internal.register_inline_sequence(sequence)
  add_attribute(name) { increment_sequence(sequence) }
end
singleton_method_added(name) click to toggle source
# File lib/factory_bot/definition_proxy.rb, line 32
def singleton_method_added(name)
  message = "Defining methods in blocks (trait or factory) is not supported (#{name})"
  raise FactoryBot::MethodDefinitionError, message
end
skip_create() click to toggle source
# File lib/factory_bot/definition_proxy.rb, line 167
def skip_create
  @definition.skip_create
end
to_create(&block) click to toggle source
# File lib/factory_bot/definition_proxy.rb, line 163
def to_create(&block)
  @definition.to_create(&block)
end
trait(name, &block) click to toggle source
# File lib/factory_bot/definition_proxy.rb, line 175
def trait(name, &block)
  @definition.define_trait(Trait.new(name, &block))
end
traits_for_enum(attribute_name, values = nil) click to toggle source

Creates traits for enumerable values.

Example:

factory :task do
  traits_for_enum :status, [:started, :finished]
end

Equivalent to:

factory :task do
  trait :started do
    status { :started }
  end

  trait :finished do
    status { :finished }
  end
end

Example:

factory :task do
  traits_for_enum :status, {started: 1, finished: 2}
end

Example:

class Task
  def statuses
    {started: 1, finished: 2}
  end
end

factory :task do
  traits_for_enum :status
end

Both equivalent to:

factory :task do
  trait :started do
    status { 1 }
  end

  trait :finished do
    status { 2 }
  end
end

Arguments:

attribute_name: +Symbol+ or +String+
  the name of the attribute these traits will set the value of
values: +Array+, +Hash+, or other +Enumerable+
  An array of trait names, or a mapping of trait names to values for
  those traits. When this argument is not provided, factory_bot will
  attempt to get the values by calling the pluralized `attribute_name`
  class method.
# File lib/factory_bot/definition_proxy.rb, line 233
def traits_for_enum(attribute_name, values = nil)
  @definition.register_enum(Enum.new(attribute_name, values))
end
transient(&block) click to toggle source
# File lib/factory_bot/definition_proxy.rb, line 52
def transient(&block)
  proxy = DefinitionProxy.new(@definition, true)
  proxy.instance_eval(&block)
end

Private Instance Methods

__declare_attribute__(name, block) click to toggle source
# File lib/factory_bot/definition_proxy.rb, line 243
def __declare_attribute__(name, block)
  if block.nil?
    declaration = Declaration::Implicit.new(name, @definition, @ignore)
    @definition.declare_attribute(declaration)
  else
    add_attribute(name, &block)
  end
end
__valid_association_options?(options) click to toggle source
# File lib/factory_bot/definition_proxy.rb, line 252
def __valid_association_options?(options)
  options.respond_to?(:has_key?) && options.has_key?(:factory)
end