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.
- checksums.yaml +5 -5
- data/.gitignore +1 -0
- data/.rubocop.yml +56 -0
- data/.rubocop_todo.yml +2 -0
- data/.travis.yml +21 -6
- data/Appraisals +55 -5
- data/CHANGELOG.md +23 -1
- data/Gemfile +6 -0
- data/README.md +36 -3
- data/Rakefile +12 -2
- data/assets/switch_point.svg +1 -1
- data/benchmark/proxy.rb +62 -0
- data/gemfiles/rails_3.2.gemfile +6 -2
- data/gemfiles/rails_4.0.gemfile +6 -2
- data/gemfiles/rails_4.1.gemfile +6 -2
- data/gemfiles/rails_4.2.gemfile +11 -0
- data/gemfiles/rails_5.0.gemfile +11 -0
- data/gemfiles/rails_5.1.gemfile +11 -0
- data/gemfiles/rails_5.2.gemfile +11 -0
- data/gemfiles/rails_6.0.gemfile +11 -0
- data/lib/switch_point.rb +17 -8
- data/lib/switch_point/config.rb +15 -15
- data/lib/switch_point/connection.rb +15 -14
- data/lib/switch_point/error.rb +12 -0
- data/lib/switch_point/model.rb +48 -16
- data/lib/switch_point/proxy.rb +23 -5
- data/lib/switch_point/proxy_repository.rb +2 -0
- data/lib/switch_point/query_cache.rb +22 -0
- data/lib/switch_point/version.rb +3 -1
- data/spec/models.rb +18 -5
- data/spec/spec_helper.rb +13 -3
- data/spec/switch_point/model_spec.rb +91 -18
- data/spec/switch_point/query_cache_spec.rb +78 -0
- data/spec/switch_point_spec.rb +69 -0
- data/switch_point.gemspec +22 -17
- metadata +76 -18
- data/gemfiles/rails_edge.gemfile +0 -7
data/lib/switch_point/version.rb
CHANGED
data/spec/models.rb
CHANGED
@@ -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 =
|
89
|
-
|
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
|
data/spec/spec_helper.rb
CHANGED
@@ -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.
|
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.
|
61
|
-
|
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::
|
37
|
-
expect { Book.with_readonly { Book.create } }.to raise_error(SwitchPoint::
|
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')
|
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 '
|
232
|
-
expect
|
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
|
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
|
-
|
302
|
-
|
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
|
357
|
+
context 'when each model has a other writable' do
|
329
358
|
it {
|
330
359
|
expect {
|
331
360
|
Book.transaction_with(Book3) do
|
332
|
-
|
333
|
-
|
361
|
+
Book.create
|
362
|
+
Book3.create
|
334
363
|
end
|
335
|
-
}.to raise_error
|
364
|
+
}.to raise_error(SwitchPoint::Error)
|
336
365
|
}
|
337
366
|
end
|
338
367
|
|
339
|
-
context
|
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
|
-
|
346
|
-
|
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
|
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
|
data/spec/switch_point_spec.rb
CHANGED
@@ -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
|
data/switch_point.gemspec
CHANGED
@@ -1,28 +1,33 @@
|
|
1
|
-
#
|
2
|
-
|
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 =
|
8
|
+
spec.name = 'switch_point'
|
8
9
|
spec.version = SwitchPoint::VERSION
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
11
|
-
spec.summary =
|
12
|
-
spec.description =
|
13
|
-
spec.homepage =
|
14
|
-
spec.license =
|
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 = [
|
20
|
+
spec.require_paths = ['lib']
|
21
|
+
spec.required_ruby_version = '>= 2.5.0'
|
20
22
|
|
21
|
-
spec.add_development_dependency
|
22
|
-
spec.add_development_dependency
|
23
|
-
spec.add_development_dependency
|
24
|
-
spec.add_development_dependency
|
25
|
-
spec.add_development_dependency
|
26
|
-
spec.add_development_dependency
|
27
|
-
spec.
|
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
|