lock_method 0.4.0 → 0.5.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/CHANGELOG +16 -0
- data/Gemfile +4 -1
- data/lib/lock_method/config.rb +8 -4
- data/lib/lock_method/default_storage_client.rb +30 -27
- data/lib/lock_method/lock.rb +30 -8
- data/lib/lock_method/version.rb +1 -1
- data/lib/lock_method.rb +13 -13
- data/lock_method.gemspec +2 -11
- data/test/helper.rb +0 -3
- data/test/test_memcached_storage.rb +2 -2
- metadata +11 -44
data/CHANGELOG
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
0.5.1 / 2012-04-17
|
2
|
+
|
3
|
+
* Bug fixes
|
4
|
+
|
5
|
+
* Synchronize reading of locks among threads; previously only writing of locks had been synchronized.
|
6
|
+
|
7
|
+
0.5.0 / 2012-04-17
|
8
|
+
|
9
|
+
* Enhancements
|
10
|
+
|
11
|
+
* Tested on MRI 1.8, MRI 1.9, and JRuby
|
12
|
+
|
13
|
+
* Bug fixes
|
14
|
+
|
15
|
+
* Proper (I hope use) used of mutexes
|
16
|
+
* More OS-friendly tmp file names
|
data/Gemfile
CHANGED
data/lib/lock_method/config.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'cache'
|
2
|
-
|
2
|
+
|
3
3
|
module LockMethod
|
4
4
|
# Here's where you set config options.
|
5
5
|
#
|
@@ -8,8 +8,10 @@ module LockMethod
|
|
8
8
|
#
|
9
9
|
# You'd probably put this in your Rails config/initializers, for example.
|
10
10
|
class Config
|
11
|
-
|
12
|
-
|
11
|
+
def initialize
|
12
|
+
@mutex = ::Mutex.new
|
13
|
+
end
|
14
|
+
|
13
15
|
# Storage for keeping lockfiles.
|
14
16
|
#
|
15
17
|
# Defaults to using the filesystem's temp dir.
|
@@ -36,7 +38,9 @@ module LockMethod
|
|
36
38
|
end
|
37
39
|
|
38
40
|
def storage #:nodoc:
|
39
|
-
@storage
|
41
|
+
@storage || @mutex.synchronize do
|
42
|
+
@storage ||= DefaultStorageClient.new
|
43
|
+
end
|
40
44
|
end
|
41
45
|
|
42
46
|
# TTL for method caches. Defaults to 24 hours.
|
@@ -1,40 +1,49 @@
|
|
1
|
-
require 'singleton'
|
2
1
|
require 'tmpdir'
|
3
2
|
require 'fileutils'
|
4
|
-
require '
|
3
|
+
require 'digest/sha1'
|
4
|
+
|
5
5
|
module LockMethod
|
6
6
|
class DefaultStorageClient #:nodoc: all
|
7
|
-
|
8
|
-
include ::Singleton
|
9
|
-
|
10
7
|
class Entry
|
11
8
|
attr_reader :created_at
|
12
9
|
attr_reader :ttl
|
13
10
|
attr_reader :v
|
14
11
|
def initialize(ttl, v)
|
15
|
-
@created_at = ::Time.now
|
16
|
-
@ttl = ttl
|
12
|
+
@created_at = ::Time.now
|
13
|
+
@ttl = ttl.to_f
|
17
14
|
@v = v
|
18
15
|
end
|
19
16
|
def expired?
|
20
|
-
ttl
|
17
|
+
ttl > 0 and (::Time.now - created_at) > ttl
|
21
18
|
end
|
22
19
|
end
|
23
20
|
|
21
|
+
attr_reader :dir
|
22
|
+
|
23
|
+
def initialize
|
24
|
+
@mutex = ::Mutex.new
|
25
|
+
dir = ::File.expand_path ::File.join(::Dir.tmpdir, 'lock_method')
|
26
|
+
::FileUtils.mkdir(dir) unless ::File.directory?(dir)
|
27
|
+
@dir = dir
|
28
|
+
end
|
29
|
+
|
24
30
|
def get(k)
|
25
|
-
|
26
|
-
|
31
|
+
path = path k
|
32
|
+
@mutex.synchronize do
|
33
|
+
if ::File.exist?(path) and (entry = ::Marshal.load(::File.read(path))) and not entry.expired?
|
34
|
+
entry.v
|
35
|
+
end
|
27
36
|
end
|
28
|
-
rescue
|
37
|
+
rescue
|
38
|
+
$stderr.puts %{[lock_method] Rescued from #{$!.inspect} while trying to get a lock}
|
29
39
|
end
|
30
40
|
|
31
41
|
def set(k, v, ttl)
|
32
42
|
entry = Entry.new ttl, v
|
33
|
-
|
34
|
-
::
|
35
|
-
::File.open(path(k), ::File::RDWR|::File::CREAT, :external_encoding => 'ASCII-8BIT') do |f|
|
43
|
+
@mutex.synchronize do
|
44
|
+
::File.open(path(k), 'wb') do |f|
|
36
45
|
f.flock ::File::LOCK_EX
|
37
|
-
f.write ::Marshal.dump
|
46
|
+
f.write ::Marshal.dump entry
|
38
47
|
end
|
39
48
|
end
|
40
49
|
end
|
@@ -44,21 +53,15 @@ module LockMethod
|
|
44
53
|
end
|
45
54
|
|
46
55
|
def flush
|
47
|
-
::
|
56
|
+
::Dir["#{dir}/*.lock"].each do |path|
|
57
|
+
::FileUtils.rm_f path
|
58
|
+
end
|
48
59
|
end
|
49
|
-
|
60
|
+
|
50
61
|
private
|
51
|
-
|
52
|
-
def semaphore
|
53
|
-
@semaphore ||= ::Mutex.new
|
54
|
-
end
|
55
|
-
|
62
|
+
|
56
63
|
def path(k)
|
57
|
-
::File.join dir, k
|
58
|
-
end
|
59
|
-
|
60
|
-
def dir
|
61
|
-
::File.expand_path ::File.join(::Dir.tmpdir, 'lock_method')
|
64
|
+
::File.join dir, "#{::Digest::SHA1.hexdigest(k)}.lock"
|
62
65
|
end
|
63
66
|
end
|
64
67
|
end
|
data/lib/lock_method/lock.rb
CHANGED
@@ -3,7 +3,7 @@ module LockMethod
|
|
3
3
|
class Lock #:nodoc: all
|
4
4
|
class << self
|
5
5
|
def find(cache_key)
|
6
|
-
|
6
|
+
LockMethod.config.storage.get cache_key
|
7
7
|
end
|
8
8
|
def klass_name(obj)
|
9
9
|
(obj.is_a?(::Class) or obj.is_a?(::Module)) ? obj.to_s : obj.class.to_s
|
@@ -14,6 +14,23 @@ module LockMethod
|
|
14
14
|
def method_signature(obj, method_id)
|
15
15
|
[ klass_name(obj), method_id ].join method_delimiter(obj)
|
16
16
|
end
|
17
|
+
def resolve_lock(obj)
|
18
|
+
case obj
|
19
|
+
when ::Array
|
20
|
+
obj.map do |v|
|
21
|
+
resolve_lock v
|
22
|
+
end
|
23
|
+
when ::Hash
|
24
|
+
obj.inject({}) do |memo, (k, v)|
|
25
|
+
kk = resolve_lock k
|
26
|
+
vv = resolve_lock v
|
27
|
+
memo[kk] = vv
|
28
|
+
memo
|
29
|
+
end
|
30
|
+
else
|
31
|
+
obj.respond_to?(:as_lock) ? [obj.class.name, obj.as_lock] : obj
|
32
|
+
end
|
33
|
+
end
|
17
34
|
end
|
18
35
|
|
19
36
|
attr_reader :obj
|
@@ -21,6 +38,7 @@ module LockMethod
|
|
21
38
|
attr_reader :args
|
22
39
|
|
23
40
|
def initialize(obj, method_id, options = {})
|
41
|
+
@mutex = ::Mutex.new
|
24
42
|
@obj = obj
|
25
43
|
@method_id = method_id
|
26
44
|
options = options.symbolize_keys
|
@@ -38,23 +56,27 @@ module LockMethod
|
|
38
56
|
end
|
39
57
|
|
40
58
|
def ttl
|
41
|
-
@ttl ||=
|
59
|
+
@ttl ||= LockMethod.config.default_ttl
|
42
60
|
end
|
43
61
|
|
44
62
|
def obj_digest
|
45
|
-
@obj_digest
|
63
|
+
@obj_digest || @mutex.synchronize do
|
64
|
+
@obj_digest ||= ::Digest::SHA1.hexdigest(::Marshal.dump(Lock.resolve_lock(obj)))
|
65
|
+
end
|
46
66
|
end
|
47
67
|
|
48
68
|
def args_digest
|
49
|
-
@args_digest
|
69
|
+
@args_digest || @mutex.synchronize do
|
70
|
+
@args_digest ||= args.to_a.empty? ? 'empty' : ::Digest::SHA1.hexdigest(::Marshal.dump(Lock.resolve_lock(args)))
|
71
|
+
end
|
50
72
|
end
|
51
73
|
|
52
74
|
def delete
|
53
|
-
|
75
|
+
LockMethod.config.storage.delete cache_key
|
54
76
|
end
|
55
77
|
|
56
78
|
def save
|
57
|
-
|
79
|
+
LockMethod.config.storage.set cache_key, self, ttl
|
58
80
|
end
|
59
81
|
|
60
82
|
def locked?
|
@@ -78,7 +100,7 @@ module LockMethod
|
|
78
100
|
end
|
79
101
|
|
80
102
|
def call_and_lock(*original_method_id_and_args)
|
81
|
-
|
103
|
+
while locked? and spin?
|
82
104
|
::Kernel.sleep 0.5
|
83
105
|
end
|
84
106
|
if locked?
|
@@ -86,7 +108,7 @@ module LockMethod
|
|
86
108
|
else
|
87
109
|
begin
|
88
110
|
save
|
89
|
-
obj.send
|
111
|
+
obj.send(*original_method_id_and_args)
|
90
112
|
ensure
|
91
113
|
delete
|
92
114
|
end
|
data/lib/lock_method/version.rb
CHANGED
data/lib/lock_method.rb
CHANGED
@@ -4,20 +4,22 @@ if ::ActiveSupport::VERSION::MAJOR >= 3
|
|
4
4
|
require 'active_support/core_ext'
|
5
5
|
end
|
6
6
|
|
7
|
-
require 'lock_method/
|
7
|
+
require 'lock_method/config'
|
8
|
+
require 'lock_method/lock'
|
9
|
+
require 'lock_method/default_storage_client'
|
8
10
|
|
9
11
|
# See the README.rdoc for more info!
|
10
12
|
module LockMethod
|
11
|
-
autoload :Config, 'lock_method/config'
|
12
|
-
autoload :Lock, 'lock_method/lock'
|
13
|
-
autoload :DefaultStorageClient, 'lock_method/default_storage_client'
|
14
|
-
|
15
13
|
# This is what gets raised when you try to run a locked method.
|
16
14
|
class Locked < ::StandardError
|
17
15
|
end
|
18
16
|
|
19
|
-
|
20
|
-
|
17
|
+
MUTEX = ::Mutex.new
|
18
|
+
|
19
|
+
def LockMethod.config #:nodoc:
|
20
|
+
@config || MUTEX.synchronize do
|
21
|
+
@config ||= Config.new
|
22
|
+
end
|
21
23
|
end
|
22
24
|
|
23
25
|
# All Objects, including instances and Classes, get the <tt>#lock_method_clear</tt> method.
|
@@ -65,14 +67,12 @@ module LockMethod
|
|
65
67
|
define_method method_id do |*args1|
|
66
68
|
options = options.merge(:args => args1)
|
67
69
|
lock = ::LockMethod::Lock.new self, method_id, options
|
68
|
-
lock.call_and_lock
|
70
|
+
lock.call_and_lock(*([original_method_id]+args1))
|
69
71
|
end
|
70
72
|
end
|
71
73
|
end
|
72
74
|
end
|
73
75
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
::Module.extend ::LockMethod::ClassMethods
|
78
|
-
end
|
76
|
+
::Object.send :include, ::LockMethod::InstanceMethods
|
77
|
+
::Class.send :include, ::LockMethod::ClassMethods
|
78
|
+
::Module.send :include, ::LockMethod::ClassMethods
|
data/lock_method.gemspec
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
|
3
|
-
require "lock_method/version"
|
2
|
+
require File.expand_path('../lib/lock_method/version', __FILE__)
|
4
3
|
|
5
4
|
Gem::Specification.new do |s|
|
6
5
|
s.name = "lock_method"
|
@@ -20,12 +19,4 @@ Gem::Specification.new do |s|
|
|
20
19
|
|
21
20
|
s.add_runtime_dependency 'cache', '>=0.2.1'
|
22
21
|
s.add_runtime_dependency 'activesupport'
|
23
|
-
|
24
|
-
s.add_development_dependency 'memcached'
|
25
|
-
s.add_development_dependency 'redis'
|
26
|
-
if RUBY_VERSION >= '1.9'
|
27
|
-
s.add_development_dependency 'ruby-debug19'
|
28
|
-
else
|
29
|
-
s.add_development_dependency 'ruby-debug'
|
30
|
-
end
|
31
|
-
end
|
22
|
+
end
|
data/test/helper.rb
CHANGED
@@ -2,10 +2,7 @@ require 'rubygems'
|
|
2
2
|
require 'bundler'
|
3
3
|
Bundler.setup
|
4
4
|
require 'test/unit'
|
5
|
-
require 'ruby-debug'
|
6
5
|
require 'shared_tests'
|
7
|
-
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
8
|
-
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
9
6
|
require 'lock_method'
|
10
7
|
|
11
8
|
class Blog1
|
@@ -1,10 +1,10 @@
|
|
1
1
|
require 'helper'
|
2
2
|
|
3
|
-
require '
|
3
|
+
require 'dalli'
|
4
4
|
|
5
5
|
class TestMemcachedStorage < Test::Unit::TestCase
|
6
6
|
def setup
|
7
|
-
my_cache =
|
7
|
+
my_cache = Dalli::Client.new 'localhost:11211'
|
8
8
|
my_cache.flush
|
9
9
|
LockMethod.config.storage = my_cache
|
10
10
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lock_method
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-04-17 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: cache
|
16
|
-
requirement:
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,62 +21,28 @@ dependencies:
|
|
21
21
|
version: 0.2.1
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
25
|
-
- !ruby/object:Gem::Dependency
|
26
|
-
name: activesupport
|
27
|
-
requirement: &2152240680 !ruby/object:Gem::Requirement
|
28
|
-
none: false
|
29
|
-
requirements:
|
30
|
-
- - ! '>='
|
31
|
-
- !ruby/object:Gem::Version
|
32
|
-
version: '0'
|
33
|
-
type: :runtime
|
34
|
-
prerelease: false
|
35
|
-
version_requirements: *2152240680
|
36
|
-
- !ruby/object:Gem::Dependency
|
37
|
-
name: test-unit
|
38
|
-
requirement: &2160094560 !ruby/object:Gem::Requirement
|
39
|
-
none: false
|
40
|
-
requirements:
|
41
|
-
- - ! '>='
|
42
|
-
- !ruby/object:Gem::Version
|
43
|
-
version: '0'
|
44
|
-
type: :development
|
45
|
-
prerelease: false
|
46
|
-
version_requirements: *2160094560
|
47
|
-
- !ruby/object:Gem::Dependency
|
48
|
-
name: memcached
|
49
|
-
requirement: &2160090340 !ruby/object:Gem::Requirement
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
25
|
none: false
|
51
26
|
requirements:
|
52
27
|
- - ! '>='
|
53
28
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
55
|
-
type: :development
|
56
|
-
prerelease: false
|
57
|
-
version_requirements: *2160090340
|
29
|
+
version: 0.2.1
|
58
30
|
- !ruby/object:Gem::Dependency
|
59
|
-
name:
|
60
|
-
requirement:
|
31
|
+
name: activesupport
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
61
33
|
none: false
|
62
34
|
requirements:
|
63
35
|
- - ! '>='
|
64
36
|
- !ruby/object:Gem::Version
|
65
37
|
version: '0'
|
66
|
-
type: :
|
38
|
+
type: :runtime
|
67
39
|
prerelease: false
|
68
|
-
version_requirements:
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: ruby-debug19
|
71
|
-
requirement: &2160101140 !ruby/object:Gem::Requirement
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
72
41
|
none: false
|
73
42
|
requirements:
|
74
43
|
- - ! '>='
|
75
44
|
- !ruby/object:Gem::Version
|
76
45
|
version: '0'
|
77
|
-
type: :development
|
78
|
-
prerelease: false
|
79
|
-
version_requirements: *2160101140
|
80
46
|
description: Like alias_method, but it's lock_method!
|
81
47
|
email:
|
82
48
|
- seamus@abshere.net
|
@@ -85,6 +51,7 @@ extensions: []
|
|
85
51
|
extra_rdoc_files: []
|
86
52
|
files:
|
87
53
|
- .gitignore
|
54
|
+
- CHANGELOG
|
88
55
|
- Gemfile
|
89
56
|
- README.rdoc
|
90
57
|
- Rakefile
|
@@ -119,7 +86,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
119
86
|
version: '0'
|
120
87
|
requirements: []
|
121
88
|
rubyforge_project: lock_method
|
122
|
-
rubygems_version: 1.8.
|
89
|
+
rubygems_version: 1.8.21
|
123
90
|
signing_key:
|
124
91
|
specification_version: 3
|
125
92
|
summary: Lets you lock methods (to memcached, redis, etc.) as though you had a lockfile
|