diff options
author | Emilio Tagua <miloops@gmail.com> | 2011-02-15 12:01:04 -0300 |
---|---|---|
committer | Emilio Tagua <miloops@gmail.com> | 2011-02-15 12:01:04 -0300 |
commit | 8ee0b4414890f919594c1a388d987b5b7364a505 (patch) | |
tree | 6c6c6aa19da0eb8066d2f0b9b02b08f2cc696c29 /activerecord/lib/active_record/fixtures.rb | |
parent | 348c0ec7c656b3691aa4e687565d28259ca0f693 (diff) | |
parent | c9f1ab5365319e087e1b010a3f90626a2b8f080b (diff) | |
download | rails-8ee0b4414890f919594c1a388d987b5b7364a505.tar.gz rails-8ee0b4414890f919594c1a388d987b5b7364a505.tar.bz2 rails-8ee0b4414890f919594c1a388d987b5b7364a505.zip |
Merge remote branch 'rails/master' into identity_map
Conflicts:
activerecord/examples/performance.rb
activerecord/lib/active_record/association_preload.rb
activerecord/lib/active_record/associations.rb
activerecord/lib/active_record/associations/association_proxy.rb
activerecord/lib/active_record/autosave_association.rb
activerecord/lib/active_record/base.rb
activerecord/lib/active_record/nested_attributes.rb
activerecord/test/cases/relations_test.rb
Diffstat (limited to 'activerecord/lib/active_record/fixtures.rb')
-rw-r--r-- | activerecord/lib/active_record/fixtures.rb | 272 |
1 files changed, 131 insertions, 141 deletions
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index b46310f8e0..f065ef7753 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -1,4 +1,10 @@ require 'erb' + +begin + require 'psych' +rescue LoadError +end + require 'yaml' require 'csv' require 'zlib' @@ -6,15 +12,7 @@ require 'active_support/dependencies' require 'active_support/core_ext/array/wrap' require 'active_support/core_ext/object/blank' require 'active_support/core_ext/logger' - -if RUBY_VERSION < '1.9' - module YAML #:nodoc: - class Omap #:nodoc: - def keys; map { |k, v| k } end - def values; map { |k, v| v } end - end - end -end +require 'active_support/ordered_hash' if defined? ActiveRecord class FixtureClassNotFound < ActiveRecord::ActiveRecordError #:nodoc: @@ -446,20 +444,24 @@ class FixturesFileNotFound < StandardError; end # # Any fixture labeled "DEFAULTS" is safely ignored. -class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash) +class Fixtures MAX_ID = 2 ** 30 - 1 DEFAULT_FILTER_RE = /\.ya?ml$/ - @@all_cached_fixtures = {} + @@all_cached_fixtures = Hash.new { |h,k| h[k] = {} } - def self.reset_cache(connection = nil) - connection ||= ActiveRecord::Base.connection - @@all_cached_fixtures[connection.object_id] = {} + def self.find_table_name(table_name) # :nodoc: + ActiveRecord::Base.pluralize_table_names ? + table_name.to_s.singularize.camelize : + table_name.to_s.camelize + end + + def self.reset_cache + @@all_cached_fixtures.clear end def self.cache_for_connection(connection) - @@all_cached_fixtures[connection.object_id] ||= {} - @@all_cached_fixtures[connection.object_id] + @@all_cached_fixtures[connection] end def self.fixture_is_cached?(connection, table_name) @@ -468,11 +470,10 @@ class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash) def self.cached_fixtures(connection, keys_to_fetch = nil) if keys_to_fetch - fixtures = cache_for_connection(connection).values_at(*keys_to_fetch) + cache_for_connection(connection).values_at(*keys_to_fetch) else - fixtures = cache_for_connection(connection).values + cache_for_connection(connection).values end - fixtures.size > 1 ? fixtures : fixtures.first end def self.cache_fixtures(connection, fixtures_map) @@ -482,13 +483,11 @@ class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash) def self.instantiate_fixtures(object, table_name, fixtures, load_instances = true) object.instance_variable_set "@#{table_name.to_s.gsub('.','_')}", fixtures if load_instances - ActiveRecord::Base.silence do - fixtures.each do |name, fixture| - begin - object.instance_variable_set "@#{name}", fixture.find - rescue FixtureClassNotFound - nil - end + fixtures.each do |name, fixture| + begin + object.instance_variable_set "@#{name}", fixture.find + rescue FixtureClassNotFound + nil end end end @@ -505,36 +504,56 @@ class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash) def self.create_fixtures(fixtures_directory, table_names, class_names = {}) table_names = [table_names].flatten.map { |n| n.to_s } - table_names.each { |n| class_names[n.tr('/', '_').to_sym] = n.classify if n.include?('/') } - connection = block_given? ? yield : ActiveRecord::Base.connection + table_names.each { |n| + class_names[n.tr('/', '_').to_sym] = n.classify if n.include?('/') + } - table_names_to_fetch = table_names.reject { |table_name| fixture_is_cached?(connection, table_name) } + # FIXME: Apparently JK uses this. + connection = block_given? ? yield : ActiveRecord::Base.connection - unless table_names_to_fetch.empty? - ActiveRecord::Base.silence do - connection.disable_referential_integrity do - fixtures_map = {} + files_to_read = table_names.reject { |table_name| fixture_is_cached?(connection, table_name) } - fixtures = table_names_to_fetch.map do |table_name| - fixtures_map[table_name] = Fixtures.new(connection, table_name.tr('/', '_'), class_names[table_name.tr('/', '_').to_sym], File.join(fixtures_directory, table_name)) - end + unless files_to_read.empty? + connection.disable_referential_integrity do + fixtures_map = {} + + fixture_files = files_to_read.map do |path| + table_name = path.tr '/', '_' + + fixtures_map[path] = Fixtures.new( + connection, + table_name, + class_names[table_name.to_sym], + File.join(fixtures_directory, path)) + end - all_loaded_fixtures.update(fixtures_map) + all_loaded_fixtures.update(fixtures_map) - connection.transaction(:requires_new => true) do - fixtures.reverse.each { |fixture| fixture.delete_existing_fixtures } - fixtures.each { |fixture| fixture.insert_fixtures } + connection.transaction(:requires_new => true) do + fixture_files.each do |ff| + conn = ff.model_class.respond_to?(:connection) ? ff.model_class.connection : connection + table_rows = ff.table_rows - # 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.tr('/', '_')) + table_rows.keys.each do |table| + conn.delete "DELETE FROM #{conn.quote_table_name(table)}", 'Fixture Delete' + end + + table_rows.each do |table_name,rows| + rows.each do |row| + conn.insert_fixture(row, table_name) end end end - cache_fixtures(connection, fixtures_map) + # 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.tr('/', '_')) + end + end end + + cache_fixtures(connection, fixtures_map) end end cached_fixtures(connection, table_names) @@ -546,40 +565,60 @@ class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash) Zlib.crc32(label.to_s) % MAX_ID end - attr_reader :table_name, :name + attr_reader :table_name, :name, :fixtures, :model_class def initialize(connection, table_name, class_name, fixture_path, file_filter = DEFAULT_FILTER_RE) - @connection, @table_name, @fixture_path, @file_filter = connection, table_name, fixture_path, file_filter - @name = table_name # preserve fixture base name - @class_name = class_name || - (ActiveRecord::Base.pluralize_table_names ? @table_name.singularize.camelize : @table_name.camelize) - @table_name = "#{ActiveRecord::Base.table_name_prefix}#{@table_name}#{ActiveRecord::Base.table_name_suffix}" - @table_name = class_name.table_name if class_name.respond_to?(:table_name) - @connection = class_name.connection if class_name.respond_to?(:connection) + @connection = connection + @table_name = table_name + @fixture_path = fixture_path + @file_filter = file_filter + @name = table_name # preserve fixture base name + @class_name = class_name + + @fixtures = ActiveSupport::OrderedHash.new + @table_name = "#{ActiveRecord::Base.table_name_prefix}#{@table_name}#{ActiveRecord::Base.table_name_suffix}" + + # Should be an AR::Base type class + if class_name.is_a?(Class) + @table_name = class_name.table_name + @connection = class_name.connection + @model_class = class_name + else + @model_class = class_name.constantize rescue nil + end + read_fixture_files end - def delete_existing_fixtures - @connection.delete "DELETE FROM #{@connection.quote_table_name(table_name)}", 'Fixture Delete' + def [](x) + fixtures[x] + end + + def []=(k,v) + fixtures[k] = v + end + + def each(&block) + fixtures.each(&block) + end + + def size + fixtures.size end - def insert_fixtures + # Return a hash of rows to be inserted. The key is the table, the value is + # a list of rows to insert to that table. + def table_rows now = ActiveRecord::Base.default_timezone == :utc ? Time.now.utc : Time.now now = now.to_s(:db) # allow a standard key to be used for doing defaults in YAML - if is_a?(Hash) - delete('DEFAULTS') - else - delete(assoc('DEFAULTS')) - end + fixtures.delete('DEFAULTS') # track any join tables we need to insert later - habtm_fixtures = Hash.new do |h, habtm| - h[habtm] = HabtmFixtures.new(@connection, habtm.options[:join_table], nil, nil) - end + rows = Hash.new { |h,table| h[table] = [] } - each do |label, fixture| + rows[table_name] = fixtures.map do |label, fixture| row = fixture.to_hash if model_class && model_class < ActiveRecord::Base @@ -615,14 +654,9 @@ class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash) fk_name = (association.options[:foreign_key] || "#{association.name}_id").to_s if association.name.to_s != fk_name && value = row.delete(association.name.to_s) - if association.options[:polymorphic] - if value.sub!(/\s*\(([^\)]*)\)\s*$/, "") - target_type = $1 - target_type_name = (association.options[:foreign_type] || "#{association.name}_type").to_s - - # support polymorphic belongs_to as "label (Type)" - row[target_type_name] = target_type - end + if association.options[:polymorphic] && value.sub!(/\s*\(([^\)]*)\)\s*$/, "") + # support polymorphic belongs_to as "label (Type)" + row[association.foreign_type] = $1 end row[fk_name] = Fixtures.identify(value) @@ -630,47 +664,22 @@ class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash) when :has_and_belongs_to_many if (targets = row.delete(association.name.to_s)) targets = targets.is_a?(Array) ? targets : targets.split(/\s*,\s*/) - join_fixtures = habtm_fixtures[association] - - targets.each do |target| - join_fixtures["#{label}_#{target}"] = Fixture.new( - { association.primary_key_name => row[primary_key_name], - association.association_foreign_key => Fixtures.identify(target) }, - nil, @connection) - end + table_name = association.options[:join_table] + rows[table_name].concat targets.map { |target| + { association.foreign_key => row[primary_key_name], + association.association_foreign_key => Fixtures.identify(target) } + } end end end end - @connection.insert_fixture(fixture, @table_name) - end - - # insert any HABTM join tables we discovered - habtm_fixtures.values.each do |fixture| - fixture.delete_existing_fixtures - fixture.insert_fixtures + row end + rows end private - class HabtmFixtures < ::Fixtures #:nodoc: - def read_fixture_files; end - end - - def model_class - unless defined?(@model_class) - @model_class = - if @class_name.nil? || @class_name.is_a?(Class) - @class_name - else - @class_name.constantize rescue nil - end - end - - @model_class - end - def primary_key_name @primary_key_name ||= model_class && model_class.primary_key end @@ -724,7 +733,7 @@ class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash) raise Fixture::FormatError, "Bad data for #{@class_name} fixture named #{name} (nil)" end - self[name] = Fixture.new(data, model_class, @connection) + fixtures[name] = Fixture.new(data, model_class) end end end @@ -737,7 +746,7 @@ class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash) reader.each do |row| data = {} row.each_with_index { |cell, j| data[header[j].to_s.strip] = cell.to_s.strip } - self["#{@class_name.to_s.underscore}_#{i+=1}"] = Fixture.new(data, model_class, @connection) + fixtures["#{@class_name.to_s.underscore}_#{i+=1}"] = Fixture.new(data, model_class) end end @@ -773,44 +782,30 @@ class Fixture #:nodoc: class FormatError < FixtureError #:nodoc: end - attr_reader :model_class + attr_reader :model_class, :fixture - def initialize(fixture, model_class, connection = ActiveRecord::Base.connection) - @connection = connection - @fixture = fixture - @model_class = model_class.is_a?(Class) ? model_class : model_class.constantize rescue nil + def initialize(fixture, model_class) + @fixture = fixture + @model_class = model_class end def class_name - @model_class.name if @model_class + model_class.name if model_class end def each - @fixture.each { |item| yield item } + fixture.each { |item| yield item } end def [](key) - @fixture[key] + fixture[key] end - def to_hash - @fixture - end - - def key_list - @fixture.keys.map { |column_name| @connection.quote_column_name(column_name) }.join(', ') - end - - def value_list - cols = (model_class && model_class < ActiveRecord::Base) ? model_class.columns_hash : {} - @fixture.map do |key, value| - @connection.quote(value, cols[key]).gsub('[^\]\\n', "\n").gsub('[^\]\\r', "\r") - end.join(', ') - end + alias :to_hash :fixture def find if model_class - model_class.find(self[model_class.primary_key]) + model_class.find(fixture[model_class.primary_key]) else raise FixtureClassNotFound, "No class attached to find." end @@ -837,7 +832,9 @@ module ActiveRecord self.use_instantiated_fixtures = false self.pre_loaded_fixtures = false - self.fixture_class_names = {} + self.fixture_class_names = Hash.new do |h, table_name| + h[table_name] = Fixtures.find_table_name(table_name) + end end module ClassMethods @@ -937,7 +934,7 @@ module ActiveRecord if @@already_loaded_fixtures[self.class] @loaded_fixtures = @@already_loaded_fixtures[self.class] else - load_fixtures + @loaded_fixtures = load_fixtures @@already_loaded_fixtures[self.class] = @loaded_fixtures end ActiveRecord::Base.connection.increment_open_transactions @@ -947,7 +944,7 @@ module ActiveRecord else Fixtures.reset_cache @@already_loaded_fixtures[self.class] = nil - load_fixtures + @loaded_fixtures = load_fixtures end # Instantiate fixtures for every test if requested. @@ -971,15 +968,8 @@ module ActiveRecord private def load_fixtures - @loaded_fixtures = {} fixtures = Fixtures.create_fixtures(fixture_path, fixture_table_names, fixture_class_names) - unless fixtures.nil? - if fixtures.instance_of?(Fixtures) - @loaded_fixtures[fixtures.name] = fixtures - else - fixtures.each { |f| @loaded_fixtures[f.name] = f } - end - end + Hash[fixtures.map { |f| [f.name, f] }] end # for pre_loaded_fixtures, only require the classes once. huge speed improvement |