connection_manager 0.2.1 → 0.2.2

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.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- connection_manager (0.2.1)
4
+ connection_manager (0.2.2)
5
5
  activerecord (~> 3.0)
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -120,6 +120,7 @@ available slave connections each time it is called using a different connection
120
120
 
121
121
  ## TODO's
122
122
  * sharding - IN 2.0 AS BETA
123
+ * cross schema joins - 2.2 AS BETA tested with Mysql2 ONLY
123
124
 
124
125
  ## Other activerecord Connection gems
125
126
  * [Octopus](https://github.com/tchandy/octopus)
@@ -6,10 +6,11 @@ module ConnectionManager
6
6
  require 'connection_manager/associations'
7
7
  require 'connection_manager/secondary_connection_builder'
8
8
  require 'connection_manager/method_recorder'
9
- require 'connection_manager/connection_manager_railtie.rb' if defined?(Rails)
9
+ require 'connection_manager/connection_manager_railtie' if defined?(Rails)
10
10
 
11
11
  ActiveRecord::Base.extend(ConnectionManager::Associations)
12
12
  ActiveRecord::Base.extend(ConnectionManager::SecondaryConnectionBuilder)
13
13
 
14
+ require 'connection_manager/cross_schema_patch.rb' if (ActiveRecord::VERSION::MAJOR == 3 && ActiveRecord::VERSION::MINOR == 0)
14
15
  end
15
16
 
@@ -114,6 +114,7 @@ module ConnectionManager
114
114
  self.abstract_class = true
115
115
  end
116
116
  new_connection_class = Object.const_set(class_name, klass)
117
+ new_connection_class.table_name_prefix=("#{ActiveRecord::Base.configurations[connection_key]['database']}.")
117
118
  new_connection_class.establish_connection(connection_key)
118
119
  (const_set class_name, new_connection_class)
119
120
  all << class_name
@@ -0,0 +1,130 @@
1
+ ### https://github.com/rails/rails/issues/539
2
+ ### http://tamersalama.com/2010/09/27/nomethoderror-undefined-method-eq-for-nilnilclass/
3
+ ### https://github.com/rails/rails/blob/3-0-stable/activerecord/lib/active_record/associations.rb
4
+ # ActiveRecord 3.0 ONLY
5
+
6
+ if ActiveRecord::VERSION::MAJOR == 3 && ActiveRecord::VERSION::MINOR == 0
7
+
8
+ module ActiveRecord
9
+ module Associations
10
+ module ClassMethods
11
+ class JoinDependency
12
+ class JoinAssociation < JoinBase
13
+
14
+ def table_name_without_schema(t_name)
15
+ t_name.to_s.gsub(/.*\./, '')
16
+ end
17
+
18
+ def association_join
19
+ return @join if @join
20
+ aliased_table = Arel::Table.new(table_name.to_s.gsub(/.*\./, ''), :as => @aliased_table_name.to_s.gsub(/.*\./, ''),
21
+ :engine => arel_engine,
22
+ :columns => klass.columns)
23
+
24
+ parent_table = Arel::Table.new(parent.table_name.to_s.gsub(/.*\./, ''), :as => parent.aliased_table_name.to_s.gsub(/.*\./, ''),
25
+ :engine => arel_engine,
26
+ :columns => parent.active_record.columns)
27
+ as_conditions = reflection.options[:conditions] && process_conditions(reflection.options[:conditions], aliased_table_name)
28
+
29
+ @join = case reflection.macro
30
+
31
+ when :has_and_belongs_to_many
32
+ join_table = Arel::Table.new(options[:join_table], :as => aliased_join_table_name, :engine => arel_engine)
33
+ fk = options[:foreign_key] || reflection.active_record.to_s.foreign_key
34
+ klass_fk = options[:association_foreign_key] || klass.to_s.foreign_key
35
+
36
+ [
37
+ join_table[fk].eq(parent_table[reflection.active_record.primary_key]),
38
+ [aliased_table[klass.primary_key].eq(join_table[klass_fk]), as_conditions].reject{ |x| x.blank? }
39
+ ]
40
+ when :has_many, :has_one
41
+ if reflection.options[:through]
42
+ join_table = Arel::Table.new(through_reflection.klass.table_name, :as => aliased_join_table_name, :engine => arel_engine)
43
+ jt_as_conditions = through_reflection.options[:conditions] && process_conditions(through_reflection.options[:conditions], aliased_table_name)
44
+ jt_as_extra = jt_source_extra = jt_sti_extra = nil
45
+ first_key = second_key = as_extra = nil
46
+
47
+ if through_reflection.macro == :belongs_to
48
+ jt_primary_key = through_reflection.primary_key_name
49
+ jt_foreign_key = through_reflection.association_primary_key
50
+ else
51
+ jt_primary_key = through_reflection.active_record_primary_key
52
+ jt_foreign_key = through_reflection.primary_key_name
53
+
54
+ if through_reflection.options[:as] # has_many :through against a polymorphic join
55
+ jt_as_extra = join_table[through_reflection.options[:as].to_s + '_type'].eq(parent.active_record.base_class.name)
56
+ end
57
+ end
58
+
59
+ case source_reflection.macro
60
+ when :has_many, :has_one
61
+ if source_reflection.options[:as]
62
+ first_key = "#{source_reflection.options[:as]}_id"
63
+ second_key = options[:foreign_key] || primary_key
64
+ as_extra = aliased_table["#{source_reflection.options[:as]}_type"].eq(source_reflection.active_record.base_class.name)
65
+ else
66
+ first_key = through_reflection.klass.base_class.to_s.foreign_key
67
+ second_key = options[:foreign_key] || primary_key
68
+ end
69
+
70
+ unless through_reflection.klass.descends_from_active_record?
71
+ jt_sti_extra = join_table[through_reflection.active_record.inheritance_column].eq(through_reflection.klass.sti_name)
72
+ end
73
+ when :belongs_to
74
+ first_key = primary_key
75
+ if reflection.options[:source_type]
76
+ second_key = source_reflection.association_foreign_key
77
+ jt_source_extra = join_table[reflection.source_reflection.options[:foreign_type]].eq(reflection.options[:source_type])
78
+ else
79
+ second_key = source_reflection.primary_key_name
80
+ end
81
+ end
82
+ [
83
+ [parent_table[jt_primary_key].eq(join_table[jt_foreign_key]), jt_as_extra, jt_source_extra, jt_sti_extra, jt_as_conditions].reject{|x| x.blank? },
84
+ [aliased_table[first_key].eq(join_table[second_key]), as_extra, as_conditions].reject{ |x| x.blank? }
85
+ ]
86
+ elsif reflection.options[:as]
87
+ id_rel = aliased_table["#{reflection.options[:as]}_id"].eq(parent_table[parent.primary_key])
88
+ type_rel = aliased_table["#{reflection.options[:as]}_type"].eq(parent.active_record.base_class.name)
89
+ [id_rel, type_rel, as_conditions].reject{ |x| x.blank?}
90
+ else
91
+ foreign_key = options[:foreign_key] || reflection.active_record.name.foreign_key
92
+ [aliased_table[foreign_key].eq(parent_table[reflection.options[:primary_key] || parent.primary_key]), as_conditions].reject{ |x| x.blank? }
93
+ end
94
+ when :belongs_to
95
+ #aliased_table[options[:primary_key] || reflection.klass.primary_key].eq(parent_table[options[:foreign_key] || reflection.primary_key_name]), as_conditions].reject{ |x| x.blank? }
96
+
97
+ [aliased_table[options[:primary_key] || reflection.klass.primary_key].eq(find_belongs_to_arel_column(parent_table)), as_conditions].reject{ |x| x.blank? }
98
+
99
+ end
100
+
101
+ unless klass.descends_from_active_record?
102
+ sti_column = aliased_table[klass.inheritance_column]
103
+ sti_condition = sti_column.eq(klass.sti_name)
104
+ klass.descendants.each {|subclass| sti_condition = sti_condition.or(sti_column.eq(subclass.sti_name)) }
105
+
106
+ @join << sti_condition
107
+ end
108
+
109
+ @join
110
+ end
111
+
112
+ def find_belongs_to_arel_column(parent_table)
113
+ parent_table.columns.select{|c| (c.name == options[:foreign_key].to_sym)}[0] ||
114
+ parent_table.columns.select{|c| (c.name == reflection.primary_key_name.to_sym)}[0]
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
121
+
122
+ module Arel
123
+ class Table
124
+ private
125
+ def table_exists?
126
+ @table_exists ||= tables.key?(@name.to_s.gsub(/.*\./, '')) || engine.connection.table_exists?(name.to_s.gsub(/.*\./, ''))
127
+ end
128
+ end
129
+ end
130
+ end
@@ -1,6 +1,6 @@
1
1
  module ConnectionManager
2
2
  module SecondaryConnectionBuilder
3
-
3
+
4
4
  def database_name
5
5
  "#{connection.instance_variable_get(:@config)[:database].to_s}"
6
6
  end
@@ -77,9 +77,9 @@ module ConnectionManager
77
77
  method_name = method_name.insert(method_name.index(/\d/),"_")
78
78
  class_name = method_name.classify
79
79
  connection_methods << method_name.to_sym
80
+ # set_table_name_for_joins
80
81
  build_secondary_class(class_name,c,options)
81
- build_single_secondary_method(method_name,class_name)
82
- #set_table_name_for_joins
82
+ build_single_secondary_method(method_name,class_name)
83
83
  sub_classes << "#{self.name}::#{class_name}".constantize if options[:shards]
84
84
  end
85
85
  end
@@ -125,13 +125,18 @@ module ConnectionManager
125
125
  end
126
126
  end
127
127
  STR
128
-
128
+ connection_sub_classes << sub_class
129
+ puts connection_sub_classes
129
130
  sub_class
130
131
  end
131
132
 
132
- # def set_table_name_for_joins
133
- # self.table_name_prefix = "#{database_name}." unless database_name.match(/\.sqlite3$/)
134
- # end
133
+ def connection_sub_classes
134
+ @connection_sub_classes ||= []
135
+ end
136
+
137
+ def set_table_name_for_joins
138
+ self.table_name_prefix = "#{database_name}." unless database_name.match(/\.sqlite3$/)
139
+ end
135
140
 
136
141
  # Adds as class method to call a specific secondary conneciton.
137
142
  # Usage:
@@ -1,4 +1,4 @@
1
1
  module ConnectionManager
2
- VERSION = "0.2.1"
2
+ VERSION = "0.2.2"
3
3
  end
4
4
 
@@ -18,11 +18,12 @@ end
18
18
  class TestMigrations < ActiveRecord::Migration
19
19
 
20
20
  # all the ups
21
- def self.up(connection_name='test')
21
+ def self.up(connection_name='test', user_connection_name='cm_user_test')
22
22
  ActiveRecord::Base.establish_connection(connection_name)
23
23
  begin
24
24
  create_table :foos do |t|
25
25
  t.string :name
26
+ t.integer :user_id
26
27
  end
27
28
  create_table :fruits do |t|
28
29
  t.string :name
@@ -50,12 +51,19 @@ class TestMigrations < ActiveRecord::Migration
50
51
  rescue => e
51
52
  puts "tables failed to create: #{e}"
52
53
  end
53
-
54
+ ActiveRecord::Base.establish_connection(user_connection_name)
55
+ begin
56
+ create_table :users do |t|
57
+ t.string :name
58
+ end
59
+ rescue => e
60
+ puts "tables failed to create: #{e}"
61
+ end
62
+ ActiveRecord::Base.establish_connection(connection_name)
54
63
  end
55
64
 
56
-
57
65
  # all the downs
58
- def self.down(connection_name='test')
66
+ def self.down(connection_name='test',user_connection_name='cm_user_test')
59
67
  ActiveRecord::Base.establish_connection(connection_name)
60
68
  begin
61
69
  [:foos,:fruits,:baskets,:fruit_baskets,:regions,:types].each do |t|
@@ -64,7 +72,14 @@ class TestMigrations < ActiveRecord::Migration
64
72
  rescue => e
65
73
  puts "tables were not dropped: #{e}"
66
74
  end
67
- end
68
-
69
-
75
+ ActiveRecord::Base.establish_connection(user_connection_name)
76
+ begin
77
+ [:users].each do |t|
78
+ drop_table t
79
+ end
80
+ rescue => e
81
+ puts "tables were not dropped: #{e}"
82
+ end
83
+ ActiveRecord::Base.establish_connection(connection_name)
84
+ end
70
85
  end
@@ -1,10 +1,10 @@
1
+
1
2
  class Basket < ActiveRecord::Base
2
3
  has_many :fruit_baskets
3
4
  has_many :fruit, :through => :fruit_baskets
4
5
  #replicated
5
6
  end
6
7
 
7
-
8
8
  class Fruit < ActiveRecord::Base
9
9
  belongs_to :region
10
10
  has_many :fruit_baskets
@@ -10,29 +10,29 @@ describe ConnectionManager::SecondaryConnectionBuilder do
10
10
 
11
11
  context '#other_association_options' do
12
12
 
13
- it "should add :class_name options set to the replication subclass if :class_name is blank" do
14
- options = Fruit.secondary_association_options(:has_one, :plant, 'Slave')
15
- options[:class_name].should eql("Plant::Slave")
16
- end
13
+ it "should add :class_name options set to the replication subclass if :class_name is blank" do
14
+ options = Fruit.secondary_association_options(:has_one, :plant, 'Slave')
15
+ options[:class_name].should eql("Plant::Slave")
16
+ end
17
17
 
18
- it "should append :class_name with the replication subclass if :class_name is not bank" do
19
- options = Fruit.secondary_association_options(:has_one, :plant, 'Slave', :class_name => 'Plant')
20
- options[:class_name].should eql("Plant::Slave")
21
- end
18
+ it "should append :class_name with the replication subclass if :class_name is not bank" do
19
+ options = Fruit.secondary_association_options(:has_one, :plant, 'Slave', :class_name => 'Plant')
20
+ options[:class_name].should eql("Plant::Slave")
21
+ end
22
22
 
23
23
  context "has_one or has_many" do
24
- it "should add the :foreign_key if the :foreign_key options is not present" do
25
- options = Fruit.secondary_association_options(:has_one, :plant, 'Slave')
26
- options[:foreign_key].should eql('fruit_id')
27
- options = Fruit.secondary_association_options(:has_many, :plant, 'Slave')
28
- options[:foreign_key].should eql('fruit_id')
29
- end
24
+ it "should add the :foreign_key if the :foreign_key options is not present" do
25
+ options = Fruit.secondary_association_options(:has_one, :plant, 'Slave')
26
+ options[:foreign_key].should eql('fruit_id')
27
+ options = Fruit.secondary_association_options(:has_many, :plant, 'Slave')
28
+ options[:foreign_key].should eql('fruit_id')
29
+ end
30
30
  end
31
31
  end
32
32
 
33
33
  context '#secondary_connection_classes' do
34
34
  it "should return the :using array with the array elements classified and append with Connection" do
35
- Fruit.secondary_connection_classes({:using => ['slave_1_test_db','slave_2_test_db']}).
35
+ Fruit.secondary_connection_classes({:using => ['slave_1_test_db','slave_2_test_db']}).
36
36
  should eql(["Slave1TestDbConnection", "Slave2TestDbConnection"])
37
37
  end
38
38
 
@@ -45,5 +45,33 @@ describe ConnectionManager::SecondaryConnectionBuilder do
45
45
  end
46
46
 
47
47
  end
48
+
49
+ context "cross database joins" do
50
+ ConnectionManager::Connections.initialize(:env => 'test')
51
+
52
+ class Foo < ActiveRecord::Base
53
+ set_table_name_for_joins
54
+ belongs_to :user
55
+ end
56
+
57
+ class User < CmUserConnection
58
+ set_table_name_for_joins
59
+ has_many :foos
60
+ end
61
+
62
+ before :all do
63
+ @user = User.new(:name => "Testing")
64
+ @user.save
65
+ @foo = Foo.new(:user_id => @user.id)
66
+ @foo.save
67
+ end
68
+
69
+ it "should work" do
70
+ @user.foos.blank?.should be_false
71
+ found = Foo.select('users.name AS user_name').joins(:user).where(:id => @foo.id).first
72
+ puts Foo.select('users.name AS user_name').joins(:user).where(:id => @foo.id).to_sql
73
+ found.user_name.blank?.should be_false
74
+ end
75
+ end
48
76
  end
49
77
 
@@ -4,14 +4,18 @@
4
4
  common: &common
5
5
  adapter: mysql2
6
6
  username: root
7
- password: some_password
7
+ password: omegared
8
8
  pool: 100
9
9
  timeout: 5000
10
10
 
11
11
  test:
12
12
  <<: *common
13
13
  database: cm_test
14
-
14
+
15
+ cm_user_test:
16
+ <<: *common
17
+ database: cm_user_test
18
+
15
19
  slave_1_cm_test:
16
20
  <<: *common
17
21
  database: cm_test
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: connection_manager
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-01-09 00:00:00.000000000Z
12
+ date: 2012-01-11 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
16
- requirement: &70350833047080 !ruby/object:Gem::Requirement
16
+ requirement: &70223799590600 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '3.0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70350833047080
24
+ version_requirements: *70223799590600
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: sqlite3
27
- requirement: &70350833046660 !ruby/object:Gem::Requirement
27
+ requirement: &70223799590180 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *70350833046660
35
+ version_requirements: *70223799590180
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rspec
38
- requirement: &70350833046200 !ruby/object:Gem::Requirement
38
+ requirement: &70223799589720 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '0'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *70350833046200
46
+ version_requirements: *70223799589720
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: autotest
49
- requirement: &70350833045780 !ruby/object:Gem::Requirement
49
+ requirement: &70223799589300 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: '0'
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *70350833045780
57
+ version_requirements: *70223799589300
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: mocha
60
- requirement: &70350833045360 !ruby/object:Gem::Requirement
60
+ requirement: &70223799588880 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: '0'
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *70350833045360
68
+ version_requirements: *70223799588880
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: factory_girl
71
- requirement: &70350833044940 !ruby/object:Gem::Requirement
71
+ requirement: &70223799588460 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ! '>='
@@ -76,10 +76,10 @@ dependencies:
76
76
  version: '0'
77
77
  type: :development
78
78
  prerelease: false
79
- version_requirements: *70350833044940
79
+ version_requirements: *70223799588460
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: mysql2
82
- requirement: &70350833044520 !ruby/object:Gem::Requirement
82
+ requirement: &70223799588040 !ruby/object:Gem::Requirement
83
83
  none: false
84
84
  requirements:
85
85
  - - ! '>='
@@ -87,7 +87,7 @@ dependencies:
87
87
  version: '0'
88
88
  type: :development
89
89
  prerelease: false
90
- version_requirements: *70350833044520
90
+ version_requirements: *70223799588040
91
91
  description: Simplifies connecting to Muliple and Replication databases with rails
92
92
  and active_record
93
93
  email:
@@ -108,6 +108,7 @@ files:
108
108
  - lib/connection_manager/associations.rb
109
109
  - lib/connection_manager/connection_manager_railtie.rb
110
110
  - lib/connection_manager/connections.rb
111
+ - lib/connection_manager/cross_schema_patch.rb
111
112
  - lib/connection_manager/method_recorder.rb
112
113
  - lib/connection_manager/secondary_connection_builder.rb
113
114
  - lib/connection_manager/version.rb