123456789_123456789_123456789_123456789_123456789_

Module: ActiveRecord::TestFixtures

Relationships & Source Files
Namespace Children
Modules:
Super Chains via Extension / Inclusion / Inheritance
Class Chain:
Defined in: activerecord/lib/active_record/test_fixtures.rb

Class Method Summary

::ActiveSupport::Concern - Extended

class_methods

Define class methods from given block.

included

Evaluate given block in context of base class, so that you can write class macros here.

prepended

Evaluate given block in context of base class, so that you can write class macros here.

append_features, prepend_features

Instance Attribute Summary

Instance Method Summary

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/test_fixtures.rb', line 256

def method_missing(method, ...)
  if fixture_sets.key?(method.name)
    _active_record_fixture(method, ...)
  else
    super
  end
end

DSL Calls

included

[ GitHub ]


20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'activerecord/lib/active_record/test_fixtures.rb', line 20

included do
  ##
  # :singleton-method: fixture_paths
  #
  # Returns the ActiveRecord::FixtureSet collection

  ##
  # :singleton-method: fixture_paths=
  #
  # :call-seq:
  #   fixture_paths=(fixture_paths)
  class_attribute :fixture_paths, instance_writer: false, default: []
  class_attribute :fixture_table_names, default: []
  class_attribute :fixture_class_names, default: {}
  class_attribute :use_transactional_tests, default: true
  class_attribute :use_instantiated_fixtures, default: false # true, false, or :no_instances
  class_attribute :pre_loaded_fixtures, default: false
  class_attribute :lock_threads, default: true
  class_attribute :fixture_sets, default: {}

  ActiveSupport.run_load_hooks(:active_record_fixtures, self)
end

Instance Attribute Details

#load_instances?Boolean (readonly, private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/test_fixtures.rb', line 252

def load_instances?
  use_instantiated_fixtures != :no_instances
end

#run_in_transaction?Boolean (readonly, private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/test_fixtures.rb', line 100

def run_in_transaction?
  use_transactional_tests &&
    !self.class.uses_transaction?(name)
end

Instance Method Details

#_active_record_fixture(fixture_set_name, *fixture_names) (private) Also known as: #fixture

[ GitHub ]

  
# File 'activerecord/lib/active_record/test_fixtures.rb', line 272

def _active_record_fixture(fixture_set_name, *fixture_names)
  if fs_name = fixture_sets[fixture_set_name.name]
    access_fixture(fs_name, *fixture_names)
  else
    raise StandardError, "No fixture set named '#{fixture_set_name.inspect}'"
  end
end

#access_fixture(fs_name, *fixture_names) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/test_fixtures.rb', line 281

def access_fixture(fs_name, *fixture_names)
  force_reload = fixture_names.pop if fixture_names.last == true || fixture_names.last == :reload
  return_single_record = fixture_names.size == 1

  fixture_names = @loaded_fixtures[fs_name].fixtures.keys if fixture_names.empty?
  @fixture_cache[fs_name] ||= {}

  instances = fixture_names.map do |f_name|
    f_name = f_name.to_s if f_name.is_a?(Symbol)
    @fixture_cache[fs_name].delete(f_name) if force_reload

    if @loaded_fixtures[fs_name][f_name]
      @fixture_cache[fs_name][f_name] ||= @loaded_fixtures[fs_name][f_name].find
    else
      raise StandardError, "No fixture named '#{f_name}' found for fixture set '#{fs_name}'"
    end
  end

  return_single_record ? instances.first : instances
end

#after_teardown

This method is for internal use only.
[ GitHub ]

  
# File 'activerecord/lib/active_record/test_fixtures.rb', line 14

def after_teardown # :nodoc:
  super
ensure
  teardown_fixtures
end

#before_setup

This method is for internal use only.
[ GitHub ]

  
# File 'activerecord/lib/active_record/test_fixtures.rb', line 9

def before_setup # :nodoc:
  setup_fixtures
  super
end

#fixture(fixture_set_name, *fixture_names) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/test_fixtures.rb', line 279

alias_method :fixture, :_active_record_fixture

#instantiate_fixtures (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/test_fixtures.rb', line 240

def instantiate_fixtures
  if pre_loaded_fixtures
    raise RuntimeError, "Load fixtures before instantiating them." if ActiveRecord::FixtureSet.all_loaded_fixtures.empty?
    ActiveRecord::FixtureSet.instantiate_all_loaded_fixtures(self, load_instances?)
  else
    raise RuntimeError, "Load fixtures before instantiating them." if @loaded_fixtures.nil?
    @loaded_fixtures.each_value do |fixture_set|
      ActiveRecord::FixtureSet.instantiate_fixtures(self, fixture_set, load_instances?)
    end
  end
end

#invalidate_already_loaded_fixtures (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/test_fixtures.rb', line 149

def invalidate_already_loaded_fixtures
  @@already_loaded_fixtures.clear
end

#load_fixtures(config) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/test_fixtures.rb', line 236

def load_fixtures(config)
  ActiveRecord::FixtureSet.create_fixtures(fixture_paths, fixture_table_names, fixture_class_names, config).index_by(&:name)
end

#respond_to_missing?(method, include_private = false) ⇒ Boolean (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/test_fixtures.rb', line 264

def respond_to_missing?(method, include_private = false)
  if include_private && fixture_sets.key?(method.name)
    true
  else
    super
  end
end

#setup_fixtures(config = ActiveRecord::Base) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/test_fixtures.rb', line 105

def setup_fixtures(config = ActiveRecord::Base)
  if pre_loaded_fixtures && !use_transactional_tests
    raise RuntimeError, "pre_loaded_fixtures requires use_transactional_tests"
  end

  @fixture_cache = {}
  @fixture_cache_key = [self.class.fixture_table_names.dup, self.class.fixture_paths.dup, self.class.fixture_class_names.dup]
  @fixture_connection_pools = []
  @@already_loaded_fixtures ||= {}
  @connection_subscriber = nil
  @saved_pool_configs = Hash.new { |hash, key| hash[key] = {} }

  if run_in_transaction?
    # Load fixtures once and begin transaction.
    @loaded_fixtures = @@already_loaded_fixtures[@fixture_cache_key]
    unless @loaded_fixtures
      @@already_loaded_fixtures.clear
      @loaded_fixtures = @@already_loaded_fixtures[@fixture_cache_key] = load_fixtures(config)
    end

    setup_transactional_fixtures
  else
    # Load fixtures for every test.
    ActiveRecord::FixtureSet.reset_cache
    invalidate_already_loaded_fixtures
    @loaded_fixtures = load_fixtures(config)
  end

  # Instantiate fixtures for every test if requested.
  instantiate_fixtures if use_instantiated_fixtures
end

#setup_shared_connection_pool (private)

Shares the writing connection pool with connections on other handlers.

In an application with a primary and replica the test fixtures need to share a connection pool so that the reading connection can see data in the open transaction on the writing connection.

[ GitHub ]

  
# File 'activerecord/lib/active_record/test_fixtures.rb', line 200

def setup_shared_connection_pool
  handler = ActiveRecord::Base.connection_handler

  handler.connection_pool_names.each do |name|
    pool_manager = handler.send(:connection_name_to_pool_manager)[name]
    pool_manager.shard_names.each do |shard_name|
      writing_pool_config = pool_manager.get_pool_config(ActiveRecord.writing_role, shard_name)
      @saved_pool_configs[name][shard_name] ||= {}
      pool_manager.role_names.each do |role|
        next unless pool_config = pool_manager.get_pool_config(role, shard_name)
        next if pool_config == writing_pool_config

        @saved_pool_configs[name][shard_name][role] = pool_config
        pool_manager.set_pool_config(role, shard_name, writing_pool_config)
      end
    end
  end
end

#setup_transactional_fixtures (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/test_fixtures.rb', line 153

def setup_transactional_fixtures
  setup_shared_connection_pool

  # Begin transactions for connections already established
  @fixture_connection_pools = ActiveRecord::Base.connection_handler.connection_pool_list(:writing)
  @fixture_connection_pools.each do |pool|
    pool.pin_connection!(lock_threads)
    pool.lease_connection
  end

  # When connections are established in the future, begin a transaction too
  @connection_subscriber = ActiveSupport::Notifications.subscribe("!connection.active_record") do |_, _, _, _, payload|
    connection_name = payload[:connection_name] if payload.key?(:connection_name)
    shard = payload[:shard] if payload.key?(:shard)

    if connection_name
      pool = ActiveRecord::Base.connection_handler.retrieve_connection_pool(connection_name, shard: shard)
      if pool
        setup_shared_connection_pool

        unless @fixture_connection_pools.include?(pool)
          pool.pin_connection!(lock_threads)
          pool.lease_connection
          @fixture_connection_pools << pool
        end
      end
    end
  end
end

#teardown_fixtures (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/test_fixtures.rb', line 137

def teardown_fixtures
  # Rollback changes if a transaction is active.
  if run_in_transaction?
    teardown_transactional_fixtures
  else
    ActiveRecord::FixtureSet.reset_cache
    invalidate_already_loaded_fixtures
  end

  ActiveRecord::Base.connection_handler.clear_active_connections!(:all)
end

#teardown_shared_connection_pool (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/test_fixtures.rb', line 219

def teardown_shared_connection_pool
  handler = ActiveRecord::Base.connection_handler

  @saved_pool_configs.each_pair do |name, shards|
    pool_manager = handler.send(:connection_name_to_pool_manager)[name]
    shards.each_pair do |shard_name, roles|
      roles.each_pair do |role, pool_config|
        next unless pool_manager.get_pool_config(role, shard_name)

        pool_manager.set_pool_config(role, shard_name, pool_config)
      end
    end
  end

  @saved_pool_configs.clear
end

#teardown_transactional_fixtures (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/test_fixtures.rb', line 183

def teardown_transactional_fixtures
  ActiveSupport::Notifications.unsubscribe(@connection_subscriber) if @connection_subscriber
  unless @fixture_connection_pools.map(&:unpin_connection!).all?
    # Something caused the transaction to be committed or rolled back
    # We can no longer trust the database is in a clean state.
    @@already_loaded_fixtures.clear
  end
  @fixture_connection_pools.clear
  teardown_shared_connection_pool
end