db-charmer 1.7.0.pre6 → 1.7.0.pre7

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES CHANGED
@@ -1,5 +1,7 @@
1
1
  1.7.0 (not released yet):
2
2
 
3
+ Beta feature: Rails 3 support
4
+
3
5
  Beta feature: Added force_slave_reads functionality. Now we could have models with slaves
4
6
  that are not used by default, but could be turned on globally (per-controller or per-action).
5
7
 
@@ -560,26 +560,36 @@ return an array of sharding connection names or connection configurations to be
560
560
  establish shard connections in a loop.
561
561
 
562
562
 
563
- == Documentation
563
+ == Documentation/Questions
564
564
 
565
565
  For more information on the plugin internals, please check out the source code. All the plugin's
566
- code is ~100% covered with a tests that were placed in a separate staging rails project located
566
+ code is ~100% covered with tests that were placed in a separate staging rails project located
567
567
  at http://github.com/kovyrin/db-charmer-sandbox. The project has unit tests for all or at least the
568
568
  most of the parts of plugin's code.
569
569
 
570
+ If you have any questions regarding this project, you could contact the author using
571
+ the DbCharmer Users Group mailing list:
572
+
573
+ - Group Info: http://groups.google.com/group/db-charmer
574
+ - Subscribe using the info page or by sending an email to db-charmer-subscribe@googlegroups.com
575
+
570
576
 
571
577
  == What Ruby and Rails implementations does it work for?
572
578
 
573
- We have a continuous integration setups for this plugin on MRI 1.8.7 with Rails 2.2 and 2.3.
574
- We use the plugin in production on Scribd.com with MRI (rubyee) 1.8.7 and Rails 2.2, Rails 2.3,
575
- Sinatra and plain Rack applications.
579
+ We have a continuous integration setups for this plugin on MRI 1.8.7 with Rails 2.2, 2.3 and 3.0.
580
+ We use the plugin in production on Scribd.com with MRI (Ruby Enterprise Edition) 1.8.7 and
581
+ Rails 2.2, Rails 2.3, Sinatra and plain Rack applications.
582
+
583
+ Starting with version 1.7.0 we support Rails 3.0, but until further notice we consider it a
584
+ beta-quality feature since we do not run any production software on this version of Rails. If you
585
+ run your application on Rails 3.0, please contact the author.
576
586
 
577
587
 
578
588
  == Who are the authors?
579
589
 
580
590
  This plugin has been created in Scribd.com for our internal use and then the sources were opened for
581
- other people to use. Most of the code in this package has been developed by Alexey Kovyrin for Scribd.com
582
- and is released under the MIT license. For more details, see the LICENSE file.
591
+ other people to use. Most of the code in this package has been developed by Oleksiy Kovyrin for
592
+ Scribd.com and is released under the MIT license. For more details, see the LICENSE file.
583
593
 
584
594
  Other contributors who have helped with the development of this library are (alphabetically ordered):
585
595
  * Allen Madsen
@@ -7,7 +7,7 @@ Gem::Specification.new do |s|
7
7
  s.version = DbCharmer::Version::STRING
8
8
  s.platform = Gem::Platform::RUBY
9
9
 
10
- s.authors = [ 'Alexey Kovyrin' ]
10
+ s.authors = [ 'Oleksiy Kovyrin' ]
11
11
  s.email = 'alexey@kovyrin.net'
12
12
  s.homepage = 'http://github.com/kovyrin/db-charmer'
13
13
  s.summary = 'ActiveRecord Connections Magic (slaves, multiple connections, etc)'
@@ -15,16 +15,15 @@ Gem::Specification.new do |s|
15
15
 
16
16
  s.rdoc_options = [ '--charset=UTF-8' ]
17
17
 
18
- s.add_development_dependency 'rspec'
19
- s.add_development_dependency 'yard'
20
- s.add_development_dependency 'actionpack'
21
-
22
18
  s.files = `git ls-files`.split("\n")
23
19
  s.require_paths = [ 'lib' ]
24
20
  s.extra_rdoc_files = [ 'LICENSE', 'README.rdoc' ]
25
21
 
26
22
  # Dependencies
27
- s.add_dependency 'activesupport', '~> 2.2'
28
- s.add_dependency 'activerecord', '~> 2.2'
29
- end
23
+ s.add_dependency 'activesupport', '< 3.1'
24
+ s.add_dependency 'activerecord', '< 3.1'
30
25
 
26
+ s.add_development_dependency 'rspec'
27
+ s.add_development_dependency 'yard'
28
+ s.add_development_dependency 'actionpack'
29
+ end
@@ -1,3 +1,6 @@
1
+ # In Rails 2.2 they did not add it to the autoload so it won't work w/o this require
2
+ require 'active_record/version' unless defined?(::ActiveRecord::VERSION::MAJOR)
3
+
1
4
  module DbCharmer
2
5
  # Configure autoload
3
6
  autoload :Sharding, 'db_charmer/sharding'
@@ -6,6 +9,16 @@ module DbCharmer
6
9
  autoload :ForceSlaveReads, 'db_charmer/action_controller/force_slave_reads'
7
10
  end
8
11
 
12
+ # Used in all Rails3-specific places
13
+ def self.rails3?
14
+ ::ActiveRecord::VERSION::MAJOR > 2
15
+ end
16
+
17
+ # Used in all Rails2-specific places
18
+ def self.rails2?
19
+ ::ActiveRecord::VERSION::MAJOR == 2
20
+ end
21
+
9
22
  # Accessors
10
23
  @@connections_should_exist = true
11
24
  mattr_accessor :connections_should_exist
@@ -62,7 +75,8 @@ private
62
75
  old_hijack_new_classes = @@hijack_new_classes
63
76
  begin
64
77
  @@hijack_new_classes = true
65
- ::ActiveRecord::Base.send(:subclasses).each do |subclass|
78
+ subclasses_method = DbCharmer.rails3? ? :descendants : :subclasses
79
+ ::ActiveRecord::Base.send(subclasses_method).each do |subclass|
66
80
  subclass.hijack_connection!
67
81
  end
68
82
  yield
@@ -87,9 +101,16 @@ ActiveRecord::Base.extend(DbCharmer::ActiveRecord::ClassAttributes)
87
101
  require 'db_charmer/active_record/connection_switching'
88
102
  ActiveRecord::Base.extend(DbCharmer::ActiveRecord::ConnectionSwitching)
89
103
 
90
- # Enable misc AR extensions
91
- require 'db_charmer/abstract_adapter/log_formatting'
92
- ActiveRecord::ConnectionAdapters::AbstractAdapter.send(:include, DbCharmer::AbstractAdapter::LogFormatting)
104
+ # Enable AR logging extensions
105
+ if DbCharmer.rails3?
106
+ require 'db_charmer/rails3/abstract_adapter/connection_name'
107
+ require 'db_charmer/rails3/active_record/log_subscriber'
108
+ ActiveRecord::LogSubscriber.send(:include, DbCharmer::ActiveRecord::LogSubscriber)
109
+ ActiveRecord::ConnectionAdapters::AbstractAdapter.send(:include, DbCharmer::AbstractAdapter::ConnectionName)
110
+ else
111
+ require 'db_charmer/rails2/abstract_adapter/log_formatting'
112
+ ActiveRecord::ConnectionAdapters::AbstractAdapter.send(:include, DbCharmer::AbstractAdapter::LogFormatting)
113
+ end
93
114
 
94
115
  # Enable connection proxy in AR
95
116
  require 'db_charmer/active_record/multi_db_proxy'
@@ -97,9 +118,19 @@ ActiveRecord::Base.extend(DbCharmer::ActiveRecord::MultiDbProxy::ClassMethods)
97
118
  ActiveRecord::Base.extend(DbCharmer::ActiveRecord::MultiDbProxy::MasterSlaveClassMethods)
98
119
  ActiveRecord::Base.send(:include, DbCharmer::ActiveRecord::MultiDbProxy::InstanceMethods)
99
120
 
100
- # Enable connection proxy for scopes
101
- require 'db_charmer/active_record/named_scope/scope_proxy'
102
- ActiveRecord::NamedScope::Scope.send(:include, DbCharmer::ActiveRecord::NamedScope::ScopeProxy)
121
+ # Enable connection proxy for relations
122
+ if DbCharmer.rails3?
123
+ require 'db_charmer/rails3/active_record/relation_method'
124
+ require 'db_charmer/rails3/active_record/relation/connection_routing'
125
+ ActiveRecord::Base.extend(DbCharmer::ActiveRecord::RelationMethod)
126
+ ActiveRecord::Relation.send(:include, DbCharmer::ActiveRecord::Relation::ConnectionRouting)
127
+ end
128
+
129
+ # Enable connection proxy for scopes (rails 2.x only)
130
+ if DbCharmer.rails2?
131
+ require 'db_charmer/rails2/active_record/named_scope/scope_proxy'
132
+ ActiveRecord::NamedScope::Scope.send(:include, DbCharmer::ActiveRecord::NamedScope::ScopeProxy)
133
+ end
103
134
 
104
135
  # Enable connection proxy for associations
105
136
  # WARNING: Inject methods to association class right here (they proxy include calls somewhere else, so include does not work)
@@ -131,7 +162,12 @@ require 'db_charmer/active_record/migration/multi_db_migrations'
131
162
  ActiveRecord::Migration.extend(DbCharmer::ActiveRecord::Migration::MultiDbMigrations)
132
163
 
133
164
  # Enable the magic
134
- require 'db_charmer/active_record/finder_overrides'
165
+ if DbCharmer.rails3?
166
+ require 'db_charmer/rails3/active_record/master_slave_routing'
167
+ else
168
+ require 'db_charmer/rails2/active_record/master_slave_routing'
169
+ end
170
+
135
171
  require 'db_charmer/active_record/sharding'
136
172
  require 'db_charmer/active_record/db_magic'
137
173
  ActiveRecord::Base.extend(DbCharmer::ActiveRecord::DbMagic)
@@ -35,8 +35,10 @@ module DbCharmer
35
35
  end
36
36
 
37
37
  module InstanceMethods
38
+ DISPATCH_METHOD = (DbCharmer.rails3?) ? :process_action : :perform_action
39
+
38
40
  def self.included(base)
39
- base.alias_method_chain :perform_action, :forced_slave_reads
41
+ base.alias_method_chain DISPATCH_METHOD, :forced_slave_reads
40
42
  end
41
43
 
42
44
  def force_slave_reads!
@@ -53,11 +55,13 @@ module DbCharmer
53
55
 
54
56
  protected
55
57
 
56
- def perform_action_with_forced_slave_reads
57
- DbCharmer.with_controller(self) do
58
- perform_action_without_forced_slave_reads
58
+ class_eval <<-EOF
59
+ def #{DISPATCH_METHOD}_with_forced_slave_reads(*args, &block)
60
+ DbCharmer.with_controller(self) do
61
+ #{DISPATCH_METHOD}_without_forced_slave_reads(*args, &block)
62
+ end
59
63
  end
60
- end
64
+ EOF
61
65
  end
62
66
 
63
67
  end
@@ -29,6 +29,7 @@ module DbCharmer
29
29
  def hijack_connection!
30
30
  return if self.respond_to?(:connection_with_magic)
31
31
  class << self
32
+ # Make sure we check our accessors before going to the default connection retrieval method
32
33
  def connection_with_magic
33
34
  db_charmer_remapped_connection || db_charmer_connection_proxy || connection_without_magic
34
35
  end
@@ -62,8 +63,8 @@ module DbCharmer
62
63
  end
63
64
 
64
65
  #-----------------------------------------------------------------------------------------------------------------
65
- def switch_connection_to(conn, require_config_to_exist = true)
66
- new_conn = coerce_to_connection_proxy(conn, require_config_to_exist)
66
+ def switch_connection_to(conn, should_exist = true)
67
+ new_conn = coerce_to_connection_proxy(conn, should_exist)
67
68
 
68
69
  if db_charmer_connection_proxy.is_a?(DbCharmer::Sharding::StubConnection)
69
70
  db_charmer_connection_proxy.set_real_connection(new_conn)
@@ -71,7 +72,10 @@ module DbCharmer
71
72
 
72
73
  self.db_charmer_connection_proxy = new_conn
73
74
  self.hijack_connection!
75
+
76
+ # self.reset_column_information
74
77
  end
78
+
75
79
  end
76
80
  end
77
81
  end
@@ -21,7 +21,7 @@ module DbCharmer
21
21
  forced_slave_reads = opt.has_key?(:force_slave_reads) ? opt[:force_slave_reads] : true
22
22
 
23
23
  # Setup all the slaves related magic if needed
24
- setup_slaves_magic(opt[:slaves], forced_slave_reads, should_exist) if opt[:slaves].any?
24
+ setup_slaves_magic(opt[:slaves], forced_slave_reads, should_exist)
25
25
 
26
26
  # Setup inheritance magic
27
27
  setup_children_magic(opt)
@@ -63,15 +63,20 @@ module DbCharmer
63
63
  end
64
64
 
65
65
  def setup_slaves_magic(slaves, force_slave_reads, should_exist = true)
66
+ self.db_charmer_force_slave_reads = force_slave_reads
67
+
68
+ # Initialize the slave connections list
66
69
  self.db_charmer_slaves = slaves.collect do |slave|
67
70
  coerce_to_connection_proxy(slave, should_exist)
68
71
  end
72
+ return if db_charmer_slaves.empty?
69
73
 
70
- self.db_charmer_force_slave_reads = force_slave_reads
71
-
72
- self.extend(DbCharmer::ActiveRecord::FinderOverrides::ClassMethods)
73
- self.send(:include, DbCharmer::ActiveRecord::FinderOverrides::InstanceMethods)
74
+ # Enable on_slave/on_master methods
74
75
  self.extend(DbCharmer::ActiveRecord::MultiDbProxy::MasterSlaveClassMethods)
76
+
77
+ # Enable automatic master/slave queries routing (we have specialized versions on those modules for rails2/3)
78
+ self.extend(DbCharmer::ActiveRecord::MasterSlaveRouting::ClassMethods)
79
+ self.send(:include, DbCharmer::ActiveRecord::MasterSlaveRouting::InstanceMethods)
75
80
  end
76
81
 
77
82
  end
@@ -62,6 +62,15 @@ module DbCharmer
62
62
  def on_master(proxy_target = nil, &block)
63
63
  on_db(db_charmer_default_connection, proxy_target, &block)
64
64
  end
65
+
66
+ def first_level_on_slave
67
+ first_level = db_charmer_top_level_connection? && on_master.connection.open_transactions.zero?
68
+ if first_level && db_charmer_force_slave_reads? && db_charmer_slaves.any?
69
+ on_slave { yield }
70
+ else
71
+ yield
72
+ end
73
+ end
65
74
  end
66
75
  end
67
76
  end
@@ -4,6 +4,9 @@ module DbCharmer
4
4
  # We need to do this because in Rails 2.3 BasicObject does not remove object_id method, which is stupid
5
5
  undef_method(:object_id) if instance_methods.member?('object_id')
6
6
 
7
+ # We use this to get a connection class from the proxy
8
+ attr_accessor :abstract_connection_class
9
+
7
10
  def initialize(abstract_class, db_name)
8
11
  @abstract_connection_class = abstract_class
9
12
  @db_name = db_name
@@ -11,6 +11,7 @@ module DbCharmer
11
11
  @config[:connection_name]
12
12
  end
13
13
 
14
+ # Rails 2.X specific logging method
14
15
  def format_log_entry_with_connection_name(message, dump = nil)
15
16
  msg = connection_name ? "[#{connection_name}] " : ''
16
17
  msg = " \e[0;34;1m#{msg}\e[0m" if connection_name && ::ActiveRecord::Base.colorize_logging
@@ -1,6 +1,6 @@
1
1
  module DbCharmer
2
2
  module ActiveRecord
3
- module FinderOverrides
3
+ module MasterSlaveRouting
4
4
 
5
5
  module ClassMethods
6
6
  SLAVE_METHODS = [ :find_by_sql, :count_by_sql, :calculate ]
@@ -34,18 +34,6 @@ module DbCharmer
34
34
  super(*args, &block)
35
35
  end
36
36
  end
37
-
38
- private
39
-
40
- def first_level_on_slave
41
- first_level = db_charmer_top_level_connection? && on_master.connection.open_transactions.zero?
42
- if first_level && db_charmer_force_slave_reads?
43
- on_slave { yield }
44
- else
45
- yield
46
- end
47
- end
48
-
49
37
  end
50
38
 
51
39
  module InstanceMethods
@@ -0,0 +1,38 @@
1
+ module DbCharmer
2
+ module AbstractAdapter
3
+ module ConnectionName
4
+
5
+ # We use this proxy to push connection name down to instrumenters w/o monkey-patching the log method itself
6
+ class InstrumenterDecorator < ActiveSupport::BasicObject
7
+ def initialize(adapter, instrumenter)
8
+ @adapter = adapter
9
+ @instrumenter = instrumenter
10
+ end
11
+
12
+ def instrument(name, payload = {}, &block)
13
+ payload[:connection_name] ||= @adapter.connection_name
14
+ @instrumenter.instrument(name, payload, &block)
15
+ end
16
+
17
+ def method_missing(meth, *args, &block)
18
+ @instrumenter.send(meth, *args, &block)
19
+ end
20
+ end
21
+
22
+ def self.included(base)
23
+ base.alias_method_chain :initialize, :connection_name
24
+ end
25
+
26
+ def connection_name
27
+ raise "Can't find connection configuration!" unless @config
28
+ @config[:connection_name]
29
+ end
30
+
31
+ def initialize_with_connection_name(*args)
32
+ initialize_without_connection_name(*args)
33
+ @instrumenter = InstrumenterDecorator.new(self, @instrumenter)
34
+ end
35
+
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,23 @@
1
+ module DbCharmer
2
+ module ActiveRecord
3
+ module LogSubscriber
4
+
5
+ def self.included(base)
6
+ base.send(:attr_accessor, :connection_name)
7
+ base.alias_method_chain :sql, :connection_name
8
+ base.alias_method_chain :debug, :connection_name
9
+ end
10
+
11
+ def sql_with_connection_name(event)
12
+ self.connection_name = event.payload[:connection_name]
13
+ sql_without_connection_name(event)
14
+ end
15
+
16
+ def debug_with_connection_name(msg)
17
+ conn = connection_name ? color(" [#{connection_name}]", ActiveSupport::LogSubscriber::BLUE, true) : ''
18
+ debug_without_connection_name(conn + msg)
19
+ end
20
+
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,46 @@
1
+ module DbCharmer
2
+ module ActiveRecord
3
+ module MasterSlaveRouting
4
+
5
+ module ClassMethods
6
+ SLAVE_METHODS = [ :find_by_sql, :count_by_sql ]
7
+ MASTER_METHODS = [ ] # I don't know any methods in AR::Base that change data directly w/o going to the relation object
8
+
9
+ SLAVE_METHODS.each do |slave_method|
10
+ class_eval <<-EOF, __FILE__, __LINE__ + 1
11
+ def #{slave_method}(*args, &block)
12
+ first_level_on_slave do
13
+ super(*args, &block)
14
+ end
15
+ end
16
+ EOF
17
+ end
18
+
19
+ MASTER_METHODS.each do |master_method|
20
+ class_eval <<-EOF, __FILE__, __LINE__ + 1
21
+ def #{master_method}(*args, &block)
22
+ on_master do
23
+ super(*args, &block)
24
+ end
25
+ end
26
+ EOF
27
+ end
28
+ end
29
+
30
+ module InstanceMethods
31
+ MASTER_METHODS = [ :reload ]
32
+
33
+ MASTER_METHODS.each do |master_method|
34
+ class_eval <<-EOF, __FILE__, __LINE__ + 1
35
+ def #{master_method}(*args, &block)
36
+ self.class.on_master do
37
+ super(*args, &block)
38
+ end
39
+ end
40
+ EOF
41
+ end
42
+ end
43
+
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,147 @@
1
+ module DbCharmer
2
+ module ActiveRecord
3
+ module Relation
4
+ module ConnectionRouting
5
+
6
+ # All the methods that could be querying the database
7
+ SLAVE_METHODS = [ :calculate, :exists? ]
8
+ MASTER_METHODS = [ :delete, :delete_all, :destroy, :destroy_all, :reload, :update, :update_all ]
9
+ ALL_METHODS = SLAVE_METHODS + MASTER_METHODS
10
+
11
+ DB_CHARMER_ATTRIBUTES = [ :db_charmer_connection, :db_charmer_connection_is_forced, :db_charmer_enable_slaves ]
12
+
13
+ # Define the default relation connection + override all the query methods here
14
+ def self.included(base)
15
+ init_attributes(base)
16
+ init_routing(base)
17
+ end
18
+
19
+ # Define our attributes + spawn methods shit needs to be changed to make sure our accessors are copied over to the new instances
20
+ def self.init_attributes(base)
21
+ DB_CHARMER_ATTRIBUTES.each do |attr|
22
+ base.send(:attr_accessor, attr)
23
+ end
24
+
25
+ # Override spawn methods
26
+ base.alias_method_chain :except, :db_charmer
27
+ base.alias_method_chain :only, :db_charmer
28
+ end
29
+
30
+ # Override all query methods
31
+ def self.init_routing(base)
32
+ ALL_METHODS.each do |meth|
33
+ base.alias_method_chain meth, :db_charmer
34
+ end
35
+
36
+ # Special case: for normal selects we go to the slave, but for selects with a lock we should use master
37
+ base.alias_method_chain :to_a, :db_charmer
38
+ end
39
+
40
+ # Copy db_charmer attributes in addition to what they're copying
41
+ def except_with_db_charmer(*args)
42
+ except_without_db_charmer(*args).tap do |result|
43
+ copy_db_charmer_options(self, result)
44
+ end
45
+ end
46
+
47
+ # Copy db_charmer attributes in addition to what they're copying
48
+ def only_with_db_charmer(*args)
49
+ only_without_db_charmer(*args).tap do |result|
50
+ copy_db_charmer_options(self, result)
51
+ end
52
+ end
53
+
54
+ # Copy our accessors from one instance to another
55
+ def copy_db_charmer_options(src, dst)
56
+ DB_CHARMER_ATTRIBUTES.each do |attr|
57
+ dst.send("#{attr}=".to_sym, src.send(attr))
58
+ end
59
+ end
60
+
61
+ # Connection switching (changes the default relation connection)
62
+ def on_db(con, &block)
63
+ if block_given?
64
+ @klass.on_db(con, &block)
65
+ else
66
+ clone.tap do |result|
67
+ result.db_charmer_connection = con
68
+ result.db_charmer_connection_is_forced = true
69
+ end
70
+ end
71
+ end
72
+
73
+ # Make sure we get the right connection here
74
+ def connection
75
+ @klass.on_db(db_charmer_connection).connection
76
+ end
77
+
78
+ # Selects preferred destination (master/slave/default) for a query
79
+ def select_destination(method, recommendation = :default)
80
+ # If this relation was created within a forced connection block (e.g Model.on_db(:foo).relation)
81
+ # Then we should use that connection everywhere except cases when a model is slave-enabled
82
+ # in those cases DML queries go to the master
83
+ if db_charmer_connection_is_forced
84
+ return :master if db_charmer_enable_slaves && MASTER_METHODS.member?(method)
85
+ return :default
86
+ end
87
+
88
+ # If this relation is created from a slave-enabled model, let's do the routing if possible
89
+ if db_charmer_enable_slaves
90
+ return :slave if SLAVE_METHODS.member?(method)
91
+ return :master if MASTER_METHODS.member?(method)
92
+ else
93
+ # Make sure we do not use recommended destination
94
+ recommendation = :default
95
+ end
96
+
97
+ # If nothing else came up, let's use the default or recommended connection
98
+ return recommendation
99
+ end
100
+
101
+ # Switch the model to default relation connection
102
+ def switch_connection_for_method(method, recommendation = nil)
103
+ # Choose where to send the query
104
+ destination ||= select_destination(method, recommendation)
105
+
106
+ # What method to use
107
+ on_db_method = [ :on_db, db_charmer_connection ]
108
+ on_db_method = :on_master if destination == :master
109
+ on_db_method = :first_level_on_slave if destination == :slave
110
+
111
+ # Perform the query
112
+ @klass.send(*on_db_method) do
113
+ yield
114
+ end
115
+ end
116
+
117
+ # For normal selects we go to the slave, but for selects with a lock we should use master
118
+ def to_a_with_db_charmer(*args, &block)
119
+ preferred_destination = :slave
120
+ preferred_destination = :master if lock_value
121
+
122
+ switch_connection_for_method(:to_a, preferred_destination) do
123
+ to_a_without_db_charmer(*args, &block)
124
+ end
125
+ end
126
+
127
+ # Need this to mimick alias_method_chain name generation (exists? => exists_with_db_charmer?)
128
+ def self.aliased_method_name(target, with)
129
+ aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1
130
+ "#{aliased_target}_#{with}_db_charmer#{punctuation}"
131
+ end
132
+
133
+ # Override all the query methods here
134
+ ALL_METHODS.each do |method|
135
+ class_eval <<-EOF, __FILE__, __LINE__ + 1
136
+ def #{aliased_method_name method, :with}(*args, &block)
137
+ switch_connection_for_method(:#{method.to_s}) do
138
+ #{aliased_method_name method, :without}(*args, &block)
139
+ end
140
+ end
141
+ EOF
142
+ end
143
+
144
+ end
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,28 @@
1
+ module DbCharmer
2
+ module ActiveRecord
3
+ module RelationMethod
4
+
5
+ def self.extended(base)
6
+ class << base
7
+ alias_method_chain :relation, :db_charmer
8
+ alias_method_chain :arel_engine, :db_charmer
9
+ end
10
+ end
11
+
12
+ # Create a relation object and initialize its default connection
13
+ def relation_with_db_charmer(*args, &block)
14
+ relation_without_db_charmer(*args, &block).tap do |rel|
15
+ rel.db_charmer_connection = self.connection
16
+ rel.db_charmer_enable_slaves = self.db_charmer_slaves.any?
17
+ rel.db_charmer_connection_is_forced = !db_charmer_top_level_connection?
18
+ end
19
+ end
20
+
21
+ # Use the model itself an engine for Arel, do not fall back to AR::Base
22
+ def arel_engine_with_db_charmer(*)
23
+ self
24
+ end
25
+
26
+ end
27
+ end
28
+ end
@@ -18,6 +18,10 @@ module DbCharmer
18
18
  @real_conn = real_conn
19
19
  end
20
20
 
21
+ def db_charmer_connection_name
22
+ "StubConnection"
23
+ end
24
+
21
25
  def real_connection
22
26
  # Return memoized real connection
23
27
  return @real_conn if @real_conn
@@ -44,6 +48,10 @@ module DbCharmer
44
48
  raise ::ActiveRecord::ConnectionNotEstablished, "No real connection to proxy this method to!"
45
49
  end
46
50
 
51
+ if real_connection.kind_of?(DbCharmer::Sharding::StubConnection)
52
+ raise ::ActiveRecord::ConnectionNotEstablished, "You have to switch connection on your model before using it!"
53
+ end
54
+
47
55
  # Proxy the call to our real connection target
48
56
  real_connection.__send__(meth, *args, &block)
49
57
  end
@@ -3,7 +3,7 @@ module DbCharmer
3
3
  MAJOR = 1
4
4
  MINOR = 7
5
5
  PATCH = 0
6
- BUILD = 'pre6'
6
+ BUILD = 'pre7'
7
7
 
8
8
  STRING = [MAJOR, MINOR, PATCH, BUILD].compact.join('.')
9
9
  end
metadata CHANGED
@@ -1,55 +1,56 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: db-charmer
3
3
  version: !ruby/object:Gem::Version
4
- hash: 1923832053
4
+ hash: 1923832055
5
5
  prerelease: 6
6
6
  segments:
7
7
  - 1
8
8
  - 7
9
9
  - 0
10
10
  - pre
11
- - 6
12
- version: 1.7.0.pre6
11
+ - 7
12
+ version: 1.7.0.pre7
13
13
  platform: ruby
14
14
  authors:
15
- - Alexey Kovyrin
15
+ - Oleksiy Kovyrin
16
16
  autorequire:
17
17
  bindir: bin
18
18
  cert_chain: []
19
19
 
20
- date: 2011-06-27 00:00:00 -04:00
21
- default_executable:
20
+ date: 2011-08-27 00:00:00 Z
22
21
  dependencies:
23
22
  - !ruby/object:Gem::Dependency
24
- name: rspec
23
+ name: activesupport
25
24
  prerelease: false
26
25
  requirement: &id001 !ruby/object:Gem::Requirement
27
26
  none: false
28
27
  requirements:
29
- - - ">="
28
+ - - <
30
29
  - !ruby/object:Gem::Version
31
- hash: 3
30
+ hash: 5
32
31
  segments:
33
- - 0
34
- version: "0"
35
- type: :development
32
+ - 3
33
+ - 1
34
+ version: "3.1"
35
+ type: :runtime
36
36
  version_requirements: *id001
37
37
  - !ruby/object:Gem::Dependency
38
- name: yard
38
+ name: activerecord
39
39
  prerelease: false
40
40
  requirement: &id002 !ruby/object:Gem::Requirement
41
41
  none: false
42
42
  requirements:
43
- - - ">="
43
+ - - <
44
44
  - !ruby/object:Gem::Version
45
- hash: 3
45
+ hash: 5
46
46
  segments:
47
- - 0
48
- version: "0"
49
- type: :development
47
+ - 3
48
+ - 1
49
+ version: "3.1"
50
+ type: :runtime
50
51
  version_requirements: *id002
51
52
  - !ruby/object:Gem::Dependency
52
- name: actionpack
53
+ name: rspec
53
54
  prerelease: false
54
55
  requirement: &id003 !ruby/object:Gem::Requirement
55
56
  none: false
@@ -63,34 +64,32 @@ dependencies:
63
64
  type: :development
64
65
  version_requirements: *id003
65
66
  - !ruby/object:Gem::Dependency
66
- name: activesupport
67
+ name: yard
67
68
  prerelease: false
68
69
  requirement: &id004 !ruby/object:Gem::Requirement
69
70
  none: false
70
71
  requirements:
71
- - - ~>
72
+ - - ">="
72
73
  - !ruby/object:Gem::Version
73
- hash: 7
74
+ hash: 3
74
75
  segments:
75
- - 2
76
- - 2
77
- version: "2.2"
78
- type: :runtime
76
+ - 0
77
+ version: "0"
78
+ type: :development
79
79
  version_requirements: *id004
80
80
  - !ruby/object:Gem::Dependency
81
- name: activerecord
81
+ name: actionpack
82
82
  prerelease: false
83
83
  requirement: &id005 !ruby/object:Gem::Requirement
84
84
  none: false
85
85
  requirements:
86
- - - ~>
86
+ - - ">="
87
87
  - !ruby/object:Gem::Version
88
- hash: 7
88
+ hash: 3
89
89
  segments:
90
- - 2
91
- - 2
92
- version: "2.2"
93
- type: :runtime
90
+ - 0
91
+ version: "0"
92
+ type: :development
94
93
  version_requirements: *id005
95
94
  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.
96
95
  email: alexey@kovyrin.net
@@ -111,21 +110,26 @@ files:
111
110
  - db-charmer.gemspec
112
111
  - init.rb
113
112
  - lib/db_charmer.rb
114
- - lib/db_charmer/abstract_adapter/log_formatting.rb
115
113
  - lib/db_charmer/action_controller/force_slave_reads.rb
116
114
  - lib/db_charmer/active_record/association_preload.rb
117
115
  - lib/db_charmer/active_record/class_attributes.rb
118
116
  - lib/db_charmer/active_record/connection_switching.rb
119
117
  - lib/db_charmer/active_record/db_magic.rb
120
- - lib/db_charmer/active_record/finder_overrides.rb
121
118
  - lib/db_charmer/active_record/migration/multi_db_migrations.rb
122
119
  - lib/db_charmer/active_record/multi_db_proxy.rb
123
- - lib/db_charmer/active_record/named_scope/scope_proxy.rb
124
120
  - lib/db_charmer/active_record/sharding.rb
125
121
  - lib/db_charmer/connection_factory.rb
126
122
  - lib/db_charmer/connection_proxy.rb
127
123
  - lib/db_charmer/core_extensions.rb
128
124
  - lib/db_charmer/force_slave_reads.rb
125
+ - lib/db_charmer/rails2/abstract_adapter/log_formatting.rb
126
+ - lib/db_charmer/rails2/active_record/master_slave_routing.rb
127
+ - lib/db_charmer/rails2/active_record/named_scope/scope_proxy.rb
128
+ - lib/db_charmer/rails3/abstract_adapter/connection_name.rb
129
+ - lib/db_charmer/rails3/active_record/log_subscriber.rb
130
+ - lib/db_charmer/rails3/active_record/master_slave_routing.rb
131
+ - lib/db_charmer/rails3/active_record/relation/connection_routing.rb
132
+ - lib/db_charmer/rails3/active_record/relation_method.rb
129
133
  - lib/db_charmer/sharding.rb
130
134
  - lib/db_charmer/sharding/connection.rb
131
135
  - lib/db_charmer/sharding/method.rb
@@ -136,7 +140,6 @@ files:
136
140
  - lib/db_charmer/sharding/stub_connection.rb
137
141
  - lib/db_charmer/version.rb
138
142
  - lib/tasks/databases.rake
139
- has_rdoc: true
140
143
  homepage: http://github.com/kovyrin/db-charmer
141
144
  licenses: []
142
145
 
@@ -168,7 +171,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
168
171
  requirements: []
169
172
 
170
173
  rubyforge_project:
171
- rubygems_version: 1.6.2
174
+ rubygems_version: 1.8.8
172
175
  signing_key:
173
176
  specification_version: 3
174
177
  summary: ActiveRecord Connections Magic (slaves, multiple connections, etc)