mirror of
https://github.com/thoughtbot/factory_bot.git
synced 2022-11-09 11:43:51 -05:00
e7ddcbccc5
In 241e8e5fb4
, this call to `undef_method`
was introduced to suppress the warning printed by Ruby when a method is
redefined.
That warning is only printed when the method is already defined on the
class in question; it isn't printed when the method was inherited from
an ancestor, since overriding methods by shadowing them is a feature.
However, `method_defined?` returns true for inherited methods, which
means we're sometimes undefining methods that wouldn't cause a warning.
If a factory defines an attribute named `object_id` (admittedly not a
great idea), we will undefine the `object_id` method inherited from the
`Object` class, and the following warning will be printed:
factory_bot/evaluator.rb:70: warning: undefining `object_id' may cause serious problems
By only undefining the method if it's defined on the current class, we
can avoid undefining inherited methods and triggering this warning.
82 lines
2.2 KiB
Ruby
82 lines
2.2 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 =~ /^__|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) do
|
|
FactoryBot.use_parent_strategy ? @build_strategy.class : :create
|
|
end
|
|
|
|
traits_and_overrides += [overrides.except(:strategy)]
|
|
|
|
runner = FactoryRunner.new(factory_name, strategy_override, traits_and_overrides)
|
|
@build_strategy.association(runner)
|
|
end
|
|
|
|
def instance=(object_instance)
|
|
@instance = object_instance
|
|
end
|
|
|
|
def method_missing(method_name, *args, &block) # rubocop:disable Style/MethodMissing
|
|
if @instance.respond_to?(method_name)
|
|
@instance.send(method_name, *args, &block)
|
|
else
|
|
SyntaxRunner.new.send(method_name, *args, &block)
|
|
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
|