lockable 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,5 @@
1
+ --require spec_helper
2
+ --order random
3
+ --colour
4
+ --format nested
5
+ --drb
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'pry'
6
+ gem 'rspec'
7
+ gem 'redis'
data/README.md ADDED
@@ -0,0 +1 @@
1
+ # Lockable
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ task :default do
4
+ system "rake -T"
5
+ end
6
+
7
+ desc 'cleanup working copy'
8
+ task :clean do
9
+ FileUtils.rm_rf %w(coverage pkg) + Dir['log/*.log'] + Dir['tmp/*']
10
+ end
@@ -0,0 +1,3 @@
1
+ module Lockable
2
+ VERSION = '0.0.1'
3
+ end
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
@@ -0,0 +1,5 @@
1
+ Bundler.require
2
+
3
+ require 'lockable'
4
+
5
+ Lockable.redis = Redis.new
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