aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/fixtures.rb
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/active_record/fixtures.rb')
-rw-r--r--activerecord/lib/active_record/fixtures.rb140
1 files changed, 60 insertions, 80 deletions
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index fee3f51d9e..10e9be20b5 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -2,6 +2,7 @@ require 'erb'
require 'yaml'
require 'zlib'
require 'active_support/dependencies'
+require 'active_support/core_ext/digest/uuid'
require 'active_record/fixture_set/file'
require 'active_record/errors'
@@ -14,9 +15,10 @@ module ActiveRecord
# They are stored in YAML files, one file per model, which are placed in the directory
# appointed by <tt>ActiveSupport::TestCase.fixture_path=(path)</tt> (this is automatically
# configured for Rails, so you can just put your files in <tt><your-rails-app>/test/fixtures/</tt>).
- # The fixture file ends with the <tt>.yml</tt> file extension (Rails example:
- # <tt><your-rails-app>/test/fixtures/web_sites.yml</tt>). The format of a fixture file looks
- # like this:
+ # The fixture file ends with the +.yml+ file extension, for example:
+ # <tt><your-rails-app>/test/fixtures/web_sites.yml</tt>).
+ #
+ # The format of a fixture file looks like this:
#
# rubyonrails:
# id: 1
@@ -32,7 +34,7 @@ module ActiveRecord
# is followed by an indented list of key/value pairs in the "key: value" format. Records are
# separated by a blank line for your viewing pleasure.
#
- # Note that fixtures are unordered. If you want ordered fixtures, use the omap YAML type.
+ # Note: Fixtures are unordered. If you want ordered fixtures, use the omap YAML type.
# See http://yaml.org/type/omap.html
# for the specification. You will need ordered fixtures when you have foreign key constraints
# on keys in the same table. This is commonly needed for tree structures. Example:
@@ -60,8 +62,8 @@ module ActiveRecord
# end
# end
#
- # By default, <tt>test_helper.rb</tt> will load all of your fixtures into your test database,
- # so this test will succeed.
+ # By default, +test_helper.rb+ will load all of your fixtures into your test
+ # database, so this test will succeed.
#
# The testing environment will automatically load the all fixtures into the database before each
# test. To ensure consistent data, the environment deletes the fixtures before running the load.
@@ -124,7 +126,7 @@ module ActiveRecord
# that is included in <tt>ActiveRecord::FixtureSet.context_class</tt>.
#
# - define a helper method in `test_helper.rb`
- # class FixtureFileHelpers
+ # module FixtureFileHelpers
# def file_sha(path)
# Digest::SHA2.hexdigest(File.read(Rails.root.join('test/fixtures', path)))
# end
@@ -179,6 +181,9 @@ module ActiveRecord
# * Stable, autogenerated IDs
# * Label references for associations (belongs_to, has_one, has_many)
# * HABTM associations as inline lists
+ #
+ # There are some more advanced features available even if the id is specified:
+ #
# * Autofilled timestamp columns
# * Fixture label interpolation
# * Support for YAML defaults
@@ -361,6 +366,7 @@ module ActiveRecord
# geeksomnia:
# name: Geeksomnia's Account
# subdomain: $LABEL
+ # email: $LABEL@email.com
#
# Also, sometimes (like when porting older join table fixtures) you'll need
# to be able to get a hold of the identifier for a given label. ERB
@@ -372,8 +378,9 @@ module ActiveRecord
#
# == Support for YAML defaults
#
- # You probably already know how to use YAML to set and reuse defaults in
- # your <tt>database.yml</tt> file. You can use the same technique in your fixtures:
+ # You can set and reuse defaults in your fixtures YAML file.
+ # This is the same technique used in the +database.yml+ file to specify
+ # defaults:
#
# DEFAULTS: &DEFAULTS
# created_on: <%= 3.weeks.ago.to_s(:db) %>
@@ -389,7 +396,8 @@ module ActiveRecord
# Any fixture labeled "DEFAULTS" is safely ignored.
class FixtureSet
#--
- # An instance of FixtureSet is normally stored in a single YAML file and possibly in a folder with the same name.
+ # An instance of FixtureSet is normally stored in a single YAML file and
+ # possibly in a folder with the same name.
#++
MAX_ID = 2 ** 30 - 1
@@ -459,13 +467,7 @@ module ActiveRecord
@config = config
# Remove string values that aren't constants or subclasses of AR
- @class_names.delete_if { |k,klass|
- unless klass.is_a? Class
- klass = klass.safe_constantize
- ActiveSupport::Deprecation.warn("The ability to pass in strings as a class name will be removed in Rails 4.2, consider using the class itself instead.")
- end
- !insert_class(@class_names, k, klass)
- }
+ @class_names.delete_if { |klass_name, klass| !insert_class(@class_names, klass_name, klass) }
end
def [](fs_name)
@@ -516,14 +518,14 @@ module ActiveRecord
::File.join(fixtures_directory, fs_name))
end
- all_loaded_fixtures.update(fixtures_map)
+ update_all_loaded_fixtures fixtures_map
connection.transaction(:requires_new => true) do
fixture_sets.each do |fs|
conn = fs.model_class.respond_to?(:connection) ? fs.model_class.connection : connection
table_rows = fs.table_rows
- table_rows.keys.each do |table|
+ table_rows.each_key do |table|
conn.delete "DELETE FROM #{conn.quote_table_name(table)}", 'Fixture Delete'
end
@@ -549,9 +551,13 @@ module ActiveRecord
end
# Returns a consistent, platform-independent identifier for +label+.
- # Identifiers are positive integers less than 2^32.
- def self.identify(label)
- Zlib.crc32(label.to_s) % MAX_ID
+ # Integer identifiers are values less than 2^30. UUIDs are RFC 4122 version 5 SHA-1 hashes.
+ def self.identify(label, column_type = :integer)
+ if column_type == :uuid
+ Digest::UUID.uuid_v5(Digest::UUID::OID_NAMESPACE, label.to_s)
+ else
+ Zlib.crc32(label.to_s) % MAX_ID
+ end
end
# Superclass for the evaluation contexts used by ERB fixtures.
@@ -559,6 +565,10 @@ module ActiveRecord
@context_class ||= Class.new
end
+ def self.update_all_loaded_fixtures(fixtures_map) # :nodoc:
+ all_loaded_fixtures.update(fixtures_map)
+ end
+
attr_reader :table_name, :name, :fixtures, :model_class, :config
def initialize(connection, name, class_name, path, config = ActiveRecord::Base)
@@ -567,10 +577,6 @@ module ActiveRecord
@config = config
@model_class = nil
- if class_name.is_a?(String)
- ActiveSupport::Deprecation.warn("The ability to pass in strings as a class name will be removed in Rails 4.2, consider using the class itself instead.")
- end
-
if class_name.is_a?(Class) # TODO: Should be an AR::Base type class, or any?
@model_class = class_name
else
@@ -627,12 +633,12 @@ module ActiveRecord
# interpolate the fixture label
row.each do |key, value|
- row[key] = label if "$LABEL" == value
+ row[key] = value.gsub("$LABEL", label.to_s) if value.is_a?(String)
end
# generate a primary key if necessary
if has_primary_key_column? && !row.include?(primary_key_name)
- row[primary_key_name] = ActiveRecord::FixtureSet.identify(label)
+ row[primary_key_name] = ActiveRecord::FixtureSet.identify(label, primary_key_type)
end
# If STI is used, find the correct subclass for association reflection
@@ -643,19 +649,20 @@ module ActiveRecord
model_class
end
- reflection_class.reflect_on_all_associations.each do |association|
+ reflection_class._reflections.each_value do |association|
case association.macro
when :belongs_to
# Do not replace association name with association foreign key if they are named the same
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] && value.sub!(/\s*\(([^\)]*)\)\s*$/, "")
+ if association.polymorphic? && value.sub!(/\s*\(([^\)]*)\)\s*$/, "")
# support polymorphic belongs_to as "label (Type)"
row[association.foreign_type] = $1
end
- row[fk_name] = ActiveRecord::FixtureSet.identify(value)
+ fk_type = association.active_record.columns_hash[fk_name].type
+ row[fk_name] = ActiveRecord::FixtureSet.identify(value, fk_type)
end
when :has_many
if association.options[:through]
@@ -682,6 +689,10 @@ module ActiveRecord
def name
@association.name
end
+
+ def primary_key_type
+ @association.klass.column_types[@association.klass.primary_key].type
+ end
end
class HasManyThroughProxy < ReflectionProxy # :nodoc:
@@ -699,17 +710,22 @@ module ActiveRecord
@primary_key_name ||= model_class && model_class.primary_key
end
+ def primary_key_type
+ @primary_key_type ||= model_class && model_class.column_types[model_class.primary_key].type
+ end
+
def add_join_records(rows, row, association)
# This is the case when the join table has no fixtures file
if (targets = row.delete(association.name.to_s))
- table_name = association.join_table
- lhs_key = association.lhs_key
- rhs_key = association.rhs_key
+ table_name = association.join_table
+ column_type = association.primary_key_type
+ lhs_key = association.lhs_key
+ rhs_key = association.rhs_key
targets = targets.is_a?(Array) ? targets : targets.split(/\s*,\s*/)
rows[table_name].concat targets.map { |target|
{ lhs_key => row[primary_key_name],
- rhs_key => ActiveRecord::FixtureSet.identify(target) }
+ rhs_key => ActiveRecord::FixtureSet.identify(target, column_type) }
}
end
end
@@ -729,7 +745,7 @@ module ActiveRecord
end
def column_names
- @column_names ||= @connection.columns(@table_name).collect { |c| c.name }
+ @column_names ||= @connection.columns(@table_name).collect(&:name)
end
def read_fixture_files(path, model_class)
@@ -752,12 +768,6 @@ module ActiveRecord
end
- #--
- # Deprecate 'Fixtures' in favor of 'FixtureSet'.
- #++
- # :nodoc:
- Fixtures = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('ActiveRecord::Fixtures', 'ActiveRecord::FixtureSet')
-
class Fixture #:nodoc:
include Enumerable
@@ -790,7 +800,9 @@ module ActiveRecord
def find
if model_class
- model_class.find(fixture[model_class.primary_key])
+ model_class.unscoped do
+ model_class.find(fixture[model_class.primary_key])
+ end
else
raise FixtureClassNotFound, "No class attached to find."
end
@@ -850,38 +862,13 @@ module ActiveRecord
fixture_set_names = Dir["#{fixture_path}/{**,*}/*.{yml}"]
fixture_set_names.map! { |f| f[(fixture_path.to_s.size + 1)..-5] }
else
- fixture_set_names = fixture_set_names.flatten.map { |n| n.to_s }
+ fixture_set_names = fixture_set_names.flatten.map(&:to_s)
end
self.fixture_table_names |= fixture_set_names
- require_fixture_classes(fixture_set_names, self.config)
setup_fixture_accessors(fixture_set_names)
end
- def try_to_load_dependency(file_name)
- require_dependency file_name
- rescue LoadError => e
- # Let's hope the developer has included it
- # Let's warn in case this is a subdependency, otherwise
- # subdependency error messages are totally cryptic
- if ActiveRecord::Base.logger
- ActiveRecord::Base.logger.warn("Unable to load #{file_name}, underlying cause #{e.message} \n\n #{e.backtrace.join("\n")}")
- end
- end
-
- def require_fixture_classes(fixture_set_names = nil, config = ActiveRecord::Base)
- if fixture_set_names
- fixture_set_names = fixture_set_names.map { |n| n.to_s }
- else
- fixture_set_names = fixture_table_names
- end
-
- fixture_set_names.each do |file_name|
- file_name = file_name.singularize if config.pluralize_table_names
- try_to_load_dependency(file_name)
- end
- end
-
def setup_fixture_accessors(fixture_set_names = nil)
fixture_set_names = Array(fixture_set_names || fixture_table_names)
methods = Module.new do
@@ -895,7 +882,7 @@ module ActiveRecord
@fixture_cache[fs_name] ||= {}
instances = fixture_names.map do |f_name|
- f_name = f_name.to_s
+ 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]
@@ -915,7 +902,7 @@ module ActiveRecord
def uses_transaction(*methods)
@uses_transaction = [] unless defined?(@uses_transaction)
- @uses_transaction.concat methods.map { |m| m.to_s }
+ @uses_transaction.concat methods.map(&:to_s)
end
def uses_transaction?(method)
@@ -958,7 +945,7 @@ module ActiveRecord
end
# Instantiate fixtures for every test if requested.
- instantiate_fixtures(config) if use_instantiated_fixtures
+ instantiate_fixtures if use_instantiated_fixtures
end
def teardown_fixtures
@@ -985,16 +972,9 @@ module ActiveRecord
Hash[fixtures.map { |f| [f.name, f] }]
end
- # for pre_loaded_fixtures, only require the classes once. huge speed improvement
- @@required_fixture_classes = false
-
- def instantiate_fixtures(config)
+ def instantiate_fixtures
if pre_loaded_fixtures
raise RuntimeError, 'Load fixtures before instantiating them.' if ActiveRecord::FixtureSet.all_loaded_fixtures.empty?
- unless @@required_fixture_classes
- self.class.require_fixture_classes ActiveRecord::FixtureSet.all_loaded_fixtures.keys, config
- @@required_fixture_classes = true
- end
ActiveRecord::FixtureSet.instantiate_all_loaded_fixtures(self, load_instances?)
else
raise RuntimeError, 'Load fixtures before instantiating them.' if @loaded_fixtures.nil?