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/strategy/stub.rb
Simon Coffey fd3bf889f8
Stub strategy disables all persistence instance methods
Previously, instances created with the Stub strategy disabled a number of
methods defined by ActiveRecord::Persistence, to ensure that stubbed instances
did not read or write to the database. However, this blacklist was incomplete,
allowing some methods (e.g. #save!) to be called on stub instances.

This expands the blacklist to disable all methods defined by
ActiveRecord::Persistence that are not explicitly defined on stub instances
(currently #new_record?, #persisted?), or do not talk to the database at all
(#becomes, #becomes!).

The blacklist is correct as of current Rails master (the pre-release for Rails
5.0). It also, as a result, includes a couple of methods that are not defined
by ActiveRecord::Persistence in earlier versions of Rails, and will not
automatically include methods added to this module in future versions of
Rails.

It would be possible to make this automatic using reflection, and instead
using a whitelist of allowed methods:

    ActiveRecord::Persistence.public_instance_methods

However, at present this test is not dependent on ActiveRecord, so I have left
this for future work if desired.
2016-07-19 05:20:26 -04:00

96 lines
2.5 KiB
Ruby

module FactoryGirl
module Strategy
class Stub
@@next_id = 1000
DISABLED_PERSISTENCE_METHODS = [
:connection,
:decrement!,
:decrement,
:delete,
:destroy!,
:destroy,
:destroyed?,
:increment!,
:increment,
:reload,
:save!,
:save,
:toggle!,
:toggle,
:touch,
:update!,
:update,
:update_attribute,
:update_attributes!,
:update_attributes,
:update_column,
:update_columns,
].freeze
def association(runner)
runner.run(:build_stubbed)
end
def result(evaluation)
evaluation.object.tap do |instance|
stub_database_interaction_on_result(instance)
clear_changed_attributes_on_result(instance)
evaluation.notify(:after_stub, instance)
end
end
private
def next_id
@@next_id += 1
end
def stub_database_interaction_on_result(result_instance)
result_instance.id ||= next_id
result_instance.instance_eval do
def persisted?
!new_record?
end
def new_record?
id.nil?
end
DISABLED_PERSISTENCE_METHODS.each do |write_method|
define_singleton_method(write_method) do |*args|
raise "stubbed models are not allowed to access the database - #{self.class}##{write_method}(#{args.join(",")})"
end
end
end
created_at_missing_default = result_instance.respond_to?(:created_at) && !result_instance.created_at
result_instance_missing_created_at = !result_instance.respond_to?(:created_at)
if created_at_missing_default || result_instance_missing_created_at
result_instance.instance_eval do
def created_at
@created_at ||= Time.now.in_time_zone
end
end
end
end
def clear_changed_attributes_on_result(result_instance)
unless result_instance.respond_to?(:clear_changes_information)
result_instance.extend ActiveModelDirtyBackport
end
result_instance.clear_changes_information
end
end
module ActiveModelDirtyBackport
def clear_changes_information
@previously_changed = ActiveSupport::HashWithIndifferentAccess.new
@changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
end
end
end
end