remote_lock 1.0.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.
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