with_advisory_lock 4.0.0 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/ci.yml +80 -0
  3. data/.gitignore +3 -0
  4. data/.tool-versions +1 -0
  5. data/Appraisals +37 -11
  6. data/CHANGELOG.md +131 -0
  7. data/Gemfile +0 -12
  8. data/README.md +85 -171
  9. data/gemfiles/{activerecord_5.1.gemfile → activerecord_6.1.gemfile} +4 -2
  10. data/gemfiles/{activerecord_5.2.gemfile → activerecord_7.0.gemfile} +4 -2
  11. data/gemfiles/activerecord_7.1.gemfile +14 -0
  12. data/lib/with_advisory_lock/base.rb +16 -2
  13. data/lib/with_advisory_lock/concern.rb +13 -2
  14. data/lib/with_advisory_lock/database_adapter_support.rb +10 -3
  15. data/lib/with_advisory_lock/failed_to_acquire_lock.rb +7 -0
  16. data/lib/with_advisory_lock/flock.rb +4 -3
  17. data/lib/with_advisory_lock/mysql.rb +6 -16
  18. data/lib/with_advisory_lock/postgresql.rb +9 -7
  19. data/lib/with_advisory_lock/version.rb +3 -1
  20. data/lib/with_advisory_lock.rb +1 -1
  21. data/test/concern_test.rb +23 -10
  22. data/test/lock_test.rb +61 -28
  23. data/test/nesting_test.rb +14 -46
  24. data/test/options_test.rb +35 -33
  25. data/test/parallelism_test.rb +35 -37
  26. data/test/shared_test.rb +93 -90
  27. data/test/test_helper.rb +52 -0
  28. data/test/test_models.rb +9 -7
  29. data/test/thread_test.rb +23 -22
  30. data/test/transaction_test.rb +34 -36
  31. data/with_advisory_lock.gemspec +24 -23
  32. metadata +27 -41
  33. data/.travis.install-mysql-5.7.sh +0 -11
  34. data/.travis.yml +0 -32
  35. data/gemfiles/activerecord_4.2.gemfile +0 -19
  36. data/gemfiles/activerecord_5.0.gemfile +0 -19
  37. data/lib/with_advisory_lock/nested_advisory_lock_error.rb +0 -14
  38. data/test/database.yml +0 -17
  39. data/test/minitest_helper.rb +0 -40
  40. data/tests.sh +0 -11
@@ -1,35 +1,37 @@
1
- require 'minitest_helper'
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
2
4
  require 'forwardable'
3
5
 
4
- describe 'parallelism' do
5
- class FindOrCreateWorker
6
- extend Forwardable
7
- def_delegators :@thread, :join, :wakeup, :status, :to_s
6
+ class FindOrCreateWorker
7
+ extend Forwardable
8
+ def_delegators :@thread, :join, :wakeup, :status, :to_s
8
9
 
9
- def initialize(name, use_advisory_lock)
10
- @name = name
11
- @use_advisory_lock = use_advisory_lock
12
- @thread = Thread.new { work_later }
13
- end
10
+ def initialize(name, use_advisory_lock)
11
+ @name = name
12
+ @use_advisory_lock = use_advisory_lock
13
+ @thread = Thread.new { work_later }
14
+ end
14
15
 
15
- def work_later
16
- sleep
17
- ActiveRecord::Base.connection_pool.with_connection do
18
- if @use_advisory_lock
19
- Tag.with_advisory_lock(@name) { work }
20
- else
21
- work
22
- end
16
+ def work_later
17
+ sleep
18
+ ActiveRecord::Base.connection_pool.with_connection do
19
+ if @use_advisory_lock
20
+ Tag.with_advisory_lock(@name) { work }
21
+ else
22
+ work
23
23
  end
24
24
  end
25
+ end
25
26
 
26
- def work
27
- Tag.transaction do
28
- Tag.where(name: @name).first_or_create
29
- end
27
+ def work
28
+ Tag.transaction do
29
+ Tag.where(name: @name).first_or_create
30
30
  end
31
31
  end
32
+ end
32
33
 
34
+ class ParallelismTest < GemTestCase
33
35
  def run_workers
34
36
  @names = @iterations.times.map { |iter| "iteration ##{iter}" }
35
37
  @names.each do |name|
@@ -37,9 +39,7 @@ describe 'parallelism' do
37
39
  FindOrCreateWorker.new(name, @use_advisory_lock)
38
40
  end
39
41
  # Wait for all the threads to get ready:
40
- until workers.all? { |ea| ea.status == 'sleep' }
41
- sleep(0.1)
42
- end
42
+ sleep(0.1) until workers.all? { |ea| ea.status == 'sleep' }
43
43
  # OK, GO!
44
44
  workers.each(&:wakeup)
45
45
  # Then wait for them to finish:
@@ -49,29 +49,27 @@ describe 'parallelism' do
49
49
  ActiveRecord::Base.connection_pool.connection
50
50
  end
51
51
 
52
- before :each do
52
+ setup do
53
53
  ActiveRecord::Base.connection.reconnect!
54
54
  @workers = 10
55
55
  end
56
56
 
57
- # < SQLite, understandably, throws "The database file is locked (database is locked)"
58
-
59
- it 'creates multiple duplicate rows without advisory locks' do
60
- skip if env_db == :sqlite
57
+ test 'creates multiple duplicate rows without advisory locks' do
58
+ skip if %i[sqlite3 jdbcsqlite3].include?(env_db)
61
59
  @use_advisory_lock = false
62
60
  @iterations = 1
63
61
  run_workers
64
- Tag.all.size.must_be :>, @iterations # <- any duplicated rows will make me happy.
65
- TagAudit.all.size.must_be :>, @iterations # <- any duplicated rows will make me happy.
66
- Label.all.size.must_be :>, @iterations # <- any duplicated rows will make me happy.
62
+ assert_operator(Tag.all.size, :>, @iterations) # <- any duplicated rows will make me happy.
63
+ assert_operator(TagAudit.all.size, :>, @iterations) # <- any duplicated rows will make me happy.
64
+ assert_operator(Label.all.size, :>, @iterations) # <- any duplicated rows will make me happy.
67
65
  end
68
66
 
69
- it "doesn't create multiple duplicate rows with advisory locks" do
67
+ test "doesn't create multiple duplicate rows with advisory locks" do
70
68
  @use_advisory_lock = true
71
69
  @iterations = 10
72
70
  run_workers
73
- Tag.all.size.must_equal @iterations # <- any duplicated rows will NOT make me happy.
74
- TagAudit.all.size.must_equal @iterations # <- any duplicated rows will NOT make me happy.
75
- Label.all.size.must_equal @iterations # <- any duplicated rows will NOT make me happy.
71
+ assert_equal(@iterations, Tag.all.size) # <- any duplicated rows will NOT make me happy.
72
+ assert_equal(@iterations, TagAudit.all.size) # <- any duplicated rows will NOT make me happy.
73
+ assert_equal(@iterations, Label.all.size) # <- any duplicated rows will NOT make me happy.
76
74
  end
77
75
  end
data/test/shared_test.rb CHANGED
@@ -1,131 +1,134 @@
1
- require 'minitest_helper'
1
+ # frozen_string_literal: true
2
2
 
3
- describe 'shared locks' do
4
- def supported?
5
- env_db != :mysql
6
- end
7
-
8
- class SharedTestWorker
9
- def initialize(shared)
10
- @shared = shared
3
+ require 'test_helper'
4
+ class SharedTestWorker
5
+ def initialize(shared)
6
+ @shared = shared
11
7
 
12
- @locked = nil
13
- @cleanup = false
14
- @thread = Thread.new { work }
15
- end
8
+ @locked = nil
9
+ @cleanup = false
10
+ @thread = Thread.new { work }
11
+ end
16
12
 
17
- def locked?
18
- sleep 0.01 while @locked.nil? && @thread.alive?
19
- @locked
20
- end
13
+ def locked?
14
+ sleep 0.01 while @locked.nil? && @thread.alive?
15
+ @locked
16
+ end
21
17
 
22
- def cleanup!
23
- @cleanup = true
24
- @thread.join
25
- raise if @thread.status.nil?
26
- end
18
+ def cleanup!
19
+ @cleanup = true
20
+ @thread.join
21
+ raise if @thread.status.nil?
22
+ end
27
23
 
28
- private
24
+ private
29
25
 
30
- def work
31
- ActiveRecord::Base.connection_pool.with_connection do
32
- Tag.with_advisory_lock('test', timeout_seconds: 0, shared: @shared) do
33
- @locked = true
34
- sleep 0.01 until @cleanup
35
- end
36
- @locked = false
26
+ def work
27
+ ActiveRecord::Base.connection_pool.with_connection do
28
+ Tag.with_advisory_lock('test', timeout_seconds: 0, shared: @shared) do
29
+ @locked = true
37
30
  sleep 0.01 until @cleanup
38
31
  end
32
+ @locked = false
33
+ sleep 0.01 until @cleanup
39
34
  end
40
35
  end
36
+ end
37
+
38
+ class SharedLocksTest < GemTestCase
39
+ def supported?
40
+ %i[trilogy mysql2 jdbcmysql].exclude?(env_db)
41
+ end
41
42
 
42
- it 'does not allow two exclusive locks' do
43
+ test 'does not allow two exclusive locks' do
43
44
  one = SharedTestWorker.new(false)
44
- one.locked?.must_equal true
45
+ assert_predicate(one, :locked?)
45
46
 
46
47
  two = SharedTestWorker.new(false)
47
- two.locked?.must_equal false
48
+ refute(two.locked?)
48
49
 
49
50
  one.cleanup!
50
51
  two.cleanup!
51
52
  end
53
+ end
52
54
 
53
- describe 'not supported' do
54
- before do
55
- skip if supported?
56
- end
55
+ class NotSupportedEnvironmentTest < SharedLocksTest
56
+ setup do
57
+ skip if supported?
58
+ end
57
59
 
58
- it 'raises an error when attempting to use a shared lock' do
59
- one = SharedTestWorker.new(true)
60
- one.locked?.must_be_nil
61
- exception = proc {
62
- one.cleanup!
63
- }.must_raise ArgumentError
64
- exception.message.must_include 'not supported'
60
+ test 'raises an error when attempting to use a shared lock' do
61
+ one = SharedTestWorker.new(true)
62
+ assert_nil(one.locked?)
63
+
64
+ exception = assert_raises(ArgumentError) do
65
+ one.cleanup!
65
66
  end
67
+
68
+ assert_match(/#{Regexp.escape('not supported')}/, exception.message)
66
69
  end
70
+ end
67
71
 
68
- describe 'supported' do
69
- before do
70
- skip unless supported?
71
- end
72
+ class SupportedEnvironmentTest < SharedLocksTest
73
+ setup do
74
+ skip unless supported?
75
+ end
72
76
 
73
- it 'does allow two shared locks' do
74
- one = SharedTestWorker.new(true)
75
- one.locked?.must_equal true
77
+ test 'does allow two shared locks' do
78
+ one = SharedTestWorker.new(true)
79
+ assert_predicate(one, :locked?)
76
80
 
77
- two = SharedTestWorker.new(true)
78
- two.locked?.must_equal true
81
+ two = SharedTestWorker.new(true)
82
+ assert_predicate(two, :locked?)
79
83
 
80
- one.cleanup!
81
- two.cleanup!
82
- end
84
+ one.cleanup!
85
+ two.cleanup!
86
+ end
83
87
 
84
- it 'does not allow exclusive lock with shared lock' do
85
- one = SharedTestWorker.new(true)
86
- one.locked?.must_equal true
88
+ test 'does not allow exclusive lock with shared lock' do
89
+ one = SharedTestWorker.new(true)
90
+ assert_predicate(one, :locked?)
87
91
 
88
- two = SharedTestWorker.new(false)
89
- two.locked?.must_equal false
92
+ two = SharedTestWorker.new(false)
93
+ refute(two.locked?)
90
94
 
91
- three = SharedTestWorker.new(true)
92
- three.locked?.must_equal true
95
+ three = SharedTestWorker.new(true)
96
+ assert_predicate(three, :locked?)
93
97
 
94
- one.cleanup!
95
- two.cleanup!
96
- three.cleanup!
97
- end
98
+ one.cleanup!
99
+ two.cleanup!
100
+ three.cleanup!
101
+ end
98
102
 
99
- it 'does not allow shared lock with exclusive lock' do
100
- one = SharedTestWorker.new(false)
101
- one.locked?.must_equal true
103
+ test 'does not allow shared lock with exclusive lock' do
104
+ one = SharedTestWorker.new(false)
105
+ assert_predicate(one, :locked?)
102
106
 
103
- two = SharedTestWorker.new(true)
104
- two.locked?.must_equal false
107
+ two = SharedTestWorker.new(true)
108
+ refute(two.locked?)
105
109
 
106
- one.cleanup!
107
- two.cleanup!
108
- end
110
+ one.cleanup!
111
+ two.cleanup!
112
+ end
109
113
 
110
- describe 'PostgreSQL' do
111
- before do
112
- skip unless env_db == :postgresql
113
- end
114
+ class PostgreSQLTest < SupportedEnvironmentTest
115
+ setup do
116
+ skip unless env_db == :postgresql
117
+ end
114
118
 
115
- def pg_lock_modes
116
- ActiveRecord::Base.connection.select_values("SELECT mode FROM pg_locks WHERE locktype = 'advisory';")
117
- end
119
+ def pg_lock_modes
120
+ ActiveRecord::Base.connection.select_values("SELECT mode FROM pg_locks WHERE locktype = 'advisory';")
121
+ end
118
122
 
119
- it 'allows shared lock to be upgraded to an exclusive lock' do
120
- pg_lock_modes.must_equal %w[]
121
- Tag.with_advisory_lock 'test', shared: true do
122
- pg_lock_modes.must_equal %w[ShareLock]
123
- Tag.with_advisory_lock 'test', shared: false do
124
- pg_lock_modes.must_equal %w[ShareLock ExclusiveLock]
125
- end
123
+ test 'allows shared lock to be upgraded to an exclusive lock' do
124
+ assert_empty(pg_lock_modes)
125
+ Tag.with_advisory_lock 'test', shared: true do
126
+ assert_equal(%w[ShareLock], pg_lock_modes)
127
+ Tag.with_advisory_lock 'test', shared: false do
128
+ assert_equal(%w[ShareLock ExclusiveLock], pg_lock_modes)
126
129
  end
127
- pg_lock_modes.must_equal %w[]
128
130
  end
131
+ assert_empty(pg_lock_modes)
129
132
  end
130
133
  end
131
134
  end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'erb'
4
+ require 'active_record'
5
+ require 'with_advisory_lock'
6
+ require 'tmpdir'
7
+ require 'securerandom'
8
+ begin
9
+ require 'activerecord-trilogy-adapter'
10
+ ActiveSupport.on_load(:active_record) do
11
+ require "trilogy_adapter/connection"
12
+ ActiveRecord::Base.public_send :extend, TrilogyAdapter::Connection
13
+ end
14
+ rescue LoadError
15
+ # do nothing
16
+ end
17
+
18
+ ActiveRecord::Base.configurations = {
19
+ default_env: {
20
+ url: ENV.fetch('DATABASE_URL', "sqlite3://#{Dir.tmpdir}/#{SecureRandom.hex}.sqlite3"),
21
+ properties: { allowPublicKeyRetrieval: true } # for JRuby madness
22
+ }
23
+ }
24
+
25
+ ENV['WITH_ADVISORY_LOCK_PREFIX'] ||= SecureRandom.hex
26
+
27
+ ActiveRecord::Base.establish_connection
28
+
29
+ def env_db
30
+ @env_db ||= ActiveRecord::Base.connection_db_config.adapter.to_sym
31
+ end
32
+
33
+ ActiveRecord::Migration.verbose = false
34
+
35
+ require 'test_models'
36
+ require 'minitest'
37
+ require 'maxitest/autorun'
38
+ require 'mocha/minitest'
39
+
40
+ class GemTestCase < ActiveSupport::TestCase
41
+ setup do
42
+ ENV['FLOCK_DIR'] = Dir.mktmpdir
43
+ Tag.delete_all
44
+ TagAudit.delete_all
45
+ Label.delete_all
46
+ end
47
+ teardown do
48
+ FileUtils.remove_entry_secure ENV['FLOCK_DIR']
49
+ end
50
+ end
51
+
52
+ puts "Testing with #{env_db} database, ActiveRecord #{ActiveRecord.gem_version} and #{RUBY_ENGINE} #{RUBY_ENGINE_VERSION} as #{RUBY_VERSION}"
data/test/test_models.rb CHANGED
@@ -1,12 +1,14 @@
1
- ActiveRecord::Schema.define(:version => 0) do
2
- create_table "tags", :force => true do |t|
3
- t.string "name"
1
+ # frozen_string_literal: true
2
+
3
+ ActiveRecord::Schema.define(version: 0) do
4
+ create_table 'tags', force: true do |t|
5
+ t.string 'name'
4
6
  end
5
- create_table "tag_audits", :id => false, :force => true do |t|
6
- t.string "tag_name"
7
+ create_table 'tag_audits', id: false, force: true do |t|
8
+ t.string 'tag_name'
7
9
  end
8
- create_table "labels", :id => false, :force => true do |t|
9
- t.string "name"
10
+ create_table 'labels', id: false, force: true do |t|
11
+ t.string 'name'
10
12
  end
11
13
  end
12
14
 
data/test/thread_test.rb CHANGED
@@ -1,16 +1,17 @@
1
- require 'minitest_helper'
1
+ # frozen_string_literal: true
2
2
 
3
- describe 'separate thread tests' do
4
- let(:lock_name) { 'testing 1,2,3' } # OMG COMMAS
3
+ require 'test_helper'
5
4
 
6
- before do
5
+ class SeparateThreadTest < GemTestCase
6
+ setup do
7
+ @lock_name = 'testing 1,2,3' # OMG COMMAS
7
8
  @mutex = Mutex.new
8
9
  @t1_acquired_lock = false
9
10
  @t1_return_value = nil
10
11
 
11
12
  @t1 = Thread.new do
12
13
  ActiveRecord::Base.connection_pool.with_connection do
13
- @t1_return_value = Label.with_advisory_lock(lock_name) do
14
+ @t1_return_value = Label.with_advisory_lock(@lock_name) do
14
15
  @mutex.synchronize { @t1_acquired_lock = true }
15
16
  sleep
16
17
  't1 finished'
@@ -19,42 +20,42 @@ describe 'separate thread tests' do
19
20
  end
20
21
 
21
22
  # Wait for the thread to acquire the lock:
22
- until @mutex.synchronize { @t1_acquired_lock } do
23
- sleep(0.1)
24
- end
23
+ sleep(0.1) until @mutex.synchronize { @t1_acquired_lock }
25
24
  ActiveRecord::Base.connection.reconnect!
26
25
  end
27
26
 
28
- after do
27
+ teardown do
29
28
  @t1.wakeup if @t1.status == 'sleep'
30
29
  @t1.join
31
30
  end
32
31
 
33
- it '#with_advisory_lock with a 0 timeout returns false immediately' do
34
- response = Label.with_advisory_lock(lock_name, 0) do
35
- fail 'should not be yielded to'
32
+ test '#with_advisory_lock with a 0 timeout returns false immediately' do
33
+ response = Label.with_advisory_lock(@lock_name, 0) do
34
+ raise 'should not be yielded to'
36
35
  end
37
- response.must_be_false
36
+ assert_not(response)
38
37
  end
39
38
 
40
- it '#with_advisory_lock yields to the provided block' do
41
- @t1_acquired_lock.must_be_true
39
+ test '#with_advisory_lock yields to the provided block' do
40
+ assert(@t1_acquired_lock)
42
41
  end
43
42
 
44
- it '#advisory_lock_exists? returns true when another thread has the lock' do
45
- Tag.advisory_lock_exists?(lock_name).must_be_true
43
+ test '#advisory_lock_exists? returns true when another thread has the lock' do
44
+ assert(Tag.advisory_lock_exists?(@lock_name))
46
45
  end
47
46
 
48
- it 'can re-establish the lock after the other thread releases it' do
47
+ test 'can re-establish the lock after the other thread releases it' do
49
48
  @t1.wakeup
50
49
  @t1.join
51
- @t1_return_value.must_equal 't1 finished'
50
+ assert_equal('t1 finished', @t1_return_value)
52
51
 
53
52
  # We should now be able to acquire the lock immediately:
54
53
  reacquired = false
55
- Label.with_advisory_lock(lock_name, 0) do
54
+ lock_result = Label.with_advisory_lock(@lock_name, 0) do
56
55
  reacquired = true
57
- end.must_be_true
58
- reacquired.must_be_true
56
+ end
57
+
58
+ assert(lock_result)
59
+ assert(reacquired)
59
60
  end
60
61
  end
@@ -1,70 +1,68 @@
1
- require 'minitest_helper'
1
+ # frozen_string_literal: true
2
2
 
3
- describe 'transaction scoping' do
3
+ require 'test_helper'
4
+
5
+ class TransactionScopingTest < GemTestCase
4
6
  def supported?
5
- env_db == :postgresql
7
+ %i[postgresql jdbcpostgresql].include?(env_db)
6
8
  end
7
9
 
8
- describe 'not supported' do
9
- before do
10
- skip if supported?
11
- end
10
+ test 'raises an error when attempting to use transaction level locks if not supported' do
11
+ skip if supported?
12
12
 
13
- it 'raises an error when attempting to use transaction level locks' do
14
- Tag.transaction do
15
- exception = proc {
16
- Tag.with_advisory_lock 'test', transaction: true do
17
- raise 'should not get here'
18
- end
19
- }.must_raise ArgumentError
20
- exception.message.must_include 'not supported'
13
+ Tag.transaction do
14
+ exception = assert_raises(ArgumentError) do
15
+ Tag.with_advisory_lock 'test', transaction: true do
16
+ raise 'should not get here'
17
+ end
21
18
  end
19
+
20
+ assert_match(/#{Regexp.escape('not supported')}/, exception.message)
22
21
  end
23
22
  end
24
23
 
25
- describe 'supported' do
26
- before do
24
+ class PostgresqlTest < TransactionScopingTest
25
+ setup do
27
26
  skip unless env_db == :postgresql
27
+ @pg_lock_count = lambda do
28
+ ActiveRecord::Base.connection.select_value("SELECT COUNT(*) FROM pg_locks WHERE locktype = 'advisory';").to_i
29
+ end
28
30
  end
29
31
 
30
- def pg_lock_count
31
- ActiveRecord::Base.connection.select_value("SELECT COUNT(*) FROM pg_locks WHERE locktype = 'advisory';").to_i
32
- end
33
-
34
- specify 'session locks release after the block executes' do
32
+ test 'session locks release after the block executes' do
35
33
  Tag.transaction do
36
- pg_lock_count.must_equal 0
34
+ assert_equal(0, @pg_lock_count.call)
37
35
  Tag.with_advisory_lock 'test' do
38
- pg_lock_count.must_equal 1
36
+ assert_equal(1, @pg_lock_count.call)
39
37
  end
40
- pg_lock_count.must_equal 0
38
+ assert_equal(0, @pg_lock_count.call)
41
39
  end
42
40
  end
43
41
 
44
- specify 'session locks release when transaction fails inside block' do
42
+ test 'session locks release when transaction fails inside block' do
45
43
  Tag.transaction do
46
- pg_lock_count.must_equal 0
44
+ assert_equal(0, @pg_lock_count.call)
47
45
 
48
- exception = proc {
46
+ exception = assert_raises(ActiveRecord::StatementInvalid) do
49
47
  Tag.with_advisory_lock 'test' do
50
48
  Tag.connection.execute 'SELECT 1/0;'
51
49
  end
52
- }.must_raise ActiveRecord::StatementInvalid
53
- exception.message.must_include 'division by zero'
50
+ end
54
51
 
55
- pg_lock_count.must_equal 0
52
+ assert_match(/#{Regexp.escape('division by zero')}/, exception.message)
53
+ assert_equal(0, @pg_lock_count.call)
56
54
  end
57
55
  end
58
56
 
59
- specify 'transaction level locks hold until the transaction completes' do
57
+ test 'transaction level locks hold until the transaction completes' do
60
58
  Tag.transaction do
61
- pg_lock_count.must_equal 0
59
+ assert_equal(0, @pg_lock_count.call)
62
60
  Tag.with_advisory_lock 'test', transaction: true do
63
- pg_lock_count.must_equal 1
61
+ assert_equal(1, @pg_lock_count.call)
64
62
  end
65
- pg_lock_count.must_equal 1
63
+ assert_equal(1, @pg_lock_count.call)
66
64
  end
67
- pg_lock_count.must_equal 0
65
+ assert_equal(0, @pg_lock_count.call)
68
66
  end
69
67
  end
70
68
  end
@@ -1,29 +1,30 @@
1
- lib = File.expand_path('../lib', __FILE__)
2
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
- require 'with_advisory_lock/version'
1
+ # frozen_string_literal: true
4
2
 
5
- Gem::Specification.new do |gem|
6
- gem.name = "with_advisory_lock"
7
- gem.version = WithAdvisoryLock::VERSION
8
- gem.authors = ['Matthew McEachen']
9
- gem.email = %w(matthew+github@mceachen.org)
10
- gem.homepage = 'https://github.com/mceachen/with_advisory_lock'
11
- gem.summary = %q{Advisory locking for ActiveRecord}
12
- gem.description = %q{Advisory locking for ActiveRecord}
13
- gem.license = 'MIT'
3
+ require 'English'
4
+ require_relative 'lib/with_advisory_lock/version'
14
5
 
15
- gem.files = `git ls-files`.split($/)
16
- gem.test_files = gem.files.grep(%r{^test/})
17
- gem.require_paths = %w(lib)
18
- gem.required_ruby_version = '>= 2.2.10'
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'with_advisory_lock'
8
+ spec.version = WithAdvisoryLock::VERSION
9
+ spec.authors = ['Matthew McEachen', 'Abdelkader Boudih']
10
+ spec.email = %w[matthew+github@mceachen.org terminale@gmail.com]
11
+ spec.homepage = 'https://github.com/ClosureTree/with_advisory_lock'
12
+ spec.summary = 'Advisory locking for ActiveRecord'
13
+ spec.description = 'Advisory locking for ActiveRecord'
14
+ spec.license = 'MIT'
19
15
 
20
- gem.add_runtime_dependency 'activerecord', '>= 4.2'
16
+ spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
17
+ spec.test_files = spec.files.grep(%r{^test/})
18
+ spec.require_paths = %w[lib]
19
+ spec.metadata = { "rubyspecs_mfa_required" => "true" }
20
+ spec.required_ruby_version = '>= 2.7.0'
21
+ spec.metadata["yard.run"] = "yri"
21
22
 
23
+ spec.add_runtime_dependency 'activerecord', '>= 6.1'
22
24
 
23
- gem.add_development_dependency 'yard'
24
- gem.add_development_dependency 'minitest'
25
- gem.add_development_dependency 'minitest-great_expectations'
26
- gem.add_development_dependency 'minitest-reporters'
27
- gem.add_development_dependency 'mocha'
28
- gem.add_development_dependency 'appraisal'
25
+ spec.add_development_dependency 'appraisal'
26
+ spec.add_development_dependency 'maxitest'
27
+ spec.add_development_dependency 'minitest-reporters'
28
+ spec.add_development_dependency 'mocha'
29
+ spec.add_development_dependency 'yard'
29
30
  end