db-charmer 1.7.0 → 1.7.1

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/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)