Module: DbCharmer::ActiveRecord::Relation::ConnectionRouting

Defined in:
lib/db_charmer/rails3/active_record/relation/connection_routing.rb

Constant Summary collapse

SLAVE_METHODS =

All the methods that could be querying the database

[ :calculate, :exists? ]
MASTER_METHODS =
[ :delete, :delete_all, :destroy, :destroy_all, :reload, :update, :update_all ]
ALL_METHODS =
SLAVE_METHODS + MASTER_METHODS
DB_CHARMER_ATTRIBUTES =
[ :db_charmer_connection, :db_charmer_connection_is_forced, :db_charmer_enable_slaves ]

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.aliased_method_name(target, with) ⇒ Object

Need this to mimick alias_method_chain name generation (exists? => exists_with_db_charmer?)



128
129
130
131
# File 'lib/db_charmer/rails3/active_record/relation/connection_routing.rb', line 128

def self.aliased_method_name(target, with)
  aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1
  "#{aliased_target}_#{with}_db_charmer#{punctuation}"
end

.included(base) ⇒ Object

Define the default relation connection + override all the query methods here



14
15
16
17
# File 'lib/db_charmer/rails3/active_record/relation/connection_routing.rb', line 14

def self.included(base)
  init_attributes(base)
  init_routing(base)
end

.init_attributes(base) ⇒ Object

Define our attributes + spawn methods shit needs to be changed to make sure our accessors are copied over to the new instances



20
21
22
23
24
25
26
27
28
# File 'lib/db_charmer/rails3/active_record/relation/connection_routing.rb', line 20

def self.init_attributes(base)
  DB_CHARMER_ATTRIBUTES.each do |attr|
    base.send(:attr_accessor, attr)
  end

  # Override spawn methods
  base.alias_method_chain :except, :db_charmer
  base.alias_method_chain :only, :db_charmer
end

.init_routing(base) ⇒ Object

Override all query methods



31
32
33
34
35
36
37
38
# File 'lib/db_charmer/rails3/active_record/relation/connection_routing.rb', line 31

def self.init_routing(base)
  ALL_METHODS.each do |meth|
    base.alias_method_chain meth, :db_charmer
  end

  # Special case: for normal selects we go to the slave, but for selects with a lock we should use master
  base.alias_method_chain :to_a, :db_charmer
end

Instance Method Details

#connectionObject

Make sure we get the right connection here



74
75
76
# File 'lib/db_charmer/rails3/active_record/relation/connection_routing.rb', line 74

def connection
  @klass.on_db(db_charmer_connection).connection
end

#copy_db_charmer_options(src, dst) ⇒ Object

Copy our accessors from one instance to another



55
56
57
58
59
# File 'lib/db_charmer/rails3/active_record/relation/connection_routing.rb', line 55

def copy_db_charmer_options(src, dst)
  DB_CHARMER_ATTRIBUTES.each do |attr|
    dst.send("#{attr}=".to_sym, src.send(attr))
  end
end

#except_with_db_charmer(*args) ⇒ Object

Copy db_charmer attributes in addition to what they’re copying



41
42
43
44
45
# File 'lib/db_charmer/rails3/active_record/relation/connection_routing.rb', line 41

def except_with_db_charmer(*args)
  except_without_db_charmer(*args).tap do |result|
    copy_db_charmer_options(self, result)
  end
end

#on_db(con, &block) ⇒ Object

Connection switching (changes the default relation connection)



62
63
64
65
66
67
68
69
70
71
# File 'lib/db_charmer/rails3/active_record/relation/connection_routing.rb', line 62

def on_db(con, &block)
  if block_given?
    @klass.on_db(con, &block)
  else
    clone.tap do |result|
      result.db_charmer_connection = con
      result.db_charmer_connection_is_forced = true
    end
  end
end

#only_with_db_charmer(*args) ⇒ Object

Copy db_charmer attributes in addition to what they’re copying



48
49
50
51
52
# File 'lib/db_charmer/rails3/active_record/relation/connection_routing.rb', line 48

def only_with_db_charmer(*args)
  only_without_db_charmer(*args).tap do |result|
    copy_db_charmer_options(self, result)
  end
end

#select_destination(method, recommendation = :default) ⇒ Object

Selects preferred destination (master/slave/default) for a query



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/db_charmer/rails3/active_record/relation/connection_routing.rb', line 79

def select_destination(method, recommendation = :default)
  # If this relation was created within a forced connection block (e.g Model.on_db(:foo).relation)
  # Then we should use that connection everywhere except cases when a model is slave-enabled
  # in those cases DML queries go to the master
  if db_charmer_connection_is_forced
    return :master if db_charmer_enable_slaves && MASTER_METHODS.member?(method)
    return :default
  end

  # If this relation is created from a slave-enabled model, let's do the routing if possible
  if db_charmer_enable_slaves
    return :slave if SLAVE_METHODS.member?(method)
    return :master if MASTER_METHODS.member?(method)
  else
    # Make sure we do not use recommended destination
    recommendation = :default
  end

  # If nothing else came up, let's use the default or recommended connection
  return recommendation
end

#switch_connection_for_method(method, recommendation = nil) ⇒ Object

Switch the model to default relation connection



102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/db_charmer/rails3/active_record/relation/connection_routing.rb', line 102

def switch_connection_for_method(method, recommendation = nil)
  # Choose where to send the query
  destination ||= select_destination(method, recommendation)

  # What method to use
  on_db_method = [ :on_db, db_charmer_connection ]
  on_db_method = :on_master if destination == :master
  on_db_method = :first_level_on_slave if destination == :slave

  # Perform the query
  @klass.send(*on_db_method) do
    yield
  end
end

#to_a_with_db_charmer(*args, &block) ⇒ Object

For normal selects we go to the slave, but for selects with a lock we should use master



118
119
120
121
122
123
124
125
# File 'lib/db_charmer/rails3/active_record/relation/connection_routing.rb', line 118

def to_a_with_db_charmer(*args, &block)
  preferred_destination = :slave
  preferred_destination = :master if lock_value

  switch_connection_for_method(:to_a, preferred_destination) do
    to_a_without_db_charmer(*args, &block)
  end
end