remote_lock 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2013 HouseTrip
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,94 @@
1
+ [![Build Status](https://travis-ci.org/HouseTrip/remote_lock.png)](https://travis-ci.org/HouseTrip/remote_lock)
2
+
3
+ remote_lock
4
+ ===========
5
+
6
+ This is a rewrite of a initial extraction from Nick Kallen's [cache-money](http://github.com/nkallen/cache-money) and
7
+ also a fork from James Golick [memcache-lock](https://github.com/jamesgolick/memcache-lock)
8
+
9
+ This adds supports for memcache or redis as lock storage.
10
+
11
+ Installation
12
+ ------------
13
+
14
+ ```shell
15
+ gem install remote-lock
16
+ ```
17
+
18
+ Initialization
19
+ -------------
20
+
21
+ * Lock using memcached:
22
+
23
+ ```ruby
24
+ # memcache = MemCache.new(YAML.load(File.read("/path/to/memcache/config")))
25
+ # Or whatever way you have your memcache connection
26
+ $lock = RemoteLock.new(RemoteLock::Adapters::Memcached.new(memcache))
27
+ ```
28
+
29
+ * Lock using redis:
30
+
31
+ ```ruby
32
+ # redis = Redis.new
33
+ # Or whatever way you have your redis connection
34
+ $lock = RemoteLock.new(RemoteLock::Adapters::Redis.new(redis))
35
+ ```
36
+
37
+ Usage
38
+ -----
39
+
40
+ Then, wherever you'd like to lock a key, use it like this:
41
+
42
+ ```ruby
43
+ $lock.synchronize("some-key") do
44
+ # stuff that needs synchronization in here
45
+ end
46
+ ```
47
+
48
+ Options:
49
+
50
+ * TTL
51
+
52
+ By default keys will expire after 60 seconds, you can define this per key:
53
+
54
+ ```ruby
55
+ $lock.synchronize("my-key", expiry: 30.seconds) do ... end
56
+ ```
57
+
58
+ * Attempts
59
+
60
+ By default it will try 11 times to lock the resource, this can be set per key:
61
+
62
+ ```ruby
63
+ $lock.synchronize("my-key", retries: 5) do ... end
64
+ ```
65
+
66
+ * Tries interval
67
+
68
+ You can customize the interval between tries, initially it's 10ms:
69
+
70
+ ```ruby
71
+ $lock.synchronize("my-key", initial_wait: 10e-3) do ... end
72
+ ```
73
+
74
+ For more info, see lib/remote_lock.rb. It's very straightforward to read.
75
+
76
+ Note on Patches/Pull Requests
77
+ =============================
78
+
79
+ * Fork the project.
80
+ * Make your feature addition or bug fix.
81
+ * Add tests for it. This is important so I don't break it in a
82
+ future version unintentionally.
83
+ * Commit, do not mess with rakefile, version, or history.
84
+ (if you want to have your own version, that is fine but
85
+ bump version in a commit by itself I can ignore when I pull)
86
+ * Send me a pull request. Bonus points for topic branches.
87
+
88
+ License
89
+ ============
90
+ MIT licence. Copyright (c) 2013 HouseTrip Ltd.
91
+
92
+
93
+
94
+ Based on the memcache-lock gem: https://github.com/jamesgolick/memcache-lock
data/Rakefile ADDED
@@ -0,0 +1,26 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+ require 'rdoc/task'
4
+
5
+
6
+ desc 'Run spec tests'
7
+ task :spec do
8
+ RSpec::Core::RakeTask.new do |spec|
9
+ spec.pattern = "./spec/**/*_spec.rb"
10
+ end
11
+ end
12
+
13
+ task :default => :spec
14
+
15
+ Rake::RDocTask.new do |rdoc|
16
+ if File.exist?('VERSION')
17
+ version = File.read('VERSION')
18
+ else
19
+ version = ""
20
+ end
21
+
22
+ rdoc.rdoc_dir = 'rdoc'
23
+ rdoc.title = "memcache-lock #{version}"
24
+ rdoc.rdoc_files.include('README*')
25
+ rdoc.rdoc_files.include('lib/**/*.rb')
26
+ end
@@ -0,0 +1,57 @@
1
+ class RemoteLock
2
+ class Error < RuntimeError; end
3
+
4
+ DEFAULT_OPTIONS = {
5
+ :initial_wait => 10e-3, # seconds -- first soft fail will wait for 10ms
6
+ :expiry => 60, # seconds
7
+ :retries => 11, # these defaults will retry for a total 41sec max
8
+ }
9
+
10
+ def initialize(adapter, prefix = nil)
11
+ raise "Invalid Adapter" unless Adapters::Base.valid?(adapter)
12
+ @adapter = adapter
13
+ @prefix = prefix
14
+ end
15
+
16
+ def synchronize(key, options={})
17
+ if acquired?(key)
18
+ yield
19
+ else
20
+ acquire_lock(key, options)
21
+ begin
22
+ yield
23
+ ensure
24
+ release_lock(key)
25
+ end
26
+ end
27
+ end
28
+
29
+ def acquire_lock(key, options = {})
30
+ options = DEFAULT_OPTIONS.merge(options)
31
+ 1.upto(options[:retries]) do |attempt|
32
+ success = @adapter.store(key_for(key), options[:expiry])
33
+ return if success
34
+ break if attempt == options[:retries]
35
+ Kernel.sleep(2 ** (attempt + rand - 1) * options[:initial_wait])
36
+ end
37
+ raise RemoteLock::Error, "Couldn't acquire lock for: #{key}"
38
+ end
39
+
40
+ def release_lock(key)
41
+ @adapter.delete(key_for(key))
42
+ end
43
+
44
+ def acquired?(key)
45
+ @adapter.has_key?(key_for(key))
46
+ end
47
+
48
+ private
49
+
50
+ def key_for(string)
51
+ [@prefix, "lock", string].compact.join('|')
52
+ end
53
+
54
+ end
55
+
56
+ require 'remote_lock/adapters/memcached'
57
+ require 'remote_lock/adapters/redis'
@@ -0,0 +1,40 @@
1
+ require 'securerandom'
2
+
3
+ module RemoteLock::Adapters
4
+ class Base
5
+
6
+ def initialize(connection)
7
+ @connection = connection
8
+ end
9
+
10
+ def store(key, options = {})
11
+ raise NotImplementedError
12
+ end
13
+
14
+ def has_key?(key, options = {})
15
+ raise NotImplementedError
16
+ end
17
+
18
+ def delete(key)
19
+ raise NotImplementedError
20
+ end
21
+
22
+ def self.valid?(adapter)
23
+ adapter.respond_to?(:store) &&
24
+ adapter.respond_to?(:has_key?) &&
25
+ adapter.respond_to?(:delete)
26
+ end
27
+
28
+ private
29
+
30
+ # Globally unique ID for the current thread (or close enough)
31
+ def uid
32
+ "#{Socket.gethostname}-#{Process.pid}-#{thread_id}"
33
+ end
34
+
35
+ def thread_id
36
+ Thread.current[:thread_uid] ||= SecureRandom.hex(4)
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,20 @@
1
+ require 'remote_lock/adapters/base'
2
+
3
+ module RemoteLock::Adapters
4
+ class Memcached < Base
5
+
6
+ def store(key, expires_in_seconds)
7
+ status = @connection.add(key, uid, expires_in_seconds)
8
+ status =~ /^STORED/
9
+ end
10
+
11
+ def delete(key)
12
+ @connection.delete(key)
13
+ end
14
+
15
+ def has_key?(key)
16
+ @connection.get(key) == uid
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,21 @@
1
+ require 'remote_lock/adapters/base'
2
+
3
+ module RemoteLock::Adapters
4
+ class Redis < Base
5
+
6
+ def store(key, expires_in_seconds)
7
+ @connection.setnx(key, uid).tap do |status|
8
+ @connection.expire(key, expires_in_seconds) if status
9
+ end
10
+ end
11
+
12
+ def delete(key)
13
+ @connection.del(key)
14
+ end
15
+
16
+ def has_key?(key)
17
+ @connection.get(key) == uid
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,3 @@
1
+ class RemoteLock
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,5 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hash do
4
+ it_behaves_like 'a remote lock adapter', {}
5
+ end
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+
3
+ module RemoteLock::Adapters
4
+ describe Memcached do
5
+ it_behaves_like 'a remote lock adapter', memcache
6
+
7
+ context "Memcache scope" do
8
+ let(:adapter) { Memcached.new(memcache) }
9
+ let(:uid) { '1234' }
10
+ let(:test_key) { "test_key" }
11
+
12
+ before do
13
+ adapter.stub(:uid).and_return(uid)
14
+ end
15
+
16
+ describe "#store" do
17
+ it "should store the lock in memcached" do
18
+ memcache.get(test_key).should be_nil
19
+ adapter.store(test_key, 100)
20
+ memcache.get(test_key).should eq uid
21
+ end
22
+ end
23
+
24
+ describe "#has_key?" do
25
+ it "should return true if the key exists in memcache with uid value" do
26
+ memcache.add(test_key, uid)
27
+ adapter.has_key?(test_key).should be_true
28
+ end
29
+
30
+ it "should return false if the key doesn't exist in memcache or is a different uid" do
31
+ memcache.add(test_key, "notvalid")
32
+ adapter.has_key?(test_key).should be_false
33
+ memcache.delete(test_key)
34
+ adapter.has_key?(test_key).should be_false
35
+ end
36
+ end
37
+
38
+ describe "#delete" do
39
+ it "should remove the key from memcached" do
40
+ memcache.add(test_key, uid)
41
+ adapter.delete(test_key)
42
+ memcache.get(test_key).should be_nil
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,61 @@
1
+ require 'spec_helper'
2
+
3
+ module RemoteLock::Adapters
4
+ describe Redis do
5
+ it_behaves_like 'a remote lock adapter', redis
6
+
7
+ context "Redis scope" do
8
+ let(:adapter) { Redis.new(redis) }
9
+ let(:uid) { '1234' }
10
+ let(:test_key) { "test_key" }
11
+
12
+ before do
13
+ adapter.stub(:uid).and_return(uid)
14
+ end
15
+
16
+ describe "#store" do
17
+ it "should store the lock in memcached" do
18
+ redis.get(test_key).should be_nil
19
+ adapter.store(test_key, 100)
20
+ redis.get(test_key).should eq uid
21
+ end
22
+
23
+ context "expiry" do
24
+ it "should expire the key after the time is over" do
25
+ adapter.store(test_key, 1)
26
+ sleep 1.1
27
+ redis.exists(test_key).should be_false
28
+ end
29
+
30
+ it "should expire the key after the time is over" do
31
+ adapter.store(test_key, 10)
32
+ sleep 0.5
33
+ redis.exists(test_key).should be_true
34
+ end
35
+ end
36
+ end
37
+
38
+ describe "#has_key?" do
39
+ it "should return true if the key exists in memcache with uid value" do
40
+ redis.setnx(test_key, uid)
41
+ adapter.has_key?(test_key).should be_true
42
+ end
43
+
44
+ it "should return false if the key doesn't exist in memcache or is a different uid" do
45
+ redis.setnx(test_key, "notvalid")
46
+ adapter.has_key?(test_key).should be_false
47
+ redis.del(test_key)
48
+ adapter.has_key?(test_key).should be_false
49
+ end
50
+ end
51
+
52
+ describe "#delete" do
53
+ it "should remove the key from memcached" do
54
+ redis.setnx(test_key, uid)
55
+ adapter.delete(test_key)
56
+ redis.get(test_key).should be_nil
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
data/spec/memcache.yml ADDED
@@ -0,0 +1,2 @@
1
+ test:
2
+ servers: localhost:11211
@@ -0,0 +1,150 @@
1
+ require 'spec_helper'
2
+
3
+ describe RemoteLock do
4
+
5
+ adapters = {
6
+ :memcached => RemoteLock::Adapters::Memcached.new(memcache),
7
+ :redis => RemoteLock::Adapters::Redis.new(redis)
8
+ }
9
+
10
+ adapters.each_pair do |name, adapter|
11
+ context "Using adapter: #{name}" do
12
+ before do
13
+ Kernel.stub(:sleep)
14
+ end
15
+
16
+ let(:lock) { RemoteLock.new(adapter) }
17
+
18
+ describe "#synchronize" do
19
+
20
+ it "yields the block" do
21
+ expect { |call|
22
+ lock.synchronize('lock_key', &call)
23
+ }.to yield_control
24
+ end
25
+
26
+ it "acquires the specified lock before the block is run" do
27
+ adapter.has_key?("lock_key").should be_false
28
+ lock.synchronize('lock_key') do
29
+ adapter.has_key?("lock|lock_key").should be_true
30
+ end
31
+ end
32
+
33
+ it "releases the lock after the block is run" do
34
+ adapter.has_key?("lock_key").should be_false
35
+ expect { |call| lock.synchronize('lock_key', &call) }.to yield_control
36
+ adapter.has_key?("lock|lock_key").should be_false
37
+ end
38
+
39
+ it "releases the lock even if the block raises" do
40
+ adapter.has_key?("lock|lock_key").should be_false
41
+ lock.synchronize('lock_key') { raise } rescue nil
42
+ adapter.has_key?("lock|lock_key").should be_false
43
+ end
44
+
45
+ specify "does not block on recursive lock acquisition" do
46
+ lock.synchronize('lock_key') do
47
+ lambda {
48
+ expect{ |call| lock.synchronize('lock_key', &call) }.to yield_control
49
+ }.should_not raise_error
50
+ end
51
+ end
52
+
53
+ it "permits recursive calls from the same thread" do
54
+ lock.acquire_lock('lock_key')
55
+ lambda {
56
+ expect { |call| lock.synchronize('lock_key', &call) }.to yield_control
57
+ }.should_not raise_error
58
+ end
59
+
60
+ it "prevents calls from different threads" do
61
+ lock.acquire_lock('lock_key')
62
+ another_thread do
63
+ lambda {
64
+ expect { |call| lock.synchronize('lock_key', &call) }.to_not yield_control
65
+ }.should raise_error(RemoteLock::Error)
66
+ end
67
+ end
68
+ end
69
+
70
+ describe '#acquire_lock' do
71
+ specify "creates a lock at a given cache key" do
72
+ adapter.has_key?("lock|lock_key").should be_false
73
+ lock.acquire_lock("lock_key")
74
+ adapter.has_key?("lock|lock_key").should be_true
75
+ end
76
+
77
+ specify "retries specified number of times" do
78
+ lock.acquire_lock('lock_key')
79
+ another_process do
80
+ adapter.should_receive(:store).exactly(3).times.and_return(false)
81
+ lambda {
82
+ lock.acquire_lock('lock_key', :expiry => 10, :retries => 3)
83
+ }.should raise_error(RemoteLock::Error)
84
+ end
85
+ end
86
+
87
+ specify "correctly sets timeout on entries" do
88
+ adapter.should_receive(:store).with('lock|lock_key', 42).and_return true
89
+ lock.acquire_lock('lock_key', :expiry => 42)
90
+ end
91
+
92
+ specify "prevents two processes from acquiring the same lock at the same time" do
93
+ lock.acquire_lock('lock_key')
94
+ another_process do
95
+ lambda { lock.acquire_lock('lock_key') }.should raise_error(RemoteLock::Error)
96
+ end
97
+ end
98
+
99
+ specify "prevents two threads from acquiring the same lock at the same time" do
100
+ lock.acquire_lock('lock_key')
101
+ another_thread do
102
+ lambda { lock.acquire_lock('lock_key') }.should raise_error(RemoteLock::Error)
103
+ end
104
+ end
105
+
106
+ specify "prevents a given thread from acquiring the same lock twice" do
107
+ lock.acquire_lock('lock_key')
108
+ lambda { lock.acquire_lock('lock_key') }.should raise_error(RemoteLock::Error)
109
+ end
110
+ end
111
+
112
+ describe '#release_lock' do
113
+ specify "deletes the lock for a given cache key" do
114
+ adapter.has_key?("lock|lock_key").should be_false
115
+ lock.acquire_lock("lock_key")
116
+ adapter.has_key?("lock|lock_key").should be_true
117
+ lock.release_lock("lock_key")
118
+ adapter.has_key?("lock|lock_key").should be_false
119
+ end
120
+ end
121
+
122
+ context "lock prefixing" do
123
+ it "should prefix the key name when a prefix is set" do
124
+ lock = RemoteLock.new(adapter, "staging_server")
125
+ lock.acquire_lock("lock_key")
126
+ adapter.has_key?("staging_server|lock|lock_key").should be_true
127
+ end
128
+ end
129
+ end
130
+ end
131
+
132
+ # helpers
133
+
134
+ def another_process
135
+ current_pid = Process.pid
136
+ Process.stub :pid => (current_pid + 1)
137
+ redis.client.reconnect
138
+ yield
139
+ Process.unstub :pid
140
+ redis.client.reconnect
141
+ end
142
+
143
+ def another_thread
144
+ old_tid = Thread.current[:thread_uid]
145
+ Thread.current[:thread_uid] = nil
146
+ yield
147
+ Thread.current[:thread_uid] = old_tid
148
+ end
149
+
150
+ end
@@ -0,0 +1,21 @@
1
+ $:.unshift(File.dirname(__FILE__))
2
+ $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'remote_lock'
4
+ require 'memcache'
5
+ require 'redis'
6
+
7
+ require "rspec"
8
+ require "rspec/core"
9
+ require 'rspec/core/rake_task'
10
+ require 'yaml'
11
+
12
+ Dir.glob(File.join(File.dirname(__FILE__), 'support/**/*.rb')).each do |file|
13
+ require file
14
+ end
15
+
16
+ RSpec.configure do |config|
17
+ config.before :each do
18
+ memcache.flush_all
19
+ redis.flushdb
20
+ end
21
+ end
@@ -0,0 +1,27 @@
1
+ shared_examples_for "a remote lock adapter" do |storage|
2
+ let(:adapter) { described_class.new(storage) }
3
+ let(:key) { 'my_test_key' }
4
+
5
+ it "should behave like RemoteLock::Strategies::Base" do
6
+ RemoteLock::Adapters::Base.valid?(adapter).should be_true
7
+ end
8
+
9
+
10
+ describe "#store" do
11
+ it "should set the key as acquired" do
12
+ adapter.has_key?(key).should be_false
13
+ adapter.store(key, 100)
14
+ adapter.has_key?(key).should be_true
15
+ end
16
+ end
17
+
18
+ describe "#delete" do
19
+ it "should remove the key as locked" do
20
+ adapter.store(key, 100)
21
+ adapter.has_key?(key).should be_true
22
+ adapter.delete(key)
23
+ adapter.has_key?(key).should be_false
24
+ end
25
+ end
26
+
27
+ end
@@ -0,0 +1,7 @@
1
+ def memcache
2
+ return $memcache if $memcache
3
+ config = YAML.load(IO.read((File.expand_path(File.join(File.dirname(__FILE__) , "../memcache.yml")))))['test']
4
+ $memcache = MemCache.new(config)
5
+ $memcache.servers = config['servers']
6
+ $memcache
7
+ end
@@ -0,0 +1,3 @@
1
+ def redis
2
+ $redis ||= Redis.new()
3
+ end
metadata ADDED
@@ -0,0 +1,199 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: remote_lock
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Julien Letessier
9
+ - Tiago Scolari
10
+ - Arne Hartherz
11
+ - Pedro Cunha
12
+ - Khiet Le
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+ date: 2013-11-08 00:00:00.000000000 Z
17
+ dependencies:
18
+ - !ruby/object:Gem::Dependency
19
+ name: rake
20
+ requirement: !ruby/object:Gem::Requirement
21
+ none: false
22
+ requirements:
23
+ - - ! '>='
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
26
+ type: :development
27
+ prerelease: false
28
+ version_requirements: !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ - !ruby/object:Gem::Dependency
35
+ name: pry
36
+ requirement: !ruby/object:Gem::Requirement
37
+ none: false
38
+ requirements:
39
+ - - ! '>='
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ type: :development
43
+ prerelease: false
44
+ version_requirements: !ruby/object:Gem::Requirement
45
+ none: false
46
+ requirements:
47
+ - - ! '>='
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ - !ruby/object:Gem::Dependency
51
+ name: pry-nav
52
+ requirement: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ! '>='
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ type: :development
59
+ prerelease: false
60
+ version_requirements: !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ - !ruby/object:Gem::Dependency
67
+ name: rspec
68
+ requirement: !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ! '>='
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ type: :development
75
+ prerelease: false
76
+ version_requirements: !ruby/object:Gem::Requirement
77
+ none: false
78
+ requirements:
79
+ - - ! '>='
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ - !ruby/object:Gem::Dependency
83
+ name: rdoc
84
+ requirement: !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ! '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ none: false
94
+ requirements:
95
+ - - ! '>='
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ - !ruby/object:Gem::Dependency
99
+ name: redis
100
+ requirement: !ruby/object:Gem::Requirement
101
+ none: false
102
+ requirements:
103
+ - - ! '>='
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ type: :development
107
+ prerelease: false
108
+ version_requirements: !ruby/object:Gem::Requirement
109
+ none: false
110
+ requirements:
111
+ - - ! '>='
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ - !ruby/object:Gem::Dependency
115
+ name: memcache-client
116
+ requirement: !ruby/object:Gem::Requirement
117
+ none: false
118
+ requirements:
119
+ - - ! '>='
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ type: :development
123
+ prerelease: false
124
+ version_requirements: !ruby/object:Gem::Requirement
125
+ none: false
126
+ requirements:
127
+ - - ! '>='
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
130
+ description: remote-based mutexes
131
+ email:
132
+ - julien.letessier@gmail.com
133
+ - tscolari@gmail.com
134
+ - arne.hartherz@makandra.de
135
+ - pkunha@gmail.com
136
+ - kle@housetrip.com
137
+ executables: []
138
+ extensions: []
139
+ extra_rdoc_files: []
140
+ files:
141
+ - lib/remote_lock/adapters/base.rb
142
+ - lib/remote_lock/adapters/memcached.rb
143
+ - lib/remote_lock/adapters/redis.rb
144
+ - lib/remote_lock/version.rb
145
+ - lib/remote_lock.rb
146
+ - LICENSE
147
+ - Rakefile
148
+ - README.md
149
+ - spec/adapters/hash_spec.rb
150
+ - spec/adapters/memcached_spec.rb
151
+ - spec/adapters/redis_spec.rb
152
+ - spec/memcache.yml
153
+ - spec/remote_lock_spec.rb
154
+ - spec/spec_helper.rb
155
+ - spec/support/behave_as_a_remote_lock_adapter.rb
156
+ - spec/support/memcached_storage.rb
157
+ - spec/support/redis_storage.rb
158
+ homepage: http://github.com/HouseTrip/remote_lock
159
+ licenses:
160
+ - MIT
161
+ post_install_message:
162
+ rdoc_options: []
163
+ require_paths:
164
+ - lib
165
+ required_ruby_version: !ruby/object:Gem::Requirement
166
+ none: false
167
+ requirements:
168
+ - - ! '>='
169
+ - !ruby/object:Gem::Version
170
+ version: '0'
171
+ segments:
172
+ - 0
173
+ hash: 589636169546274691
174
+ required_rubygems_version: !ruby/object:Gem::Requirement
175
+ none: false
176
+ requirements:
177
+ - - ! '>='
178
+ - !ruby/object:Gem::Version
179
+ version: '0'
180
+ segments:
181
+ - 0
182
+ hash: 589636169546274691
183
+ requirements: []
184
+ rubyforge_project:
185
+ rubygems_version: 1.8.23
186
+ signing_key:
187
+ specification_version: 3
188
+ summary: Leverages (memcached|redis)'s atomic operation to provide a distributed locking
189
+ / synchromisation mechanism.
190
+ test_files:
191
+ - spec/adapters/hash_spec.rb
192
+ - spec/adapters/memcached_spec.rb
193
+ - spec/adapters/redis_spec.rb
194
+ - spec/memcache.yml
195
+ - spec/remote_lock_spec.rb
196
+ - spec/spec_helper.rb
197
+ - spec/support/behave_as_a_remote_lock_adapter.rb
198
+ - spec/support/memcached_storage.rb
199
+ - spec/support/redis_storage.rb