switch_point 0.3.0 → 0.3.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.
- 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
|