diff options
-rw-r--r-- | activerecord/CHANGELOG | 2 | ||||
-rwxr-xr-x | activerecord/lib/active_record/fixtures.rb | 71 | ||||
-rw-r--r-- | activerecord/test/connection_test_mysql.rb | 1 | ||||
-rwxr-xr-x | activerecord/test/fixtures_test.rb | 31 |
4 files changed, 90 insertions, 15 deletions
diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 6d74ebc70f..d32ad83791 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,7 @@ *SVN* +* Added fixture caching that'll speed up a normal fixture-powered test suite between 50% and 100% #9682 [frederick.cheung@gmail.com] + * Correctly quote id list for limited eager loading. #7482 [tmacedo] * Fixed that using version-targetted migrates would fail on loggers other than the default one #7430 [valeksenko] diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index 5ecd0a19b8..1eded9fd39 100755 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -218,6 +218,34 @@ end class Fixtures < YAML::Omap DEFAULT_FILTER_RE = /\.ya?ml$/ + @@all_cached_fixtures = {} + + def self.reset_cache(connection=ActiveRecord::Base.connection) + @@all_cached_fixtures[connection.object_id] = {} + end + + def self.cache_for_connection(connection) + @@all_cached_fixtures[connection.object_id] ||= {} + @@all_cached_fixtures[connection.object_id] + end + + def self.fixture_is_cached?(connection, table_name) + cache_for_connection(connection)[table_name] + end + + def self.cached_fixtures(connection, keys_to_fetch = nil) + if keys_to_fetch + fixtures = cache_for_connection(connection).values_at(*keys_to_fetch) + else + fixtures = cache_for_connection(connection).values + end + fixtures.size > 1 ? fixtures : fixtures.first + end + + def self.cache_fixtures(connection, fixtures) + cache_for_connection(connection).merge! fixtures.index_by(&:table_name) + end + def self.instantiate_fixtures(object, table_name, fixtures, load_instances=true) object.instance_variable_set "@#{table_name.to_s.gsub('.','_')}", fixtures if load_instances @@ -246,29 +274,34 @@ class Fixtures < YAML::Omap table_names = [table_names].flatten.map { |n| n.to_s } connection = block_given? ? yield : ActiveRecord::Base.connection - ActiveRecord::Base.silence do - fixtures_map = {} + table_names_to_fetch = table_names.reject {|table_name| fixture_is_cached?(connection, table_name)} + + unless table_names_to_fetch.empty? + ActiveRecord::Base.silence do + fixtures_map = {} - fixtures = table_names.map do |table_name| - fixtures_map[table_name] = Fixtures.new(connection, File.split(table_name.to_s).last, class_names[table_name.to_sym], File.join(fixtures_directory, table_name.to_s)) - end + fixtures = table_names_to_fetch.map do |table_name| + fixtures_map[table_name] = Fixtures.new(connection, File.split(table_name.to_s).last, class_names[table_name.to_sym], File.join(fixtures_directory, table_name.to_s)) + end - all_loaded_fixtures.merge! fixtures_map + all_loaded_fixtures.merge! fixtures_map - connection.transaction(Thread.current['open_transactions'].to_i == 0) do - fixtures.reverse.each { |fixture| fixture.delete_existing_fixtures } - fixtures.each { |fixture| fixture.insert_fixtures } + connection.transaction(Thread.current['open_transactions'].to_i == 0) do + fixtures.reverse.each { |fixture| fixture.delete_existing_fixtures } + fixtures.each { |fixture| fixture.insert_fixtures } - # Cap primary key sequences to max(pk). - if connection.respond_to?(:reset_pk_sequence!) - table_names.each do |table_name| - connection.reset_pk_sequence!(table_name) + # Cap primary key sequences to max(pk). + if connection.respond_to?(:reset_pk_sequence!) + table_names.each do |table_name| + connection.reset_pk_sequence!(table_name) + end end end + + cache_fixtures(connection, fixtures) end - - return fixtures.size > 1 ? fixtures : fixtures.first end + return cached_fixtures(connection, table_names) end @@ -547,6 +580,10 @@ module Test #:nodoc: @fixture_cache = Hash.new + if !use_transactional_fixtures? + Fixtures.reset_cache #we don't want the test to use any of our cached data + end + # Load fixtures once and begin transaction. if use_transactional_fixtures? if @@already_loaded_fixtures[self.class] @@ -573,6 +610,10 @@ module Test #:nodoc: def teardown_with_fixtures return unless defined?(ActiveRecord::Base) && !ActiveRecord::Base.configurations.blank? + if !use_transactional_fixtures? + Fixtures.reset_cache #the non transactional test may have fiddled with the data - dump caches + end + # Rollback changes if a transaction is active. if use_transactional_fixtures? && Thread.current['open_transactions'] != 0 ActiveRecord::Base.connection.rollback_db_transaction diff --git a/activerecord/test/connection_test_mysql.rb b/activerecord/test/connection_test_mysql.rb index e3f589c4af..fe132cb5ed 100644 --- a/activerecord/test/connection_test_mysql.rb +++ b/activerecord/test/connection_test_mysql.rb @@ -1,6 +1,7 @@ require "#{File.dirname(__FILE__)}/abstract_unit" class MysqlConnectionTest < Test::Unit::TestCase + self.use_transactional_fixtures = false def setup @connection = ActiveRecord::Base.connection end diff --git a/activerecord/test/fixtures_test.rb b/activerecord/test/fixtures_test.rb index 3e61c7b465..c666005b69 100755 --- a/activerecord/test/fixtures_test.rb +++ b/activerecord/test/fixtures_test.rb @@ -58,6 +58,7 @@ class FixturesTest < Test::Unit::TestCase if ActiveRecord::Base.connection.supports_migrations? def test_inserts_with_pre_and_suffix + Fixtures.reset_cache ActiveRecord::Base.connection.create_table :prefix_topics_suffix do |t| t.column :title, :string t.column :author_name, :string @@ -425,3 +426,33 @@ class LoadAllFixturesTest < Test::Unit::TestCase assert_equal %w(developers people tasks), fixture_table_names.sort end end + +class FasterFixturesTest < Test::Unit::TestCase + fixtures :categories, :authors + + def run(*args, &block) + Fixtures.reset_cache + super(*args, &block) + end + + def load_extra_fixture(name) + fixture = create_fixtures(name) + assert fixture.is_a?(Fixtures) + @loaded_fixtures[fixture.table_name] = fixture + end + + def test_cache + assert Fixtures.fixture_is_cached?(ActiveRecord::Base.connection, 'categories') + assert Fixtures.fixture_is_cached?(ActiveRecord::Base.connection, 'authors') + + assert_no_queries do + create_fixtures('categories') + create_fixtures('authors') + end + + load_extra_fixture('posts') + assert Fixtures.fixture_is_cached?(ActiveRecord::Base.connection, 'posts') + self.class.setup_fixture_accessors('posts') + assert_equal 'Welcome to the weblog', posts(:welcome).title + end +end |