db-charmer 1.7.0 → 1.7.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES CHANGED
@@ -1,3 +1,11 @@
1
+ 1.7.1 (2012-04-22):
2
+
3
+ Beta feature: Rails 3.1 and 3.2 support
4
+
5
+ Thanks to the community (and Eugene Pimenov aka @libc in particular) we now have support
6
+ for Rails versions up to 3.2.1, including new migrations classes.
7
+
8
+ ----------------------------------------------------------------------------------------
1
9
  1.7.0 (2011-08-29):
2
10
 
3
11
  Beta feature: Rails 3 support
@@ -1,6 +1,6 @@
1
1
  = DB Charmer - ActiveRecord Connection Magic Plugin
2
2
 
3
- +DbCharmer+ is a simple yet powerful plugin for ActiveRecord that significantly extends its ability to work with
3
+ +DbCharmer+ is a simple yet powerful plugin for ActiveRecord that significantly extends its ability to work with
4
4
  multiple databases and/or database servers. The major features we add to ActiveRecord are:
5
5
 
6
6
  1. Simple management for AR model connections (+switch_connection_to+ method)
@@ -292,7 +292,7 @@ send their reads to slaves. Example:
292
292
 
293
293
  3) <tt>force_slave_reads!</tt> +ActionController+ instance method, that could be used within
294
294
  your actions or in controller filters to temporarily switch your models to forced slave reads
295
- mode. This method could be useful for cases when the same actions could be called by logged-in
295
+ mode. This method could be useful for cases when the same actions could be called by logged-in
296
296
  and anonymous users. Then you could authorize users in <tt>before_filter</tt> and call
297
297
  <tt>force_slave_reads!</tt> method for anonymous page views.
298
298
 
@@ -419,7 +419,7 @@ tables with hundreds of millions to billions of rows and allows relatively easy
419
419
  to be implemented on top.
420
420
 
421
421
  4) +db_block_group_map+ - really similar to the +db_block_map+ method with a single difference: this method
422
- allows you to have a set of databases (table groups) on each server and every group would be handled as a
422
+ allows you to have a set of databases (table groups) on each server and every group would be handled as a
423
423
  separate shard of data. This approach is really useful for pre-sharding of your data before scaling your
424
424
  application out. You can easily start with one server, having 10-20-50 separate databases, and then
425
425
  move those databases to different servers as you see your database outgrow one machine.
@@ -9,7 +9,7 @@ Gem::Specification.new do |s|
9
9
 
10
10
  s.authors = [ 'Oleksiy Kovyrin' ]
11
11
  s.email = 'alexey@kovyrin.net'
12
- s.homepage = 'http://github.com/kovyrin/db-charmer'
12
+ s.homepage = 'http://kovyrin.github.com/db-charmer'
13
13
  s.summary = 'ActiveRecord Connections Magic (slaves, multiple connections, etc)'
14
14
  s.description = 'DbCharmer is a Rails plugin (and gem) that could be used to manage AR model connections, implement master/slave query schemes, sharding and other magic features many high-scale applications need.'
15
15
 
@@ -20,8 +20,8 @@ Gem::Specification.new do |s|
20
20
  s.extra_rdoc_files = [ 'LICENSE', 'README.rdoc' ]
21
21
 
22
22
  # Dependencies
23
- s.add_dependency 'activesupport', '< 3.1'
24
- s.add_dependency 'activerecord', '< 3.1'
23
+ s.add_dependency 'activesupport', '<= 3.2.3'
24
+ s.add_dependency 'activerecord', '<= 3.2.3'
25
25
 
26
26
  s.add_development_dependency 'rspec'
27
27
  s.add_development_dependency 'yard'
@@ -14,6 +14,11 @@ module DbCharmer
14
14
  ::ActiveRecord::VERSION::MAJOR > 2
15
15
  end
16
16
 
17
+ # Used in all Rails3.1-specific places
18
+ def self.rails31?
19
+ rails3? && ::ActiveRecord::VERSION::MINOR >= 1
20
+ end
21
+
17
22
  # Used in all Rails2-specific places
18
23
  def self.rails2?
19
24
  ::ActiveRecord::VERSION::MAJOR == 2
@@ -134,32 +139,49 @@ end
134
139
 
135
140
  # Enable connection proxy for associations
136
141
  # WARNING: Inject methods to association class right here (they proxy include calls somewhere else, so include does not work)
137
- module ActiveRecord
138
- module Associations
139
- class AssociationProxy
140
- def proxy?
141
- true
142
- end
142
+ association_proxy_class = DbCharmer.rails31? ? ActiveRecord::Associations::CollectionProxy : ActiveRecord::Associations::AssociationProxy
143
+ association_proxy_class.class_eval do
144
+ def proxy?
145
+ true
146
+ end
143
147
 
144
- def on_db(con, proxy_target = nil, &block)
145
- proxy_target ||= self
146
- @reflection.klass.on_db(con, proxy_target, &block)
147
- end
148
+ if DbCharmer.rails31?
149
+ def on_db(con, proxy_target = nil, &block)
150
+ proxy_target ||= self
151
+ @association.klass.on_db(con, proxy_target, &block)
152
+ end
148
153
 
149
- def on_slave(con = nil, &block)
150
- @reflection.klass.on_slave(con, self, &block)
151
- end
154
+ def on_slave(con = nil, &block)
155
+ @association.klass.on_slave(con, self, &block)
156
+ end
152
157
 
153
- def on_master(&block)
154
- @reflection.klass.on_master(self, &block)
155
- end
158
+ def on_master(&block)
159
+ @association.klass.on_master(self, &block)
160
+ end
161
+ else
162
+ def on_db(con, proxy_target = nil, &block)
163
+ proxy_target ||= self
164
+ @reflection.klass.on_db(con, proxy_target, &block)
165
+ end
166
+
167
+ def on_slave(con = nil, &block)
168
+ @reflection.klass.on_slave(con, self, &block)
169
+ end
170
+
171
+ def on_master(&block)
172
+ @reflection.klass.on_master(self, &block)
156
173
  end
157
174
  end
158
175
  end
159
176
 
160
177
  # Enable multi-db migrations
161
178
  require 'db_charmer/active_record/migration/multi_db_migrations'
162
- ActiveRecord::Migration.extend(DbCharmer::ActiveRecord::Migration::MultiDbMigrations)
179
+ ActiveRecord::Migration.send(:include, DbCharmer::ActiveRecord::Migration::MultiDbMigrations)
180
+
181
+ if DbCharmer.rails31?
182
+ require 'db_charmer/rails31/active_record/migration/command_recorder'
183
+ ActiveRecord::Migration::CommandRecorder.send(:include, DbCharmer::ActiveRecord::Migration::CommandRecorder)
184
+ end
163
185
 
164
186
  # Enable the magic
165
187
  if DbCharmer.rails3?
@@ -173,11 +195,19 @@ require 'db_charmer/active_record/db_magic'
173
195
  ActiveRecord::Base.extend(DbCharmer::ActiveRecord::DbMagic)
174
196
 
175
197
  # Setup association preload magic
176
- require 'db_charmer/active_record/association_preload'
177
- ActiveRecord::Base.extend(DbCharmer::ActiveRecord::AssociationPreload)
198
+ if DbCharmer.rails31?
199
+ require 'db_charmer/rails31/active_record/preloader/association'
200
+ ActiveRecord::Associations::Preloader::Association.send(:include, DbCharmer::ActiveRecord::Preloader::Association)
201
+ require 'db_charmer/rails31/active_record/preloader/has_and_belongs_to_many'
202
+ ActiveRecord::Associations::Preloader::HasAndBelongsToMany.send(:include, DbCharmer::ActiveRecord::Preloader::HasAndBelongsToMany)
203
+ else
204
+ require 'db_charmer/active_record/association_preload'
205
+ ActiveRecord::Base.extend(DbCharmer::ActiveRecord::AssociationPreload)
206
+
207
+ # Open up really useful API method
208
+ ActiveRecord::AssociationPreload::ClassMethods.send(:public, :preload_associations)
209
+ end
178
210
 
179
- # Open up really useful API method
180
- ActiveRecord::AssociationPreload::ClassMethods.send(:public, :preload_associations)
181
211
 
182
212
  class ::ActiveRecord::Base
183
213
  class << self
@@ -55,7 +55,7 @@ module DbCharmer
55
55
 
56
56
  protected
57
57
 
58
- class_eval <<-EOF
58
+ class_eval <<-EOF, __FILE__, __LINE__+1
59
59
  def #{DISPATCH_METHOD}_with_forced_slave_reads(*args, &block)
60
60
  DbCharmer.with_controller(self) do
61
61
  #{DISPATCH_METHOD}_without_forced_slave_reads(*args, &block)
@@ -7,7 +7,8 @@ module DbCharmer
7
7
  ASSOCIATION_TYPES.each do |association_type|
8
8
  base.class_eval <<-EOF, __FILE__, __LINE__ + 1
9
9
  def self.preload_#{association_type}_association(records, reflection, preload_options = {})
10
- if self.db_charmer_top_level_connection? || self.db_charmer_default_connection != reflection.klass.db_charmer_default_connection
10
+ if self.db_charmer_top_level_connection? || reflection.options[:polymorphic] ||
11
+ self.db_charmer_default_connection != reflection.klass.db_charmer_default_connection
11
12
  return super(records, reflection, preload_options)
12
13
  end
13
14
  reflection.klass.on_db(self) do
@@ -34,6 +34,16 @@ module DbCharmer
34
34
  db_charmer_remapped_connection || db_charmer_connection_proxy || connection_without_magic
35
35
  end
36
36
  alias_method_chain :connection, :magic
37
+
38
+ def connection_pool_with_magic
39
+ abstract_connection_class = connection.abstract_connection_class rescue nil # respond_to? doesn't work on connection_proxy...
40
+ if abstract_connection_class
41
+ connection_handler.retrieve_connection_pool(abstract_connection_class) || connection_pool_without_magic
42
+ else
43
+ connection_pool_without_magic
44
+ end
45
+ end
46
+ alias_method_chain :connection_pool, :magic
37
47
  end
38
48
  end
39
49
 
@@ -38,9 +38,15 @@ module DbCharmer
38
38
  def setup_children_magic(opt)
39
39
  self.db_charmer_opts = opt.clone
40
40
 
41
- def self.inherited(child)
42
- child.db_magic(self.db_charmer_opts)
43
- super
41
+ unless self.respond_to?(:inherited_with_db_magic)
42
+ class << self
43
+ def inherited_with_db_magic(child)
44
+ o = inherited_without_db_magic(child)
45
+ child.db_magic(self.db_charmer_opts)
46
+ o
47
+ end
48
+ alias_method_chain :inherited, :db_magic
49
+ end
44
50
  end
45
51
  end
46
52
 
@@ -82,4 +88,3 @@ module DbCharmer
82
88
  end
83
89
  end
84
90
  end
85
-
@@ -3,23 +3,84 @@ module DbCharmer
3
3
  module Migration
4
4
  module MultiDbMigrations
5
5
 
6
- def self.extended(base)
7
- class << base
8
- alias_method_chain :migrate, :db_wrapper
6
+ def self.append_features(base)
7
+ return false if base < self
8
+ super
9
+ base.extend const_get("ClassMethods") if const_defined?("ClassMethods")
10
+
11
+ base.class_eval do
12
+ if DbCharmer.rails31?
13
+ alias_method_chain :migrate, :db_wrapper
14
+ else
15
+ class << self
16
+ alias_method_chain :migrate, :db_wrapper
17
+ end
18
+ end
9
19
  end
10
20
  end
11
21
 
12
- @@multi_db_names = {}
13
- def multi_db_names
14
- @@multi_db_names[self.name] || @@multi_db_names['ActiveRecord::Migration']
15
- end
22
+ module ClassMethods
23
+ @@multi_db_names = {}
24
+ def multi_db_names
25
+ @@multi_db_names[self.name] || @@multi_db_names['ActiveRecord::Migration']
26
+ end
27
+
28
+ def multi_db_names=(names)
29
+ @@multi_db_names[self.name] = names
30
+ end
31
+
32
+ unless DbCharmer.rails31?
33
+ def migrate_with_db_wrapper(direction)
34
+ if names = multi_db_names
35
+ names.each do |multi_db_name|
36
+ on_db(multi_db_name) do
37
+ migrate_without_db_wrapper(direction)
38
+ end
39
+ end
40
+ else
41
+ migrate_without_db_wrapper(direction)
42
+ end
43
+ end
44
+
45
+ def on_db(db_name)
46
+ name = db_name.is_a?(Hash) ? db_name[:connection_name] : db_name.inspect
47
+ announce "Switching connection to #{name}"
48
+ # Switch connection
49
+ old_proxy = ::ActiveRecord::Base.db_charmer_connection_proxy
50
+ db_name = nil if db_name == :default
51
+ ::ActiveRecord::Base.switch_connection_to(db_name, DbCharmer.connections_should_exist?)
52
+ # Yield the block
53
+ yield
54
+ ensure
55
+ # Switch it back
56
+ ::ActiveRecord::Base.verify_active_connections!
57
+ announce "Switching connection back"
58
+ ::ActiveRecord::Base.switch_connection_to(old_proxy)
59
+ end
60
+ end
61
+
62
+ def db_magic(opts = {})
63
+ # Collect connections from all possible options
64
+ conns = [ opts[:connection], opts[:connections] ]
65
+ conns << shard_connections(opts[:sharded_connection]) if opts[:sharded_connection]
66
+
67
+ # Get a unique set of connections
68
+ conns = conns.flatten.compact.uniq
69
+ raise ArgumentError, "No connection name - no magic!" unless conns.any?
70
+
71
+ # Save connections
72
+ self.multi_db_names = conns
73
+ end
16
74
 
17
- def multi_db_names=(names)
18
- @@multi_db_names[self.name] = names
75
+ # Return a list of connections to shards in a sharded connection
76
+ def shard_connections(conn_name)
77
+ conn = DbCharmer::Sharding.sharded_connection(conn_name)
78
+ conn.shard_connections
79
+ end
19
80
  end
20
81
 
21
82
  def migrate_with_db_wrapper(direction)
22
- if names = multi_db_names
83
+ if names = self.class.multi_db_names
23
84
  names.each do |multi_db_name|
24
85
  on_db(multi_db_name) do
25
86
  migrate_without_db_wrapper(direction)
@@ -30,41 +91,46 @@ module DbCharmer
30
91
  end
31
92
  end
32
93
 
33
- def on_db(db_name)
94
+ def record_on_db(db_name, block)
95
+ recorder = ::ActiveRecord::Migration::CommandRecorder.new(DbCharmer::ConnectionFactory.connect(db_name))
96
+ old_recorder, @connection = @connection, recorder
97
+ block.call
98
+ old_recorder.record :on_db, [db_name, @connection]
99
+ @connection = old_recorder
100
+ end
101
+
102
+ def replay_commands_on_db(name, commands)
103
+ on_db(name) do
104
+ commands.each do |cmd, args|
105
+ send(cmd, *args)
106
+ end
107
+ end
108
+ end
109
+
110
+ def on_db(db_name, &block)
111
+ if @connection.is_a?(::ActiveRecord::Migration::CommandRecorder)
112
+ record_on_db(db_name, block)
113
+ return
114
+ end
115
+
34
116
  name = db_name.is_a?(Hash) ? db_name[:connection_name] : db_name.inspect
35
117
  announce "Switching connection to #{name}"
36
118
  # Switch connection
37
- old_proxy = ::ActiveRecord::Base.db_charmer_connection_proxy
119
+ old_connection, old_proxy = @connection, ::ActiveRecord::Base.db_charmer_connection_proxy
38
120
  db_name = nil if db_name == :default
39
121
  ::ActiveRecord::Base.switch_connection_to(db_name, DbCharmer.connections_should_exist?)
40
122
  # Yield the block
41
- yield
123
+ ::ActiveRecord::Base.connection_pool.with_connection do |conn|
124
+ @connection = conn
125
+ yield
126
+ end
42
127
  ensure
128
+ @connection = old_connection
43
129
  # Switch it back
44
130
  ::ActiveRecord::Base.verify_active_connections!
45
131
  announce "Switching connection back"
46
132
  ::ActiveRecord::Base.switch_connection_to(old_proxy)
47
133
  end
48
-
49
- def db_magic(opts = {})
50
- # Collect connections from all possible options
51
- conns = [ opts[:connection], opts[:connections] ]
52
- conns << shard_connections(opts[:sharded_connection]) if opts[:sharded_connection]
53
-
54
- # Get a unique set of connections
55
- conns = conns.flatten.compact.uniq
56
- raise ArgumentError, "No connection name - no magic!" unless conns.any?
57
-
58
- # Save connections
59
- self.multi_db_names = conns
60
- end
61
-
62
- # Return a list of connections to shards in a sharded connection
63
- def shard_connections(conn_name)
64
- conn = DbCharmer::Sharding.sharded_connection(conn_name)
65
- conn.shard_connections
66
- end
67
-
68
134
  end
69
135
  end
70
136
  end
@@ -0,0 +1,11 @@
1
+ module DbCharmer
2
+ module ActiveRecord
3
+ module Migration
4
+ module CommandRecorder
5
+ def invert_on_db(args)
6
+ [:replay_commands_on_db, [args.first, args[1].inverse]]
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,21 @@
1
+ module DbCharmer
2
+ module ActiveRecord
3
+ module Preloader
4
+ module Association
5
+ extend ActiveSupport::Concern
6
+ included do
7
+ alias_method_chain :build_scope, :db_magic
8
+ end
9
+
10
+ def build_scope_with_db_magic
11
+ if model.db_charmer_top_level_connection? || reflection.options[:polymorphic] ||
12
+ model.db_charmer_default_connection != klass.db_charmer_default_connection
13
+ build_scope_without_db_magic
14
+ else
15
+ build_scope_without_db_magic.on_db(model)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,23 @@
1
+ module DbCharmer
2
+ module ActiveRecord
3
+ module Preloader
4
+ module HasAndBelongsToMany
5
+ extend ActiveSupport::Concern
6
+ included do
7
+ alias_method_chain :records_for, :db_magic
8
+ end
9
+
10
+ def records_for_with_db_magic(ids)
11
+ if model.db_charmer_top_level_connection? || reflection.options[:polymorphic] ||
12
+ model.db_charmer_default_connection != klass.db_charmer_default_connection
13
+ records_for_without_db_magic(ids)
14
+ else
15
+ klass.on_db(model) do
16
+ records_for_without_db_magic(ids)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -22,7 +22,7 @@ module DbCharmer
22
22
  def support_default_shard?
23
23
  sharder.respond_to?(:support_default_shard?) && sharder.support_default_shard?
24
24
  end
25
-
25
+
26
26
  def default_connection
27
27
  @default_connection ||= DbCharmer::Sharding::StubConnection.new(self)
28
28
  end
@@ -201,7 +201,7 @@ module DbCharmer
201
201
 
202
202
  # Prepare model for working with our shards table
203
203
  def prepare_shard_model
204
- ShardInfo.set_table_name(shards_table)
204
+ ShardInfo.table_name = shards_table
205
205
  ShardInfo.switch_connection_to(connection)
206
206
  end
207
207
 
@@ -2,7 +2,7 @@ module DbCharmer
2
2
  module Version
3
3
  MAJOR = 1
4
4
  MINOR = 7
5
- PATCH = 0
5
+ PATCH = 1
6
6
  BUILD = nil
7
7
 
8
8
  STRING = [MAJOR, MINOR, PATCH, BUILD].compact.join('.')
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: db-charmer
3
3
  version: !ruby/object:Gem::Version
4
- hash: 11
4
+ hash: 9
5
5
  prerelease:
6
6
  segments:
7
7
  - 1
8
8
  - 7
9
- - 0
10
- version: 1.7.0
9
+ - 1
10
+ version: 1.7.1
11
11
  platform: ruby
12
12
  authors:
13
13
  - Oleksiy Kovyrin
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-08-29 00:00:00 Z
18
+ date: 2012-04-22 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: activesupport
@@ -23,13 +23,14 @@ dependencies:
23
23
  requirement: &id001 !ruby/object:Gem::Requirement
24
24
  none: false
25
25
  requirements:
26
- - - <
26
+ - - <=
27
27
  - !ruby/object:Gem::Version
28
- hash: 5
28
+ hash: 9
29
29
  segments:
30
30
  - 3
31
- - 1
32
- version: "3.1"
31
+ - 2
32
+ - 3
33
+ version: 3.2.3
33
34
  type: :runtime
34
35
  version_requirements: *id001
35
36
  - !ruby/object:Gem::Dependency
@@ -38,13 +39,14 @@ dependencies:
38
39
  requirement: &id002 !ruby/object:Gem::Requirement
39
40
  none: false
40
41
  requirements:
41
- - - <
42
+ - - <=
42
43
  - !ruby/object:Gem::Version
43
- hash: 5
44
+ hash: 9
44
45
  segments:
45
46
  - 3
46
- - 1
47
- version: "3.1"
47
+ - 2
48
+ - 3
49
+ version: 3.2.3
48
50
  type: :runtime
49
51
  version_requirements: *id002
50
52
  - !ruby/object:Gem::Dependency
@@ -128,6 +130,9 @@ files:
128
130
  - lib/db_charmer/rails3/active_record/master_slave_routing.rb
129
131
  - lib/db_charmer/rails3/active_record/relation/connection_routing.rb
130
132
  - lib/db_charmer/rails3/active_record/relation_method.rb
133
+ - lib/db_charmer/rails31/active_record/migration/command_recorder.rb
134
+ - lib/db_charmer/rails31/active_record/preloader/association.rb
135
+ - lib/db_charmer/rails31/active_record/preloader/has_and_belongs_to_many.rb
131
136
  - lib/db_charmer/sharding.rb
132
137
  - lib/db_charmer/sharding/connection.rb
133
138
  - lib/db_charmer/sharding/method.rb
@@ -138,7 +143,7 @@ files:
138
143
  - lib/db_charmer/sharding/stub_connection.rb
139
144
  - lib/db_charmer/version.rb
140
145
  - lib/tasks/databases.rake
141
- homepage: http://github.com/kovyrin/db-charmer
146
+ homepage: http://kovyrin.github.com/db-charmer
142
147
  licenses: []
143
148
 
144
149
  post_install_message:
@@ -167,7 +172,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
167
172
  requirements: []
168
173
 
169
174
  rubyforge_project:
170
- rubygems_version: 1.8.8
175
+ rubygems_version: 1.8.23
171
176
  signing_key:
172
177
  specification_version: 3
173
178
  summary: ActiveRecord Connections Magic (slaves, multiple connections, etc)