connection_manager 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
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