with_advisory_lock 2.0.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9a06363d16fb4769a2ee0486a8f0295dfe6e0c21
4
- data.tar.gz: 3ce9cf5d558777ab689388fc086ce1fc2b18e1aa
3
+ metadata.gz: aeb99a0ddec77a51eeb59a41a26411a25390c65b
4
+ data.tar.gz: 3ee6f67835cbd6f3fe515f41b8134478ef2102ce
5
5
  SHA512:
6
- metadata.gz: 1e162976296fc77f93eb8a46bf2681c4a7694727317a384b38bdfc552cd7dba6738a84e035879df673256624a6bdf3c0a74e16a677ce06adae80b2ee9daf59b8
7
- data.tar.gz: 5454c4de4150ceaa24c797ae726913ed0b239fdfcbf19471985d760d76c41a2b78da3ea1df4c733afb1ba75301e02ae0550252876e3b3e4b145e4984b4c5e3b4
6
+ metadata.gz: 58919a4b30f793b056f8c9f2af7f06f40673f5e9e6ef68213760f49b348f9044087ab16a1b398619e7c47bcba14db7eecadaf50a6f5652bfa5e36bb0ef1d9fc7
7
+ data.tar.gz: a819c9b4d0c2607505b90f9dbb8f981c041c3608ae69a63e7dac621d105479be268b5d425f1ec946c225b36175d595ed2115782a9a239cd3ad671c6e3b25d665
@@ -23,10 +23,9 @@ before_script:
23
23
  - mysql -e 'create database with_advisory_lock_test'
24
24
  - psql -c 'create database with_advisory_lock_test' -U postgres
25
25
 
26
- addons:
27
- postgresql: "9.3"
28
-
29
26
  matrix:
30
27
  allow_failures:
31
28
  - gemfile: gemfiles/activerecord_edge.gemfile
29
+ - rvm: jruby-19mode # travis' version of jruby has issues. Tests pass with jruby 1.7.13/java 1.8.0_11 on mac.
32
30
  - rvm: rbx-2
31
+
data/README.md CHANGED
@@ -41,9 +41,14 @@ A value of zero will try the lock only once. If the lock is acquired, the block
41
41
  will be yielded to. If the lock is currently being held, the block will not be called.
42
42
 
43
43
  Note that if a non-nil value is provided for `timeout_seconds`, the block will not be invoked if
44
- the lock cannot be acquired within that timeframe.
44
+ the lock cannot be acquired within that time-frame.
45
45
 
46
46
  ### Return values
47
+
48
+ The return value of `with_advisory_lock_result` is a `WithAdvisoryLock::Result` instance,
49
+ which has a `lock_was_acquired?` method and a `result` accessor method, which is
50
+ the returned value of the given block. If your block may validly return false, you should use
51
+ this method.
47
52
 
48
53
  The return value of ```with_advisory_lock``` will be the result of the yielded block,
49
54
  if the lock was able to be acquired and the block yielded, or ```false```, if you provided
@@ -134,6 +139,20 @@ end
134
139
 
135
140
  ## Changelog
136
141
 
142
+ ### 3.0.0
143
+
144
+ * Added jruby/PostgreSQL support for Rails 4.x
145
+ * Reworked threaded tests to allow jruby tests to pass
146
+
147
+ #### API changes
148
+
149
+ * `yield_with_lock_and_timeout` and `yield_with_lock` now return instances of
150
+ `WithAdvisoryLock::Result`, so blocks that return `false` are not misinterpreted
151
+ as a failure to lock. As this changes the interface (albeit internal methods), the major version
152
+ number was incremented.
153
+ * `with_advisory_lock_result` was introduced, which clarifies whether the lock was acquired
154
+ versus the yielded block returned false.
155
+
137
156
  ### 2.0.0
138
157
 
139
158
  * Lock timeouts of 0 now attempt the lock once, as per suggested by
@@ -1,6 +1,21 @@
1
1
  require 'zlib'
2
2
 
3
3
  module WithAdvisoryLock
4
+ class Result
5
+ attr_reader :result
6
+
7
+ def initialize(lock_was_acquired, result = false)
8
+ @lock_was_acquired = lock_was_acquired
9
+ @result = result
10
+ end
11
+
12
+ def lock_was_acquired?
13
+ @lock_was_acquired
14
+ end
15
+ end
16
+
17
+ FAILED_TO_LOCK = Result.new(false)
18
+
4
19
  class Base
5
20
  attr_reader :connection, :lock_name, :timeout_seconds
6
21
 
@@ -15,20 +30,22 @@ module WithAdvisoryLock
15
30
  end
16
31
 
17
32
  def self.lock_stack
33
+ # access doesn't need to be synchronized as it is only accessed by the current thread.
18
34
  Thread.current[:with_advisory_lock_stack] ||= []
19
35
  end
20
-
21
36
  delegate :lock_stack, to: 'self.class'
22
37
 
23
38
  def already_locked?
24
39
  lock_stack.include? lock_str
25
40
  end
26
41
 
27
- def with_advisory_lock_if_needed
42
+ def with_advisory_lock_if_needed(&block)
28
43
  if already_locked?
29
- yield
44
+ Result.new(true, yield)
45
+ elsif timeout_seconds == 0
46
+ yield_with_lock(&block)
30
47
  else
31
- yield_with_lock { yield }
48
+ yield_with_lock_and_timeout(&block)
32
49
  end
33
50
  end
34
51
 
@@ -42,35 +59,35 @@ module WithAdvisoryLock
42
59
  end
43
60
  end
44
61
 
45
- def advisory_lock_exists?
46
- acquired_lock = try_lock
47
- ensure
48
- release_lock if acquired_lock
62
+ def yield_with_lock_and_timeout(&block)
63
+ give_up_at = Time.now + @timeout_seconds if @timeout_seconds
64
+ while @timeout_seconds.nil? || Time.now < give_up_at do
65
+ r = yield_with_lock(&block)
66
+ return r if r.lock_was_acquired?
67
+ # Randomizing sleep time may help reduce contention.
68
+ sleep(rand(0.05..0.15))
69
+ end
70
+ FAILED_TO_LOCK
49
71
  end
50
72
 
51
73
  def yield_with_lock
52
- give_up_at = Time.now + @timeout_seconds if @timeout_seconds
53
- begin
54
- if try_lock
55
- begin
56
- lock_stack.push(lock_str)
57
- return yield
58
- ensure
59
- lock_stack.pop
60
- release_lock
61
- end
62
- else
63
- # sleep between 1/20 and ~1/5 of a second.
64
- # Randomizing sleep time may help reduce contention.
65
- sleep(rand * 0.15 + 0.05)
74
+ if try_lock
75
+ begin
76
+ lock_stack.push(lock_str)
77
+ result = block_given? ? yield : nil
78
+ Result.new(true, result)
79
+ ensure
80
+ lock_stack.pop
81
+ release_lock
66
82
  end
67
- end while @timeout_seconds.nil? || Time.now < give_up_at
68
- false # failed to get lock in time.
83
+ else
84
+ FAILED_TO_LOCK
85
+ end
69
86
  end
70
87
 
71
- # The timestamp prevents AR from caching the result improperly, and is ignored.
72
- def query_cache_buster
73
- "AS t#{(Time.now.to_f * 1000).to_i}"
88
+ # Prevent AR from caching results improperly
89
+ def unique_column_name
90
+ "t#{SecureRandom.hex}"
74
91
  end
75
92
  end
76
93
  end
@@ -1,23 +1,24 @@
1
- # Tried desperately to monkeypatch the polymorphic connection object,
2
- # but rails autoloading is too clever by half. Pull requests are welcome.
3
-
4
1
  require 'active_support/concern'
5
2
 
6
3
  module WithAdvisoryLock
7
4
  module Concern
8
5
  extend ActiveSupport::Concern
9
-
10
6
  delegate :with_advisory_lock, :advisory_lock_exists?, to: 'self.class'
11
7
 
12
8
  module ClassMethods
13
9
  def with_advisory_lock(lock_name, timeout_seconds=nil, &block)
10
+ result = with_advisory_lock_result(lock_name, timeout_seconds, &block)
11
+ result.lock_was_acquired? ? result.result : false
12
+ end
13
+
14
+ def with_advisory_lock_result(lock_name, timeout_seconds=nil, &block)
14
15
  impl = impl_class.new(connection, lock_name, timeout_seconds)
15
16
  impl.with_advisory_lock_if_needed(&block)
16
17
  end
17
18
 
18
19
  def advisory_lock_exists?(lock_name)
19
20
  impl = impl_class.new(connection, lock_name, 0)
20
- impl.already_locked? || !impl.yield_with_lock { true }
21
+ impl.already_locked? || !impl.yield_with_lock.lock_was_acquired?
21
22
  end
22
23
 
23
24
  def current_advisory_lock
@@ -27,12 +28,12 @@ module WithAdvisoryLock
27
28
  private
28
29
 
29
30
  def impl_class
30
- case WithAdvisoryLock::DatabaseAdapterSupport.new(connection).adapter
31
- when :postgresql
31
+ adapter = WithAdvisoryLock::DatabaseAdapterSupport.new(connection)
32
+ if adapter.postgresql?
32
33
  WithAdvisoryLock::PostgreSQL
33
- when :mysql
34
+ elsif adapter.mysql?
34
35
  WithAdvisoryLock::MySQL
35
- else #sqlite
36
+ else
36
37
  WithAdvisoryLock::Flock
37
38
  end
38
39
  end
@@ -15,11 +15,5 @@ module WithAdvisoryLock
15
15
  def sqlite?
16
16
  :sqlite3 == @sym_name
17
17
  end
18
-
19
- def adapter
20
- return :mysql if mysql?
21
- return :postgresql if postgresql?
22
- :sqlite3
23
- end
24
18
  end
25
19
  end
@@ -7,20 +7,15 @@ module WithAdvisoryLock
7
7
  "MySQL doesn't support nested Advisory Locks",
8
8
  lock_stack.dup)
9
9
  end
10
- # Returns 1 if the lock was obtained successfully,
11
- # 0 if the attempt timed out (for example, because another client has
12
- # previously locked the name), or NULL if an error occurred
13
- # (such as running out of memory or the thread was killed with mysqladmin kill).
14
- sql = "SELECT GET_LOCK(#{quoted_lock_str}, 0) #{query_cache_buster}"
15
- connection.select_value(sql).to_i > 0
10
+ execute_successful?("GET_LOCK(#{quoted_lock_str}, 0)")
16
11
  end
17
12
 
18
13
  def release_lock
19
- # Returns > 0 if the lock was released,
20
- # 0 if the lock was not established by this thread
21
- # (in which case the lock is not released), and
22
- # NULL if the named lock did not exist.
23
- sql = "SELECT RELEASE_LOCK(#{quoted_lock_str}) #{query_cache_buster}"
14
+ execute_successful?("RELEASE_LOCK(#{quoted_lock_str})")
15
+ end
16
+
17
+ def execute_successful?(mysql_function)
18
+ sql = "SELECT #{mysql_function} AS #{unique_column_name}"
24
19
  connection.select_value(sql).to_i > 0
25
20
  end
26
21
 
@@ -2,15 +2,18 @@ module WithAdvisoryLock
2
2
  class PostgreSQL < Base
3
3
  # See http://www.postgresql.org/docs/9.1/static/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS
4
4
  def try_lock
5
- # pg_try_advisory_lock will either obtain the lock immediately and return true
6
- # or return false if the lock cannot be acquired immediately
7
- sql = "SELECT pg_try_advisory_lock(#{lock_keys.join(',')}) #{query_cache_buster}"
8
- 't' == connection.select_value(sql).to_s
5
+ execute_successful?('pg_try_advisory_lock')
9
6
  end
10
7
 
11
8
  def release_lock
12
- sql = "SELECT pg_advisory_unlock(#{lock_keys.join(',')}) #{query_cache_buster}"
13
- 't' == connection.select_value(sql).to_s
9
+ execute_successful?('pg_advisory_unlock')
10
+ end
11
+
12
+ def execute_successful?(pg_function)
13
+ sql = "SELECT #{pg_function}(#{lock_keys.join(',')}) AS #{unique_column_name}"
14
+ result = connection.select_value(sql)
15
+ # MRI returns 't', jruby returns true. YAY!
16
+ (result == 't' || result == true)
14
17
  end
15
18
 
16
19
  # PostgreSQL wants 2 32bit integers as the lock key.
@@ -1,3 +1,3 @@
1
1
  module WithAdvisoryLock
2
- VERSION = Gem::Version.new('2.0.0')
2
+ VERSION = Gem::Version.new('3.0.0')
3
3
  end
@@ -1,22 +1,23 @@
1
1
  require 'minitest_helper'
2
2
 
3
3
  describe 'class methods' do
4
- let(:lock_name) { "test lock #{rand(1024)}" }
4
+ let(:lock_name) { 'test lock' }
5
5
 
6
6
  describe '.current_advisory_lock' do
7
- it "returns nil outside an advisory lock request" do
7
+ it 'returns nil outside an advisory lock request' do
8
8
  Tag.current_advisory_lock.must_be_nil
9
9
  end
10
10
 
11
11
  it 'returns the name of the last lock acquired' do
12
12
  Tag.with_advisory_lock(lock_name) do
13
+ # The lock name may have a prefix if WITH_ADVISORY_LOCK_PREFIX env is set
13
14
  Tag.current_advisory_lock.must_match /#{lock_name}/
14
15
  end
15
16
  end
16
17
  end
17
18
 
18
19
  describe '.advisory_lock_exists?' do
19
- it "returns false for an unacquired lock" do
20
+ it 'returns false for an unacquired lock' do
20
21
  Tag.advisory_lock_exists?(lock_name).must_be_false
21
22
  end
22
23
 
@@ -27,13 +28,12 @@ describe 'class methods' do
27
28
  end
28
29
  end
29
30
 
30
- describe "0 timeout" do
31
+ describe 'zero timeout_seconds' do
31
32
  it 'attempts the lock exactly once with no timeout' do
32
- block_was_yielded = false
33
+ expected = SecureRandom.base64
33
34
  Tag.with_advisory_lock(lock_name, 0) do
34
- block_was_yielded = true
35
- end
36
- block_was_yielded.must_be_true
35
+ expected
36
+ end.must_equal expected
37
37
  end
38
38
  end
39
39
  end
@@ -4,14 +4,14 @@ require 'with_advisory_lock'
4
4
  require 'tmpdir'
5
5
  require 'securerandom'
6
6
 
7
- db_config = File.expand_path("database.yml", File.dirname(__FILE__))
8
- ActiveRecord::Base.configurations = YAML::load(ERB.new(IO.read(db_config)).result)
9
-
10
7
  def env_db
11
- (ENV["DB"] || :mysql).to_sym
8
+ (ENV['DB'] || :mysql).to_sym
12
9
  end
13
10
 
14
- ENV["WITH_ADVISORY_LOCK_PREFIX"] ||= SecureRandom.base64
11
+ db_config = File.expand_path('database.yml', File.dirname(__FILE__))
12
+ ActiveRecord::Base.configurations = YAML::load(ERB.new(IO.read(db_config)).result)
13
+
14
+ ENV['WITH_ADVISORY_LOCK_PREFIX'] ||= SecureRandom.hex
15
15
 
16
16
  ActiveRecord::Base.establish_connection(env_db)
17
17
  ActiveRecord::Migration.verbose = false
@@ -19,14 +19,18 @@ ActiveRecord::Migration.verbose = false
19
19
  require 'test_models'
20
20
  begin
21
21
  require 'minitest'
22
- rescue LoadError => rails_four_zero_is_lame
22
+ rescue LoadError
23
+ puts 'Failed to load the minitest gem; built-in version will be used.'
23
24
  end
24
25
  require 'minitest/autorun'
25
26
  require 'minitest/great_expectations'
27
+ if ActiveRecord::VERSION::MAJOR > 3
28
+ # minitest-reporters-1.0.5/lib/minitest/old_activesupport_fix.rb:7:in `remove_method': method `run' not defined in ActiveSupport::Testing::SetupAndTeardown::ForMinitest (NameError)
29
+ require 'minitest/reporters'
30
+ Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new
31
+ end
26
32
  require 'mocha/setup'
27
33
 
28
- Thread.abort_on_exception = true
29
-
30
34
  class MiniTest::Spec
31
35
  before do
32
36
  ENV['FLOCK_DIR'] = Dir.mktmpdir
@@ -1,45 +1,52 @@
1
1
  require 'minitest_helper'
2
+ require 'forwardable'
2
3
 
3
- describe "parallelism" do
4
+ describe 'parallelism' do
4
5
  class FindOrCreateWorker
5
- def initialize(target, run_at, name, use_advisory_lock)
6
- @thread = Thread.new do
7
- ActiveRecord::Base.connection_pool.with_connection do
8
- sleep((run_at - Time.now).to_f)
9
- if use_advisory_lock
10
- Tag.with_advisory_lock(name) { work(name) }
11
- else
12
- work(name)
13
- end
6
+ extend Forwardable
7
+ def_delegators :@thread, :join, :wakeup, :join, :status, :to_s
8
+
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
14
+
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
14
22
  end
15
23
  end
16
24
  end
17
25
 
18
- def work(name)
26
+ def work
19
27
  Tag.transaction do
20
- Tag.where(name: name).first_or_create
28
+ Tag.where(name: @name).first_or_create
21
29
  end
22
30
  end
23
-
24
- def join
25
- @thread.join
26
- end
27
31
  end
28
32
 
29
33
  def run_workers
30
- all_workers = []
31
34
  @names = @iterations.times.map { |iter| "iteration ##{iter}" }
32
35
  @names.each do |name|
33
- wake_time = 1.second.from_now
34
36
  workers = @workers.times.map do
35
- FindOrCreateWorker.new(@target, wake_time, name, @use_advisory_lock)
37
+ FindOrCreateWorker.new(name, @use_advisory_lock)
38
+ end
39
+ # Wait for all the threads to get ready:
40
+ until workers.all? { |ea| ea.status == 'sleep' }
41
+ sleep(0.1)
36
42
  end
43
+ # OK, GO!
44
+ workers.each(&:wakeup)
45
+ # Then wait for them to finish:
37
46
  workers.each(&:join)
38
- all_workers += workers
39
47
  end
40
48
  # Ensure we're still connected:
41
49
  ActiveRecord::Base.connection_pool.connection
42
- all_workers
43
50
  end
44
51
 
45
52
  before :each do
@@ -47,14 +54,14 @@ describe "parallelism" do
47
54
  @workers = 10
48
55
  end
49
56
 
50
- it "creates multiple duplicate rows without advisory locks" do
57
+ it 'creates multiple duplicate rows without advisory locks' do
51
58
  @use_advisory_lock = false
52
59
  @iterations = 1
53
60
  run_workers
54
61
  Tag.all.size.must_be :>, @iterations # <- any duplicated rows will make me happy.
55
62
  TagAudit.all.size.must_be :>, @iterations # <- any duplicated rows will make me happy.
56
63
  Label.all.size.must_be :>, @iterations # <- any duplicated rows will make me happy.
57
- end unless env_db == :sqlite
64
+ end unless env_db == :sqlite # < SQLite, understandably, throws "The database file is locked (database is locked)"
58
65
 
59
66
  it "doesn't create multiple duplicate rows with advisory locks" do
60
67
  @use_advisory_lock = true
@@ -66,50 +73,3 @@ describe "parallelism" do
66
73
  end
67
74
  end
68
75
 
69
- describe "separate thread tests" do
70
- let(:lock_name) { "testing 1,2,3" }
71
-
72
- before do
73
- @t1_acquired_lock = false
74
- @t1_return_value = nil
75
-
76
- @t1 = Thread.new do
77
- ActiveRecord::Base.connection_pool.with_connection do
78
- @t1_return_value = Label.with_advisory_lock(lock_name) do
79
- t1_acquired_lock = true
80
- sleep(0.4)
81
- 't1 finished'
82
- end
83
- end
84
- end
85
-
86
- # Wait for the thread to acquire the lock:
87
- sleep(0.1)
88
- ActiveRecord::Base.connection.reconnect!
89
- end
90
-
91
- after do
92
- @t1.join
93
- end
94
-
95
- it "#with_advisory_lock with a 0 timeout returns false immediately" do
96
- response = Label.with_advisory_lock(lock_name, 0) {}
97
- response.must_be_false
98
- end
99
-
100
- it "#advisory_lock_exists? returns true when another thread has the lock" do
101
- Tag.advisory_lock_exists?(lock_name).must_be_true
102
- end
103
-
104
- it "can re-establish the lock after the other thread releases it" do
105
- @t1.join
106
- @t1_return_value.must_equal 't1 finished'
107
-
108
- # We should now be able to acquire the lock immediately:
109
- reacquired = false
110
- Label.with_advisory_lock(lock_name, 0) do
111
- reacquired = true
112
- end.must_be_true
113
- reacquired.must_be_true
114
- end
115
- end
@@ -0,0 +1,60 @@
1
+ require 'minitest_helper'
2
+
3
+ describe 'separate thread tests' do
4
+ let(:lock_name) { 'testing 1,2,3' } # OMG COMMAS
5
+
6
+ before do
7
+ @mutex = Mutex.new
8
+ @t1_acquired_lock = false
9
+ @t1_return_value = nil
10
+
11
+ @t1 = Thread.new do
12
+ ActiveRecord::Base.connection_pool.with_connection do
13
+ @t1_return_value = Label.with_advisory_lock(lock_name) do
14
+ @mutex.synchronize { @t1_acquired_lock = true }
15
+ sleep
16
+ 't1 finished'
17
+ end
18
+ end
19
+ end
20
+
21
+ # Wait for the thread to acquire the lock:
22
+ until @mutex.synchronize { @t1_acquired_lock } do
23
+ sleep(0.1)
24
+ end
25
+ ActiveRecord::Base.connection.reconnect!
26
+ end
27
+
28
+ after do
29
+ @t1.wakeup if @t1.status == 'sleep'
30
+ @t1.join
31
+ end
32
+
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'
36
+ end
37
+ response.must_be_false
38
+ end
39
+
40
+ it '#with_advisory_lock yields to the provided block' do
41
+ @t1_acquired_lock.must_be_true
42
+ end
43
+
44
+ it '#advisory_lock_exists? returns true when another thread has the lock' do
45
+ Tag.advisory_lock_exists?(lock_name).must_be_true
46
+ end
47
+
48
+ it 'can re-establish the lock after the other thread releases it' do
49
+ @t1.wakeup
50
+ @t1.join
51
+ @t1_return_value.must_equal 't1 finished'
52
+
53
+ # We should now be able to acquire the lock immediately:
54
+ reacquired = false
55
+ Label.with_advisory_lock(lock_name, 0) do
56
+ reacquired = true
57
+ end.must_be_true
58
+ reacquired.must_be_true
59
+ end
60
+ end
data/tests.sh CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/bin/bash -e
2
2
  export DB
3
3
 
4
- for RUBY in 2.1.2 jruby-1.7.12 ; do
4
+ for RUBY in 2.1.2 jruby-1.7.13 ; do
5
5
  rbenv local $RUBY
6
6
  for DB in mysql postgresql sqlite ; do
7
7
  echo "$DB | $(ruby -v)"
8
- appraisal bundle update
9
- appraisal rake test
8
+ # appraisal bundle update
9
+ appraisal rake test --verbose
10
10
  done
11
11
  done
@@ -19,11 +19,12 @@ Gem::Specification.new do |gem|
19
19
  gem.require_paths = %w(lib)
20
20
 
21
21
  gem.add_runtime_dependency 'activerecord', '>= 3.2'
22
+ gem.add_runtime_dependency 'thread_safe'
22
23
 
23
- gem.add_development_dependency 'rake'
24
24
  gem.add_development_dependency 'yard'
25
25
  gem.add_development_dependency 'minitest'
26
26
  gem.add_development_dependency 'minitest-great_expectations'
27
+ gem.add_development_dependency 'minitest-reporters'
27
28
  gem.add_development_dependency 'mocha'
28
29
  gem.add_development_dependency 'appraisal'
29
30
  end
metadata CHANGED
@@ -1,111 +1,125 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: with_advisory_lock
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthew McEachen
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-07-09 00:00:00.000000000 Z
11
+ date: 2014-08-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: activerecord
15
14
  requirement: !ruby/object:Gem::Requirement
16
15
  requirements:
17
- - - ">="
16
+ - - '>='
18
17
  - !ruby/object:Gem::Version
19
18
  version: '3.2'
20
- type: :runtime
19
+ name: activerecord
21
20
  prerelease: false
21
+ type: :runtime
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">="
24
+ - - '>='
25
25
  - !ruby/object:Gem::Version
26
26
  version: '3.2'
27
27
  - !ruby/object:Gem::Dependency
28
- name: rake
29
28
  requirement: !ruby/object:Gem::Requirement
30
29
  requirements:
31
- - - ">="
30
+ - - '>='
32
31
  - !ruby/object:Gem::Version
33
32
  version: '0'
34
- type: :development
33
+ name: thread_safe
35
34
  prerelease: false
35
+ type: :runtime
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ">="
38
+ - - '>='
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: yard
43
42
  requirement: !ruby/object:Gem::Requirement
44
43
  requirements:
45
- - - ">="
44
+ - - '>='
46
45
  - !ruby/object:Gem::Version
47
46
  version: '0'
48
- type: :development
47
+ name: yard
49
48
  prerelease: false
49
+ type: :development
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ">="
52
+ - - '>='
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: minitest
57
56
  requirement: !ruby/object:Gem::Requirement
58
57
  requirements:
59
- - - ">="
58
+ - - '>='
60
59
  - !ruby/object:Gem::Version
61
60
  version: '0'
62
- type: :development
61
+ name: minitest
63
62
  prerelease: false
63
+ type: :development
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ">="
66
+ - - '>='
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
- name: minitest-great_expectations
71
70
  requirement: !ruby/object:Gem::Requirement
72
71
  requirements:
73
- - - ">="
72
+ - - '>='
74
73
  - !ruby/object:Gem::Version
75
74
  version: '0'
76
- type: :development
75
+ name: minitest-great_expectations
77
76
  prerelease: false
77
+ type: :development
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - ">="
80
+ - - '>='
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
- name: mocha
85
84
  requirement: !ruby/object:Gem::Requirement
86
85
  requirements:
87
- - - ">="
86
+ - - '>='
88
87
  - !ruby/object:Gem::Version
89
88
  version: '0'
90
- type: :development
89
+ name: minitest-reporters
91
90
  prerelease: false
91
+ type: :development
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - ">="
94
+ - - '>='
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
97
  - !ruby/object:Gem::Dependency
98
- name: appraisal
99
98
  requirement: !ruby/object:Gem::Requirement
100
99
  requirements:
101
- - - ">="
100
+ - - '>='
102
101
  - !ruby/object:Gem::Version
103
102
  version: '0'
103
+ name: mocha
104
+ prerelease: false
104
105
  type: :development
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ requirement: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - '>='
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ name: appraisal
105
118
  prerelease: false
119
+ type: :development
106
120
  version_requirements: !ruby/object:Gem::Requirement
107
121
  requirements:
108
- - - ">="
122
+ - - '>='
109
123
  - !ruby/object:Gem::Version
110
124
  version: '0'
111
125
  description: Advisory locking for ActiveRecord
@@ -115,8 +129,8 @@ executables: []
115
129
  extensions: []
116
130
  extra_rdoc_files: []
117
131
  files:
118
- - ".gitignore"
119
- - ".travis.yml"
132
+ - .gitignore
133
+ - .travis.yml
120
134
  - Appraisals
121
135
  - Gemfile
122
136
  - LICENSE.txt
@@ -142,30 +156,31 @@ files:
142
156
  - test/nesting_test.rb
143
157
  - test/parallelism_test.rb
144
158
  - test/test_models.rb
159
+ - test/thread_test.rb
145
160
  - tests.sh
146
161
  - with_advisory_lock.gemspec
147
162
  homepage: https://github.com/mceachen/with_advisory_lock
148
163
  licenses:
149
164
  - MIT
150
165
  metadata: {}
151
- post_install_message:
166
+ post_install_message:
152
167
  rdoc_options: []
153
168
  require_paths:
154
169
  - lib
155
170
  required_ruby_version: !ruby/object:Gem::Requirement
156
171
  requirements:
157
- - - ">="
172
+ - - '>='
158
173
  - !ruby/object:Gem::Version
159
174
  version: '0'
160
175
  required_rubygems_version: !ruby/object:Gem::Requirement
161
176
  requirements:
162
- - - ">="
177
+ - - '>='
163
178
  - !ruby/object:Gem::Version
164
179
  version: '0'
165
180
  requirements: []
166
- rubyforge_project:
167
- rubygems_version: 2.3.0
168
- signing_key:
181
+ rubyforge_project:
182
+ rubygems_version: 2.1.9
183
+ signing_key:
169
184
  specification_version: 4
170
185
  summary: Advisory locking for ActiveRecord
171
186
  test_files:
@@ -176,4 +191,5 @@ test_files:
176
191
  - test/nesting_test.rb
177
192
  - test/parallelism_test.rb
178
193
  - test/test_models.rb
179
- has_rdoc:
194
+ - test/thread_test.rb
195
+ has_rdoc: