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_bot/factory.rb

164 lines
3.9 KiB
Ruby
Raw Normal View History

require "active_support/core_ext/hash/keys"
require "active_support/inflector"
module FactoryBot
2012-05-05 02:31:31 -04:00
# @api private
class Factory
2012-05-05 02:31:31 -04:00
attr_reader :name, :definition
2011-08-12 10:35:41 -04:00
2012-05-05 02:31:31 -04:00
def initialize(name, options = {})
assert_valid_options(options)
@name = name.respond_to?(:to_sym) ? name.to_sym : name.to_s.underscore.to_sym
@parent = options[:parent]
@aliases = options[:aliases] || []
@class_name = options[:class]
@definition = Definition.new(@name, options[:traits] || [])
@compiled = false
end
delegate :add_callback, :declare_attribute, :to_create, :define_trait, :constructor,
:defined_traits, :inherit_traits, :append_traits, to: :@definition
2011-10-14 22:34:51 -04:00
2012-05-05 02:31:31 -04:00
def build_class
@build_class ||= if class_name.is_a? Class
class_name
else
class_name.to_s.camelize.constantize
end
end
2012-05-05 02:31:31 -04:00
def run(build_strategy, overrides, &block)
2012-04-20 16:59:39 -04:00
block ||= ->(result) { result }
compile
2011-11-20 21:42:59 -05:00
strategy = StrategyCalculator.new(build_strategy).strategy.new
2011-12-16 11:15:13 -05:00
evaluator = evaluator_class.new(strategy, overrides.symbolize_keys)
attribute_assigner = AttributeAssigner.new(evaluator, build_class, &compiled_constructor)
2011-12-16 11:15:13 -05:00
2017-09-28 08:17:17 -04:00
evaluation =
Evaluation.new(evaluator, attribute_assigner, compiled_to_create)
evaluation.add_observer(CallbacksObserver.new(callbacks, evaluator))
strategy.result(evaluation).tap(&block)
end
2009-09-15 15:47:47 -04:00
def human_names
names.map { |name| name.to_s.humanize.downcase }
2009-09-15 15:47:47 -04:00
end
def associations
evaluator_class.attribute_list.associations
end
# Names for this factory, including aliases.
#
# Example:
#
2012-03-09 17:20:38 -05:00
# factory :user, aliases: [:author] do
# # ...
# end
#
# FactoryBot.create(:author).class
# # => User
#
# Because an attribute defined without a value or block will build an
# association with the same name, this allows associations to be defined
# without factories, such as:
#
2012-03-09 17:20:38 -05:00
# factory :user, aliases: [:author] do
# # ...
# end
#
# factory :post do
# author
# end
#
# FactoryBot.create(:post).author.class
# # => User
def names
2011-09-23 16:33:39 -04:00
[name] + @aliases
end
def compile
2012-01-08 00:57:40 -05:00
unless @compiled
parent.compile
parent.defined_traits.each { |trait| define_trait(trait) }
Add functionality for enum traits (#1380) ## Enum traits Given a Rails model with an enum attribute: ```rb class Task < ActiveRecord::Base enum status: {queued: 0, started: 1, finished: 2} end ``` It is common for people to define traits for each possible value of the enum: ```rb FactoryBot.define do factory :task do trait :queued do status { :queued } end trait :started do status { :started } end trait :finished do status { :finished } end end end ``` With this commit, those trait definitions are no longer necessary—they are defined automatically by factory_bot. If automatically defining traits for enum attributes on every factory is not desired, it is possible to disable the feature by setting `FactoryBot.automatically_define_enum_traits = false` (see commit: [Allow opting out of automatically defining traits](https://github.com/thoughtbot/factory_bot/pull/1380/commits/5a20017351b08ce2ec9918d799e187e9eaa3ec32)). In that case, it is still possible to explicitly define traits for an enum attribute in a particular factory: ```rb FactoryBot.automatically_define_enum_traits = false FactoryBot.define do factory :task do traits_for_enum(:status) end end ``` It is also possible to use this feature for other enumerable values, not specifically tied to ActiveRecord enum attributes: ```rb class Task attr_accessor :status end FactoryBot.define do factory :task do traits_for_enum(:status, ["queued", "started", "finished"]) end end ``` The second argument here can be an enumerable object, including a Hash or Array. Closes #1049 Co-authored-by: Lance Johnson <lancejjohnson@gmail.com> Co-authored-by: PoTa <pota@mosfet.hu> Co-authored-by: Frida Casas <fridacasas.fc@gmail.com> Co-authored-by: Daniel Colson <danieljamescolson@gmail.com>
2020-05-01 17:50:51 -04:00
@definition.compile(build_class)
build_hierarchy
2012-01-08 00:57:40 -05:00
@compiled = true
end
end
def with_traits(traits)
2018-10-07 18:02:54 -04:00
clone.tap do |factory_with_traits|
factory_with_traits.append_traits traits
end
end
protected
2012-05-05 02:31:31 -04:00
def class_name
2011-10-28 21:23:06 -04:00
@class_name || parent.class_name || name
end
2012-01-03 09:31:40 -05:00
def evaluator_class
@evaluator_class ||= EvaluatorClassDefiner.new(attributes, parent.evaluator_class).evaluator_class
2012-01-03 09:31:40 -05:00
end
2011-10-07 16:43:36 -04:00
def attributes
compile
AttributeList.new(@name).tap do |list|
list.apply_attributes definition.attributes
2011-10-07 16:43:36 -04:00
end
end
def hierarchy_class
@hierarchy_class ||= Class.new(parent.hierarchy_class)
end
def hierarchy_instance
@hierarchy_instance ||= hierarchy_class.new
end
def build_hierarchy
2012-09-14 08:22:28 -04:00
hierarchy_class.build_from_definition definition
end
2011-10-07 16:06:28 -04:00
def callbacks
hierarchy_instance.callbacks
2011-10-07 16:06:28 -04:00
end
def compiled_to_create
hierarchy_instance.to_create
end
def compiled_constructor
hierarchy_instance.constructor
end
private
def assert_valid_options(options)
2012-03-09 15:24:40 -05:00
options.assert_valid_keys(:class, :parent, :aliases, :traits)
end
2010-06-07 15:51:18 -04:00
def parent
2011-10-28 21:23:06 -04:00
if @parent
FactoryBot::Internal.factory_by_name(@parent)
2011-10-28 21:23:06 -04:00
else
NullFactory.new
end
end
def initialize_copy(source)
super
@definition = @definition.clone
@evaluator_class = nil
@hierarchy_class = nil
@hierarchy_instance = nil
@compiled = false
end
end
end