switch_point 0.5.0.pre → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SwitchPoint
2
- VERSION = "0.5.0.pre"
4
+ VERSION = '0.9.0'
3
5
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  SwitchPoint.configure do |config|
2
4
  config.define_switch_point :main,
3
5
  readonly: :main_readonly,
@@ -30,8 +32,7 @@ class Book < ActiveRecord::Base
30
32
 
31
33
  private
32
34
 
33
- def do_after_save
34
- end
35
+ def do_after_save; end
35
36
  end
36
37
 
37
38
  class Book2 < ActiveRecord::Base
@@ -85,8 +86,13 @@ class DerivedNanika2 < AbstractNanika
85
86
  use_switch_point :main2
86
87
  end
87
88
 
88
- base = { adapter: 'sqlite3' }
89
- ActiveRecord::Base.configurations = {
89
+ base =
90
+ if RUBY_PLATFORM == 'java'
91
+ { adapter: 'jdbcsqlite3' }
92
+ else
93
+ { adapter: 'sqlite3' }
94
+ end
95
+ databases = {
90
96
  'main_readonly' => base.merge(database: 'main_readonly.sqlite3'),
91
97
  'main_writable' => base.merge(database: 'main_writable.sqlite3'),
92
98
  'main2_readonly' => base.merge(database: 'main2_readonly.sqlite3'),
@@ -95,8 +101,15 @@ ActiveRecord::Base.configurations = {
95
101
  'user' => base.merge(database: 'user.sqlite3'),
96
102
  'comment_readonly' => base.merge(database: 'comment_readonly.sqlite3'),
97
103
  'comment_writable' => base.merge(database: 'comment_writable.sqlite3'),
98
- 'default' => base.merge(database: 'default.sqlite3')
104
+ 'default' => base.merge(database: 'default.sqlite3'),
99
105
  }
106
+ ActiveRecord::Base.configurations =
107
+ # ActiveRecord.gem_version was introduced in ActiveRecord 4.0
108
+ if ActiveRecord.respond_to?(:gem_version) && ActiveRecord.gem_version >= Gem::Version.new('5.1.0')
109
+ { 'test' => databases }
110
+ else
111
+ databases
112
+ end
100
113
  ActiveRecord::Base.establish_connection(:default)
101
114
 
102
115
  # XXX: Check connection laziness
@@ -1,7 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ ENV['RAILS_ENV'] ||= 'test'
4
+
1
5
  require 'coveralls'
2
6
  require 'simplecov'
3
7
 
4
- SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
8
+ SimpleCov.formatters = [
5
9
  SimpleCov::Formatter::HTMLFormatter,
6
10
  Coveralls::SimpleCov::Formatter,
7
11
  ]
@@ -57,8 +61,14 @@ RSpec.configure do |config|
57
61
  end
58
62
 
59
63
  config.after(:suite) do
60
- ActiveRecord::Base.configurations.each_value do |config|
61
- FileUtils.rm_f(config[:database])
64
+ if ActiveRecord::Base.configurations.respond_to?(:configs_for)
65
+ ActiveRecord::Base.configurations.configs_for.each do |c|
66
+ FileUtils.rm_f(c.config['database'])
67
+ end
68
+ else
69
+ ActiveRecord::Base.configurations.each_value do |c|
70
+ FileUtils.rm_f(c[:database])
71
+ end
62
72
  end
63
73
  end
64
74
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe SwitchPoint::Model do
2
4
  describe '.use_switch_point' do
3
5
  after do
@@ -33,8 +35,8 @@ RSpec.describe SwitchPoint::Model do
33
35
 
34
36
  context 'when auto_writable is disabled' do
35
37
  it 'raises error when destructive query is requested in readonly mode' do
36
- expect { Book.create }.to raise_error(SwitchPoint::Connection::ReadonlyError)
37
- expect { Book.with_readonly { Book.create } }.to raise_error(SwitchPoint::Connection::ReadonlyError)
38
+ expect { Book.create }.to raise_error(SwitchPoint::ReadonlyError)
39
+ expect { Book.with_readonly { Book.create } }.to raise_error(SwitchPoint::ReadonlyError)
38
40
  expect { Book.with_writable { Book.create } }.to_not raise_error
39
41
  end
40
42
  end
@@ -69,7 +71,6 @@ RSpec.describe SwitchPoint::Model do
69
71
 
70
72
  it 'works with newly checked-out connection' do
71
73
  Thread.start do
72
- expect(Book.connection.pool.connections.size).to be > 1 # Assertion
73
74
  Book.with_writable do
74
75
  Book.create
75
76
  end
@@ -218,7 +219,7 @@ RSpec.describe SwitchPoint::Model do
218
219
  expect(Book.connection.query_cache.size).to eq(1)
219
220
  Book.with_writable do
220
221
  Book.create
221
- FileUtils.cp('main_writable.sqlite3', 'main_readonly.sqlite3') # XXX: emulate replication
222
+ FileUtils.cp('main_writable.sqlite3', 'main_readonly.sqlite3') # XXX: emulate replication
222
223
  end
223
224
  expect(Book.connection.query_cache.size).to eq(0)
224
225
  expect(Book.count).to eq(1)
@@ -228,8 +229,8 @@ RSpec.describe SwitchPoint::Model do
228
229
  end
229
230
 
230
231
  context 'without use_switch_point' do
231
- it 'bypasses given block' do
232
- expect(Note.with_writable { :bypass }).to eq(:bypass)
232
+ it 'raises error' do
233
+ expect { Note.with_writable { :bypass } }.to raise_error(SwitchPoint::UnconfiguredError)
233
234
  end
234
235
  end
235
236
 
@@ -243,6 +244,16 @@ RSpec.describe SwitchPoint::Model do
243
244
  end
244
245
  end
245
246
 
247
+ describe '#with_writable' do
248
+ it 'behaves like .with_writable' do
249
+ book = Book.with_writable { Book.create! }
250
+ book.with_writable do
251
+ expect(Book).to connect_to('main_writable.sqlite3')
252
+ end
253
+ expect(Book).to connect_to('main_readonly.sqlite3')
254
+ end
255
+ end
256
+
246
257
  describe '.with_readonly' do
247
258
  context 'when writable! is called globally' do
248
259
  before do
@@ -262,9 +273,27 @@ RSpec.describe SwitchPoint::Model do
262
273
  end
263
274
  end
264
275
 
276
+ describe '#with_readonly' do
277
+ before do
278
+ SwitchPoint.writable!(:main)
279
+ end
280
+
281
+ after do
282
+ SwitchPoint.readonly!(:main)
283
+ end
284
+
285
+ it 'behaves like .with_readonly' do
286
+ book = Book.create!
287
+ book.with_readonly do
288
+ expect(Book).to connect_to('main_readonly.sqlite3')
289
+ end
290
+ expect(Book).to connect_to('main_writable.sqlite3')
291
+ end
292
+ end
293
+
265
294
  describe '#with_mode' do
266
295
  it 'raises error if unknown mode is given' do
267
- expect { SwitchPoint::ProxyRepository.checkout(:main).with_mode(:typo) }.to raise_error
296
+ expect { SwitchPoint::ProxyRepository.checkout(:main).with_mode(:typo) }.to raise_error(ArgumentError)
268
297
  end
269
298
  end
270
299
 
@@ -292,14 +321,14 @@ RSpec.describe SwitchPoint::Model do
292
321
  end
293
322
 
294
323
  describe '.transaction_with' do
295
- context "when each model has a same writable" do
324
+ context 'when each model has a same writable' do
296
325
  before do
297
326
  @before_book_count = Book.count
298
327
  @before_book2_count = Book2.count
299
328
 
300
329
  Book.transaction_with(Book2) do
301
- new_book = Book.create
302
- new_book2 = Book2.create
330
+ Book.create
331
+ Book2.create
303
332
  end
304
333
 
305
334
  @after_book_count = Book.with_writable do
@@ -325,25 +354,25 @@ RSpec.describe SwitchPoint::Model do
325
354
  end
326
355
  end
327
356
 
328
- context "when each model has a other writable" do
357
+ context 'when each model has a other writable' do
329
358
  it {
330
359
  expect {
331
360
  Book.transaction_with(Book3) do
332
- new_book = Book.create
333
- new_book3 = Book3.create
361
+ Book.create
362
+ Book3.create
334
363
  end
335
- }.to raise_error RuntimeError
364
+ }.to raise_error(SwitchPoint::Error)
336
365
  }
337
366
  end
338
367
 
339
- context "when raise exception in transaction that include some model, and models each have other writable" do
368
+ context 'when raise exception in transaction that include some model, and models each have other writable' do
340
369
  before do
341
370
  @before_book_count = Book.count
342
371
  @before_book3_count = Book3.count
343
372
 
344
373
  Book.transaction_with(Book2) do
345
- new_book = Book.create
346
- new_book3 = Book3.with_writable do
374
+ Book.create
375
+ Book3.with_writable do
347
376
  Book3.create
348
377
  end
349
378
  raise ActiveRecord::Rollback
@@ -367,7 +396,7 @@ RSpec.describe SwitchPoint::Model do
367
396
  end
368
397
  end
369
398
 
370
- context "when nested transaction_with then parent transaction rollbacked" do
399
+ context 'when nested transaction_with then parent transaction rollbacked' do
371
400
  before do
372
401
  @before_book_count = Book.count
373
402
  @before_book3_count = Book3.count
@@ -398,4 +427,48 @@ RSpec.describe SwitchPoint::Model do
398
427
  end
399
428
  end
400
429
  end
430
+
431
+ describe '#transaction_with' do
432
+ it 'behaves like .transaction_with' do
433
+ book = Book.with_writable { Book.create! }
434
+ expect(Book.with_writable { Book.count }).to eq(1)
435
+ book.transaction_with(Book2) do
436
+ Book.create!
437
+ raise ActiveRecord::Rollback
438
+ end
439
+ expect(Book.with_writable { Book.count }).to eq(1)
440
+
441
+ expect { book.transaction_with(Book3) {} }.to raise_error(SwitchPoint::Error) # rubocop:disable Lint/EmptyBlock
442
+ end
443
+ end
444
+
445
+ describe '.cache' do
446
+ it 'enables query cache for both readonly and writable' do
447
+ Book.connection
448
+ Book.with_writable { Book.connection }
449
+
450
+ Book.cache do
451
+ expect { Book.count }.to change { Book.connection.query_cache.size }.from(0).to(1)
452
+ Book.with_writable do
453
+ expect { Book.count }.to change { Book.connection.query_cache.size }.from(0).to(1)
454
+ end
455
+ end
456
+ end
457
+ end
458
+
459
+ describe '.uncached' do
460
+ it 'disables query cache for both readonly and writable' do
461
+ Book.connection
462
+ Book.with_writable { Book.connection }
463
+
464
+ Book.cache do
465
+ Book.uncached do
466
+ expect { Book.count }.to_not change { Book.connection.query_cache.size }.from(0)
467
+ Book.with_writable do
468
+ expect { Book.count }.to_not change { Book.connection.query_cache.size }.from(0)
469
+ end
470
+ end
471
+ end
472
+ end
473
+ end
401
474
  end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'rack'
5
+
6
+ class TestApp
7
+ def call(env)
8
+ state = {}
9
+ [Nanika1, Nanika2].each do |model|
10
+ r = model.with_readonly { model.connection.query_cache_enabled }
11
+ w = model.with_writable { model.connection.query_cache_enabled }
12
+ state[model.name] = { readonly: r, writable: w }
13
+ end
14
+ env[:state] = state
15
+ :result
16
+ end
17
+ end
18
+
19
+ RSpec.describe SwitchPoint::QueryCache do
20
+ let(:app) do
21
+ Rack::Builder.new do
22
+ use SwitchPoint::QueryCache
23
+ run TestApp.new
24
+ end
25
+ end
26
+
27
+ describe '#call' do
28
+ before do
29
+ # Ensure the connection is established.
30
+ # The query cache is enabled only when connected.
31
+ # https://github.com/rails/rails/commit/25fc1f584def4c1bc36be805833194d8aee55b3a
32
+ [Nanika1, Nanika2].each do |model|
33
+ model.with_readonly { model.connection }
34
+ model.with_writable { model.connection }
35
+ end
36
+ end
37
+
38
+ it 'enables query cache of all models' do
39
+ env = {}
40
+ expect(app.call(env)).to eq(:result)
41
+ expect(env[:state]).to eq(
42
+ 'Nanika1' => { readonly: true, writable: true },
43
+ 'Nanika2' => { readonly: true, writable: true },
44
+ )
45
+ end
46
+
47
+ context 'when names are specified' do
48
+ let(:app) do
49
+ Rack::Builder.new do
50
+ use SwitchPoint::QueryCache, %i[main nanika1]
51
+ run TestApp.new
52
+ end
53
+ end
54
+
55
+ it 'enables query caches of specified models' do
56
+ env = {}
57
+ expect(app.call(env)).to eq(:result)
58
+ expect(env[:state]).to eq(
59
+ 'Nanika1' => { readonly: true, writable: true },
60
+ 'Nanika2' => { readonly: false, writable: true },
61
+ )
62
+ end
63
+ end
64
+
65
+ context 'when unknown name is specified' do
66
+ let(:app) do
67
+ Rack::Builder.new do
68
+ use SwitchPoint::QueryCache, [:unknown]
69
+ run TestApp.new
70
+ end
71
+ end
72
+
73
+ it 'raises error' do
74
+ expect { app.call({}) }.to raise_error(KeyError)
75
+ end
76
+ end
77
+ end
78
+ end
@@ -1,4 +1,50 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.describe SwitchPoint do
4
+ describe '.writable_all!' do
5
+ after do
6
+ SwitchPoint.readonly_all!
7
+ end
8
+
9
+ it 'changes connection globally' do
10
+ expect(Book).to connect_to('main_readonly.sqlite3')
11
+ expect(Book3).to connect_to('main2_readonly.sqlite3')
12
+ expect(Comment).to connect_to('comment_readonly.sqlite3')
13
+ expect(User).to connect_to('user.sqlite3')
14
+ expect(BigData).to connect_to('main_readonly_special.sqlite3')
15
+ SwitchPoint.writable_all!
16
+ expect(Book).to connect_to('main_writable.sqlite3')
17
+ expect(Book3).to connect_to('main2_writable.sqlite3')
18
+ expect(Comment).to connect_to('comment_writable.sqlite3')
19
+ expect(User).to connect_to('user.sqlite3')
20
+ expect(BigData).to connect_to('main_writable.sqlite3')
21
+ end
22
+
23
+ it 'affects thread-globally' do
24
+ SwitchPoint.writable_all!
25
+ Thread.start do
26
+ expect(Book).to connect_to('main_writable.sqlite3')
27
+ expect(Book3).to connect_to('main2_writable.sqlite3')
28
+ expect(Comment).to connect_to('comment_writable.sqlite3')
29
+ expect(User).to connect_to('user.sqlite3')
30
+ expect(BigData).to connect_to('main_writable.sqlite3')
31
+ end.join
32
+ end
33
+
34
+ context 'within with block' do
35
+ it 'changes the current mode' do
36
+ SwitchPoint.writable_all!
37
+ Book.with_readonly do
38
+ expect(Book).to connect_to('main_readonly.sqlite3')
39
+ end
40
+ expect(Book).to connect_to('main_writable.sqlite3')
41
+ Book.with_writable do
42
+ expect(Book).to connect_to('main_writable.sqlite3')
43
+ end
44
+ end
45
+ end
46
+ end
47
+
2
48
  describe '.writable!' do
3
49
  after do
4
50
  SwitchPoint.readonly!(:main)
@@ -31,6 +77,12 @@ RSpec.describe SwitchPoint do
31
77
  end
32
78
  end
33
79
  end
80
+
81
+ context 'with unknown name' do
82
+ it 'raises error' do
83
+ expect { SwitchPoint.writable!(:unknown) }.to raise_error(KeyError)
84
+ end
85
+ end
34
86
  end
35
87
 
36
88
  describe '.with_writable' do
@@ -44,5 +96,22 @@ RSpec.describe SwitchPoint do
44
96
  expect(Publisher).to connect_to('main_readonly.sqlite3')
45
97
  expect(Nanika1).to connect_to('main_readonly.sqlite3')
46
98
  end
99
+
100
+ context 'with unknown name' do
101
+ it 'raises error' do
102
+ expect { SwitchPoint.with_writable(:unknown) { raise RuntimeError } }.to raise_error(KeyError)
103
+ end
104
+ end
105
+ end
106
+
107
+ describe '.with_writable_all' do
108
+ it 'changes all connections' do
109
+ expect(Book).to connect_to('main_readonly.sqlite3')
110
+ expect(Comment).to connect_to('comment_readonly.sqlite3')
111
+ SwitchPoint.with_writable_all do
112
+ expect(Book).to connect_to('main_writable.sqlite3')
113
+ expect(Comment).to connect_to('comment_writable.sqlite3')
114
+ end
115
+ end
47
116
  end
48
117
  end
@@ -1,28 +1,33 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
5
  require 'switch_point/version'
5
6
 
6
7
  Gem::Specification.new do |spec|
7
- spec.name = "switch_point"
8
+ spec.name = 'switch_point'
8
9
  spec.version = SwitchPoint::VERSION
9
- spec.authors = ["Kohei Suzuki"]
10
- spec.email = ["eagletmt@gmail.com"]
11
- spec.summary = %q{Switching database connection between readonly one and writable one.}
12
- spec.description = %q{Switching database connection between readonly one and writable one.}
13
- spec.homepage = "https://github.com/eagletmt/switch_point"
14
- spec.license = "MIT"
10
+ spec.authors = ['Kohei Suzuki']
11
+ spec.email = ['eagletmt@gmail.com']
12
+ spec.summary = 'Switching database connection between readonly one and writable one.'
13
+ spec.description = 'Switching database connection between readonly one and writable one.'
14
+ spec.homepage = 'https://github.com/eagletmt/switch_point'
15
+ spec.license = 'MIT'
15
16
 
16
17
  spec.files = `git ls-files -z`.split("\x0")
17
18
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
19
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
- spec.require_paths = ["lib"]
20
+ spec.require_paths = ['lib']
21
+ spec.required_ruby_version = '>= 2.5.0'
20
22
 
21
- spec.add_development_dependency "appraisal"
22
- spec.add_development_dependency "bundler"
23
- spec.add_development_dependency "coveralls"
24
- spec.add_development_dependency "rake"
25
- spec.add_development_dependency "rspec", "~> 3.0.0.rc1"
26
- spec.add_development_dependency "sqlite3"
27
- spec.add_dependency "activerecord", ">= 3.2.0"
23
+ spec.add_development_dependency 'appraisal'
24
+ spec.add_development_dependency 'benchmark-ips'
25
+ spec.add_development_dependency 'bundler'
26
+ spec.add_development_dependency 'coveralls', '>= 0.8.22'
27
+ spec.add_development_dependency 'rack'
28
+ spec.add_development_dependency 'rake'
29
+ spec.add_development_dependency 'rspec', '>= 3.0'
30
+ spec.add_development_dependency 'rubocop', '>= 0.50.0'
31
+ spec.add_development_dependency 'simplecov', '~> 0.16.1' # XXX: The latest coveralls still depends on old version
32
+ spec.add_dependency 'activerecord', '>= 3.2.0', '< 6.1.0'
28
33
  end