em-hiredis 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 48fe707c4b77c8666f0466d1a33f342222f01341
4
+ data.tar.gz: f9119b5ab7ad4af24a98f8392556f85813c1baf0
5
+ SHA512:
6
+ metadata.gz: 0039ac23a12e4027902ea932b5f518be4a4ddfcf553a7cdb30103a46d465f35b2954be053135e26d90eea4c2d2f922ff550925ce89a14191a0d800d1b05b41ca
7
+ data.tar.gz: 3c5a950d9abbde1c1b1ef55a359f7804d9128b9f6527a43b5126423fc70d7c0a809fedd7dbe482a430db8f177cf2eb1f25c4013414b3fd78ee99c3c6b22d24a5
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.0 (2014-06-30)
4
+
5
+ [NEW] Lua script support (see README for details).
6
+
7
+ [NEW] `Client#reconnect!` method which disconnects, reconfigures, and reconnects.
8
+
9
+ [CHANGED] Locking abstraction re-implemented using lua (safer and simpler) [mdpye].
10
+
11
+ [CHANGED] Hiredis dependency updated to 0.5.x
12
+
3
13
  ## 0.2.1 (2013-04-22)
4
14
 
5
15
  [NEW] Support for connecting to redis on a unix socket.
data/README.md CHANGED
@@ -67,7 +67,7 @@ The regular `EM::Hiredis::Client` no longer understands pubsub messages - this l
67
67
 
68
68
  Pubsub can either be used in em-hiredis in a close-to-the-metal fashion, or you can use the convenience functionality for binding blocks to subscriptions if you prefer (recommended).
69
69
 
70
- ### Close to the metal
70
+ ### Close to the metal pubsub interface
71
71
 
72
72
  Basically just bind to `:message` and `:pmessage` events:
73
73
 
@@ -92,7 +92,7 @@ Basically just bind to `:message` and `:pmessage` events:
92
92
  }
93
93
  }
94
94
 
95
- ### Richer interface to pubsub
95
+ ### Richer pubsub interface
96
96
 
97
97
  If you pass a block to `subscribe` or `psubscribe`, the passed block will be called whenever a message arrives on that subscription:
98
98
 
@@ -118,19 +118,43 @@ If you pass a block to `subscribe` or `psubscribe`, the passed block will be cal
118
118
 
119
119
  It's possible to subscribe to the same channel multiple time and just unsubscribe a single callback using `unsubscribe_proc` or `punsubscribe_proc`.
120
120
 
121
- ## Developing
121
+ ## Lua
122
+
123
+ You can of course call EVAL or EVALSHA directly; the following is a higher-level API.
124
+
125
+ Registering a named command on a redis client defines a ruby method with the given name on the client:
126
+
127
+ redis.register_script(:multiply, <<-END)
128
+ return redis.call('get', KEYS[1]) * ARGV[1]
129
+ END
130
+
131
+ The method can be called in a very similar way to any other redis command; the only difference is that the first argument must be an array of keys, and the second (optional) an array of values.
132
+
133
+ # Multiplies the value at key foo by 2
134
+ redis.multiply(['foo'], [2]).callback { ... }
122
135
 
123
- Hacking on em-hiredis is pretty simple, make sure you have Bundler installed:
136
+ Lua commands are submitted to redis using EVALSHA for efficiency. If redis replies with a NOSCRIPT error, the command is automatically re-submitted with EVAL; this is totally transparent to your code and the intermediate 'failure' will not be passed to your errback.
124
137
 
125
- gem install bundler
126
- bundle
138
+ You may register scripts globally, in which case they will be available to all clients:
139
+
140
+ EM::Hiredis::Client.register_script(:multiply, <<-END)
141
+ return redis.call('get', KEYS[1]) * ARGV[1]
142
+ END
143
+
144
+ As a final convenience, it is possible to load all lua scripts from a directory automatically. All `.lua` files in the directory will be registered, and named according to filename (so a file called `sum.lua` becomes available as `redis.sum(...)`).
145
+
146
+ EM::Hiredis::Client.load_scripts_from('./lua_scripts')
147
+
148
+ For examples see `examples/lua.rb` or `lib/em-hiredis/lock_lua`.
149
+
150
+ ## Developing
127
151
 
128
- In order to run the tests you need to have a local redis server running on port 6379. Run all the tests:
152
+ You need bundler and a local redis server running on port 6379 to run the test suite.
129
153
 
130
154
  # WARNING: The tests call flushdb on db 9 - this clears all keys!
131
- bundle exec rake
155
+ bundle exec rake
132
156
 
133
- To run an individual test:
157
+ Run an individual spec:
134
158
 
135
159
  bundle exec rspec spec/redis_commands_spec.rb
136
160
 
data/em-hiredis.gemspec CHANGED
@@ -12,7 +12,8 @@ Gem::Specification.new do |s|
12
12
  s.summary = %q{Eventmachine redis client}
13
13
  s.description = %q{Eventmachine redis client using hiredis native parser}
14
14
 
15
- s.add_dependency 'hiredis', '~> 0.4.0'
15
+ s.add_dependency 'eventmachine', '~> 1.0'
16
+ s.add_dependency 'hiredis', '~> 0.5.0'
16
17
 
17
18
  s.add_development_dependency 'em-spec', '~> 0.2.5'
18
19
  s.add_development_dependency 'rspec', '~> 2.6.0'
@@ -0,0 +1,4 @@
1
+ local a = redis.call('get', KEYS[1])
2
+ local b = redis.call('get', KEYS[2])
3
+
4
+ return a + b
@@ -0,0 +1,35 @@
1
+ $:.unshift(File.expand_path('../../lib', __FILE__))
2
+ require 'em-hiredis'
3
+
4
+ EM.run do
5
+ scripts_dir = File.expand_path("../lua", __FILE__)
6
+ EM::Hiredis::Client.load_scripts_from(scripts_dir)
7
+
8
+ redis = EM::Hiredis.connect
9
+
10
+ redis.register_script(:multiply, <<-END)
11
+ return redis.call('get', KEYS[1]) * ARGV[1]
12
+ END
13
+
14
+ redis.set('foo', 42).callback {
15
+ redis.set('bar', 8).callback {
16
+
17
+ # Multiply is defined above.
18
+ # It is passed one key and one argument.
19
+ redis.multiply(['foo'], [2]).callback { |v|
20
+ puts "Multiply returned: #{v}"
21
+ }.errback { |e|
22
+ puts "Multiply error: #{e}"
23
+ }
24
+
25
+ # Sum is a lua script defined in sum.lua.
26
+ # It is passed two keys.
27
+ redis.sum(['foo', 'bar']).callback { |sum|
28
+ puts "Sum returned: #{sum}"
29
+ }.errback { |e|
30
+ puts "Sum error: #{e}"
31
+ }
32
+
33
+ }
34
+ }
35
+ end
@@ -21,7 +21,6 @@ module EventMachine::Hiredis
21
21
  @defs = []
22
22
  @command_queue = []
23
23
 
24
- @closing_connection = false
25
24
  @reconnect_failed_count = 0
26
25
  @reconnect_timer = nil
27
26
  @failed = false
@@ -55,7 +54,21 @@ module EventMachine::Hiredis
55
54
  end
56
55
  end
57
56
 
57
+ # Disconnect then reconnect the redis connection.
58
+ #
59
+ # Pass optional uri - e.g. to connect to a different redis server.
60
+ # Any pending redis commands will be failed, but during the reconnection
61
+ # new commands will be queued and sent after connected.
62
+ #
63
+ def reconnect!(new_uri = nil)
64
+ @connection.close_connection
65
+ configure(new_uri) if new_uri
66
+ @auto_reconnect = true
67
+ EM.next_tick { reconnect_connection }
68
+ end
69
+
58
70
  def connect
71
+ @auto_reconnect = true
59
72
  @connection = EM.connect(@host, @port, Connection, @host, @port)
60
73
 
61
74
  @connection.on(:closed) do
@@ -64,14 +77,14 @@ module EventMachine::Hiredis
64
77
  @defs = []
65
78
  @deferred_status = nil
66
79
  @connected = false
67
- unless @closing_connection
80
+ if @auto_reconnect
68
81
  # Next tick avoids reconnecting after for example EM.stop
69
82
  EM.next_tick { reconnect }
70
83
  end
71
84
  emit(:disconnected)
72
85
  EM::Hiredis.logger.info("#{@connection} Disconnected")
73
86
  else
74
- unless @closing_connection
87
+ if @auto_reconnect
75
88
  @reconnect_failed_count += 1
76
89
  @reconnect_timer = EM.add_timer(EM::Hiredis.reconnect_timeout) {
77
90
  @reconnect_timer = nil
@@ -156,11 +169,14 @@ module EventMachine::Hiredis
156
169
 
157
170
  def close_connection
158
171
  EM.cancel_timer(@reconnect_timer) if @reconnect_timer
159
- @closing_connection = true
172
+ @auto_reconnect = false
160
173
  @connection.close_connection_after_writing
161
174
  end
162
175
 
176
+ # Note: This method doesn't disconnect if already connected. You probably
177
+ # want to use `reconnect!`
163
178
  def reconnect_connection
179
+ @auto_reconnect = true
164
180
  EM.cancel_timer(@reconnect_timer) if @reconnect_timer
165
181
  reconnect
166
182
  end
@@ -1,9 +1,50 @@
1
+ require 'digest/sha1'
2
+
1
3
  module EventMachine::Hiredis
2
4
  class Client < BaseClient
3
5
  def self.connect(host = 'localhost', port = 6379)
4
6
  new(host, port).connect
5
7
  end
6
8
 
9
+ def self.load_scripts_from(dir)
10
+ Dir.glob("#{dir}/*.lua").each do |f|
11
+ name = Regexp.new(/([^\/]*)\.lua$/).match(f)[1]
12
+ lua = File.open(f, 'r').read
13
+ EM::Hiredis.logger.debug { "Registering script: #{name}" }
14
+ EM::Hiredis::Client.register_script(name, lua)
15
+ end
16
+ end
17
+
18
+ def self.register_script(name, lua)
19
+ sha = Digest::SHA1.hexdigest(lua)
20
+ self.send(:define_method, name.to_sym) { |keys, args=[]|
21
+ eval_script(lua, sha, keys, args)
22
+ }
23
+ end
24
+
25
+ def register_script(name, lua)
26
+ sha = Digest::SHA1.hexdigest(lua)
27
+ singleton = class << self; self end
28
+ singleton.send(:define_method, name.to_sym) { |keys, args=[]|
29
+ eval_script(lua, sha, keys, args)
30
+ }
31
+ end
32
+
33
+ def eval_script(lua, lua_sha, keys, args)
34
+ df = EM::DefaultDeferrable.new
35
+ method_missing(:evalsha, lua_sha, keys.size, *keys, *args).callback(
36
+ &df.method(:succeed)
37
+ ).errback { |e|
38
+ if e.kind_of?(RedisError) && e.redis_error.message.start_with?("NOSCRIPT")
39
+ self.eval(lua, keys.size, *keys, *args)
40
+ .callback(&df.method(:succeed)).errback(&df.method(:fail))
41
+ else
42
+ df.fail(e)
43
+ end
44
+ }
45
+ df
46
+ end
47
+
7
48
  def monitor(&blk)
8
49
  @monitoring = true
9
50
  method_missing(:monitor, &blk)
@@ -1,35 +1,52 @@
1
+ require 'securerandom'
2
+
1
3
  module EM::Hiredis
2
- # Distributed lock built on redis
4
+ # Cross-process re-entrant lock, backed by redis
3
5
  class Lock
4
- # Register an callback which will be called 1s before the lock expires
6
+
7
+ EM::Hiredis::Client.load_scripts_from(File.expand_path("../lock_lua", __FILE__))
8
+
9
+ # Register a callback which will be called 1s before the lock expires
10
+ # This is an informational callback, there is no hard guarantee on the timing
11
+ # of its invocation because the callback firing and lock key expiry are handled
12
+ # by different clocks (the client process and redis server respectively)
5
13
  def onexpire(&blk); @onexpire = blk; end
6
14
 
7
15
  def initialize(redis, key, timeout)
16
+ unless timeout.kind_of?(Fixnum) && timeout >= 1
17
+ raise "Timeout must be an integer and >= 1s"
18
+ end
8
19
  @redis, @key, @timeout = redis, key, timeout
9
- @locked = false
10
- @expiry = nil
20
+ @token = SecureRandom.hex
11
21
  end
12
22
 
13
23
  # Acquire the lock
14
24
  #
15
- # It is ok to call acquire again before the lock expires, which will attempt to extend the existing lock.
25
+ # This is a re-entrant lock, re-acquiring will succeed and extend the timeout
16
26
  #
17
- # Returns a deferrable which either succeeds if the lock can be acquired, or fails if it cannot. In both cases the expiry timestamp is returned (for the new lock or for the expired one respectively)
27
+ # Returns a deferrable which either succeeds if the lock can be acquired, or fails if it cannot.
18
28
  def acquire
19
29
  df = EM::DefaultDeferrable.new
20
- expiry = new_expiry
21
- @redis.setnx(@key, expiry).callback { |setnx|
22
- if setnx == 1
23
- lock_acquired(expiry)
24
- EM::Hiredis.logger.debug "#{to_s} Acquired new lock"
25
- df.succeed(expiry)
30
+ @redis.lock_acquire([@key], [@token, @timeout]).callback { |success|
31
+ if (success)
32
+ EM::Hiredis.logger.debug "#{to_s} acquired"
33
+
34
+ EM.cancel_timer(@expire_timer) if @expire_timer
35
+ @expire_timer = EM.add_timer(@timeout - 1) {
36
+ EM::Hiredis.logger.debug "#{to_s} Expires in 1s"
37
+ @onexpire.call if @onexpire
38
+ }
39
+
40
+ df.succeed
26
41
  else
27
- attempt_to_acquire_existing_lock(df)
42
+ EM::Hiredis.logger.debug "#{to_s} failed to acquire"
43
+ df.fail("Lock is not available")
28
44
  end
29
45
  }.errback { |e|
46
+ EM::Hiredis.logger.error "#{to_s} Error acquiring lock #{e}"
30
47
  df.fail(e)
31
48
  }
32
- return df
49
+ df
33
50
  end
34
51
 
35
52
  # Release the lock
@@ -37,23 +54,29 @@ module EM::Hiredis
37
54
  # Returns a deferrable
38
55
  def unlock
39
56
  EM.cancel_timer(@expire_timer) if @expire_timer
40
-
41
- unless active
42
- df = EM::DefaultDeferrable.new
43
- df.fail Error.new("Cannot unlock, lock not active")
44
- return df
45
- end
46
57
 
47
- @redis.del(@key)
48
- end
49
-
50
- # Lock has been acquired and we're within it's expiry time
51
- def active
52
- @locked && Time.now.to_i < @expiry
58
+ df = EM::DefaultDeferrable.new
59
+ @redis.lock_release([@key], [@token]).callback { |keys_removed|
60
+ if keys_removed > 0
61
+ EM::Hiredis.logger.debug "#{to_s} released"
62
+ df.succeed
63
+ else
64
+ EM::Hiredis.logger.debug "#{to_s} could not release, not held"
65
+ df.fail("Cannot release a lock we do not hold")
66
+ end
67
+ }.errback { |e|
68
+ EM::Hiredis.logger.error "#{to_s} Error releasing lock #{e}"
69
+ df.fail(e)
70
+ }
71
+ df
53
72
  end
54
73
 
55
- # This should not be used in normal operation - force clear
74
+ # This should not be used in normal operation.
75
+ # Force clear without regard to who owns the lock.
56
76
  def clear
77
+ EM::Hiredis.logger.warn "#{to_s} Force clearing lock (unsafe)"
78
+ EM.cancel_timer(@expire_timer) if @expire_timer
79
+
57
80
  @redis.del(@key)
58
81
  end
59
82
 
@@ -61,46 +84,5 @@ module EM::Hiredis
61
84
  "[lock #{@key}]"
62
85
  end
63
86
 
64
- private
65
-
66
- def attempt_to_acquire_existing_lock(df)
67
- @redis.get(@key) { |expiry_1|
68
- expiry_1 = expiry_1.to_i
69
- if expiry_1 == @expiry || expiry_1 < Time.now.to_i
70
- # Either the lock was ours or the lock has already expired
71
- expiry = new_expiry
72
- @redis.getset(@key, expiry) { |expiry_2|
73
- expiry_2 = expiry_2.to_i
74
- if expiry_2 == @expiry || expiry_2 < Time.now.to_i
75
- lock_acquired(expiry)
76
- EM::Hiredis.logger.debug "#{to_s} Acquired existing lock"
77
- df.succeed(expiry)
78
- else
79
- # Another client got there first
80
- EM::Hiredis.logger.debug "#{to_s} Could not acquire - another process acquired while we were in the process of acquiring"
81
- df.fail(expiry_2)
82
- end
83
- }
84
- else
85
- # Someone else has an active lock
86
- EM::Hiredis.logger.debug "#{to_s} Could not acquire - held by another process"
87
- df.fail(expiry_1)
88
- end
89
- }
90
- end
91
-
92
- def new_expiry
93
- Time.now.to_i + @timeout + 1
94
- end
95
-
96
- def lock_acquired(expiry)
97
- @locked = true
98
- @expiry = expiry
99
- EM.cancel_timer(@expire_timer) if @expire_timer
100
- @expire_timer = EM.add_timer(@timeout) {
101
- EM::Hiredis.logger.debug "#{to_s} Expires in 1s"
102
- @onexpire.call if @onexpire
103
- }
104
- end
105
87
  end
106
88
  end
@@ -0,0 +1,17 @@
1
+ -- Set key to token with expiry of timeout, if:
2
+ -- - It doesn't exist
3
+ -- - It exists and already has value of token (further set extends timeout)
4
+ -- Used to implement a re-entrant lock.
5
+ local key = KEYS[1]
6
+ local token = ARGV[1]
7
+ local timeout = ARGV[2]
8
+
9
+ local value = redis.call('get', key)
10
+
11
+ if value == token or not value then
12
+ -- Great, either we hold the lock or it's free for us to take
13
+ return redis.call('setex', key, timeout, token)
14
+ else
15
+ -- Someone else has it
16
+ return false
17
+ end
@@ -0,0 +1,9 @@
1
+ -- Deletes a key only if it has the value supplied as token
2
+ local key = KEYS[1]
3
+ local token = ARGV[1]
4
+
5
+ if redis.call('get', key) == token then
6
+ return redis.call('del', key)
7
+ else
8
+ return 0
9
+ end
@@ -21,11 +21,8 @@ module EM::Hiredis
21
21
  @redis, @key = redis, key
22
22
  @timeout = options[:lock_timeout] || 100
23
23
  @retry_timeout = options[:retry_interval] || 60
24
+
24
25
  @lock = EM::Hiredis::Lock.new(redis, key, @timeout)
25
- @lock.onexpire {
26
- # When the lock is about to expire, extend (called 1s before expiry)
27
- acquire()
28
- }
29
26
  @locked = false
30
27
  EM.next_tick {
31
28
  @running = true
@@ -42,6 +39,11 @@ module EM::Hiredis
42
39
  @onlocked.call if @onlocked
43
40
  @locked = true
44
41
  end
42
+
43
+ # Re-acquire lock near the end of the period
44
+ @extend_timer = EM.add_timer(@timeout.to_f * 2 / 3) {
45
+ acquire()
46
+ }
45
47
  }.errback { |e|
46
48
  if @locked
47
49
  # We were previously locked
@@ -54,7 +56,7 @@ module EM::Hiredis
54
56
  EM::Hiredis.logger.warn "Unexpected error acquiring #{@lock} #{err}"
55
57
  end
56
58
 
57
- EM.add_timer(@retry_timeout) {
59
+ @retry_timer = EM.add_timer(@retry_timeout) {
58
60
  acquire() unless @locked
59
61
  }
60
62
  }
@@ -62,6 +64,8 @@ module EM::Hiredis
62
64
 
63
65
  def stop
64
66
  @running = false
67
+ EM.cancel_timer(@extend_timer) if @extend_timer
68
+ EM.cancel_timer(@retry_timer) if @retry_timer
65
69
  if @locked
66
70
  # We were previously locked
67
71
  @onunlocked.call if @onunlocked
@@ -1,5 +1,5 @@
1
1
  module EventMachine
2
2
  module Hiredis
3
- VERSION = "0.2.1"
3
+ VERSION = "0.3.0"
4
4
  end
5
5
  end
@@ -105,7 +105,7 @@ describe EM::Hiredis::BaseClient do
105
105
  df.errback { |e|
106
106
  e.class.should == EM::Hiredis::RedisError
107
107
  e.should be_kind_of(EM::Hiredis::Error)
108
- msg = "ERR Operation against a key holding the wrong kind of value"
108
+ msg = "WRONGTYPE Operation against a key holding the wrong kind of value"
109
109
  e.message.should == msg
110
110
  # This is the wrapped error from redis:
111
111
  e.redis_error.class.should == RuntimeError
data/spec/lock_spec.rb ADDED
@@ -0,0 +1,137 @@
1
+ require 'spec_helper'
2
+
3
+ describe EventMachine::Hiredis::Lock do
4
+
5
+ def start(timeout = 1)
6
+ connect(timeout) do |redis|
7
+ @redis = redis
8
+ yield
9
+ end
10
+ end
11
+
12
+ def new_lock
13
+ EventMachine::Hiredis::Lock.new(@redis, "test-lock", 2)
14
+ end
15
+
16
+ it "can be acquired" do
17
+ start {
18
+ new_lock.acquire.callback {
19
+ done
20
+ }.errback { |e|
21
+ fail e
22
+ }
23
+ }
24
+ end
25
+
26
+ it "is re-entrant" do
27
+ start {
28
+ lock = new_lock
29
+ lock.acquire.callback {
30
+ lock.acquire.callback {
31
+ done
32
+ }.errback { |e|
33
+ fail e
34
+ }
35
+ }.errback { |e|
36
+ fail e
37
+ }
38
+ }
39
+ end
40
+
41
+ it "is exclusive" do
42
+ start {
43
+ new_lock.acquire.callback {
44
+ new_lock.acquire.errback {
45
+ done
46
+ }.callback {
47
+ fail "Should not be able to acquire lock from different client"
48
+ }
49
+ }.errback { |e|
50
+ fail e
51
+ }
52
+ }
53
+ end
54
+
55
+ it "can be released and taken by another instance" do
56
+ start {
57
+ lock = new_lock
58
+ lock.acquire.callback {
59
+ lock.unlock.callback {
60
+ new_lock.acquire.callback {
61
+ done
62
+ }.errback { |e|
63
+ fail e
64
+ }
65
+ }.errback { |e|
66
+ fail e
67
+ }
68
+ }.errback { |e|
69
+ fail e
70
+ }
71
+ }
72
+ end
73
+
74
+ it "times out" do
75
+ start(3) {
76
+ new_lock.acquire.callback {
77
+ EM.add_timer(2) {
78
+ new_lock.acquire.callback {
79
+ done
80
+ }.errback { |e|
81
+ fail e
82
+ }
83
+ }
84
+ }.errback { |e|
85
+ fail e
86
+ }
87
+ }
88
+ end
89
+
90
+ it "extends timeout on re-entry" do
91
+ start(4) {
92
+ lock = new_lock
93
+ lock.acquire.callback {
94
+ EM.add_timer(1) {
95
+ lock.acquire.callback {
96
+ EM.add_timer(1.5) {
97
+ # Check it's still locked by initial instance
98
+ new_lock.acquire.errback {
99
+ done
100
+ }.callback { |e|
101
+ fail e
102
+ }
103
+ }
104
+ }.errback { |e|
105
+ fail e
106
+ }
107
+ }
108
+ }.errback { |e|
109
+ fail e
110
+ }
111
+ }
112
+ end
113
+
114
+ it "fails to release if it has not been taken" do
115
+ start {
116
+ new_lock.unlock.errback {
117
+ done
118
+ }.callback {
119
+ fail "Released lock which had not been taken"
120
+ }
121
+ }
122
+ end
123
+
124
+ it "fails to release if taken by another instance" do
125
+ start {
126
+ new_lock.acquire.callback {
127
+ new_lock.unlock.errback {
128
+ done
129
+ }.callback {
130
+ fail "Released lock belonging to another instance"
131
+ }
132
+ }.errback { |e|
133
+ fail e
134
+ }
135
+ }
136
+ end
137
+ end
metadata CHANGED
@@ -1,78 +1,83 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: em-hiredis
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
5
- prerelease:
4
+ version: 0.3.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Martyn Loughran
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-04-22 00:00:00.000000000 Z
11
+ date: 2014-06-30 00:00:00.000000000 Z
13
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: eventmachine
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.0'
14
27
  - !ruby/object:Gem::Dependency
15
28
  name: hiredis
16
29
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
30
  requirements:
19
- - - ~>
31
+ - - "~>"
20
32
  - !ruby/object:Gem::Version
21
- version: 0.4.0
33
+ version: 0.5.0
22
34
  type: :runtime
23
35
  prerelease: false
24
36
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
37
  requirements:
27
- - - ~>
38
+ - - "~>"
28
39
  - !ruby/object:Gem::Version
29
- version: 0.4.0
40
+ version: 0.5.0
30
41
  - !ruby/object:Gem::Dependency
31
42
  name: em-spec
32
43
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
44
  requirements:
35
- - - ~>
45
+ - - "~>"
36
46
  - !ruby/object:Gem::Version
37
47
  version: 0.2.5
38
48
  type: :development
39
49
  prerelease: false
40
50
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
51
  requirements:
43
- - - ~>
52
+ - - "~>"
44
53
  - !ruby/object:Gem::Version
45
54
  version: 0.2.5
46
55
  - !ruby/object:Gem::Dependency
47
56
  name: rspec
48
57
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
58
  requirements:
51
- - - ~>
59
+ - - "~>"
52
60
  - !ruby/object:Gem::Version
53
61
  version: 2.6.0
54
62
  type: :development
55
63
  prerelease: false
56
64
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
65
  requirements:
59
- - - ~>
66
+ - - "~>"
60
67
  - !ruby/object:Gem::Version
61
68
  version: 2.6.0
62
69
  - !ruby/object:Gem::Dependency
63
70
  name: rake
64
71
  requirement: !ruby/object:Gem::Requirement
65
- none: false
66
72
  requirements:
67
- - - ! '>='
73
+ - - ">="
68
74
  - !ruby/object:Gem::Version
69
75
  version: '0'
70
76
  type: :development
71
77
  prerelease: false
72
78
  version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
79
  requirements:
75
- - - ! '>='
80
+ - - ">="
76
81
  - !ruby/object:Gem::Version
77
82
  version: '0'
78
83
  description: Eventmachine redis client using hiredis native parser
@@ -82,8 +87,8 @@ executables: []
82
87
  extensions: []
83
88
  extra_rdoc_files: []
84
89
  files:
85
- - .gitignore
86
- - .rspec
90
+ - ".gitignore"
91
+ - ".rspec"
87
92
  - CHANGELOG.md
88
93
  - Gemfile
89
94
  - LICENCE
@@ -91,6 +96,8 @@ files:
91
96
  - Rakefile
92
97
  - em-hiredis.gemspec
93
98
  - examples/getting_started.rb
99
+ - examples/lua/sum.lua
100
+ - examples/lua_example.rb
94
101
  - examples/pubsub_basics.rb
95
102
  - examples/pubsub_more.rb
96
103
  - examples/pubsub_raw.rb
@@ -100,12 +107,15 @@ files:
100
107
  - lib/em-hiredis/connection.rb
101
108
  - lib/em-hiredis/event_emitter.rb
102
109
  - lib/em-hiredis/lock.rb
110
+ - lib/em-hiredis/lock_lua/lock_acquire.lua
111
+ - lib/em-hiredis/lock_lua/lock_release.lua
103
112
  - lib/em-hiredis/persistent_lock.rb
104
113
  - lib/em-hiredis/pubsub_client.rb
105
114
  - lib/em-hiredis/version.rb
106
115
  - spec/base_client_spec.rb
107
116
  - spec/connection_spec.rb
108
117
  - spec/live_redis_protocol_spec.rb
118
+ - spec/lock_spec.rb
109
119
  - spec/pubsub_spec.rb
110
120
  - spec/redis_commands_spec.rb
111
121
  - spec/spec_helper.rb
@@ -114,38 +124,32 @@ files:
114
124
  - spec/url_param_spec.rb
115
125
  homepage: http://github.com/mloughran/em-hiredis
116
126
  licenses: []
127
+ metadata: {}
117
128
  post_install_message:
118
129
  rdoc_options: []
119
130
  require_paths:
120
131
  - lib
121
132
  required_ruby_version: !ruby/object:Gem::Requirement
122
- none: false
123
133
  requirements:
124
- - - ! '>='
134
+ - - ">="
125
135
  - !ruby/object:Gem::Version
126
136
  version: '0'
127
- segments:
128
- - 0
129
- hash: -3658727761083238148
130
137
  required_rubygems_version: !ruby/object:Gem::Requirement
131
- none: false
132
138
  requirements:
133
- - - ! '>='
139
+ - - ">="
134
140
  - !ruby/object:Gem::Version
135
141
  version: '0'
136
- segments:
137
- - 0
138
- hash: -3658727761083238148
139
142
  requirements: []
140
143
  rubyforge_project: em-hiredis
141
- rubygems_version: 1.8.24
144
+ rubygems_version: 2.2.2
142
145
  signing_key:
143
- specification_version: 3
146
+ specification_version: 4
144
147
  summary: Eventmachine redis client
145
148
  test_files:
146
149
  - spec/base_client_spec.rb
147
150
  - spec/connection_spec.rb
148
151
  - spec/live_redis_protocol_spec.rb
152
+ - spec/lock_spec.rb
149
153
  - spec/pubsub_spec.rb
150
154
  - spec/redis_commands_spec.rb
151
155
  - spec/spec_helper.rb