connection_manager 0.2.6 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. data/.gitignore +2 -1
  2. data/Gemfile +0 -1
  3. data/LICENSE.txt +1 -1
  4. data/README.md +115 -55
  5. data/Rakefile +2 -0
  6. data/connection_manager.gemspec +9 -3
  7. data/lib/connection_manager/connection_builder.rb +81 -0
  8. data/lib/connection_manager/connection_manager_railtie.rb +4 -3
  9. data/lib/connection_manager/helpers/abstract_adapter_helper.rb +40 -0
  10. data/lib/connection_manager/helpers/connection_helpers.rb +105 -0
  11. data/lib/connection_manager/{cross_schema_patch.rb → patches/cross_schema_patch.rb} +2 -3
  12. data/lib/connection_manager/replication.rb +165 -0
  13. data/lib/connection_manager/shards.rb +25 -0
  14. data/lib/connection_manager/using.rb +95 -0
  15. data/lib/connection_manager/version.rb +1 -1
  16. data/lib/connection_manager.rb +19 -8
  17. data/spec/factories.rb +7 -11
  18. data/spec/helpers/database_spec_helper.rb +52 -41
  19. data/spec/helpers/models_spec_helper.rb +23 -0
  20. data/spec/jdbcmysql_database.yml +55 -0
  21. data/spec/lib/connection_builder_spec.rb +29 -0
  22. data/spec/lib/connection_helpers_spec.rb +74 -0
  23. data/spec/lib/replication_spec.rb +134 -0
  24. data/spec/lib/shards_spec.rb +40 -0
  25. data/spec/lib/using_spec.rb +77 -0
  26. data/spec/mysql2_database.yml +28 -2
  27. data/spec/spec_helper.rb +20 -6
  28. metadata +42 -39
  29. data/Gemfile.lock +0 -56
  30. data/lib/connection_manager/associations.rb +0 -28
  31. data/lib/connection_manager/connections.rb +0 -124
  32. data/lib/connection_manager/method_recorder.rb +0 -57
  33. data/lib/connection_manager/secondary_connection_builder.rb +0 -180
  34. data/spec/integration/secondary_connection_builder_spec.rb +0 -115
  35. data/spec/lib/associations_spec.rb +0 -30
  36. data/spec/lib/connections_spec.rb +0 -92
  37. data/spec/lib/method_recorder_spec.rb +0 -43
  38. data/spec/lib/secondary_connection_builder_spec.rb +0 -77
@@ -0,0 +1,165 @@
1
+ require 'active_support/core_ext/hash/indifferent_access'
2
+ module ConnectionManager
3
+ module Replication
4
+
5
+ # Replication methods (replication_method_name, which is the option[:name] for the
6
+ # #replication method) and all thier associated connections. The key is the
7
+ # replication_method_name and the value is an array of all the replication_classes
8
+ # the replication_method has access to.
9
+ #
10
+ # EX: replication_methods[:slaves] => ['Slave1Connection',Slave2Connection]
11
+ def replication_methods
12
+ @replication_methods ||= HashWithIndifferentAccess.new
13
+ end
14
+
15
+ def replicant_classes
16
+ @replicant_classes ||= HashWithIndifferentAccess.new
17
+ end
18
+
19
+ # Is this class replicated
20
+ def replicated?
21
+ (@replicated == true)
22
+ end
23
+
24
+ # Builds a class method that returns an ActiveRecord::Relation for use with
25
+ # in ActiveRecord method chaining.
26
+ #
27
+ # EX:
28
+ # class MyClass < ActiveRecord::Base
29
+ # replicated :my_readonly_db, "FooConnection", :method_name => 'slaves'
30
+ # end
31
+ # end
32
+ #
33
+ # Options:
34
+ # * :name - name of class method to call to access replication, default to slaves
35
+ # * :readonly - forces all results to readonly
36
+ # * :type - the type of replication; :slave or :master, defaults to :slave
37
+ # * :using - list of connections to use; can be database.yml key or the name of the connection class --- DEPRECIATED
38
+ # * A Block may be passed that will be called on each of the newly created child classes
39
+ def replicated(*connections)
40
+ @replicated = true
41
+ options = {:name => "slaves"}.merge!(connections.extract_options!)
42
+ options[:type] ||= :slaves
43
+ options[:build_replicants] = true if (options[:build_replicants].blank? && options[:type] == :masters)
44
+ if options[:using]
45
+ connections = options[:using]
46
+ warn "[DEPRECATION] :using option is deprecated. Please list replication connections instead. EX: replicated :slave_connection,'OtherSlaveConnectionChild', :name => 'my_slaves'."
47
+ end
48
+
49
+ # Just incase its blank. Should be formally set by using connection class or at the model manually
50
+ self.table_name_prefix = "#{database_name}." if self.table_name_prefix.blank?
51
+
52
+ connections = connection.replication_keys(options[:type]) if connections.blank?
53
+ set_replications_to_method(connections,options[:name])
54
+ build_repliciation_class_method(options)
55
+ build_replication_association_class(options)
56
+ build_query_method_alias_method(options[:name])
57
+ build_repliciation_instance_method(options[:name])
58
+ options[:name]
59
+ end
60
+
61
+
62
+ # Get a connection class name from out replication_methods pool
63
+ # could add mutex but not sure blocking is with it.
64
+ def fetch_replication_method(method_name)
65
+ available_connections = @replication_methods[method_name]
66
+ raise ArgumentError, "No connections found for #{method_name}." if available_connections.blank?
67
+ available_connections.rotate!
68
+ available_connections[0]
69
+ end
70
+
71
+ private
72
+ # Builds replication connection classes and methods
73
+ def set_replications_to_method(connections,replication_method_name='slaves')
74
+ raise ArgumentError, "connections could not be found for #{self.name}." if connections.blank?
75
+ connections.each do |to_use|
76
+ connection_class_name = fetch_connection_class_name(to_use)
77
+ replication_methods[replication_method_name] ||= []
78
+ replication_methods[replication_method_name] << connection_class_name
79
+ end
80
+ true
81
+ end
82
+
83
+ def fetch_connection_class_name(to_use)
84
+ connection_class_name = to_use
85
+ connection_class_name = fetch_connection_class_name_from_yml_key(connection_class_name) if connection_class_name.to_s.match(/_/)
86
+ raise ArgumentError, "For #{self.name}, the class #{connection_class_name} could not be found." unless (managed_connection_classes.include?(connection_class_name))
87
+ connection_class_name
88
+ end
89
+
90
+ def fetch_connection_class_name_from_yml_key(yml_key)
91
+ found = managed_connections[yml_key]
92
+ connection_class_name = nil
93
+ if found
94
+ connection_class_name = found.first
95
+ else
96
+ raise ArgumentError, "For #{self.name}, a connection class for #{yml_key} could not be found."
97
+ end
98
+ connection_class_name
99
+ end
100
+
101
+ # Adds a class method, that calls #using with the correct connection class name
102
+ # by calling fetch_replication_method with the method name. Adds readonly
103
+ # query method class if replication is specified as readonly.
104
+ def build_repliciation_class_method(options)
105
+ class_eval <<-STR
106
+ class << self
107
+ def #{options[:name]}
108
+ using(fetch_replication_method("#{options[:name]}"))#{options[:readonly] ? '.readonly' : ''}
109
+ end
110
+ end
111
+ STR
112
+ end
113
+
114
+ # Builds a class within the model with the name of replication method. Use this
115
+ # class as the :class_name options for associations when it is nesseccary to
116
+ # ensure eager loading uses a replication connection.
117
+ #
118
+ # EX:
119
+ #
120
+ # class Foo < ActiveRecord::Base
121
+ # belongs_to :user
122
+ # replicated # use default name .slaves
123
+ # end
124
+ #
125
+ # class MyClass < ActiveRecord::Base
126
+ # has_many :foos
127
+ # has_many :foo_slaves, :class_name => 'Foo::Slaves'
128
+ # replicated
129
+ # end
130
+ #
131
+ # a = MyClass.include(:foo_slaves).first
132
+ # a.foo_slaves => [<Foo::Slave1ConnectionDup...>,<Foo::Slave1ConnectionDup...>...]
133
+ def build_replication_association_class(options)
134
+ class_eval <<-STR
135
+ class #{options[:name].titleize}
136
+ class << self
137
+ def method_missing(name, *args)
138
+ #{self.name}.#{options[:name]}.klass.send(name, *args)
139
+ end
140
+ end
141
+ end
142
+ STR
143
+ end
144
+
145
+ # Build a query method with the name of our replicaiton method. This method
146
+ # uses the relation.klass to fetch the appropriate connection, ensuring the
147
+ # correct connection is used even if the method is already defined by another class.
148
+ # We want to make sure we don't override existing methods in ActiveRecord::QueryMethods
149
+ def build_query_method_alias_method(replication_relation_name)
150
+ unless ActiveRecord::QueryMethods.instance_methods.include?(replication_relation_name.to_sym)
151
+ ActiveRecord::QueryMethods.module_eval do
152
+ define_method replication_relation_name.to_sym do
153
+ using(self.klass.fetch_replication_method(replication_relation_name))
154
+ end
155
+ end
156
+ end
157
+ end
158
+
159
+ def build_repliciation_instance_method(replication_relation_name)
160
+ define_method replication_relation_name.to_sym do
161
+ using(self.class.fetch_replication_method(replication_relation_name))
162
+ end
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,25 @@
1
+ module ConnectionManager
2
+ module Shards
3
+ @shard_class_names = []
4
+
5
+ def shard_class_names(*shard_class_names)
6
+ @shard_class_names = shard_class_names
7
+ end
8
+
9
+ # Takes a block that is call on all available shards.
10
+ def shards(*opts,&shards_block)
11
+ opts = {:include_self => true}.merge(opts.extract_options!)
12
+ raise ArgumentError, "shard_class_names have not been defined for #{self.class.name}" if @shard_class_names.length == 0
13
+ if block_given?
14
+ results = []
15
+ @shard_class_names.each do |s|
16
+ results << shards_block.call(s.constantize)
17
+ end
18
+ results << shards_block.call(self) if opts[:include_self]
19
+ return results.flatten
20
+ else
21
+ raise ArgumentError, 'shards method requires a block.'
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,95 @@
1
+ module ConnectionManager
2
+ module Using
3
+ module ClassMethods
4
+
5
+ def using(connection_class_name)
6
+ d = fetch_duplicate_class(connection_class_name)
7
+ r = ActiveRecord::Relation.new(d, d.arel_table)
8
+ r = r.readonly if d.connection.readonly?
9
+ r
10
+ end
11
+
12
+ private
13
+ # We use dup here because its just too tricky to make sure we override
14
+ # all the methods nesseccary when using a child class of the model. This
15
+ # action is lazy and the created sub is named to a constant so we only
16
+ # have to do it once.
17
+ def fetch_duplicate_class(connection_class_name)
18
+ begin
19
+ return "#{self.name}::#{connection_class_name}Dup".constantize
20
+ rescue NameError
21
+ return build_dup_class(connection_class_name)
22
+ end
23
+ end
24
+
25
+ def build_dup_class(connection_class_name)
26
+ dup_klass = class_eval <<-STR
27
+ #{connection_class_name}Dup = dup
28
+ STR
29
+
30
+ dup_klass.class_eval <<-STR
31
+ class << self
32
+ def model_name
33
+ '#{self.model_name}'
34
+ end
35
+ end
36
+ STR
37
+
38
+ extend_dup_class(dup_klass,connection_class_name)
39
+ dup_klass.table_name = table_name.to_s.split('.').last
40
+ dup_klass.table_name_prefix = connection_class_name.constantize.table_name_prefix
41
+ dup_klass
42
+ end
43
+
44
+ # Extend the connection override module from the connetion to the supplied class
45
+ def extend_dup_class(dup_class,connection_class_name)
46
+ begin
47
+ mod = "#{connection_class_name}::ConnectionOverrideMod".constantize
48
+ dup_class.extend(mod)
49
+ rescue NameError
50
+ built = build_connection_override_module(connection_class_name).constantize
51
+ dup_class.extend(built)
52
+ end
53
+ end
54
+
55
+ # Added a module to the connection class. The module is extended on dup class
56
+ # to override the connection and superclass
57
+ def build_connection_override_module(connection_class_name)
58
+ connection_class_name.constantize.class_eval <<-STR
59
+ module ConnectionOverrideMod
60
+ def connection_class
61
+ "#{connection_class_name}".constantize
62
+ end
63
+
64
+ def connection
65
+ connection_class.connection
66
+ end
67
+
68
+ def superclass
69
+ connection_class
70
+ end
71
+ end
72
+ STR
73
+ "#{connection_class_name}::ConnectionOverrideMod"
74
+ end
75
+ end
76
+
77
+ # Instance method for casting to a duplication class
78
+ def using(connection_class)
79
+ becomes(self.class.using(connection_class).klass)
80
+ end
81
+
82
+ def self.included(host_class)
83
+ host_class.extend(ClassMethods)
84
+ end
85
+ end
86
+
87
+ module UsingQueryMethod
88
+ def using(connection_class_name)
89
+ d = klass.using(connection_class_name)
90
+ relation = clone
91
+ relation.instance_variable_set(:@klass, d.klass)
92
+ relation
93
+ end
94
+ end
95
+ end
@@ -1,4 +1,4 @@
1
1
  module ConnectionManager
2
- VERSION = "0.2.6"
2
+ VERSION = "0.3.0"
3
3
  end
4
4
 
@@ -2,15 +2,26 @@ require "connection_manager/version"
2
2
 
3
3
  module ConnectionManager
4
4
  require 'active_record'
5
- require 'connection_manager/connections'
6
- require 'connection_manager/associations'
7
- require 'connection_manager/secondary_connection_builder'
8
- require 'connection_manager/method_recorder'
9
- require 'connection_manager/connection_manager_railtie' if defined?(Rails)
5
+ require 'active_support'
6
+ require 'connection_manager/helpers/abstract_adapter_helper'
7
+ require 'connection_manager/connection_builder'
8
+ require 'connection_manager/helpers/connection_helpers'
9
+ require 'connection_manager/using'
10
+ require 'connection_manager/shards'
11
+ require 'connection_manager/replication'
12
+ require 'connection_manager/patches/cross_schema_patch'
13
+ require 'connection_manager/connection_manager_railtie' if defined?(Rails)
10
14
 
11
- ActiveRecord::Base.extend(ConnectionManager::Associations)
12
- ActiveRecord::Base.extend(ConnectionManager::SecondaryConnectionBuilder)
15
+ ActiveRecord::ConnectionAdapters::AbstractAdapter.send(:include,(ConnectionManager::AbstractAdapterHelper))
16
+ ActiveRecord::Base.extend(ConnectionManager::ConnectionHelpers)
17
+ ActiveRecord::Base.extend(ConnectionManager::ConnectionBuilder)
18
+ ActiveRecord::Base.send(:include,ConnectionManager::Using)
19
+ ActiveRecord::QueryMethods.send(:include,ConnectionManager::UsingQueryMethod)
20
+ ActiveRecord::Base.extend(ConnectionManager::Replication)
21
+ ActiveRecord::Base.extend(ConnectionManager::Shards)
13
22
 
14
- require 'connection_manager/cross_schema_patch.rb' if (ActiveRecord::VERSION::MAJOR == 3 && ActiveRecord::VERSION::MINOR == 0)
23
+ ActiveSupport.on_load(:active_record) do
24
+ ActiveRecord::Base.build_connection_classes
25
+ end
15
26
  end
16
27
 
data/spec/factories.rb CHANGED
@@ -1,31 +1,27 @@
1
- FactoryGirl.define do
1
+ FactoryGirl.define do
2
+ sequence :rand_name do |n|
3
+ "foo#{(n + (n+rand)).to_s[2..6]}"
4
+ end
5
+
2
6
  factory :basket do
3
7
  name "MyString"
4
8
  end
5
- end
6
9
 
7
- FactoryGirl.define do
8
10
  factory :fruit_basket do
9
11
  fruit
10
12
  basket
11
13
  end
12
- end
13
14
 
14
- FactoryGirl.define do
15
15
  factory :fruit do
16
16
  name "MyString"
17
17
  region
18
18
  end
19
- end
20
19
 
21
- FactoryGirl.define do
22
20
  factory :region do
23
21
  name "MyString"
24
22
  end
25
- end
26
23
 
27
- FactoryGirl.define do
28
- factory :type do
29
- name "MyString"
24
+ factory :type do |f|
25
+ f.sequence(:name) {FactoryGirl.generate(:rand_name)}
30
26
  end
31
27
  end
@@ -1,8 +1,14 @@
1
1
  class TestDB
2
- def self.connect(driver='sqlite')
3
- ActiveRecord::Base.configurations = YAML::load(File.open(File.join(File.dirname(__FILE__),'..',"#{driver}_database.yml")))
2
+ def self.yml(driver='sqlite')
3
+ YAML::load(File.open(File.join(File.dirname(__FILE__),'..',"#{driver}_database.yml")))
4
+ end
5
+
6
+ def self.connect(driver='sqlite',logging=false)
7
+ ActiveRecord::Base.configurations = yml(driver)
4
8
  ActiveRecord::Base.establish_connection('test')
9
+ ActiveRecord::Base.logger = Logger.new(STDOUT) if logging
5
10
  end
11
+
6
12
  def self.clean
7
13
  [:foos,:fruits,:baskets,:fruit_baskets,:regions,:types].each do |t|
8
14
  DBSpecManagement.connection.execute("DELETE FROM #{t.to_s}")
@@ -16,41 +22,43 @@ end
16
22
 
17
23
  #Put all the test migrations here
18
24
  class TestMigrations < ActiveRecord::Migration
19
-
20
25
  # all the ups
21
- def self.up(connection_name='test', user_connection_name='cm_user_test')
22
- ActiveRecord::Base.establish_connection(connection_name)
23
- begin
24
- create_table :foos do |t|
25
- t.string :name
26
- t.integer :user_id
27
- end
28
- create_table :fruits do |t|
29
- t.string :name
30
- t.integer :region_id
31
- t.timestamps
32
- end
33
- create_table :baskets do |t|
34
- t.string :name
35
- t.timestamps
26
+ def self.up(connection_name='test',master_2_connection_name='master_2_cm_test', user_connection_name='cm_user_test')
27
+ [connection_name,master_2_connection_name].each do |c|
28
+ ActiveRecord::Base.establish_connection(c)
29
+ begin
30
+ create_table :foos do |t|
31
+ t.string :name
32
+ t.integer :user_id
33
+ end
34
+ create_table :fruits do |t|
35
+ t.string :name
36
+ t.integer :region_id
37
+ t.timestamps
38
+ end
39
+ create_table :baskets do |t|
40
+ t.string :name
41
+ t.timestamps
42
+ end
43
+ create_table :fruit_baskets do |t|
44
+ t.integer :fruit_id
45
+ t.integer :basket_id
46
+ t.timestamps
47
+ end
48
+ create_table :regions do |t|
49
+ t.string :name
50
+ t.integer :type_id
51
+ t.timestamps
52
+ end
53
+ create_table :types do |t|
54
+ t.string :name
55
+ t.timestamps
56
+ end
57
+ rescue => e
58
+ puts "tables failed to create: #{e}"
36
59
  end
37
- create_table :fruit_baskets do |t|
38
- t.integer :fruit_id
39
- t.integer :basket_id
40
- t.timestamps
41
- end
42
- create_table :regions do |t|
43
- t.string :name
44
-
45
- t.timestamps
46
- end
47
- create_table :types do |t|
48
- t.string :name
49
- t.timestamps
50
- end
51
- rescue => e
52
- puts "tables failed to create: #{e}"
53
60
  end
61
+
54
62
  ActiveRecord::Base.establish_connection(user_connection_name)
55
63
  begin
56
64
  create_table :users do |t|
@@ -59,18 +67,21 @@ class TestMigrations < ActiveRecord::Migration
59
67
  rescue => e
60
68
  puts "tables failed to create: #{e}"
61
69
  end
70
+
62
71
  ActiveRecord::Base.establish_connection(connection_name)
63
72
  end
64
73
 
65
74
  # all the downs
66
- def self.down(connection_name='test',user_connection_name='cm_user_test')
67
- ActiveRecord::Base.establish_connection(connection_name)
68
- begin
69
- [:foos,:fruits,:baskets,:fruit_baskets,:regions,:types].each do |t|
70
- drop_table t
75
+ def self.down(connection_name='test',master_2_connection_name='master_2_cm_test',user_connection_name='cm_user_test')
76
+ [connection_name,master_2_connection_name].each do |c|
77
+ ActiveRecord::Base.establish_connection(c)
78
+ begin
79
+ [:foos,:fruits,:baskets,:fruit_baskets,:regions,:types].each do |t|
80
+ drop_table t
81
+ end
82
+ rescue => e
83
+ puts "tables were not dropped: #{e}"
71
84
  end
72
- rescue => e
73
- puts "tables were not dropped: #{e}"
74
85
  end
75
86
  ActiveRecord::Base.establish_connection(user_connection_name)
76
87
  begin
@@ -1,3 +1,10 @@
1
+ class CmReplicationConnection < ActiveRecord::Base
2
+ establish_managed_connection(:slave_1_cm_test)
3
+ end
4
+
5
+ class CmReplication2Connection < ActiveRecord::Base
6
+ establish_managed_connection(:slave_2_cm_test)
7
+ end
1
8
 
2
9
  class Basket < ActiveRecord::Base
3
10
  has_many :fruit_baskets
@@ -23,3 +30,19 @@ class Region < ActiveRecord::Base
23
30
  has_one :fruit
24
31
  #replicated
25
32
  end
33
+
34
+ class Type < ActiveRecord::Base
35
+
36
+ end
37
+
38
+ class SouthernFruit < Fruit
39
+ self.table_name = 'fruits'
40
+ end
41
+
42
+ class ModelsHelper
43
+ def self.models
44
+ ["Basket", "Fruit", "FruitBasket", "Region","SouthernFruit", "Type"]
45
+ end
46
+ end
47
+
48
+
@@ -0,0 +1,55 @@
1
+ # Warning: The database defined as "test" will be erased and
2
+ # re-generated from your development database when you run "rake".
3
+ # Do not set this db to the same as development or production.
4
+ common: &common
5
+ adapter: jdbcmysql
6
+ username: root
7
+ password: omegared
8
+ pool: 100
9
+ timeout: 5000
10
+ build_connection_class: true
11
+
12
+ master: &master
13
+ username: root
14
+ password: omegared
15
+
16
+ readonly: &readonly
17
+ username: readonly
18
+ password: omegared
19
+
20
+ test:
21
+ <<: *common
22
+ <<: *master
23
+ database: cm_test
24
+ slaves: [slave_1_cm_test, slave_2_cm_test]
25
+
26
+ cm_user_test:
27
+ <<: *common
28
+ <<: *master
29
+ database: cm_user_test
30
+ slaves: [slave_1_cm_user_test]
31
+
32
+ slave_1_cm_test:
33
+ <<: *common
34
+ <<: *readonly
35
+ database: cm_test
36
+
37
+ slave_2_cm_test:
38
+ <<: *common
39
+ <<: *readonly
40
+ database: cm_test
41
+
42
+ master_2_cm_test:
43
+ <<: *common
44
+ <<: *master
45
+ database: cm_master_2_test
46
+
47
+ shard_1_cm_test:
48
+ <<: *common
49
+ <<: *master
50
+ database: legacy_cm_test
51
+
52
+ slave_1_cm_user_test:
53
+ <<: *common
54
+ <<: *readonly
55
+ database: cm_user_test
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+ describe ConnectionManager::ConnectionBuilder do
3
+
4
+ describe '#connection_class_name' do
5
+ it "should return a string for a class name appended with 'Connection' " do
6
+ ActiveRecord::Base.send(:connection_class_name,"my_database").should eql("MyDatabaseConnection")
7
+ end
8
+ it "should return remove the appended rails env" do
9
+ ActiveRecord::Base.send(:connection_class_name,"my_database_test").should eql("MyDatabaseConnection")
10
+ end
11
+ it "should use the database name from the database.yml if supplied string is only is only the Rails.env" do
12
+ ActiveRecord::Base.send(:connection_class_name,"test").should eql("BaseConnection")
13
+ end
14
+ end
15
+
16
+ describe '#build_connection_class' do
17
+ before(:all) do
18
+ ActiveRecord::Base.build_connection_class("MyConnectionClass", :test)
19
+ end
20
+ it "should add a class with supplied class name to ConnectionManager::ConnectionBuilder" do
21
+ defined?(MyConnectionClass).should be_true
22
+ MyConnectionClass.is_a?(Class).should be_true
23
+ end
24
+ it "should have a super class of ActiveRecord::Base" do
25
+ MyConnectionClass.superclass.should eql(ActiveRecord::Base)
26
+ end
27
+ end
28
+ end
29
+
@@ -0,0 +1,74 @@
1
+ require 'spec_helper'
2
+ describe ConnectionManager::ConnectionHelpers do
3
+ before(:all) do
4
+
5
+ class MyConnectionClass < ActiveRecord::Base
6
+ establish_managed_connection(:test)
7
+ end
8
+
9
+ class MyReadonlyConnectionClass < ActiveRecord::Base
10
+ establish_managed_connection({
11
+ :database => "cm_test",
12
+ :adapter => "mysql2",
13
+ :username => TestDB.yml("mysql2")["test"]["username"],
14
+ :password => TestDB.yml("mysql2")["test"]["password"]
15
+
16
+ }, {:readonly => true})
17
+ end
18
+
19
+ class MyFoo < MyConnectionClass
20
+ self.table_name = 'foos'
21
+ end
22
+
23
+ class MyReadonlyFoo < MyReadonlyConnectionClass
24
+ self.table_name = 'foos'
25
+ end
26
+ end
27
+ describe '#establish_managed_connection' do
28
+ context 'the connection class' do
29
+
30
+ it "should create abstract class" do
31
+ MyConnectionClass.abstract_class.should be_true
32
+ end
33
+
34
+ it "should prefix table name with database" do
35
+ MyConnectionClass.table_name_prefix.should eql('cm_test.')
36
+ end
37
+
38
+ it "should checkin the connection" do
39
+ ActiveRecord::Base.managed_connection_classes.include?("MyConnectionClass").should be_true
40
+ ActiveRecord::Base.managed_connection_classes.include?("MyReadonlyConnectionClass").should be_true
41
+ end
42
+ end
43
+ context 'the model' do
44
+ it "should not be readonly" do
45
+ u = MyFoo.new
46
+ u.readonly?.should_not be_true
47
+ end
48
+ it "should be readonly if readonly option for establish_managed_connection from connaction class is true" do
49
+ u = MyReadonlyFoo.new
50
+ u.readonly?.should be_true
51
+ end
52
+ end
53
+ end
54
+
55
+ describe '#use_database' do
56
+ it "should set the database/schema for the model to the supplied schema_name" do
57
+ Fruit.use_database('my_schema')
58
+ Fruit.database_name.should eql('my_schema')
59
+ end
60
+
61
+ it "should set the contactinate the schema_name and table_name; and set the table_name to that value" do
62
+ Fruit.use_database('my_schema')
63
+ Fruit.table_name.should eql('fruits')
64
+ Fruit.table_name_prefix.should eql('my_schema.')
65
+ end
66
+
67
+ it "should set the table_name if one is supplied" do
68
+ Fruit.use_database('my_schema',{:table_name => 'apples'})
69
+ Fruit.table_name.should eql('apples')
70
+ Fruit.table_name_prefix.should eql('my_schema.')
71
+ end
72
+ end
73
+ end
74
+