jakewendt-use_db 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc ADDED
@@ -0,0 +1,101 @@
1
+ = UseDb
2
+ by David Stevenson
3
+ ds@elctech.com
4
+
5
+ == USAGE
6
+
7
+ This plugin allows you to use multiple databases in your rails application.
8
+ You can switch the database for a model in the following manner:
9
+
10
+ class MyModel < ActiveRecord::Base
11
+ use_db :prefix => "secdb_", :suffix => "_cool"
12
+ end
13
+
14
+ "use_db" takes a prefix and a suffix (only 1 of which is required) which are prepended and appended onto the current RAILS_ENV.
15
+ In the above example, I would have to make the following database entries to my database.yml:
16
+
17
+ secdb_development_cool:
18
+ adapter: mysql
19
+ database: secdb_dev_db
20
+ ...
21
+
22
+ secdb_test_cool:
23
+ adapater: mysql
24
+ database: secdb_test_db
25
+ ...
26
+
27
+ It's often useful to create a single abstract model which all models using a different database extend from:
28
+
29
+ class SecdbBase < ActiveRecord::Base
30
+ use_db :prefix => "secdb_"
31
+ self.abstract_class = true
32
+ end
33
+
34
+ class MyModel < SecdbBase
35
+ # this model will use a different database automatically now
36
+ end
37
+
38
+ == MIGRATIONS
39
+
40
+ To write a migration which executes on a different database, add the following method to your
41
+ migration:
42
+
43
+ class MyMigration < ActiveRecord::Migration
44
+ def self.database_model
45
+ return "SecdbBase"
46
+ end
47
+
48
+ def self.up
49
+ ...
50
+ end
51
+
52
+ ...
53
+ end
54
+
55
+ The "self.database_model" call must return a string which is the name of the model whose connection
56
+ you want to borrow when performing the migration. If this method is undefined, the default ActiveRecord::Base
57
+ connection is used.
58
+
59
+ == TESTING
60
+
61
+ In order to test multiple databases, you must invoke a task which clones the development database
62
+ structure and copies it into the test database, clearing out the existing test data. There is a single
63
+ helper method which executes this task and you invoke it as follows:
64
+
65
+ UseDbTest.prepare_test_db(:prefix => "secdb_")
66
+
67
+ Even though it might not be the best place for it, I often place a call to this in my test helper.
68
+ You don't want it to execute for every test, so put the following guards around it:
69
+
70
+ unless defined?(CLONED_SEC_DB_FOR_TEST)
71
+ UseDbTest.prepare_test_db(:prefix => "secdb_")
72
+ CLONED_SEC_DB_FOR_TEST = true
73
+ end
74
+
75
+ == FIXTURES
76
+
77
+ Fixtures will automatically be loaded into the correct database as long as the fixture name corresponds
78
+ to the name of a model. For example, if I have a model called SecdbUser who uses a different database and
79
+ I create a fixture file called secdb_users.yml, the fixture loader will use whatever database connection
80
+ belongs to the SecdbUser model.
81
+
82
+ There is currently no other way to force a fixture to use a specific database (sorry, no join tables yet),
83
+ like there is for migrations.
84
+
85
+
86
+
87
+
88
+ == Gemified with Jeweler
89
+
90
+ vi Rakefile
91
+ rake version:write
92
+
93
+ rake version:bump:patch
94
+ rake version:bump:minor
95
+ rake version:bump:major
96
+
97
+ rake gemspec
98
+
99
+ rake install
100
+ rake release
101
+
@@ -0,0 +1 @@
1
+ require 'use_db'
@@ -0,0 +1,39 @@
1
+ #namespace :db do
2
+ # namespace :structure do
3
+ # task :dump_use_db do
4
+ # UseDbTest.other_databases.each do |options|
5
+ # puts "DUMPING TEST DB: #{options.inspect}" if UseDbPlugin.debug_print
6
+ # UseDbTest.dump_db_structure(options)
7
+ # end
8
+ # end
9
+ # end
10
+ #
11
+ # namespace :test do
12
+ # task :clone_structure => "db:test:clone_structure_use_db"
13
+ #
14
+ # task :clone_structure_use_db => ["db:structure:dump_use_db","db:test:purge_use_db"] do
15
+ # UseDbTest.other_databases.each do |options|
16
+ # puts "CLONING TEST DB: #{options.inspect}" if UseDbPlugin.debug_print
17
+ # UseDbTest.clone_db_structure(options)
18
+ # end
19
+ # end
20
+ #
21
+ # task :purge_use_db => "db:test:purge" do
22
+ # UseDbTest.other_databases.each do |options|
23
+ # puts "PURGING TEST DB: #{options.inspect}" if UseDbPlugin.debug_print
24
+ # UseDbTest.purge_db(options)
25
+ # end
26
+ # end
27
+ # end
28
+ #end
29
+
30
+ require 'use_db'
31
+ # without this, the extra databases don't load
32
+ # I'd like to find another way though
33
+ Rake::Task[:rails_env].prerequisites.unshift(:environment )
34
+
35
+ #namespace :test do
36
+ # task :units => "db:test:clone_structure_use_db"
37
+ # task :functionals => "db:test:clone_structure_use_db"
38
+ # task :integrations => "db:test:clone_structure_use_db"
39
+ #end
data/lib/use_db.rb ADDED
@@ -0,0 +1,13 @@
1
+ module UseDb
2
+ # predefine namespace
3
+ end
4
+ require 'active_record'
5
+ require 'active_record/fixtures'
6
+ require 'active_record/migration'
7
+ require 'use_db/configurations'
8
+ require 'use_db/use_db_plugin'
9
+ require 'use_db/use_db_test'
10
+ require 'use_db/test_model'
11
+ require 'use_db/override_fixtures'
12
+ require 'use_db/override_test_case'
13
+ require 'use_db/migration'
@@ -0,0 +1,35 @@
1
+ #
2
+ # include the other (potential) database.yml files
3
+ #
4
+ module UseDb::Configurations
5
+
6
+ def self.included(base)
7
+ unless base.respond_to?(:configurations_with_other_dbs)
8
+ base.extend(ClassMethods)
9
+ base.class_eval do
10
+ class << self
11
+ alias_method_chain :configurations, :other_dbs
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+ module ClassMethods
18
+
19
+ def configurations_with_other_dbs
20
+ # puts "In configurations with other dbs"
21
+ if configurations_without_other_dbs.empty?
22
+ # for rake tasks
23
+ configurations_without_other_dbs.update(YAML::load(ERB.new(IO.read(
24
+ File.join( Rails.root,'config','database.yml'))).result))
25
+ end
26
+ OTHER_DB_FILES.each do |f|
27
+ configurations_without_other_dbs.update(YAML::load(ERB.new(IO.read(f)).result))
28
+ end if defined?(OTHER_DB_FILES)
29
+ configurations_without_other_dbs
30
+ end
31
+
32
+ end
33
+
34
+ end
35
+ ActiveRecord::Base.send(:include,UseDb::Configurations)
@@ -0,0 +1,89 @@
1
+ module UseDb::Migration
2
+
3
+ def self.included(base)
4
+ unless base.respond_to?(:method_missing_without_connection_swap)
5
+ base.extend(ClassMethods)
6
+ base.class_eval do
7
+ class << self
8
+ alias_method_chain :method_missing, :connection_swap
9
+ end
10
+ end
11
+ end
12
+ end
13
+
14
+ module ClassMethods
15
+
16
+ def method_missing_with_connection_swap(method, *arguments, &block)
17
+ say_with_time "#{method}(#{arguments.map { |a| a.inspect }.join(", ")})" do
18
+ arguments[0] = ActiveRecord::Migrator.proper_table_name(arguments.first
19
+ ) unless arguments.empty? || method == :execute
20
+ if (self.respond_to?(:database_model))
21
+ write "Using custom database model's connection (#{self.database_model}) for this migration"
22
+ eval("#{self.database_model}.connection.send(method, *arguments, &block)")
23
+ else
24
+ ActiveRecord::Base.connection.send(method, *arguments, &block)
25
+ # method_missing_without_connection_swap(method, *arguments, &block)
26
+ end
27
+ end
28
+ end
29
+
30
+ def uses_db?
31
+ true
32
+ end
33
+
34
+ end
35
+
36
+ end
37
+ ActiveRecord::Migration.send(:include,UseDb::Migration)
38
+
39
+ module UseDb::Migrator
40
+
41
+ def self.included(base)
42
+ unless base.respond_to?(:get_all_versions_without_connection_swap)
43
+ base.extend(ClassMethods)
44
+ base.alias_method_chain( :record_version_state_after_migrating, :connection_swap
45
+ ) unless base.methods.include?(:record_version_state_after_migrating_without_connection_swap)
46
+ base.class_eval do
47
+ class << self
48
+ alias_method_chain :get_all_versions, :connection_swap
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ module ClassMethods
55
+
56
+ def get_all_versions_with_connection_swap
57
+ # puts "in use_db get_all_versions"
58
+ # Base.connection.select_values("SELECT version FROM #{schema_migrations_table_name}").map(&:to_i).sort
59
+ UseDbPlugin.all_use_dbs.collect(&:connection).collect{|c|
60
+ c.initialize_schema_migrations_table # in case it doesn't exist
61
+ c.select_values("SELECT version FROM #{schema_migrations_table_name}").map(&:to_i)
62
+ }.flatten.uniq.sort
63
+ end
64
+
65
+ end
66
+
67
+ def record_version_state_after_migrating_with_connection_swap(version)
68
+ just_migrated = migrations.detect { |m| m.version == version }
69
+ load(just_migrated.filename)
70
+ migration_model = just_migrated.name.constantize
71
+ if migration_model.respond_to?(:database_model)
72
+ ar_model = migration_model.database_model.constantize
73
+ ar_model.connection.initialize_schema_migrations_table
74
+ sm_table = self.class.schema_migrations_table_name
75
+ @migrated_versions ||= []
76
+ if down?
77
+ @migrated_versions.delete(version.to_i)
78
+ ar_model.connection.update("DELETE FROM #{sm_table} WHERE version = '#{version}'")
79
+ else
80
+ @migrated_versions.push(version.to_i).sort!
81
+ ar_model.connection.insert("INSERT INTO #{sm_table} (version) VALUES ('#{version}')")
82
+ end
83
+ else
84
+ record_version_state_after_migrating_without_connection_swap(version)
85
+ end
86
+ end
87
+
88
+ end
89
+ ActiveRecord::Migrator.send(:include,UseDb::Migrator)
@@ -0,0 +1,18 @@
1
+ #
2
+ # Put the fixtures in the correct database
3
+ #
4
+ module UseDb::Fixtures
5
+
6
+ def self.included(base)
7
+ unless base.respond_to?(:read_fixture_files_without_connection_set)
8
+ base.alias_method_chain :read_fixture_files, :connection_set
9
+ end
10
+ end
11
+
12
+ def read_fixture_files_with_connection_set
13
+ @connection = (model_class||ActiveRecord::Base).connection
14
+ read_fixture_files_without_connection_set
15
+ end
16
+
17
+ end
18
+ Fixtures.send(:include,UseDb::Fixtures)
@@ -0,0 +1,63 @@
1
+ #
2
+ # setup and teardown fixtures in all databases
3
+ #
4
+ module UseDb::TestFixtures
5
+
6
+ def self.included(base)
7
+ base.alias_method_chain :setup_fixtures, :use_db
8
+ base.alias_method_chain :teardown_fixtures, :use_db
9
+ end
10
+
11
+ def setup_fixtures_with_use_db
12
+ UseDbPlugin.all_use_dbs.collect do |klass|
13
+ return unless defined?(ActiveRecord) && !ActiveRecord::Base.configurations.blank?
14
+
15
+ if pre_loaded_fixtures && !use_transactional_fixtures
16
+ raise RuntimeError, 'pre_loaded_fixtures requires use_transactional_fixtures'
17
+ end
18
+
19
+ @fixture_cache = {}
20
+ @@already_loaded_fixtures ||= {}
21
+
22
+ # Load fixtures once and begin transaction.
23
+ if run_in_transaction?
24
+ if @@already_loaded_fixtures[self.class]
25
+ @loaded_fixtures = @@already_loaded_fixtures[self.class]
26
+ else
27
+ load_fixtures
28
+ @@already_loaded_fixtures[self.class] = @loaded_fixtures
29
+ end
30
+ klass.connection.increment_open_transactions
31
+ klass.connection.transaction_joinable = false
32
+ klass.connection.begin_db_transaction
33
+ # Load fixtures for every test.
34
+ else
35
+ Fixtures.reset_cache
36
+ @@already_loaded_fixtures[self.class] = nil
37
+ load_fixtures
38
+ end
39
+
40
+ # Instantiate fixtures for every test if requested.
41
+ instantiate_fixtures if use_instantiated_fixtures
42
+ end
43
+ end
44
+
45
+ def teardown_fixtures_with_use_db
46
+ UseDbPlugin.all_use_dbs.collect do |klass|
47
+ return unless defined?(ActiveRecord) && !ActiveRecord::Base.configurations.blank?
48
+
49
+ unless run_in_transaction?
50
+ Fixtures.reset_cache
51
+ end
52
+
53
+ # Rollback changes if a transaction is active.
54
+ if run_in_transaction? && klass.connection.open_transactions != 0
55
+ klass.connection.rollback_db_transaction
56
+ klass.connection.decrement_open_transactions
57
+ end
58
+ klass.clear_active_connections!
59
+ end
60
+ end
61
+
62
+ end
63
+ ActiveRecord::TestFixtures.send(:include,UseDb::TestFixtures)
@@ -0,0 +1,2 @@
1
+ require 'use_db'
2
+ Dir["#{File.dirname(__FILE__)}/../tasks/**/*.rake"].sort.each { |ext| load ext }
@@ -0,0 +1,11 @@
1
+ def create_test_model(model_name, prefix="", suffix="", rails_env=RAILS_ENV)
2
+ # puts "Creating test model '#{model_name}', :prefix => '#{prefix}', :suffix => '#{suffix}'"
3
+ str = <<-EOF
4
+ require "use_db"
5
+
6
+ class #{model_name} < ActiveRecord::Base
7
+ use_db :prefix => "#{prefix}", :suffix => "#{suffix}", :rails_env => "#{rails_env}"
8
+ end
9
+ EOF
10
+ eval(str)
11
+ end
@@ -0,0 +1,63 @@
1
+ module UseDbPlugin
2
+ # options can have one or the other of the following options:
3
+ # :prefix - Specify the prefix to append to the RAILS_ENV when finding the adapter secification in database.yml
4
+ # :suffix - Just like :prefix, only contactentated
5
+ # OR
6
+ # :adapter
7
+ # :host
8
+ # :username
9
+ # :password
10
+ # ... etc ... same as the options in establish_connection
11
+ #
12
+ # Set the following to true in your test environment
13
+ # to enable extended debugging printing during testing ...
14
+ # UseDbPlugin.debug_print = true
15
+ #
16
+
17
+ @@use_dbs = [ActiveRecord::Base]
18
+ @@debug_print = false
19
+
20
+ def use_db(options)
21
+ options_dup = options.dup
22
+ conn_spec = get_use_db_conn_spec(options)
23
+ puts "Establishing connecting on behalf of #{self.to_s} to #{conn_spec.inspect}" if UseDbPlugin.debug_print
24
+ establish_connection(conn_spec)
25
+ extend ClassMixin
26
+ @@use_dbs << self unless @@use_dbs.include?(self) || self.to_s.starts_with?("TestModel")
27
+ end
28
+
29
+ def self.all_use_dbs
30
+ return @@use_dbs
31
+ end
32
+
33
+ def self.debug_print
34
+ return @@debug_print
35
+ end
36
+
37
+ def self.debug_print=(newval)
38
+ @@debug_print = newval
39
+ end
40
+
41
+ module ClassMixin
42
+ def uses_db?
43
+ true
44
+ end
45
+ end
46
+
47
+ def get_use_db_conn_spec(options)
48
+ options.symbolize_keys! # without the bang, this was pointless
49
+ suffix = options.delete(:suffix)
50
+ prefix = options.delete(:prefix)
51
+ rails_env = options.delete(:rails_env) || RAILS_ENV
52
+ if (options[:adapter])
53
+ return options
54
+ else
55
+ str = "#{prefix}#{rails_env}#{suffix}"
56
+ connections = ActiveRecord::Base.configurations
57
+ raise "Cannot find database specification. Configuration '#{str}' expected in config/database.yml" if (connections[str].nil?)
58
+ return connections[str]
59
+ end
60
+ end
61
+
62
+ end
63
+ ActiveRecord::Base.extend(UseDbPlugin)
@@ -0,0 +1,61 @@
1
+ class UseDbTest
2
+
3
+ extend UseDbPlugin
4
+
5
+ def self.other_databases
6
+ use_db_config = (defined?(USE_DB_CONFIG)) ? USE_DB_CONFIG : "#{RAILS_ROOT}/config/use_db.yml"
7
+ YAML.load(File.read(use_db_config)).values.collect(&:symbolize_keys!)
8
+ end
9
+
10
+ def self.prepare_test_db(options={})
11
+ schema_dump(options)
12
+ schema_load(options)
13
+ end
14
+
15
+ def self.schema_dump(options)
16
+ # puts "In schema_dump"
17
+ options_dup = options.dup
18
+ options_dup[:rails_env] = "development"
19
+ conn_spec = get_use_db_conn_spec(options_dup)
20
+ test_class = setup_test_model(options[:prefix], options[:suffix], "ForSchemaDump")
21
+ test_class.establish_connection(conn_spec)
22
+ require 'active_record/schema_dumper'
23
+ File.open(schema_file(options), "w") do |file|
24
+ ActiveRecord::SchemaDumper.dump(test_class.connection, file)
25
+ end
26
+ end
27
+
28
+ def self.schema_load(options)
29
+ # puts "In schema_load"
30
+ options_dup = options.dup
31
+ conn_spec = get_use_db_conn_spec(options_dup)
32
+ ActiveRecord::Base.establish_connection(conn_spec)
33
+ file = schema_file(options)
34
+ # database.rake suggests that I should purge first, but why?
35
+ # the schema is :force => true and will wipe everything out anyway.
36
+ # using schema seems a better option than to the DSL that it came from.
37
+ if File.exists?(file)
38
+ # puts "loading #{file}"
39
+ ActiveRecord::Schema.verbose = UseDbPlugin.debug_print
40
+ load(file)
41
+ ActiveRecord::Base.connection.disconnect!
42
+ ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations["test"])
43
+ else
44
+ abort %{#{file} doesn't exist yet. Run "rake db:migrate" to create it then try again. If you do not intend to use a database, you should instead alter #{RAILS_ROOT}/config/environment.rb to prevent active_record from loading: config.frameworks -= [ :active_record ]}
45
+ end
46
+ end
47
+
48
+ def self.setup_test_model(prefix="", suffix="", model_suffix="", rails_env=RAILS_ENV)
49
+ prefix ||= ""
50
+ suffix ||= ""
51
+ model_name = "TestModel#{prefix.camelize}#{suffix.camelize}#{model_suffix}".gsub("_","").gsub("-","")
52
+ return eval(model_name) if eval("defined?(#{model_name})")
53
+ create_test_model(model_name, prefix, suffix, rails_env)
54
+ return eval(model_name)
55
+ end
56
+
57
+ def self.schema_file(options)
58
+ "#{RAILS_ROOT}/db/#{RAILS_ENV}_#{options[:prefix]}_#{options[:suffix]}_schema.rb"
59
+ end
60
+
61
+ end
data/rails/init.rb ADDED
@@ -0,0 +1 @@
1
+ require "jakewendt-use_db"
metadata ADDED
@@ -0,0 +1,107 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jakewendt-use_db
3
+ version: !ruby/object:Gem::Version
4
+ hash: 57
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 9
9
+ - 1
10
+ version: 0.9.1
11
+ platform: ruby
12
+ authors:
13
+ - David Stevenson
14
+ - George 'Jake' Wendt
15
+ autorequire:
16
+ bindir: bin
17
+ cert_chain: []
18
+
19
+ date: 2011-08-10 00:00:00 -07:00
20
+ default_executable:
21
+ dependencies:
22
+ - !ruby/object:Gem::Dependency
23
+ name: rails
24
+ prerelease: false
25
+ requirement: &id001 !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ~>
29
+ - !ruby/object:Gem::Version
30
+ hash: 7
31
+ segments:
32
+ - 2
33
+ version: "2"
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: jakewendt-rails_extension
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ hash: 3
45
+ segments:
46
+ - 0
47
+ version: "0"
48
+ type: :runtime
49
+ version_requirements: *id002
50
+ description: Gem version of use_db rails plugin
51
+ email: github@jake.otherinbox.com
52
+ executables: []
53
+
54
+ extensions: []
55
+
56
+ extra_rdoc_files:
57
+ - README.rdoc
58
+ files:
59
+ - lib/jakewendt-use_db.rb
60
+ - lib/tasks/use_db_tasks.rake
61
+ - lib/use_db.rb
62
+ - lib/use_db/configurations.rb
63
+ - lib/use_db/migration.rb
64
+ - lib/use_db/override_fixtures.rb
65
+ - lib/use_db/override_test_case.rb
66
+ - lib/use_db/tasks.rb
67
+ - lib/use_db/test_model.rb
68
+ - lib/use_db/use_db_plugin.rb
69
+ - lib/use_db/use_db_test.rb
70
+ - rails/init.rb
71
+ - README.rdoc
72
+ has_rdoc: true
73
+ homepage: http://github.com/jakewendt/use_db
74
+ licenses: []
75
+
76
+ post_install_message:
77
+ rdoc_options: []
78
+
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ hash: 3
87
+ segments:
88
+ - 0
89
+ version: "0"
90
+ required_rubygems_version: !ruby/object:Gem::Requirement
91
+ none: false
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ hash: 3
96
+ segments:
97
+ - 0
98
+ version: "0"
99
+ requirements: []
100
+
101
+ rubyforge_project:
102
+ rubygems_version: 1.6.2
103
+ signing_key:
104
+ specification_version: 3
105
+ summary: Gem version of use_db rails plugin
106
+ test_files: []
107
+