From b64de51d969e685c09a8ca231c115d999931925d Mon Sep 17 00:00:00 2001 From: Denis Korobitcin Date: Mon, 19 Oct 2015 18:40:46 +0500 Subject: [PATCH] feature: added main slaver logic --- .gitignore | 13 ++ .rspec | 2 + Appraisals | 7 + Gemfile | 3 + LICENSE.txt | 21 +++ Makefile | 31 +++++ README.md | 120 ++++++++++++++++- Rakefile | 6 + config.ru | 7 + lib/slaver.rb | 9 ++ lib/slaver/connection.rb | 168 ++++++++++++++++++++++++ lib/slaver/proxy.rb | 45 +++++++ lib/slaver/proxy_methods.rb | 63 +++++++++ lib/slaver/railtie.rb | 9 ++ lib/slaver/version.rb | 3 + slaver.gemspec | 35 +++++ spec/internal/app/models/bar.rb | 3 + spec/internal/app/models/foo.rb | 3 + spec/internal/config/.gitkeep | 0 spec/internal/db/schema.rb | 9 ++ spec/internal/log/.gitignore | 1 + spec/lib/slaver_spec.rb | 219 ++++++++++++++++++++++++++++++++ spec/spec_helper.rb | 37 ++++++ 23 files changed, 813 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 .rspec create mode 100644 Appraisals create mode 100644 Gemfile create mode 100644 LICENSE.txt create mode 100644 Makefile create mode 100644 Rakefile create mode 100644 config.ru create mode 100644 lib/slaver.rb create mode 100644 lib/slaver/connection.rb create mode 100644 lib/slaver/proxy.rb create mode 100644 lib/slaver/proxy_methods.rb create mode 100644 lib/slaver/railtie.rb create mode 100644 lib/slaver/version.rb create mode 100644 slaver.gemspec create mode 100644 spec/internal/app/models/bar.rb create mode 100644 spec/internal/app/models/foo.rb create mode 100644 spec/internal/config/.gitkeep create mode 100644 spec/internal/db/schema.rb create mode 100644 spec/internal/log/.gitignore create mode 100644 spec/lib/slaver_spec.rb create mode 100644 spec/spec_helper.rb diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..703c457 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +/.bundle/ +/.yardoc +/Gemfile.lock +/_yardoc/ +/coverage/ +/doc/ +/pkg/ +/spec/reports/ +/tmp/ +gemfiles/ +spec/internal/config/database.yml +spec/internal/test_other +spec/internal/test diff --git a/.rspec b/.rspec new file mode 100644 index 0000000..8c18f1a --- /dev/null +++ b/.rspec @@ -0,0 +1,2 @@ +--format documentation +--color diff --git a/Appraisals b/Appraisals new file mode 100644 index 0000000..e5c4912 --- /dev/null +++ b/Appraisals @@ -0,0 +1,7 @@ +appraise 'rails3.1' do + gem 'rails', '~> 3.1.0' +end + +appraise 'rails3.2' do + gem 'rails', '~> 3.2.0' +end diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..fa75df1 --- /dev/null +++ b/Gemfile @@ -0,0 +1,3 @@ +source 'https://rubygems.org' + +gemspec diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..d76a30c --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Denis Korobitcin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..110f22d --- /dev/null +++ b/Makefile @@ -0,0 +1,31 @@ +BUNDLE = bundle +BUNDLE_OPTIONS = -j 3 +RSPEC = ${APPRAISAL} rspec +APPRAISAL = ${BUNDLE} exec appraisal + +all: test + +test: configs bundler appraisal + ${RSPEC} 2>&1 + +define DATABASE_YML +test: + adapter: sqlite3 + database: test +test_other: + adapter: sqlite3 + database: test_other +endef +export DATABASE_YML + +configs: + echo "$${DATABASE_YML}" > spec/internal/config/database.yml + +bundler: + if ! gem list bundler -i > /dev/null; then \ + gem install bundler; \ + fi + ${BUNDLE} install ${BUNDLE_OPTIONS} + +appraisal: + ${APPRAISAL} install diff --git a/README.md b/README.md index cd0ec2e..78c04ae 100644 --- a/README.md +++ b/README.md @@ -1 +1,119 @@ -# slaver +[![Dolly](http://dolly.railsc.ru/badges/abak-press/slaver/master)](http://dolly.railsc.ru/projects/129/builds/latest/?ref=master) + +# Slaver + +Welcome to slaver! + +It's a simple gem for rails application within multi-database environment. +It allows you to change your current connection to other database configuration. +Some ideas was inspired by [octopus](https://github.com/tchandy/octopus). + +## WARNING + +It was tested only on `rails 3` and `ruby 1.9.3`. Other configurations may work or may not:) + +## Installation + +Add this line to your application's Gemfile: + +```ruby +gem 'slaver' +``` + +And then execute: + + $ bundle + +Or install it yourself as: + + $ gem install slaver + +## Usage + +### Config +You must have other connection on your database.yml file. For example: + +```yml +production: + adapter: pg + host: master + database: master + post: 11111 + +production_slave: + adapter: sqlite3 + host: slave + database: slave + post: 11113 + +production_mysql: + adapter: mysql + user: me + host: somewhere_else + post: 11112 + database: mysql +``` + +### Chain with AR + +Only works with class/scope methods. Connection changed until query is perfomed. After that it'll swiched back to default connection. + +```ruby + SomeModel.on(:production_mysql).where(name: 'me').first + + + # or, if you name starting with you Rails.env it can be skipped + SomeModel.on(:mysql).where(name: 'me').to_a +``` + +### Execute block on other connection + +Connection will be switched only for required class. + +```ruby + SomeModel.within(:slave) do + SomeModel.where(name: 'me') + end + + # It also can be combined with "on" method + + SomeModel.within(:mysql) do + me = SomeModel.find_by_name('me') + SomeModel.on(:slave).find_by_name('me').update_attributes(me.attributes) + end + + # ACTUNG!! + Somemodel.within(:slave) do + #!!! Will be executed on default connection for OtherModel + OtherModel.where(name: 'me').first + end +``` +### ACTUNG!!!! + +If you connection does not exists, behavior may change dependent of you current Rails environment: + - `Rails.env == production`: It'll raise `ArgumentError` + - otherwise: It'll try to switch to default connection - `Rails.env` + +### Missing features + +1. 'on' with assosiations +2. Transaction safety +3. `on` method on instance + +## Development + +To run test on local machine use `make` command. For more info please reffer to Makefile. + +## Contributing + +1. Fork it ( https://github.com/abak-press/slaver/fork ) +2. Create your feature branch (git checkout -b my-new-feature) +3. Commit your changes (git commit -am 'Add some feature') +4. Push to the branch (git push origin my-new-feature) +5. Create new Pull Request + + + +## License + +The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT). diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..b7e9ed5 --- /dev/null +++ b/Rakefile @@ -0,0 +1,6 @@ +require "bundler/gem_tasks" +require "rspec/core/rake_task" + +RSpec::Core::RakeTask.new(:spec) + +task :default => :spec diff --git a/config.ru b/config.ru new file mode 100644 index 0000000..2c8b242 --- /dev/null +++ b/config.ru @@ -0,0 +1,7 @@ +require 'rubygems' +require 'bundler' + +Bundler.require :default, :development + +Combustion.initialize! :all +run Combustion::Application diff --git a/lib/slaver.rb b/lib/slaver.rb new file mode 100644 index 0000000..b2951fc --- /dev/null +++ b/lib/slaver.rb @@ -0,0 +1,9 @@ +require 'rails' +require 'slaver/version' +require 'slaver/proxy' +require 'slaver/proxy_methods' +require 'slaver/connection' +require 'slaver/railtie' + +module Slaver +end diff --git a/lib/slaver/connection.rb b/lib/slaver/connection.rb new file mode 100644 index 0000000..fe61791 --- /dev/null +++ b/lib/slaver/connection.rb @@ -0,0 +1,168 @@ +module Slaver + module Connection + extend ActiveSupport::Concern + + included do + include ProxyMethods + end + + module ClassMethods + # TODO: Make it work with associations + # Public: Change database connection for next query + # WARNING: It'll change current DB connection until + # insert, select or execute methods call + # + # config_name - String or Symbol, name of config_section + # + # NOTE: + # if config was not found: + # 1) On production + # throws ArgumentError + # 2) Uses default configuration for current environment + # + # Exception safety: + # throws ArgumentError if no configuration was found + # + # Examples + # + # SomeModel.on(:slave).create(...) + # SomeModel.on(:slave).where(...).first + # + # It also can be chained with other 'on' methods + # SomeModel.on(:slave).on(:other).find_by(...) + # + # Returns self + def on(config_name) + @saved_config ||= @current_config + @saved_block ||= @block + @block = false + + @current_config = prepare(config_name) + + initialize_pool(@current_config) unless pools[@current_config] + + self + end + + # Public: Changes model's connection to database temporarily to execute block. + # + # config_name - String or Symbol, name of config_section + # + # NOTE: + # if config was not found: + # 1) On production + # throws ArgumentError + # 2) Uses default configuration for current environment + # + # Exception safety: + # throws ArgumentError if no configuration was found + # + # Examples + # + # SomeModel.within :test_slave do + # # do some computations here + # end + # => will execute given block with different db_connection + # + # It is also possible to nest database connection code + # + # SomeModel.within :slave do + # do some computations here + # SomeModel.within :slave2 do + # # some other computations go here + # end + # end + # => will execute given block with different db_connection + # + # Returns noting + def within(config_name) + config_name = prepare(config_name) + + initialize_pool(config_name) unless pools[config_name] + + with_config(config_name) do + keep_block do + yield + end + end + end + + def clear_config + @block = @saved_block if @saved_block + @saved_block = nil + @current_config = @block && @saved_config + @saved_config = nil + end + + def pools + @pools ||= {} + end + + def within_block? + !!@block + end + + private + + def with_config(config_name) + last_config = @current_config + @current_config = config_name + + begin + yield + ensure + @current_config = last_config + end + end + + def keep_block + last_block = @block + @block = true + + begin + yield + ensure + @block = last_block + end + end + + def initialize_pool(config_name) + if config_name == default_config + pools[config_name] = connection_pool_without_proxy + else + pools[config_name] = create_connection_pool(config_name) + end + end + + def prepare(config_name) + config_name = config_name.to_s + + return config_name if ::ActiveRecord::Base.configurations[config_name].present? + + config_name = "#{Rails.env}_#{config_name}" + + if (::ActiveRecord::Base.configurations[config_name]).blank? + if Rails.env.production? + raise ArgumentError, "Can't find #{config_name} on database configurations" + else + config_name = default_config + end + end + + config_name + end + + def default_config + Rails.env + end + + def create_connection_pool(config_name) + config = ::ActiveRecord::Base.configurations[config_name] + config.symbolize_keys! + arg = ActiveRecord::Base::ConnectionSpecification.new(config, "#{config[:adapter]}_connection") + + ActiveRecord::ConnectionAdapters::ConnectionPool.new(arg) + end + end + end +end diff --git a/lib/slaver/proxy.rb b/lib/slaver/proxy.rb new file mode 100644 index 0000000..b2e40af --- /dev/null +++ b/lib/slaver/proxy.rb @@ -0,0 +1,45 @@ +module Slaver + class Proxy + attr_accessor :klass, :config + attr_reader :connection_pool + + def get_connection(klass, config) + @klass = klass + @connection_pool = klass.pools[config] + @config = config + + self + end + + def connected? + connection_pool.connected? + end + + def clear_all_connections! + connection_pool.disconnect! + end + + def clear_active_connections! + connection_pool.release_connection + end + + def safe_connection + connection_pool.automatic_reconnect = true + if !connection_pool.connected? && klass.connection_without_proxy.query_cache_enabled + connection_pool.connection.enable_query_cache! + end + connection_pool.connection + end + + def method_missing(method, *args, &block) + klass.clear_config if should_clean?(method) + safe_connection.send(method, *args, &block) + end + + private + + def should_clean?(method) + method.to_s =~ /insert|select|execute/ && !klass.within_block? + end + end +end diff --git a/lib/slaver/proxy_methods.rb b/lib/slaver/proxy_methods.rb new file mode 100644 index 0000000..a109c49 --- /dev/null +++ b/lib/slaver/proxy_methods.rb @@ -0,0 +1,63 @@ +module Slaver + module ProxyMethods + extend ActiveSupport::Concern + + included do + class << self + alias_method_chain :connection, :proxy + alias_method_chain :connection_pool, :proxy + alias_method_chain :clear_all_connections!, :proxy + alias_method_chain :clear_active_connections!, :proxy + alias_method_chain :connected?, :proxy + end + end + + module ClassMethods + def connection_pool_with_proxy + if @current_config + connection_proxy.get_connection(self, @current_config).connection_pool + else + connection_pool_without_proxy + end + end + + def connection_with_proxy + if @current_config + connection_proxy.get_connection(self, @current_config) + else + (connection_pool && connection_pool.connection) + end + end + + def clear_active_connections_with_proxy! + if @current_config + connection_proxy.get_connection(self, @current_config).clear_active_connections! + else + clear_active_connections_without_proxy! + end + end + + def clear_all_connections_with_proxy! + if @current_config + connection_proxy.get_connection(self, @current_config).clear_all_connections! + else + clear_all_connections_without_proxy! + end + end + + def connected_with_proxy? + if @current_config + connection_proxy.get_connection(self, @current_config).connected? + else + connected_without_proxy? + end + end + + def connection_proxy + ::ActiveRecord::Base.class_variable_defined?(:@@connection_proxy) && + ::ActiveRecord::Base.class_variable_get(:@@connection_proxy) || + ::ActiveRecord::Base.class_variable_set(:@@connection_proxy, Proxy.new) + end + end + end +end diff --git a/lib/slaver/railtie.rb b/lib/slaver/railtie.rb new file mode 100644 index 0000000..ca21ed6 --- /dev/null +++ b/lib/slaver/railtie.rb @@ -0,0 +1,9 @@ +module Slaver + class Railtie < Rails::Railtie + initializer "slaver.hack_ar" do |app| + ActiveSupport.on_load(:active_record) do + include Connection + end + end + end +end diff --git a/lib/slaver/version.rb b/lib/slaver/version.rb new file mode 100644 index 0000000..616d72b --- /dev/null +++ b/lib/slaver/version.rb @@ -0,0 +1,3 @@ +module Slaver + VERSION = '0.0.1' +end diff --git a/slaver.gemspec b/slaver.gemspec new file mode 100644 index 0000000..90bd6ee --- /dev/null +++ b/slaver.gemspec @@ -0,0 +1,35 @@ +# coding: utf-8 +lib = File.expand_path('../lib', __FILE__) +$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) +require 'slaver/version' + +Gem::Specification.new do |spec| + spec.name = "slaver" + spec.version = Slaver::VERSION + spec.authors = ["Denis Korobitcin"] + spec.email = ["deniskorobitcin@gmail.com"] + + spec.summary = %q{Instant change of connection in rails application.} + spec.homepage = "https://github.com/abak-press/slaver" + spec.license = "MIT" + + spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } + spec.bindir = "exe" + spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } + spec.require_paths = ["lib"] + + spec.add_runtime_dependency 'activerecord', '~> 3.1' + spec.add_runtime_dependency 'activesupport', '~> 3.1' + + spec.add_development_dependency 'bundler' + spec.add_development_dependency 'rake' + spec.add_development_dependency 'rspec', '~> 3.3' + spec.add_development_dependency 'rspec-rails' + spec.add_development_dependency 'combustion', '~> 0.5' + spec.add_development_dependency 'appraisal' + spec.add_development_dependency 'pry-debugger' + spec.add_development_dependency 'shoulda-matchers', '< 3.0.0' + spec.add_development_dependency 'simplecov' + spec.add_development_dependency 'database_cleaner' + spec.add_development_dependency 'sqlite3' +end diff --git a/spec/internal/app/models/bar.rb b/spec/internal/app/models/bar.rb new file mode 100644 index 0000000..544e2eb --- /dev/null +++ b/spec/internal/app/models/bar.rb @@ -0,0 +1,3 @@ +class Bar < ActiveRecord::Base + belongs_to :foo +end diff --git a/spec/internal/app/models/foo.rb b/spec/internal/app/models/foo.rb new file mode 100644 index 0000000..c082364 --- /dev/null +++ b/spec/internal/app/models/foo.rb @@ -0,0 +1,3 @@ +class Foo < ActiveRecord::Base + has_many :bars +end diff --git a/spec/internal/config/.gitkeep b/spec/internal/config/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/spec/internal/db/schema.rb b/spec/internal/db/schema.rb new file mode 100644 index 0000000..5c2a5ba --- /dev/null +++ b/spec/internal/db/schema.rb @@ -0,0 +1,9 @@ +ActiveRecord::Schema.define do + create_table :foos do |t| + t.string :name + end + + create_table :bars do |t| + t.integer :foo_id + end +end diff --git a/spec/internal/log/.gitignore b/spec/internal/log/.gitignore new file mode 100644 index 0000000..bf0824e --- /dev/null +++ b/spec/internal/log/.gitignore @@ -0,0 +1 @@ +*.log \ No newline at end of file diff --git a/spec/lib/slaver_spec.rb b/spec/lib/slaver_spec.rb new file mode 100644 index 0000000..b7e6035 --- /dev/null +++ b/spec/lib/slaver_spec.rb @@ -0,0 +1,219 @@ +require 'spec_helper' + +describe Slaver do + describe '#on' do + context 'valid usage' do + it 'creates record in other db' do + Foo.on(:other).create(name: 'test') + + expect(Foo.count).to eq 0 + + expect(Foo.on(:test_other).count).to eq 1 + end + + context 'with method chain' do + it 'works with where' do + Foo.on(:other).create(name: 'test') + + expect(Foo.where(name: 'test').on(:other).first).to be + expect(Foo.where(name: 'test').first).not_to be + end + + it 'works with joins' do + foo = Foo.on(:other).create(name: 'test') + Bar.on(:other).create(foo: foo) + + expect(Bar.on(:other).joins(:foo).where(foos: {name: 'test'}).first).to be + end + + it 'works with raw queries' do + Foo.on(:other).create(name: 'test') + + result_other = + ActiveRecord::Base.on(:other).connection.select_all("SELECT * FROM foos WHERE name = 'test'").first + result = + ActiveRecord::Base.connection.select_all("SELECT * FROM foos WHERE name = 'test'").first + + expect(result_other).to be + expect(result).not_to be + end + end + + it 'allow using one connection for miltiple querries' do + Foo.on(:other).create(name: 'test') + + connection = ActiveRecord::Base.on(:other).connection + + expect(connection.select_all('SELECT * FROM foos').first).to be + expect(connection.select_all('SELECT * FROM foos').first).to be + end + + it 'can be chained multiple times' do + foo_model = Foo.on(:other) + foo_model.on(:test) + foo_model.create + + expect(Foo.count).to eq 1 + end + + it 'uses default connection on non production environment' do + Foo.on(:not_existing).create(name: 'test') + + expect(Foo.count).to eq 1 + end + end + + context 'invalid usage' do + it 'raises error if unexisting configuration provided on production env' do + Rails.env = 'production' + expect { Foo.within(:not_existing) {} }.to raise_error ArgumentError + Rails.env = 'test' + end + end + end + + describe '#within' do + context 'valid usage' do + it 'yeilds to other connection' do + Foo.within(:other) do + Foo.create + end + + expect(Foo.count).to eq 0 + + Foo.within(:test_other) do + expect(Foo.count).to eq 1 + end + end + + it 'yeilds only for one model' do + Foo.within(:other) do + Bar.create + end + + expect(Bar.on(:other).count).to eq 0 + expect(Bar.count).to eq 1 + end + + it 'works with multiple queries' do + Foo.on(:other).create(name: 'test') + Foo.within(:other) do + test = Foo.find_by_name('test') + test.update_attributes(name: 'test2') + end + + expect(Foo.on(:other).find_by_name('test2')).to be + end + + context 'with "on" method' do + it 'works properly with simple usage' do + Foo.within(:other) do + Foo.create(name: 'test') + Foo.on(:test).create(name: 'test2') + expect(Foo.find_by_name('test')).to be + end + + expect(Foo.find_by_name('test2')).to be + end + + it 'works with combination of on' do + Foo.within(:other) do + Foo.create(name: 'test') + Foo.on(:test).on(:other).on(:test).create(name: 'test2') + expect(Foo.find_by_name('test')).to be + expect(Foo.find_by_name('test2')).not_to be + end + + expect(Foo.find_by_name('test2')).to be + end + + it 'works with nesting' do + Foo.within(:other) do + Foo.create(name: 'test') + Foo.within(:test) do + Foo.on(:other).create(name: 'test2') + end + expect(Foo.find_by_name('test')).to be + expect(Foo.find_by_name('test2')).to be + end + + expect(Foo.find_by_name('test2')).not_to be + end + end + + it 'uses default connection on non production environment' do + Foo.within(:not_existing) do + Foo.create + end + + expect(Foo.count).to eq 1 + end + + it 'changes only one db' do + Foo.within(:test_other) do + Foo.create + expect(Foo.count).to eq 1 + end + + expect(Foo.count).to eq 0 + + Foo.within(:other) do + expect(Foo.count).to eq 1 + end + end + + it 'works with associations' do + Foo.within(:other) do + foo = Foo.create + bar = Bar.create(foo: foo) + + expect(bar.foo).to be + end + end + + context 'nesting' do + it 'yeilds to other connection twice' do + Foo.within(:test_other) do + Foo.create + + Foo.within(:test_other) do + Foo.create + end + + expect(Foo.count).to eq 2 + end + end + + it 'changes second level db' do + Foo.within(:other) do + Foo.create + + Foo.within(:test) do + Foo.create + end + + expect(Foo.count).to eq 1 + end + + expect(Foo.count).to eq 1 + end + end + end + + context 'invalid usage' do + it 'raises error if unexisting configuration provided on production env' do + Rails.env = 'production' + expect { Foo.within(:not_existing) {} }.to raise_error ArgumentError + Rails.env = 'test' + end + + it 'does not polute connections if error occured' do + Foo.within(:other) do + Foo.create + Foo.within(:test) { fail } rescue nil + expect(Foo.count).to eq 1 + end + end + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..ad1980f --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,37 @@ +$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) +require 'bundler/setup' +require 'simplecov' +SimpleCov.start do + minimum_coverage 90 +end + +require 'slaver' + +require 'combustion' +Combustion.initialize! :all +require 'rspec/rails' + +require 'shoulda-matchers' +require 'database_cleaner' + +abcs = YAML.load(ERB.new(File.read("#{Rails.root}/config/database.yml")).result) +Combustion::Database.drop_database(abcs['test_other']) +Combustion::Database.create_database(abcs['test_other']) +Combustion::Database.load_schema + +ActiveRecord::Base.clear_active_connections! +ActiveRecord::Base.establish_connection(abcs['test']) + +RSpec.configure do |config| + config.use_transactional_fixtures = false + + DatabaseCleaner.strategy = :truncation + + config.after(:each) do + ActiveRecord::Base.establish_connection :test_other + DatabaseCleaner.clean + + ActiveRecord::Base.establish_connection :test + DatabaseCleaner.clean + end +end