cattr_accessor :classes_cache
#class cache for storing already founded classes from models
@@classes_cache = {}
def load_user_fixtures(*table_names)
fixtures = {}
table_names = table_names.flatten.collect{|t| t.to_s}
table_names.each do |table_name|
unless @@classes_cache[table_name].nil?
klas = @@classes_cache[table_name]
else
begin
#try to find class name from table name
klas = eval(table_name.classify)
rescue
#go to model directory, run through all models and search for table name
classes = Dir.entries(RAILS_ROOT + "/app/models").select{|d| d.include?(".rb")}.collect{|f| File.basename(f, ".rb").classify}
klas_names = classes.select{|f| (eval("#{f}.table_name") rescue false)==table_name }
klas_name = klas_names.blank? ? table_name.classify : klas_names.first
klas = eval(klas_name)
end
@@classes_cache[table_name] = klas
end
#load fixture
fixtures[table_name] = Fixtures.create_fixtures(File.dirname(__FILE__) + "/fixtures", table_name,
{table_name.to_sym => klas.name}){klas.connection}
end
#run through all fixtures and instantiate them
fixtures.each_pair do |table_name, fixs|
Fixtures.instantiate_fixtures(self, table_name, fixs)
end
end
self.use_transactional_fixtures = false
self.use_instantiated_fixtures = false
To load your fixtures in tests use implemented helper method in your unit or functional tests like this(notice, that load_user_fixtures is instance method, but regular fixtures method is class method)
require File.dirname(__FILE__) + '/../test_helper'
class EmployeeTest < Test::Unit::TestCase
def setup
load_user_fixtures :employees, :users
end
end
AR has a small bug when asking for table_name to class, which is inherited from abstract class. So we have to set table names even if they are the same as model name.
First set abstract class as usual to define connection.
class AnotherDatabaseConnector < ActiveRecord::Base
self.abstract_class = true
establish_connection "another_database_#{RAILS_ENV}"
# all subclasses use connection from here
end
class User < AnotherDatabaseConnector
set_table_name "users" # we need to set this explicitly
end
If you have any problems or suggestions, you can discuss email me robert.cigan@skvely.cz, or chat directly using ICQ 82645774
- rimmer
legacy_test:
adapter: sqlserver
host: (local)
database: legacy_test
username: user
password: password
class LegacyBase < ActiveRecord::Base
self.abstract_class = true
establish_connection("legacy_#{RAILS_ENV}")
end
All model classes that access the legacy database extend the legacy base class.
class LegacyWeb < LegacyBase
end
Note If you use ruby script/create model some model make sure you edit the generated class and replace the generated parent class (MyClass < ActiveRecord::Base) with the LegacyBase class as shown above.
class WebTest < Test::Unit::TestCase
# fixtures :webs, :lists # Can't use this when using multiple databases
def setup
ActiveRecord::Base.connection = LegacyBase.connection
Fixtures.create_fixtures(File.dirname(__FILE__) + "/../fixtures", "webs"){LegacyBase.connection}
@web = Web.find("abc")
end
# Replace this with your real tests.
def test_truth
assert_kind_of Web, @web
end
end
Note I found that the ActiveRecord::Base.connection attribute had to be set otherwise when the test fixtures are inserted during the unit test runs, the column names in the SQL were being formatted with the default test database adapter’s formatting. In my case the mysql_adapter was adding ”’” around each column name in the insert statement causing an invalid sql error when trying to insert into the legacy MS SQLServer database.
def setup
ActiveRecord::Base.establish_connection("#{RAILS_ENV}")
Fixtures.create_fixtures(File.dirname(__FILE__) + "/../fixtures", "scrum_foos")
end
Note The connection does not need to be specified for the create_fixtures call
Thanks to everyone who contributed to the multiple database topics on the wiki HowtoUseMultipleDatabases, couldn’t have figured this out without all of the good information.
- JSzod
I followed the previous solutions, but couldn’t figure out how to clone the structure from “legacy_development” to “legacy_test”.
I came up with a solution to use your legacy databases with your main test database. It has the benefit that fixtures will work as usual, but will break if you have tables with the same name in different lagacy databases. You need legacy.rake and base.rb below. Use at your own risk! I’m a newbie just hacking away. :)
Run with: rake db:test:prepare:legacy
# lib/tasks/legacy.rake
#
# - prepare all legacy databases with rake db:test:prepare:legacy
# - db:test:*:legacy_db to run any db:test: task on database legacy_db
#
# list your legacy dbs here:
legacy_dbs = %w(ciministry)
def prepend(prefix, array)
array.collect { |item| prefix + item }
end
namespace :db do
namespace :test do
namespace :prepare do
desc "like db:test:prepare plus copies all legacy database structure to the test database"
task :legacy => "db:test:prepare" do
prepend("db:test:clone_structure:", legacy_dbs).each do |task|
Rake::Task[task].invoke
end
end
end
commands = %w(clone clone_structure)
commands.each do |command|
legacy_dbs.each do |legacy_db|
namespace command do
desc "like db:test:#{command} but for #{legacy_db}"
task legacy_db do
config_path = "config/environments"
# db:test:clone will complain without this environments file
File.copy("#{config_path}/#{RAILS_ENV}.rb", "#{config_path}/#{legacy_db}.rb")
# fake the environment to get the legacy database's structure in the test db
backup_env = RAILS_ENV;
RAILS_ENV = legacy_db
Rake::Task["db:test:#{command}"].invoke
# restore
File.delete("#{config_path}/#{legacy_db}.rb")
RAILS_ENV = backup_env;
end
end
end
end
end
end
Use this as your abstract base instead of the one presented above
class LegacyBase < ActiveRecord::Base
self.abstract_class = true
if RAILS_ENV == "test"
establish_connection("test")
else
establish_connection("legacydb")
end
end