switch_point 0.5.0.pre → 0.9.0

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.
@@ -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