2004-11-23 20:04:44 -05:00
require 'erb'
require 'yaml'
2009-05-11 23:22:20 -04:00
require 'zlib'
2008-11-24 13:02:00 -05:00
require 'active_support/dependencies'
2013-08-25 05:22:36 -04:00
require 'active_support/core_ext/securerandom'
2012-10-07 14:32:48 -04:00
require 'active_record/fixture_set/file'
2012-03-22 19:25:48 -04:00
require 'active_record/errors'
2005-10-13 15:06:15 -04:00
2012-03-22 19:25:48 -04:00
module ActiveRecord
2007-12-08 23:37:46 -05:00
class FixtureClassNotFound < ActiveRecord :: ActiveRecordError #:nodoc:
end
2006-03-06 18:03:35 -05:00
2011-09-03 18:20:18 -04:00
# \Fixtures are a way of organizing data that you want to test against; in short, sample data.
#
# 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:
#
# rubyonrails:
# id: 1
# name: Ruby on Rails
# url: http://www.rubyonrails.org
#
# google:
# id: 2
# name: Google
# url: http://www.google.com
#
# This fixture file includes two fixtures. Each YAML fixture (ie. record) is given a name and
# 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.
# 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:
#
# --- !omap
# - parent:
# id: 1
# parent_id: NULL
# title: Parent
# - child:
# id: 2
# parent_id: 1
# title: Child
#
# = Using Fixtures in Test Cases
#
# Since fixtures are a testing construct, we use them in our unit and functional tests. There
# are two ways to use the fixtures, but first let's take a look at a sample unit test:
#
# require 'test_helper'
#
# class WebSiteTest < ActiveSupport::TestCase
# test "web_site_count" do
# assert_equal 2, WebSite.count
# end
# end
#
# By default, <tt>test_helper.rb</tt> 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.
#
# In addition to being available in the database, the fixture's data may also be accessed by
# using a special dynamic method, which has the same name as the model, and accepts the
# name of the fixture to instantiate:
#
# test "find" do
# assert_equal "Ruby on Rails", web_sites(:rubyonrails).name
# end
#
# Alternatively, you may enable auto-instantiation of the fixture data. For instance, take the
# following tests:
#
# test "find_alt_method_1" do
# assert_equal "Ruby on Rails", @web_sites['rubyonrails']['name']
# end
#
# test "find_alt_method_2" do
2012-05-15 03:40:03 -04:00
# assert_equal "Ruby on Rails", @rubyonrails.name
2011-09-03 18:20:18 -04:00
# end
#
# In order to use these methods to access fixtured data within your testcases, you must specify one of the
# following in your <tt>ActiveSupport::TestCase</tt>-derived class:
#
# - to fully enable instantiated fixtures (enable alternate methods #1 and #2 above)
# self.use_instantiated_fixtures = true
#
# - create only the hash for the fixtures, do not 'find' each instance (enable alternate method #1 only)
# self.use_instantiated_fixtures = :no_instances
#
# Using either of these alternate methods incurs a performance hit, as the fixtured data must be fully
# traversed in the database to create the fixture hash and/or instance variables. This is expensive for
# large sets of fixtured data.
#
# = Dynamic fixtures with ERB
#
# Some times you don't care about the content of the fixtures as much as you care about the volume.
# In these cases, you can mix ERB in with your YAML fixtures to create a bunch of fixtures for load
# testing, like:
#
# <% 1.upto(1000) do |i| %>
# fix_<%= i %>:
# id: <%= i %>
# name: guy_<%= 1 %>
# <% end %>
#
# This will create 1000 very simple fixtures.
#
# Using ERB, you can also inject dynamic values into your fixtures with inserts like
# <tt><%= Date.today.strftime("%Y-%m-%d") %></tt>.
# This is however a feature to be used with some caution. The point of fixtures are that they're
# stable units of predictable sample data. If you feel that you need to inject dynamic values, then
# perhaps you should reexamine whether your application is properly testable. Hence, dynamic values
# in fixtures are to be considered a code smell.
#
2013-11-24 12:52:53 -05:00
# Helper methods defined in a fixture will not be available in other fixtures, to prevent against
# unwanted inter-test dependencies. Methods used by multiple fixtures should be defined in a module
# that is included in <tt>ActiveRecord::FixtureSet.context_class</tt>.
#
# - define a helper method in `test_helper.rb`
# class FixtureFileHelpers
# def file_sha(path)
# Digest::SHA2.hexdigest(File.read(Rails.root.join('test/fixtures', path)))
# end
# end
# ActiveRecord::FixtureSet.context_class.send :include, FixtureFileHelpers
#
# - use the helper method in a fixture
# photo:
# name: kitten.png
# sha: <%= file_sha 'files/kitten.png' %>
#
2011-09-03 18:20:18 -04:00
# = Transactional Fixtures
#
# Test cases can use begin+rollback to isolate their changes to the database instead of having to
# delete+insert for every test case.
#
# class FooTest < ActiveSupport::TestCase
# self.use_transactional_fixtures = true
#
# test "godzilla" do
# assert !Foo.all.empty?
# Foo.destroy_all
# assert Foo.all.empty?
# end
#
# test "godzilla aftermath" do
# assert !Foo.all.empty?
# end
# end
#
# If you preload your test database with all fixture data (probably in the rake task) and use
# transactional fixtures, then you may omit all fixtures declarations in your test cases since
# all the data's already there and every case rolls back its changes.
#
# In order to use instantiated fixtures with preloaded data, set +self.pre_loaded_fixtures+ to
# true. This will provide access to fixture data for every table that has been loaded through
# fixtures (depending on the value of +use_instantiated_fixtures+).
#
# When *not* to use transactional fixtures:
#
# 1. You're testing whether a transaction works correctly. Nested transactions don't commit until
# all parent transactions commit, particularly, the fixtures transaction which is begun in setup
# and rolled back in teardown. Thus, you won't be able to verify
# the results of your transaction until Active Record supports nested transactions or savepoints (in progress).
# 2. Your database does not support transactions. Every Active Record database supports transactions except MySQL MyISAM.
# Use InnoDB, MaxDB, or NDB instead.
#
# = Advanced Fixtures
#
# Fixtures that don't specify an ID get some extra features:
#
# * Stable, autogenerated IDs
# * Label references for associations (belongs_to, has_one, has_many)
# * HABTM associations as inline lists
# * Autofilled timestamp columns
# * Fixture label interpolation
# * Support for YAML defaults
#
# == Stable, Autogenerated IDs
#
# Here, have a monkey fixture:
#
# george:
# id: 1
# name: George the Monkey
#
# reginald:
# id: 2
# name: Reginald the Pirate
#
# Each of these fixtures has two unique identifiers: one for the database
# and one for the humans. Why don't we generate the primary key instead?
# Hashing each fixture's label yields a consistent ID:
#
# george: # generated id: 503576764
# name: George the Monkey
#
# reginald: # generated id: 324201669
# name: Reginald the Pirate
#
# Active Record looks at the fixture's model class, discovers the correct
# primary key, and generates it right before inserting the fixture
# into the database.
#
# The generated ID for a given label is constant, so we can discover
# any fixture's ID without loading anything, as long as we know the label.
#
# == Label references for associations (belongs_to, has_one, has_many)
#
# Specifying foreign keys in fixtures can be very fragile, not to
# mention difficult to read. Since Active Record can figure out the ID of
# any fixture from its label, you can specify FK's by label instead of ID.
#
# === belongs_to
#
# Let's break out some more monkeys and pirates.
#
# ### in pirates.yml
#
# reginald:
# id: 1
# name: Reginald the Pirate
# monkey_id: 1
#
# ### in monkeys.yml
#
# george:
# id: 1
# name: George the Monkey
# pirate_id: 1
#
# Add a few more monkeys and pirates and break this into multiple files,
# and it gets pretty hard to keep track of what's going on. Let's
# use labels instead of IDs:
#
# ### in pirates.yml
#
# reginald:
# name: Reginald the Pirate
# monkey: george
#
# ### in monkeys.yml
#
# george:
# name: George the Monkey
# pirate: reginald
#
# Pow! All is made clear. Active Record reflects on the fixture's model class,
# finds all the +belongs_to+ associations, and allows you to specify
# a target *label* for the *association* (monkey: george) rather than
# a target *id* for the *FK* (<tt>monkey_id: 1</tt>).
#
# ==== Polymorphic belongs_to
#
# Supporting polymorphic relationships is a little bit more complicated, since
# Active Record needs to know what type your association is pointing at. Something
# like this should look familiar:
#
# ### in fruit.rb
#
2012-11-10 10:16:21 -05:00
# belongs_to :eater, polymorphic: true
2011-09-03 18:20:18 -04:00
#
# ### in fruits.yml
#
# apple:
# id: 1
# name: apple
# eater_id: 1
# eater_type: Monkey
#
# Can we do better? You bet!
#
# apple:
# eater: george (Monkey)
#
# Just provide the polymorphic target type and Active Record will take care of the rest.
#
# === has_and_belongs_to_many
#
# Time to give our monkey some fruit.
#
# ### in monkeys.yml
#
# george:
# id: 1
# name: George the Monkey
#
# ### in fruits.yml
#
# apple:
# id: 1
# name: apple
#
# orange:
# id: 2
# name: orange
#
# grape:
# id: 3
# name: grape
#
# ### in fruits_monkeys.yml
#
# apple_george:
# fruit_id: 1
# monkey_id: 1
#
# orange_george:
# fruit_id: 2
# monkey_id: 1
#
# grape_george:
# fruit_id: 3
# monkey_id: 1
#
# Let's make the HABTM fixture go away.
#
# ### in monkeys.yml
#
# george:
# id: 1
# name: George the Monkey
# fruits: apple, orange, grape
#
# ### in fruits.yml
#
# apple:
# name: apple
#
# orange:
# name: orange
#
# grape:
# name: grape
#
# Zap! No more fruits_monkeys.yml file. We've specified the list of fruits
# on George's fixture, but we could've just as easily specified a list
# of monkeys on each fruit. As with +belongs_to+, Active Record reflects on
# the fixture's model class and discovers the +has_and_belongs_to_many+
# associations.
#
# == Autofilled Timestamp Columns
#
# If your table/model specifies any of Active Record's
# standard timestamp columns (+created_at+, +created_on+, +updated_at+, +updated_on+),
# they will automatically be set to <tt>Time.now</tt>.
#
# If you've set specific values, they'll be left alone.
#
# == Fixture label interpolation
#
# The label of the current fixture is always available as a column value:
#
# geeksomnia:
# name: Geeksomnia's Account
# subdomain: $LABEL
2014-03-15 21:49:04 -04:00
# email: $LABEL@email.com
2011-09-03 18:20:18 -04:00
#
# 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
# to the rescue:
#
# george_reginald:
2012-05-12 07:17:03 -04:00
# monkey_id: <%= ActiveRecord::FixtureSet.identify(:reginald) %>
# pirate_id: <%= ActiveRecord::FixtureSet.identify(:george) %>
2011-09-03 18:20:18 -04:00
#
# == 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:
#
# DEFAULTS: &DEFAULTS
# created_on: <%= 3.weeks.ago.to_s(:db) %>
#
# first:
# name: Smurf
2013-01-16 22:22:23 -05:00
# <<: *DEFAULTS
2011-09-03 18:20:18 -04:00
#
# second:
# name: Fraggle
2013-01-16 22:22:23 -05:00
# <<: *DEFAULTS
2011-09-03 18:20:18 -04:00
#
# Any fixture labeled "DEFAULTS" is safely ignored.
2012-05-12 07:17:03 -04:00
class FixtureSet
2012-01-01 12:54:52 -05:00
#--
2012-05-12 07:17:03 -04:00
# An instance of FixtureSet is normally stored in a single YAML file and possibly in a folder with the same name.
2012-01-01 12:54:52 -05:00
#++
2012-05-12 09:42:54 -04:00
2011-05-07 16:20:51 -04:00
MAX_ID = 2 ** 30 - 1
2004-12-09 10:52:54 -05:00
2011-05-07 16:20:51 -04:00
@@all_cached_fixtures = Hash . new { | h , k | h [ k ] = { } }
2007-09-28 10:56:07 -04:00
2013-08-26 18:24:23 -04:00
def self . default_fixture_model_name ( fixture_set_name , config = ActiveRecord :: Base ) # :nodoc:
config . pluralize_table_names ?
2012-01-01 12:54:52 -05:00
fixture_set_name . singularize . camelize :
fixture_set_name . camelize
2011-05-07 16:20:51 -04:00
end
2011-02-11 17:33:03 -05:00
2013-08-26 18:24:23 -04:00
def self . default_fixture_table_name ( fixture_set_name , config = ActiveRecord :: Base ) # :nodoc:
" #{ config . table_name_prefix } " \
2012-01-01 12:54:52 -05:00
" #{ fixture_set_name . tr ( '/' , '_' ) } " \
2013-08-26 18:24:23 -04:00
" #{ config . table_name_suffix } " . to_sym
2011-12-20 15:58:26 -05:00
end
2011-05-07 16:20:51 -04:00
def self . reset_cache
@@all_cached_fixtures . clear
end
2007-09-28 10:56:07 -04:00
2011-05-07 16:20:51 -04:00
def self . cache_for_connection ( connection )
@@all_cached_fixtures [ connection ]
end
2007-09-28 10:56:07 -04:00
2011-05-07 16:20:51 -04:00
def self . fixture_is_cached? ( connection , table_name )
cache_for_connection ( connection ) [ table_name ]
end
2007-09-28 10:05:03 -04:00
2011-05-07 16:20:51 -04:00
def self . cached_fixtures ( connection , keys_to_fetch = nil )
if keys_to_fetch
cache_for_connection ( connection ) . values_at ( * keys_to_fetch )
else
cache_for_connection ( connection ) . values
end
2007-09-28 10:05:03 -04:00
end
2011-05-07 16:20:51 -04:00
def self . cache_fixtures ( connection , fixtures_map )
cache_for_connection ( connection ) . update ( fixtures_map )
end
2007-09-28 10:56:07 -04:00
2012-01-01 12:54:52 -05:00
def self . instantiate_fixtures ( object , fixture_set , load_instances = true )
2011-05-07 16:20:51 -04:00
if load_instances
2012-01-01 12:54:52 -05:00
fixture_set . each do | fixture_name , fixture |
2011-05-07 16:20:51 -04:00
begin
2012-01-01 12:54:52 -05:00
object . instance_variable_set " @ #{ fixture_name } " , fixture . find
2011-05-07 16:20:51 -04:00
rescue FixtureClassNotFound
nil
end
2005-03-20 08:42:35 -05:00
end
2005-02-07 09:10:44 -05:00
end
2004-11-23 20:04:44 -05:00
end
2005-10-23 15:02:38 -04:00
2011-05-07 16:20:51 -04:00
def self . instantiate_all_loaded_fixtures ( object , load_instances = true )
2012-01-01 12:54:52 -05:00
all_loaded_fixtures . each_value do | fixture_set |
instantiate_fixtures ( object , fixture_set , load_instances )
2011-05-07 16:20:51 -04:00
end
2005-04-03 13:50:11 -04:00
end
2007-09-28 10:56:07 -04:00
2011-05-07 16:20:51 -04:00
cattr_accessor :all_loaded_fixtures
self . all_loaded_fixtures = { }
2004-12-09 10:52:54 -05:00
2013-09-05 17:30:37 -04:00
class ClassCache
def initialize ( class_names , config )
2013-09-05 17:55:11 -04:00
@class_names = class_names . stringify_keys
2013-09-05 17:30:37 -04:00
@config = config
2013-09-09 18:16:41 -04:00
# 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
2014-01-16 14:57:22 -05:00
ActiveSupport :: Deprecation . warn ( " The ability to pass in strings as a class name to `set_fixture_class` will be removed in Rails 4.2. Use the class itself instead. " )
2013-09-09 18:16:41 -04:00
end
! insert_class ( @class_names , k , klass )
}
2013-09-05 17:30:37 -04:00
end
def [] ( fs_name )
2013-09-05 17:38:43 -04:00
@class_names . fetch ( fs_name ) {
klass = default_fixture_model ( fs_name , @config ) . safe_constantize
2013-09-09 18:16:41 -04:00
insert_class ( @class_names , fs_name , klass )
2013-09-05 17:38:43 -04:00
}
2013-09-05 17:30:37 -04:00
end
private
2013-09-09 18:16:41 -04:00
def insert_class ( class_names , name , klass )
# We only want to deal with AR objects.
if klass && klass < ActiveRecord :: Base
class_names [ name ] = klass
else
class_names [ name ] = nil
end
end
2013-09-05 17:30:37 -04:00
def default_fixture_model ( fs_name , config )
ActiveRecord :: FixtureSet . default_fixture_model_name ( fs_name , config )
end
end
2013-08-26 18:24:23 -04:00
def self . create_fixtures ( fixtures_directory , fixture_set_names , class_names = { } , config = ActiveRecord :: Base )
2012-01-01 12:54:52 -05:00
fixture_set_names = Array ( fixture_set_names ) . map ( & :to_s )
2013-09-05 17:30:37 -04:00
class_names = ClassCache . new class_names , config
2007-02-25 12:31:43 -05:00
2011-05-07 16:20:51 -04:00
# FIXME: Apparently JK uses this.
connection = block_given? ? yield : ActiveRecord :: Base . connection
2007-09-28 10:56:07 -04:00
2012-01-01 12:54:52 -05:00
files_to_read = fixture_set_names . reject { | fs_name |
fixture_is_cached? ( connection , fs_name )
2011-05-07 16:20:51 -04:00
}
2007-02-25 12:31:43 -05:00
2011-05-07 16:20:51 -04:00
unless files_to_read . empty?
connection . disable_referential_integrity do
fixtures_map = { }
2007-02-25 12:31:43 -05:00
2012-01-01 12:54:52 -05:00
fixture_sets = files_to_read . map do | fs_name |
2013-09-05 17:30:37 -04:00
klass = class_names [ fs_name ]
2013-09-09 18:16:41 -04:00
conn = klass ? klass . connection : connection
2012-05-12 07:17:03 -04:00
fixtures_map [ fs_name ] = new ( # ActiveRecord::FixtureSet.new
2013-09-05 17:47:42 -04:00
conn ,
2012-01-01 12:54:52 -05:00
fs_name ,
2013-09-05 17:30:37 -04:00
klass ,
2012-01-01 12:54:52 -05:00
:: File . join ( fixtures_directory , fs_name ) )
2011-05-07 16:20:51 -04:00
end
2007-02-25 12:31:43 -05:00
2011-05-07 16:20:51 -04:00
all_loaded_fixtures . update ( fixtures_map )
2004-12-09 10:52:54 -05:00
2011-05-07 16:20:51 -04:00
connection . transaction ( :requires_new = > true ) do
2012-01-01 12:54:52 -05:00
fixture_sets . each do | fs |
conn = fs . model_class . respond_to? ( :connection ) ? fs . model_class . connection : connection
2014-01-09 17:17:04 -05:00
table_rows = fs . table_rows
table_rows . keys . each do | table |
conn . delete " DELETE FROM #{ conn . quote_table_name ( table ) } " , 'Fixture Delete'
end
table_rows . each do | fixture_set_name , rows |
rows . each do | row |
conn . insert_fixture ( row , fixture_set_name )
end
2007-10-26 01:56:46 -04:00
end
2007-09-28 10:05:03 -04:00
end
2007-09-28 10:56:07 -04:00
2011-05-07 16:20:51 -04:00
# Cap primary key sequences to max(pk).
if connection . respond_to? ( :reset_pk_sequence! )
2012-01-01 12:54:52 -05:00
fixture_sets . each do | fs |
connection . reset_pk_sequence! ( fs . table_name )
2011-05-07 16:20:51 -04:00
end
2007-09-28 10:05:03 -04:00
end
2005-10-23 15:02:38 -04:00
end
2011-02-11 18:01:51 -05:00
2011-05-07 16:20:51 -04:00
cache_fixtures ( connection , fixtures_map )
end
2005-10-15 23:45:39 -04:00
end
2012-01-01 12:54:52 -05:00
cached_fixtures ( connection , fixture_set_names )
2005-10-23 15:02:38 -04:00
end
2011-05-07 16:20:51 -04:00
# Returns a consistent, platform-independent identifier for +label+.
2014-04-04 11:52:49 -04:00
# Integer identifiers are values less than 2^30. UUIDs are RFC 4122 version 5 SHA-1 hashes.
2013-08-25 05:22:36 -04:00
def self . identify ( label , column_type = :integer )
if column_type == :uuid
SecureRandom . uuid_v5 ( SecureRandom :: UUID_OID_NAMESPACE , label . to_s )
else
Zlib . crc32 ( label . to_s ) % MAX_ID
end
2011-02-11 19:35:15 -05:00
end
2013-11-24 12:52:53 -05:00
# Superclass for the evaluation contexts used by ERB fixtures.
def self . context_class
@context_class || = Class . new
end
2013-08-26 18:24:23 -04:00
attr_reader :table_name , :name , :fixtures , :model_class , :config
2004-12-01 07:25:04 -05:00
2013-08-26 18:24:23 -04:00
def initialize ( connection , name , class_name , path , config = ActiveRecord :: Base )
2012-01-01 12:54:52 -05:00
@name = name
@path = path
2013-08-26 18:24:23 -04:00
@config = config
2013-09-06 13:32:50 -04:00
@model_class = nil
2011-02-11 13:03:37 -05:00
2013-08-24 04:11:18 -04:00
if class_name . is_a? ( String )
2014-01-16 14:57:22 -05:00
ActiveSupport :: Deprecation . warn ( " The ability to pass in strings as a class name to `FixtureSet.new` will be removed in Rails 4.2. Use the class itself instead. " )
2013-08-24 04:11:18 -04:00
end
2012-01-01 12:54:52 -05:00
if class_name . is_a? ( Class ) # TODO: Should be an AR::Base type class, or any?
2011-12-20 15:58:26 -05:00
@model_class = class_name
2011-05-07 16:20:51 -04:00
else
2013-08-24 04:11:18 -04:00
@model_class = class_name . safe_constantize if class_name
2011-05-07 16:20:51 -04:00
end
2011-02-11 13:03:37 -05:00
2013-09-05 17:47:42 -04:00
@connection = connection
2011-12-20 15:58:26 -05:00
@table_name = ( model_class . respond_to? ( :table_name ) ?
model_class . table_name :
2013-08-26 18:24:23 -04:00
self . class . default_fixture_table_name ( name , config ) )
2011-12-20 15:58:26 -05:00
2013-09-06 13:35:37 -04:00
@fixtures = read_fixture_files path , @model_class
2011-05-07 16:20:51 -04:00
end
2004-12-01 07:25:04 -05:00
2011-05-07 16:20:51 -04:00
def [] ( x )
fixtures [ x ]
end
2007-10-26 01:56:46 -04:00
2011-05-07 16:20:51 -04:00
def []= ( k , v )
fixtures [ k ] = v
end
2007-10-26 01:56:46 -04:00
2011-05-07 16:20:51 -04:00
def each ( & block )
fixtures . each ( & block )
end
2007-10-26 01:56:46 -04:00
2011-05-07 16:20:51 -04:00
def size
fixtures . size
end
2007-10-26 01:56:46 -04:00
2013-12-03 09:04:25 -05:00
# Returns a hash of rows to be inserted. The key is the table, the value is
2011-05-07 16:20:51 -04:00
# a list of rows to insert to that table.
def table_rows
2013-08-26 18:24:23 -04:00
now = config . default_timezone == :utc ? Time . now . utc : Time . now
2011-05-07 16:20:51 -04:00
now = now . to_s ( :db )
2007-10-26 01:56:46 -04:00
2011-05-07 16:20:51 -04:00
# allow a standard key to be used for doing defaults in YAML
fixtures . delete ( 'DEFAULTS' )
2007-10-26 01:56:46 -04:00
2011-05-07 16:20:51 -04:00
# track any join tables we need to insert later
rows = Hash . new { | h , table | h [ table ] = [ ] }
2007-10-26 01:56:46 -04:00
2011-05-07 16:20:51 -04:00
rows [ table_name ] = fixtures . map do | label , fixture |
row = fixture . to_hash
2013-09-05 17:38:43 -04:00
if model_class
2011-05-07 16:20:51 -04:00
# fill in timestamp columns if they aren't specified and the model is set to record_timestamps
if model_class . record_timestamps
2012-01-01 12:54:52 -05:00
timestamp_column_names . each do | c_name |
row [ c_name ] = now unless row . key? ( c_name )
2011-05-07 16:20:51 -04:00
end
2007-11-26 17:46:11 -05:00
end
2011-05-07 16:20:51 -04:00
# interpolate the fixture label
row . each do | key , value |
2014-03-15 21:49:04 -04:00
row [ key ] = value . gsub ( " $LABEL " , label ) if value . is_a? ( String )
2011-05-07 16:20:51 -04:00
end
2007-11-26 17:45:43 -05:00
2011-05-07 16:20:51 -04:00
# generate a primary key if necessary
if has_primary_key_column? && ! row . include? ( primary_key_name )
2013-08-25 05:22:36 -04:00
row [ primary_key_name ] = ActiveRecord :: FixtureSet . identify ( label , primary_key_type )
2011-05-07 16:20:51 -04:00
end
2007-11-20 16:53:22 -05:00
2011-05-07 16:20:51 -04:00
# If STI is used, find the correct subclass for association reflection
reflection_class =
if row . include? ( inheritance_column_name )
row [ inheritance_column_name ] . constantize rescue model_class
else
model_class
2007-10-26 01:56:46 -04:00
end
2011-05-07 16:20:51 -04:00
2014-05-24 14:03:30 -04:00
reflection_class . _reflections . values . each do | association |
2011-05-07 16:20:51 -04:00
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*$ / , " " )
# support polymorphic belongs_to as "label (Type)"
row [ association . foreign_type ] = $1
end
2014-04-10 14:43:51 -04:00
fk_type = association . active_record . columns_hash [ association . foreign_key ] . type
2013-08-25 05:22:36 -04:00
row [ fk_name ] = ActiveRecord :: FixtureSet . identify ( value , fk_type )
2011-05-07 16:20:51 -04:00
end
2013-09-06 14:29:40 -04:00
when :has_many
if association . options [ :through ]
2013-09-06 14:47:02 -04:00
add_join_records ( rows , row , HasManyThroughProxy . new ( association ) )
2013-09-06 14:29:40 -04:00
end
2007-10-26 01:56:46 -04:00
end
end
end
2011-05-07 16:20:51 -04:00
row
end
rows
2007-10-26 01:56:46 -04:00
end
2013-09-06 14:47:02 -04:00
class ReflectionProxy # :nodoc:
def initialize ( association )
@association = association
2011-05-07 16:20:51 -04:00
end
2007-11-26 17:45:43 -05:00
2013-09-06 14:47:02 -04:00
def join_table
@association . join_table
2013-09-06 14:38:28 -04:00
end
2013-09-06 14:47:02 -04:00
def name
@association . name
end
2013-08-25 05:22:36 -04:00
def primary_key_type
@association . klass . column_types [ @association . klass . primary_key ] . type
end
2013-09-06 14:47:02 -04:00
end
2013-09-06 14:36:02 -04:00
2013-09-06 14:47:02 -04:00
class HasManyThroughProxy < ReflectionProxy # :nodoc:
def rhs_key
@association . foreign_key
end
def lhs_key
@association . through_reflection . foreign_key
2013-09-06 14:29:40 -04:00
end
2013-09-06 14:47:02 -04:00
end
private
def primary_key_name
@primary_key_name || = model_class && model_class . primary_key
end
2013-08-25 05:22:36 -04:00
def primary_key_type
@primary_key_type || = model_class && model_class . column_types [ model_class . primary_key ] . type
end
2013-09-06 14:47:02 -04:00
def add_join_records ( rows , row , association )
2013-09-05 17:30:37 -04:00
# This is the case when the join table has no fixtures file
2013-08-01 20:46:44 -04:00
if ( targets = row . delete ( association . name . to_s ) )
2013-08-25 05:22:36 -04:00
table_name = association . join_table
column_type = association . primary_key_type
lhs_key = association . lhs_key
rhs_key = association . rhs_key
2013-09-06 14:47:02 -04:00
targets = targets . is_a? ( Array ) ? targets : targets . split ( / \ s*, \ s* / )
rows [ table_name ] . concat targets . map { | target |
{ lhs_key = > row [ primary_key_name ] ,
2013-08-25 05:22:36 -04:00
rhs_key = > ActiveRecord :: FixtureSet . identify ( target , column_type ) }
2013-09-06 14:47:02 -04:00
}
2013-08-01 20:46:44 -04:00
end
end
2011-05-07 16:20:51 -04:00
def has_primary_key_column?
@has_primary_key_column || = primary_key_name &&
model_class . columns . any? { | c | c . name == primary_key_name }
end
2007-10-26 01:56:46 -04:00
2011-05-07 16:20:51 -04:00
def timestamp_column_names
@timestamp_column_names || =
%w( created_at created_on updated_at updated_on ) & column_names
end
2007-11-26 17:46:11 -05:00
2011-05-07 16:20:51 -04:00
def inheritance_column_name
@inheritance_column_name || = model_class && model_class . inheritance_column
end
2007-10-26 01:56:46 -04:00
2011-05-07 16:20:51 -04:00
def column_names
@column_names || = @connection . columns ( @table_name ) . collect { | c | c . name }
2004-11-23 20:04:44 -05:00
end
2013-09-06 13:35:37 -04:00
def read_fixture_files ( path , model_class )
yaml_files = Dir [ " #{ path } /{**,*}/*.yml " ] . select { | f |
2011-05-10 14:01:23 -04:00
:: File . file? ( f )
2013-09-06 13:35:37 -04:00
} + [ yaml_file_path ( path ) ]
2011-05-10 14:01:23 -04:00
2013-09-06 13:35:37 -04:00
yaml_files . each_with_object ( { } ) do | file , fixtures |
2012-05-12 07:17:03 -04:00
FixtureSet :: File . open ( file ) do | fh |
2012-01-01 12:54:52 -05:00
fh . each do | fixture_name , row |
fixtures [ fixture_name ] = ActiveRecord :: Fixture . new ( row , model_class )
2011-05-07 16:20:51 -04:00
end
2007-09-28 10:56:07 -04:00
end
end
end
2004-12-09 10:52:54 -05:00
2013-09-06 13:35:37 -04:00
def yaml_file_path ( path )
" #{ path } .yml "
2011-05-07 16:20:51 -04:00
end
2004-12-07 05:24:13 -05:00
2011-05-07 16:20:51 -04:00
end
2004-11-23 20:04:44 -05:00
2012-05-12 09:42:54 -04:00
#--
# Deprecate 'Fixtures' in favor of 'FixtureSet'.
#++
2012-10-21 12:49:14 -04:00
# :nodoc:
2012-10-10 18:34:41 -04:00
Fixtures = ActiveSupport :: Deprecation :: DeprecatedConstantProxy . new ( 'ActiveRecord::Fixtures' , 'ActiveRecord::FixtureSet' )
2012-05-12 09:42:54 -04:00
2011-05-07 16:20:51 -04:00
class Fixture #:nodoc:
include Enumerable
2007-09-28 10:56:07 -04:00
2011-05-07 16:20:51 -04:00
class FixtureError < StandardError #:nodoc:
end
2007-09-28 10:56:07 -04:00
2011-05-07 16:20:51 -04:00
class FormatError < FixtureError #:nodoc:
end
2004-11-23 20:04:44 -05:00
2011-05-07 16:20:51 -04:00
attr_reader :model_class , :fixture
2007-05-26 02:26:50 -04:00
2011-05-07 16:20:51 -04:00
def initialize ( fixture , model_class )
@fixture = fixture
@model_class = model_class
end
2008-01-04 21:20:57 -05:00
2011-05-07 16:20:51 -04:00
def class_name
model_class . name if model_class
end
2004-11-23 20:04:44 -05:00
2011-05-07 16:20:51 -04:00
def each
fixture . each { | item | yield item }
end
2004-11-23 20:04:44 -05:00
2011-05-07 16:20:51 -04:00
def [] ( key )
fixture [ key ]
end
2004-11-23 20:04:44 -05:00
2011-05-07 16:20:51 -04:00
alias :to_hash :fixture
2004-12-09 10:52:54 -05:00
2011-05-07 16:20:51 -04:00
def find
if model_class
model_class . find ( fixture [ model_class . primary_key ] )
else
raise FixtureClassNotFound , " No class attached to find. "
end
2005-02-07 09:10:44 -05:00
end
2004-11-23 20:04:44 -05:00
end
end
2008-11-07 15:40:56 -05:00
module ActiveRecord
module TestFixtures
2009-05-28 12:35:36 -04:00
extend ActiveSupport :: Concern
2009-05-11 22:23:47 -04:00
2013-04-03 20:02:09 -04:00
def before_setup
setup_fixtures
super
end
def after_teardown
super
teardown_fixtures
end
2009-05-11 22:23:47 -04:00
2013-04-03 20:02:09 -04:00
included do
class_attribute :fixture_path , :instance_writer = > false
2010-07-28 17:44:21 -04:00
class_attribute :fixture_table_names
class_attribute :fixture_class_names
class_attribute :use_transactional_fixtures
2012-01-01 12:54:52 -05:00
class_attribute :use_instantiated_fixtures # true, false, or :no_instances
2010-07-28 17:44:21 -04:00
class_attribute :pre_loaded_fixtures
2013-08-26 18:24:23 -04:00
class_attribute :config
2009-05-11 22:23:47 -04:00
self . fixture_table_names = [ ]
2010-01-06 13:38:11 -05:00
self . use_transactional_fixtures = true
self . use_instantiated_fixtures = false
2009-05-11 22:23:47 -04:00
self . pre_loaded_fixtures = false
2013-08-26 18:24:23 -04:00
self . config = ActiveRecord :: Base
2007-02-25 12:31:43 -05:00
2012-01-01 12:54:52 -05:00
self . fixture_class_names = Hash . new do | h , fixture_set_name |
2013-08-26 18:24:23 -04:00
h [ fixture_set_name ] = ActiveRecord :: FixtureSet . default_fixture_model_name ( fixture_set_name , self . config )
2011-02-11 17:33:03 -05:00
end
2008-11-07 15:40:56 -05:00
end
module ClassMethods
2011-12-20 15:06:30 -05:00
# Sets the model class for a fixture when the class name cannot be inferred from the fixture name.
#
# Examples:
#
2012-11-10 10:16:21 -05:00
# set_fixture_class some_fixture: SomeModel,
2011-12-20 15:06:30 -05:00
# 'namespaced/fixture' => Another::Model
#
# The keys must be the fixture names, that coincide with the short paths to the fixture files.
2008-11-07 15:40:56 -05:00
def set_fixture_class ( class_names = { } )
2011-12-20 15:06:30 -05:00
self . fixture_class_names = self . fixture_class_names . merge ( class_names . stringify_keys )
2008-11-07 15:40:56 -05:00
end
2004-12-14 13:01:28 -05:00
2012-12-18 15:27:52 -05:00
def fixtures ( * fixture_set_names )
2012-01-01 12:54:52 -05:00
if fixture_set_names . first == :all
2013-08-05 15:35:49 -04:00
fixture_set_names = Dir [ " #{ fixture_path } /{**,*}/*.{yml} " ]
2013-03-21 17:10:44 -04:00
fixture_set_names . map! { | f | f [ ( fixture_path . to_s . size + 1 ) .. - 5 ] }
2008-11-07 15:40:56 -05:00
else
2012-01-01 12:54:52 -05:00
fixture_set_names = fixture_set_names . flatten . map { | n | n . to_s }
2008-01-05 08:34:15 -05:00
end
2012-01-01 12:54:52 -05:00
self . fixture_table_names |= fixture_set_names
2013-08-26 18:24:23 -04:00
require_fixture_classes ( fixture_set_names , self . config )
2012-01-01 12:54:52 -05:00
setup_fixture_accessors ( fixture_set_names )
2008-11-07 15:40:56 -05:00
end
def try_to_load_dependency ( file_name )
require_dependency file_name
rescue LoadError = > e
2013-04-03 20:02:09 -04:00
# Let's hope the developer has included it
2008-11-07 15:40:56 -05:00
# 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 " ) } " )
2008-03-17 17:48:28 -04:00
end
2008-11-07 15:40:56 -05:00
end
2008-05-14 15:09:49 -04:00
2013-08-26 18:24:23 -04:00
def require_fixture_classes ( fixture_set_names = nil , config = ActiveRecord :: Base )
2012-01-01 12:54:52 -05:00
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 |
2013-08-26 18:24:23 -04:00
file_name = file_name . singularize if config . pluralize_table_names
2008-11-07 15:40:56 -05:00
try_to_load_dependency ( file_name )
2004-12-16 12:14:41 -05:00
end
2008-11-07 15:40:56 -05:00
end
2004-12-09 10:52:54 -05:00
2012-01-01 12:54:52 -05:00
def setup_fixture_accessors ( fixture_set_names = nil )
fixture_set_names = Array ( fixture_set_names || fixture_table_names )
2011-02-15 18:52:27 -05:00
methods = Module . new do
2012-01-01 12:54:52 -05:00
fixture_set_names . each do | fs_name |
fs_name = fs_name . to_s
accessor_name = fs_name . tr ( '/' , '_' ) . to_sym
2007-06-04 23:47:02 -04:00
2012-01-01 12:54:52 -05:00
define_method ( accessor_name ) do | * fixture_names |
force_reload = fixture_names . pop if fixture_names . last == true || fixture_names . last == :reload
2007-02-25 14:27:56 -05:00
2012-01-01 12:54:52 -05:00
@fixture_cache [ fs_name ] || = { }
2007-02-25 14:27:56 -05:00
2012-01-01 12:54:52 -05:00
instances = fixture_names . map do | f_name |
f_name = f_name . to_s
@fixture_cache [ fs_name ] . delete ( f_name ) if force_reload
2007-06-04 23:47:02 -04:00
2012-01-01 12:54:52 -05:00
if @loaded_fixtures [ fs_name ] [ f_name ]
@fixture_cache [ fs_name ] [ f_name ] || = @loaded_fixtures [ fs_name ] [ f_name ] . find
2011-02-15 18:52:27 -05:00
else
2012-01-01 12:54:52 -05:00
raise StandardError , " No fixture named ' #{ f_name } ' found for fixture set ' #{ fs_name } ' "
2010-08-28 12:57:50 -04:00
end
2007-06-04 23:47:02 -04:00
end
2005-06-03 07:49:34 -04:00
2011-02-15 18:52:27 -05:00
instances . size == 1 ? instances . first : instances
end
2011-12-29 10:36:00 -05:00
private accessor_name
2008-11-07 15:40:56 -05:00
end
2008-01-05 08:34:15 -05:00
end
2011-02-15 18:52:27 -05:00
include methods
2008-11-07 15:40:56 -05:00
end
2005-06-10 10:58:02 -04:00
2008-11-07 15:40:56 -05:00
def uses_transaction ( * methods )
@uses_transaction = [ ] unless defined? ( @uses_transaction )
2010-08-13 18:59:15 -04:00
@uses_transaction . concat methods . map { | m | m . to_s }
2005-06-10 10:58:02 -04:00
end
2008-11-07 15:40:56 -05:00
def uses_transaction? ( method )
@uses_transaction = [ ] unless defined? ( @uses_transaction )
@uses_transaction . include? ( method . to_s )
2005-06-10 10:58:02 -04:00
end
2008-11-07 15:40:56 -05:00
end
2005-06-10 10:58:02 -04:00
2008-11-26 02:50:57 -05:00
def run_in_transaction?
2008-11-07 15:40:56 -05:00
use_transactional_fixtures &&
! self . class . uses_transaction? ( method_name )
end
2006-06-28 21:13:55 -04:00
2013-08-26 18:24:23 -04:00
def setup_fixtures ( config = ActiveRecord :: Base )
2008-11-07 15:40:56 -05:00
if pre_loaded_fixtures && ! use_transactional_fixtures
raise RuntimeError , 'pre_loaded_fixtures requires use_transactional_fixtures'
end
2005-10-25 14:14:09 -04:00
2008-11-07 15:40:56 -05:00
@fixture_cache = { }
2011-12-10 19:33:08 -05:00
@fixture_connections = [ ]
2008-11-16 11:51:05 -05:00
@@already_loaded_fixtures || = { }
2008-11-07 15:40:56 -05:00
# Load fixtures once and begin transaction.
2008-11-26 02:50:57 -05:00
if run_in_transaction?
2008-11-07 15:40:56 -05:00
if @@already_loaded_fixtures [ self . class ]
@loaded_fixtures = @@already_loaded_fixtures [ self . class ]
2005-03-06 08:51:55 -05:00
else
2013-08-26 18:24:23 -04:00
@loaded_fixtures = load_fixtures ( config )
2008-11-07 15:40:56 -05:00
@@already_loaded_fixtures [ self . class ] = @loaded_fixtures
2005-03-06 08:51:55 -05:00
end
2011-10-05 20:21:43 -04:00
@fixture_connections = enlist_fixture_connections
@fixture_connections . each do | connection |
2012-09-14 14:06:02 -04:00
connection . begin_transaction joinable : false
2011-10-05 20:21:43 -04:00
end
2008-11-07 15:40:56 -05:00
# Load fixtures for every test.
else
2012-05-12 07:17:03 -04:00
ActiveRecord :: FixtureSet . reset_cache
2008-11-07 15:40:56 -05:00
@@already_loaded_fixtures [ self . class ] = nil
2013-08-26 18:24:23 -04:00
@loaded_fixtures = load_fixtures ( config )
2005-03-06 08:51:55 -05:00
end
2008-11-07 15:40:56 -05:00
# Instantiate fixtures for every test if requested.
2013-08-26 18:24:23 -04:00
instantiate_fixtures ( config ) if use_instantiated_fixtures
2008-11-07 15:40:56 -05:00
end
def teardown_fixtures
# Rollback changes if a transaction is active.
2011-10-05 20:21:43 -04:00
if run_in_transaction?
@fixture_connections . each do | connection |
2012-09-14 14:06:02 -04:00
connection . rollback_transaction if connection . transaction_open?
2011-10-05 20:21:43 -04:00
end
@fixture_connections . clear
2012-11-26 21:11:21 -05:00
else
ActiveRecord :: FixtureSet . reset_cache
2004-11-23 20:04:44 -05:00
end
2012-11-26 21:11:21 -05:00
2008-11-07 15:40:56 -05:00
ActiveRecord :: Base . clear_active_connections!
end
2004-12-09 10:52:54 -05:00
2011-10-05 20:21:43 -04:00
def enlist_fixture_connections
2012-11-09 09:36:58 -05:00
ActiveRecord :: Base . connection_handler . connection_pool_list . map ( & :connection )
2011-10-05 20:21:43 -04:00
end
2008-11-07 15:40:56 -05:00
private
2013-08-26 18:24:23 -04:00
def load_fixtures ( config )
fixtures = ActiveRecord :: FixtureSet . create_fixtures ( fixture_path , fixture_table_names , fixture_class_names , config )
2011-02-11 19:22:38 -05:00
Hash [ fixtures . map { | f | [ f . name , f ] } ]
2008-11-07 15:40:56 -05:00
end
2005-04-18 03:52:58 -04:00
2008-11-07 15:40:56 -05:00
# for pre_loaded_fixtures, only require the classes once. huge speed improvement
@@required_fixture_classes = false
2005-04-18 03:52:58 -04:00
2013-08-26 18:24:23 -04:00
def instantiate_fixtures ( config )
2008-11-07 15:40:56 -05:00
if pre_loaded_fixtures
2012-05-12 07:17:03 -04:00
raise RuntimeError , 'Load fixtures before instantiating them.' if ActiveRecord :: FixtureSet . all_loaded_fixtures . empty?
2008-11-07 15:40:56 -05:00
unless @@required_fixture_classes
2013-08-26 18:24:23 -04:00
self . class . require_fixture_classes ActiveRecord :: FixtureSet . all_loaded_fixtures . keys , config
2008-11-07 15:40:56 -05:00
@@required_fixture_classes = true
end
2012-05-12 07:17:03 -04:00
ActiveRecord :: FixtureSet . instantiate_all_loaded_fixtures ( self , load_instances? )
2008-11-07 15:40:56 -05:00
else
raise RuntimeError , 'Load fixtures before instantiating them.' if @loaded_fixtures . nil?
2012-01-01 12:54:52 -05:00
@loaded_fixtures . each_value do | fixture_set |
2012-05-12 07:17:03 -04:00
ActiveRecord :: FixtureSet . instantiate_fixtures ( self , fixture_set , load_instances? )
2005-03-06 08:51:55 -05:00
end
2004-12-16 12:14:41 -05:00
end
2008-11-07 15:40:56 -05:00
end
2005-04-18 03:52:58 -04:00
2008-11-07 15:40:56 -05:00
def load_instances?
use_instantiated_fixtures != :no_instances
end
2004-12-16 12:14:41 -05:00
end
2005-01-01 14:22:16 -05:00
end
2013-11-24 12:52:53 -05:00
class ActiveRecord :: FixtureSet :: RenderContext # :nodoc:
def self . create_subclass
Class . new ActiveRecord :: FixtureSet . context_class do
def get_binding
binding ( )
end
end
end
end