switch_point 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d470827ada389d47ee22e4956e6a45a42203abbb
4
- data.tar.gz: 0b73cf90012e2fb62556830421c220cf01e59b21
3
+ metadata.gz: c52a7c39b406241e2df69b1d0dfae3a6c45d15d3
4
+ data.tar.gz: 606dd78b88ad59873c9d7e187c110163899f1ffb
5
5
  SHA512:
6
- metadata.gz: 62fde9258b6d872a319643c32935cbeda95bf2ce80c56f1ca96a73a2bf519562b51fbdf22feb870be8538418f4c273ec828d1ca2279d4678fb6977a701821cd0
7
- data.tar.gz: 296199d722f3dc08deb552f7258796f2d124346cc1268af7302f209f255cb5f8c2ba27a6fcec4ecfffc44c0a6ed7b193768e2d48c03641c26be33fe00345fe3b
6
+ metadata.gz: 1e9097c7f4e5696728fc57a0f190ccd6a4c65d665f1fbb7cfbc89e67cd77658f288d8a4f16a7360e3e34c103a1fdbb050b132eafc13e077901f1af25f1d8cf75
7
+ data.tar.gz: 02bcf5bc10f0c2c4a3b7ee04f3d33b1a94edcfc0f1b91ef1ff2251963052983c3d24cdb67f8a80b6e73daae802d1448352eb573a364d41559d228752efe89337
@@ -1,2 +1,6 @@
1
+ ## 0.2.0 (2014-05-29)
2
+ - Always send destructive operations to writable connection
3
+ - Fix bug on pooled connections
4
+
1
5
  ## 0.1.0 (2014-05-28)
2
6
  - Initial release
@@ -19,7 +19,7 @@ module SwitchPoint
19
19
  end
20
20
 
21
21
  def readonly!(name)
22
- ProxyRepository.find(name).readonly!
22
+ ProxyRepository.checkout(name).readonly!
23
23
  end
24
24
 
25
25
  def writable_all!
@@ -29,7 +29,7 @@ module SwitchPoint
29
29
  end
30
30
 
31
31
  def writable!(name)
32
- ProxyRepository.find(name).writable!
32
+ ProxyRepository.checkout(name).writable!
33
33
  end
34
34
  end
35
35
  extend ClassMethods
@@ -37,5 +37,12 @@ end
37
37
 
38
38
  ActiveSupport.on_load(:active_record) do
39
39
  require 'switch_point/model'
40
+ require 'switch_point/connection'
40
41
  ActiveRecord::Base.send(:include, SwitchPoint::Model)
42
+ ActiveRecord::ConnectionAdapters::AbstractAdapter.class_eval do
43
+ include SwitchPoint::Connection
44
+ SwitchPoint::Connection::DESTRUCTIVE_METHODS.each do |method_name|
45
+ alias_method_chain method_name, :switch_point
46
+ end
47
+ end
41
48
  end
@@ -14,6 +14,10 @@ module SwitchPoint
14
14
  "#{mode}_#{database_name(name, mode)}".camelize
15
15
  end
16
16
 
17
+ def fetch(name)
18
+ @switch_points.fetch(name)
19
+ end
20
+
17
21
  def keys
18
22
  @switch_points.keys
19
23
  end
@@ -0,0 +1,41 @@
1
+ require 'switch_point/proxy_repository'
2
+
3
+ module SwitchPoint
4
+ module Connection
5
+ # See ActiveRecord::ConnectionAdapters::QueryCache
6
+ DESTRUCTIVE_METHODS = [:insert, :update, :delete]
7
+
8
+ DESTRUCTIVE_METHODS.each do |method_name|
9
+ define_method(:"#{method_name}_with_switch_point") do |*args, &block|
10
+ switch_point = self.pool.instance_variable_get(:@switch_point)
11
+ parent_method = :"#{method_name}_without_switch_point"
12
+ if switch_point
13
+ proxy = ProxyRepository.find(switch_point[:name])
14
+ case switch_point[:mode]
15
+ when :readonly
16
+ Connection.proxy_to_writable(proxy, method_name, *args, &block)
17
+ when :writable
18
+ Connection.purge_readonly_query_cache(proxy)
19
+ send(parent_method, *args, &block)
20
+ else
21
+ raise RuntimeError.new("Unknown mode #{switch_point[:mode]} is given with #{name}")
22
+ end
23
+ else
24
+ send(parent_method, *args, &block)
25
+ end
26
+ end
27
+ end
28
+
29
+ def self.proxy_to_writable(proxy, method_name, *args, &block)
30
+ proxy.with_writable do
31
+ proxy.connection.send(method_name, *args, &block)
32
+ end
33
+ end
34
+
35
+ def self.purge_readonly_query_cache(proxy)
36
+ proxy.with_readonly do
37
+ proxy.connection.clear_query_cache
38
+ end
39
+ end
40
+ end
41
+ end
@@ -5,21 +5,19 @@ module SwitchPoint
5
5
  def self.included(model)
6
6
  model.singleton_class.class_eval do
7
7
  include ClassMethods
8
- prepend ConnectionHook
8
+ alias_method_chain :connection, :switch_point
9
9
  end
10
10
  end
11
11
 
12
- module ConnectionHook
13
- def connection
12
+ module ClassMethods
13
+ def connection_with_switch_point
14
14
  if @switch_point_name
15
15
  switch_point_proxy.connection
16
16
  else
17
- super
17
+ connection_without_switch_point
18
18
  end
19
19
  end
20
- end
21
20
 
22
- module ClassMethods
23
21
  def with_readonly(&block)
24
22
  switch_point_proxy.with_readonly(&block)
25
23
  end
@@ -28,14 +26,19 @@ module SwitchPoint
28
26
  switch_point_proxy.with_writable(&block)
29
27
  end
30
28
 
31
- private
32
-
33
29
  def use_switch_point(name)
30
+ assert_existing_switch_point!(name)
34
31
  @switch_point_name = name
35
32
  end
36
33
 
34
+ private
35
+
36
+ def assert_existing_switch_point!(name)
37
+ SwitchPoint.config.fetch(name)
38
+ end
39
+
37
40
  def switch_point_proxy
38
- @switch_point_proxy ||= ProxyRepository.find(@switch_point_name)
41
+ ProxyRepository.checkout(@switch_point_name)
39
42
  end
40
43
  end
41
44
  end
@@ -1,5 +1,3 @@
1
- require 'switch_point/writable_connection_hook'
2
-
3
1
  module SwitchPoint
4
2
  class Proxy
5
3
  def initialize(name)
@@ -8,9 +6,8 @@ module SwitchPoint
8
6
  model = define_model(SwitchPoint.config.model_name(name, mode))
9
7
  @models[mode] = model
10
8
  model.establish_connection(SwitchPoint.config.database_name(name, mode))
11
- memorize_switch_point_name(name, model.connection)
9
+ memorize_switch_point(name, mode, model.connection)
12
10
  end
13
- @models[:writable].connection.extend(WritableConnectionHook)
14
11
  @mode = :readonly
15
12
  end
16
13
 
@@ -20,8 +17,9 @@ module SwitchPoint
20
17
  model
21
18
  end
22
19
 
23
- def memorize_switch_point_name(name, connection)
24
- connection.instance_variable_set(:@switch_point_name, name)
20
+ def memorize_switch_point(name, mode, connection)
21
+ switch_point = { name: name, mode: mode }
22
+ connection.pool.instance_variable_set(:@switch_point, switch_point)
25
23
  end
26
24
 
27
25
  def readonly!
@@ -5,14 +5,22 @@ module SwitchPoint
5
5
  class ProxyRepository
6
6
  include Singleton
7
7
 
8
+ def self.checkout(name)
9
+ instance.checkout(name)
10
+ end
11
+
8
12
  def self.find(name)
9
13
  instance.find(name)
10
14
  end
11
15
 
12
- def find(name)
16
+ def checkout(name)
13
17
  proxies[name] ||= Proxy.new(name)
14
18
  end
15
19
 
20
+ def find(name)
21
+ proxies.fetch(name)
22
+ end
23
+
16
24
  def proxies
17
25
  @proxies ||= {}
18
26
  end
@@ -1,3 +1,3 @@
1
1
  module SwitchPoint
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -38,10 +38,8 @@ RSpec.configure do |config|
38
38
  end
39
39
 
40
40
  config.after(:each) do
41
- Book.delete_all
42
- Book.with_writable do
43
- Book.delete_all
44
- end
41
+ Book.delete_all # This queries to writable database
42
+ FileUtils.cp('main_writable.sqlite3', 'main_readonly.sqlite3')
45
43
  end
46
44
  end
47
45
 
@@ -1,4 +1,26 @@
1
1
  RSpec.describe SwitchPoint::Model do
2
+ describe '.use_switch_point' do
3
+ after do
4
+ Book.use_switch_point :main
5
+ end
6
+
7
+ it 'changes connection' do
8
+ expect(Book).to connect_to('main_readonly.sqlite3')
9
+ Book.use_switch_point :comment
10
+ expect(Book).to connect_to('comment_readonly.sqlite3')
11
+ end
12
+
13
+ context 'with non-existing switch point name' do
14
+ it 'raises error' do
15
+ expect {
16
+ Class.new(ActiveRecord::Base) do
17
+ use_switch_point :not_found
18
+ end
19
+ }.to raise_error(KeyError)
20
+ end
21
+ end
22
+ end
23
+
2
24
  describe '.connection' do
3
25
  it 'returns readonly connection by default' do
4
26
  expect(Book).to connect_to('main_readonly.sqlite3')
@@ -8,6 +30,21 @@ RSpec.describe SwitchPoint::Model do
8
30
  expect(Note).to connect_to('default.sqlite3')
9
31
  end
10
32
 
33
+ it 'sends destructive queries to writable' do
34
+ Book.create
35
+ Book.with_readonly { expect(Book.count).to eq(0) }
36
+ Book.with_writable { expect(Book.count).to eq(1) }
37
+ end
38
+
39
+ it 'works with newly checked-out connection' do
40
+ Thread.start do
41
+ expect(Book.connection.pool.connections.size).to be > 1 # Assertion
42
+ Book.create
43
+ Book.with_readonly { expect(Book.count).to eq(0) }
44
+ Book.with_writable { expect(Book.count).to eq(1) }
45
+ end.join
46
+ end
47
+
11
48
  context 'without switch_point configuration' do
12
49
  it 'returns default connection' do
13
50
  expect(Note.connection).to equal(ActiveRecord::Base.connection)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: switch_point
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kohei Suzuki
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-05-28 00:00:00.000000000 Z
11
+ date: 2014-05-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: appraisal
@@ -115,11 +115,11 @@ files:
115
115
  - gemfiles/rails_4.1.gemfile
116
116
  - lib/switch_point.rb
117
117
  - lib/switch_point/config.rb
118
+ - lib/switch_point/connection.rb
118
119
  - lib/switch_point/model.rb
119
120
  - lib/switch_point/proxy.rb
120
121
  - lib/switch_point/proxy_repository.rb
121
122
  - lib/switch_point/version.rb
122
- - lib/switch_point/writable_connection_hook.rb
123
123
  - spec/models.rb
124
124
  - spec/spec_helper.rb
125
125
  - spec/switch_point/model_spec.rb
@@ -1,17 +0,0 @@
1
- require 'switch_point/proxy_repository'
2
-
3
- module SwitchPoint
4
- # Propagate clear_query_cache in writable connection to readonly connection
5
- module WritableConnectionHook
6
- # See ActiveRecord::ConnectionAdapters::QueryCache
7
- [:insert, :update, :delete].each do |method_name|
8
- define_method(method_name) do |*args, &block|
9
- proxy = ProxyRepository.find(@switch_point_name)
10
- proxy.with_readonly do
11
- proxy.connection.clear_query_cache
12
- end
13
- super(*args, &block)
14
- end
15
- end
16
- end
17
- end