switch_point 0.3.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/lib/switch_point/config.rb +14 -7
- data/lib/switch_point/connection.rb +33 -11
- data/lib/switch_point/proxy.rb +24 -6
- data/lib/switch_point/version.rb +1 -1
- data/spec/models.rb +12 -0
- data/spec/spec_helper.rb +4 -3
- data/spec/switch_point/model_spec.rb +17 -0
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d4e8e6fce7bc9a152422f2b1ede1df15b39791c6
|
4
|
+
data.tar.gz: 785d1e66b759b372edb68201454238bd62879ef1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 83d13eaf42dcc9e266c1bc3e703b04798ca0bb17473591a819e36c09eb40a294e3a4c57b42970428a9b09f159ac3a20a443d2f158ced0a5f309cd894ebe6d769
|
7
|
+
data.tar.gz: e5c054d22e52bcb1f37ba3d46fb6487b1a462b53f7b443fd9f389195bdd0ed668bd5a3b9df13006a0b2104047febe8b87b02b46e59e8e5ea1342d2f4bbd86741
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
## 0.3.1 (2014-06-04)
|
2
|
+
- Support defaulting to writable ActiveRecord::Base connection
|
3
|
+
- When `:writable` key is omitted, ActiveRecord::Base is used for the writable connection.
|
4
|
+
|
1
5
|
## 0.3.0 (2014-06-04)
|
2
6
|
- Improve thread safety
|
3
7
|
- Raise appropriate error if unknown mode is given to with_connection
|
data/lib/switch_point/config.rb
CHANGED
@@ -14,7 +14,11 @@ module SwitchPoint
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def model_name(name, mode)
|
17
|
-
|
17
|
+
if switch_points[name][mode]
|
18
|
+
"#{name}_#{mode}".camelize
|
19
|
+
else
|
20
|
+
nil
|
21
|
+
end
|
18
22
|
end
|
19
23
|
|
20
24
|
def fetch(name)
|
@@ -28,12 +32,15 @@ module SwitchPoint
|
|
28
32
|
private
|
29
33
|
|
30
34
|
def assert_valid_config!(config)
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
35
|
+
unless config.has_key?(:readonly)
|
36
|
+
raise ArgumentError.new(":readonly key is required")
|
37
|
+
end
|
38
|
+
unless config[:readonly].is_a?(Symbol)
|
39
|
+
raise TypeError.new(":readonly's value must be Symbol")
|
40
|
+
end
|
41
|
+
if config.has_key?(:writable)
|
42
|
+
unless config[:writable].is_a?(Symbol)
|
43
|
+
raise TypeError.new(":writable's value must be Symbol")
|
37
44
|
end
|
38
45
|
end
|
39
46
|
nil
|
@@ -7,22 +7,44 @@ module SwitchPoint
|
|
7
7
|
|
8
8
|
DESTRUCTIVE_METHODS.each do |method_name|
|
9
9
|
define_method(:"#{method_name}_with_switch_point") do |*args, &block|
|
10
|
-
switch_point = self.pool.instance_variable_get(:@switch_point)
|
11
10
|
parent_method = :"#{method_name}_without_switch_point"
|
12
|
-
if
|
11
|
+
if self.pool.equal?(ActiveRecord::Base.connection.pool)
|
12
|
+
Connection.handle_base_connection(self, parent_method, *args, &block)
|
13
|
+
else
|
14
|
+
Connection.handle_generated_connection(self, parent_method, method_name, *args, &block)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.handle_base_connection(conn, parent_method, *args, &block)
|
20
|
+
switch_points = conn.pool.instance_variable_get(:@switch_points)
|
21
|
+
if switch_points
|
22
|
+
switch_points.each do |switch_point|
|
13
23
|
proxy = ProxyRepository.find(switch_point[:name])
|
14
|
-
|
15
|
-
|
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}")
|
24
|
+
if switch_point[:mode] != :writable
|
25
|
+
raise RuntimeError.new("ActiveRecord::Base's switch_points must be writable, but #{switch_point[:name]} is #{switch_point[:mode]}")
|
22
26
|
end
|
27
|
+
purge_readonly_query_cache(proxy)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
conn.send(parent_method, *args, &block)
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.handle_generated_connection(conn, parent_method, method_name, *args, &block)
|
34
|
+
switch_point = conn.pool.instance_variable_get(:@switch_point)
|
35
|
+
if switch_point
|
36
|
+
proxy = ProxyRepository.find(switch_point[:name])
|
37
|
+
case switch_point[:mode]
|
38
|
+
when :readonly
|
39
|
+
proxy_to_writable(proxy, method_name, *args, &block)
|
40
|
+
when :writable
|
41
|
+
purge_readonly_query_cache(proxy)
|
42
|
+
conn.send(parent_method, *args, &block)
|
23
43
|
else
|
24
|
-
|
44
|
+
raise RuntimeError.new("Unknown mode #{switch_point[:mode]} is given with #{name}")
|
25
45
|
end
|
46
|
+
else
|
47
|
+
conn.send(parent_method, *args, &block)
|
26
48
|
end
|
27
49
|
end
|
28
50
|
|
data/lib/switch_point/proxy.rb
CHANGED
@@ -9,22 +9,35 @@ module SwitchPoint
|
|
9
9
|
@initial_name = name
|
10
10
|
@current_name = name
|
11
11
|
AVAILABLE_MODES.each do |mode|
|
12
|
-
model = define_model(
|
13
|
-
model.establish_connection(SwitchPoint.config.database_name(name, mode))
|
12
|
+
model = define_model(name, mode)
|
14
13
|
memorize_switch_point(name, mode, model.connection)
|
15
14
|
end
|
16
15
|
@global_mode = DEFAULT_MODE
|
17
16
|
end
|
18
17
|
|
19
|
-
def define_model(
|
18
|
+
def define_model(name, mode)
|
20
19
|
model = Class.new(ActiveRecord::Base)
|
21
|
-
|
20
|
+
model_name = SwitchPoint.config.model_name(name, mode)
|
21
|
+
if model_name
|
22
|
+
Proxy.const_set(model_name, model)
|
23
|
+
model.establish_connection(SwitchPoint.config.database_name(name, mode))
|
24
|
+
end
|
22
25
|
model
|
23
26
|
end
|
24
27
|
|
25
28
|
def memorize_switch_point(name, mode, connection)
|
26
29
|
switch_point = { name: name, mode: mode }
|
27
|
-
connection.pool
|
30
|
+
pool = connection.pool
|
31
|
+
if pool.equal?(ActiveRecord::Base.connection.pool)
|
32
|
+
if mode != :writable
|
33
|
+
raise RuntimeError.new("ActiveRecord::Base's switch_points must be writable, but #{name} is #{mode}")
|
34
|
+
end
|
35
|
+
switch_points = pool.instance_variable_get(:@switch_points) || []
|
36
|
+
switch_points << switch_point
|
37
|
+
pool.instance_variable_set(:@switch_points, switch_points)
|
38
|
+
else
|
39
|
+
pool.instance_variable_set(:@switch_point, switch_point)
|
40
|
+
end
|
28
41
|
end
|
29
42
|
|
30
43
|
def thread_local_mode
|
@@ -103,7 +116,12 @@ module SwitchPoint
|
|
103
116
|
|
104
117
|
def connection
|
105
118
|
ProxyRepository.checkout(@current_name) # Ensure the target proxy is created
|
106
|
-
|
119
|
+
model_name = SwitchPoint.config.model_name(@current_name, mode)
|
120
|
+
if model_name
|
121
|
+
Proxy.const_get(model_name).connection
|
122
|
+
else
|
123
|
+
ActiveRecord::Base.connection
|
124
|
+
end
|
107
125
|
end
|
108
126
|
end
|
109
127
|
end
|
data/lib/switch_point/version.rb
CHANGED
data/spec/models.rb
CHANGED
@@ -11,6 +11,10 @@ SwitchPoint.configure do |config|
|
|
11
11
|
config.define_switch_point :special,
|
12
12
|
readonly: :main_readonly_special,
|
13
13
|
writable: :main_writable
|
14
|
+
config.define_switch_point :nanika1,
|
15
|
+
readonly: :main_readonly
|
16
|
+
config.define_switch_point :nanika2,
|
17
|
+
readonly: :main_readonly
|
14
18
|
end
|
15
19
|
|
16
20
|
class Book < ActiveRecord::Base
|
@@ -36,6 +40,14 @@ end
|
|
36
40
|
class Note < ActiveRecord::Base
|
37
41
|
end
|
38
42
|
|
43
|
+
class Nanika1 < ActiveRecord::Base
|
44
|
+
use_switch_point :nanika1
|
45
|
+
end
|
46
|
+
|
47
|
+
class Nanika2 < ActiveRecord::Base
|
48
|
+
use_switch_point :nanika2
|
49
|
+
end
|
50
|
+
|
39
51
|
base = { adapter: 'sqlite3' }
|
40
52
|
ActiveRecord::Base.configurations = {
|
41
53
|
'main_readonly' => base.merge(database: 'main_readonly.sqlite3'),
|
data/spec/spec_helper.rb
CHANGED
@@ -24,11 +24,12 @@ RSpec.configure do |config|
|
|
24
24
|
end
|
25
25
|
|
26
26
|
config.before(:suite) do
|
27
|
-
sql = 'CREATE TABLE books (id integer primary key autoincrement)'
|
28
|
-
Book.connection.execute(sql)
|
29
27
|
Book.with_writable do
|
30
|
-
Book.connection.execute(
|
28
|
+
Book.connection.execute('CREATE TABLE books (id integer primary key autoincrement)')
|
31
29
|
end
|
30
|
+
FileUtils.cp('main_writable.sqlite3', 'main_readonly.sqlite3')
|
31
|
+
|
32
|
+
Note.connection.execute('CREATE TABLE notes (id integer primary key autoincrement)')
|
32
33
|
end
|
33
34
|
|
34
35
|
config.after(:suite) do
|
@@ -68,6 +68,23 @@ RSpec.describe SwitchPoint::Model do
|
|
68
68
|
end
|
69
69
|
end
|
70
70
|
end
|
71
|
+
|
72
|
+
context 'without :writable' do
|
73
|
+
it 'sends destructive queries to ActiveRecord::Base' do
|
74
|
+
expect(Nanika1).to connect_to('main_readonly.sqlite3')
|
75
|
+
Nanika1.with_writable do
|
76
|
+
expect(Nanika1).to connect_to('default.sqlite3')
|
77
|
+
expect(Nanika1.connection).to equal(ActiveRecord::Base.connection)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'clears all query caches' do
|
82
|
+
expect(Nanika1.connection).to_not equal(Nanika2.connection)
|
83
|
+
expect(Nanika1.connection).to receive(:clear_query_cache).once
|
84
|
+
expect(Nanika2.connection).to receive(:clear_query_cache).once
|
85
|
+
Note.create
|
86
|
+
end
|
87
|
+
end
|
71
88
|
end
|
72
89
|
|
73
90
|
describe '.with_writable' do
|