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/evaluator.rb
Daniel Colson ebc4cd5b84 Make the evaluator instance public
closes #1309

It has come up several times over the years that folks want direct
access to the evaluator instance, in most cases to build up more complex
networks of associations. Some folks are already doing this with the
less public `@instance` instance variable.

Given that this is such a minimal change, and it makes the library more
flexible for these more complex use cases, this commit adds an
`attr_reader` for the evaluator instance. Now folks can reference it
without reaching into the private api.

Documentation to follow as part of
https://github.com/thoughtbot/factory_bot/issues/1268
2020-07-08 12:41:06 -04:00

90 lines
2.7 KiB
Ruby

require "active_support/core_ext/hash/except"
require "active_support/core_ext/class/attribute"
module FactoryBot
# @api private
class Evaluator
class_attribute :attribute_lists
private_instance_methods.each do |method|
undef_method(method) unless method.match?(/^__|initialize/)
end
def initialize(build_strategy, overrides = {})
@build_strategy = build_strategy
@overrides = overrides
@cached_attributes = overrides
@instance = nil
@overrides.each do |name, value|
singleton_class.define_attribute(name) { value }
end
end
def association(factory_name, *traits_and_overrides)
overrides = traits_and_overrides.extract_options!
strategy_override = overrides.fetch(:strategy) {
FactoryBot.use_parent_strategy ? @build_strategy.class : :create
}
traits_and_overrides += [overrides.except(:strategy)]
runner = FactoryRunner.new(factory_name, strategy_override, traits_and_overrides)
@build_strategy.association(runner)
end
attr_accessor :instance
if ::Gem::Version.new(::RUBY_VERSION) >= ::Gem::Version.new("2.7")
def method_missing(method_name, *args, **kwargs, &block) # rubocop:disable Style/MethodMissingSuper, Style/MissingRespondToMissing
if @instance.respond_to?(method_name)
@instance.send(method_name, *args, **kwargs, &block)
else
SyntaxRunner.new.send(method_name, *args, **kwargs, &block)
end
end
else
def method_missing(method_name, *args, &block) # rubocop:disable Style/MethodMissingSuper, Style/MissingRespondToMissing
if @instance.respond_to?(method_name)
@instance.send(method_name, *args, &block)
else
SyntaxRunner.new.send(method_name, *args, &block)
end
end
end
def respond_to_missing?(method_name, _include_private = false)
@instance.respond_to?(method_name) || SyntaxRunner.new.respond_to?(method_name)
end
def __override_names__
@overrides.keys
end
def increment_sequence(sequence)
sequence.next(self)
end
def self.attribute_list
AttributeList.new.tap do |list|
attribute_lists.each do |attribute_list|
list.apply_attributes attribute_list.to_a
end
end
end
def self.define_attribute(name, &block)
if instance_methods(false).include?(name) || private_instance_methods(false).include?(name)
undef_method(name)
end
define_method(name) do
if @cached_attributes.key?(name)
@cached_attributes[name]
else
@cached_attributes[name] = instance_exec(&block)
end
end
end
end
end