lockable 0.0.1
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/.gitignore +17 -0
- data/.rspec +5 -0
- data/Gemfile +7 -0
- data/README.md +1 -0
- data/Rakefile +10 -0
- data/lib/lockable/version.rb +3 -0
- data/lib/lockable.rb +64 -0
- data/lockable.gemspec +25 -0
- data/spec/lockable_spec.rb +114 -0
- data/spec/spec_helper.rb +5 -0
- metadata +111 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# Lockable
|
data/Rakefile
ADDED
data/lib/lockable.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'lockable/version'
|
2
|
+
|
3
|
+
module Lockable
|
4
|
+
|
5
|
+
class << self
|
6
|
+
|
7
|
+
attr_accessor :redis
|
8
|
+
attr_writer :lock_timeout
|
9
|
+
|
10
|
+
DEFAULT_LOCK_TIMEOUT = 5 * 60 * 60
|
11
|
+
|
12
|
+
def lock_timeout
|
13
|
+
@lock_timeout || DEFAULT_LOCK_TIMEOUT
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
def lock!(holder)
|
19
|
+
raise ArgumentError unless holder
|
20
|
+
|
21
|
+
return true if acquire_lock(holder)
|
22
|
+
|
23
|
+
if locked? and lock_holder == holder.to_s
|
24
|
+
return extend_lock
|
25
|
+
end
|
26
|
+
|
27
|
+
false
|
28
|
+
end
|
29
|
+
|
30
|
+
def locked?
|
31
|
+
!!lock_holder
|
32
|
+
end
|
33
|
+
|
34
|
+
def unlock!
|
35
|
+
!!redis.del(lock_key)
|
36
|
+
end
|
37
|
+
|
38
|
+
def lock_holder
|
39
|
+
redis.get(lock_key)
|
40
|
+
end
|
41
|
+
|
42
|
+
def lock_timeout
|
43
|
+
redis.ttl(lock_key).to_i
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def acquire_lock(holder)
|
49
|
+
!!redis.set(lock_key, holder, ex: Lockable.lock_timeout, nx: true)
|
50
|
+
end
|
51
|
+
|
52
|
+
def extend_lock
|
53
|
+
!!redis.expire(lock_key, Lockable.lock_timeout)
|
54
|
+
end
|
55
|
+
|
56
|
+
def lock_key
|
57
|
+
"lock:#{self.class.name.downcase}:#{id}"
|
58
|
+
end
|
59
|
+
|
60
|
+
def redis
|
61
|
+
Lockable.redis
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
data/lockable.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
|
4
|
+
$LOAD_PATH.unshift lib
|
5
|
+
|
6
|
+
require 'lockable/version'
|
7
|
+
|
8
|
+
Gem::Specification.new do |spec|
|
9
|
+
spec.name = 'lockable'
|
10
|
+
spec.version = Lockable::VERSION
|
11
|
+
spec.authors = ['cmertz']
|
12
|
+
spec.email = ['chris@nimel.de']
|
13
|
+
spec.description = 'minimalist locking using redis'
|
14
|
+
spec.summary = 'minimalist locking using redis'
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency 'bundler', '~> 1.3'
|
22
|
+
spec.add_development_dependency 'rake'
|
23
|
+
|
24
|
+
spec.add_dependency 'redis'
|
25
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
describe Lockable do
|
4
|
+
|
5
|
+
subject do
|
6
|
+
OpenStruct.new(id: 1).extend(Lockable)
|
7
|
+
end
|
8
|
+
|
9
|
+
before do
|
10
|
+
Lockable.redis.del(subject.send(:lock_key))
|
11
|
+
end
|
12
|
+
|
13
|
+
describe '#lock!' do
|
14
|
+
|
15
|
+
it 'needs a lock holder' do
|
16
|
+
expect { subject.lock!(nil) }.to raise_error(ArgumentError)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'locks' do
|
20
|
+
subject.lock!('toto').should be_true
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'already locked' do
|
24
|
+
|
25
|
+
before do
|
26
|
+
Lockable.lock_timeout = 10
|
27
|
+
subject.lock!('toto')
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'does not lock again' do
|
31
|
+
subject.lock!('tata')
|
32
|
+
subject.lock_holder.should eq 'toto'
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'extends the ttl' do
|
36
|
+
Lockable.lock_timeout = 100
|
37
|
+
subject.lock!('toto')
|
38
|
+
subject.lock_timeout.should eq 100
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'does not extend the ttl' do
|
42
|
+
Lockable.lock_timeout = 100
|
43
|
+
subject.lock!('tata')
|
44
|
+
subject.lock_timeout.should eq 10
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe '#lock_timeout' do
|
51
|
+
|
52
|
+
it 'does not have a timeout if not locked' do
|
53
|
+
subject.lock_timeout.should == -1
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'sets a ttl' do
|
57
|
+
subject.lock!('toto')
|
58
|
+
subject.lock_timeout.should_not be_nil
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
describe '#locked?' do
|
64
|
+
|
65
|
+
it { should_not be_locked }
|
66
|
+
|
67
|
+
it 'should be locked' do
|
68
|
+
expect { subject.lock!('toto') }.to change{
|
69
|
+
subject.locked?
|
70
|
+
}.from(false).to(true)
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
describe '#unlock!' do
|
76
|
+
|
77
|
+
before do
|
78
|
+
subject.lock!('toto')
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'is not locked anymore' do
|
82
|
+
expect { subject.unlock! }.to change{
|
83
|
+
subject.locked?
|
84
|
+
}.from(true).to(false)
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'does not have a lock_holder anymore' do
|
88
|
+
expect { subject.unlock! }.to change{
|
89
|
+
subject.lock_holder
|
90
|
+
}.from('toto').to(nil)
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'does not have a lock_timeout anymore' do
|
94
|
+
expect { subject.unlock! }.to change{
|
95
|
+
subject.lock_timeout
|
96
|
+
}.to(-1)
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
describe '#lock_holder' do
|
102
|
+
|
103
|
+
it 'does not have one' do
|
104
|
+
subject.lock_holder.should be nil
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'does have one' do
|
108
|
+
subject.lock!('toto')
|
109
|
+
subject.lock_holder.should eq 'toto'
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: lockable
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- cmertz
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-11-14 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.3'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.3'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rake
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: redis
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
description: minimalist locking using redis
|
63
|
+
email:
|
64
|
+
- chris@nimel.de
|
65
|
+
executables: []
|
66
|
+
extensions: []
|
67
|
+
extra_rdoc_files: []
|
68
|
+
files:
|
69
|
+
- .gitignore
|
70
|
+
- .rspec
|
71
|
+
- Gemfile
|
72
|
+
- README.md
|
73
|
+
- Rakefile
|
74
|
+
- lib/lockable.rb
|
75
|
+
- lib/lockable/version.rb
|
76
|
+
- lockable.gemspec
|
77
|
+
- spec/lockable_spec.rb
|
78
|
+
- spec/spec_helper.rb
|
79
|
+
homepage:
|
80
|
+
licenses: []
|
81
|
+
post_install_message:
|
82
|
+
rdoc_options: []
|
83
|
+
require_paths:
|
84
|
+
- lib
|
85
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
86
|
+
none: false
|
87
|
+
requirements:
|
88
|
+
- - ! '>='
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
segments:
|
92
|
+
- 0
|
93
|
+
hash: 3763012555333573935
|
94
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
95
|
+
none: false
|
96
|
+
requirements:
|
97
|
+
- - ! '>='
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '0'
|
100
|
+
segments:
|
101
|
+
- 0
|
102
|
+
hash: 3763012555333573935
|
103
|
+
requirements: []
|
104
|
+
rubyforge_project:
|
105
|
+
rubygems_version: 1.8.25
|
106
|
+
signing_key:
|
107
|
+
specification_version: 3
|
108
|
+
summary: minimalist locking using redis
|
109
|
+
test_files:
|
110
|
+
- spec/lockable_spec.rb
|
111
|
+
- spec/spec_helper.rb
|