promiscuous 1.0.0.beta3 → 1.0.0.beta4
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.
- checksums.yaml +4 -4
- data/lib/promiscuous.rb +2 -0
- data/lib/promiscuous/config.rb +4 -6
- data/lib/promiscuous/publisher/model/active_record.rb +1 -1
- data/lib/promiscuous/publisher/model/mock.rb +2 -1
- data/lib/promiscuous/publisher/transport/persistence/active_record.rb +22 -12
- data/lib/promiscuous/redis.rb +12 -213
- data/lib/promiscuous/subscriber/unit_of_work.rb +5 -6
- data/lib/promiscuous/subscriber/worker/eventual_destroyer.rb +2 -1
- data/lib/promiscuous/version.rb +1 -1
- metadata +36 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a2be6a0c5f3e918b401df4b70498f2120e2493e1
|
4
|
+
data.tar.gz: 14a8ff258fd6cb14d59d7ad8efb874a37bb5424e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d98fd116d3dd2e123a703cca6feb25be23a75ad139df43f0c0e7a1a2091ceb84d782c46ae455f06385f4136d69533ea01243389ab42c5719a1970b68b59e28a4
|
7
|
+
data.tar.gz: de9801711b20a656c81fc53fcf74c67015e8bd9e9b25c8af1c20a3d0045e02a5008c0b2f3d427ec84e5bc369aa15434ebbe5d11d45a46156043cf2cb3ccdeec8
|
data/lib/promiscuous.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'active_support/dependencies/autoload'
|
1
2
|
require 'active_support/core_ext'
|
2
3
|
require 'active_model/callbacks'
|
3
4
|
require 'multi_json'
|
@@ -42,6 +43,7 @@ module Promiscuous
|
|
42
43
|
def connect
|
43
44
|
AMQP.connect
|
44
45
|
Redis.connect
|
46
|
+
|
45
47
|
@should_be_connected = true
|
46
48
|
end
|
47
49
|
|
data/lib/promiscuous/config.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
module Promiscuous::Config
|
2
2
|
mattr_accessor :app, :backend, :amqp_url,
|
3
3
|
:publisher_amqp_url, :subscriber_amqp_url, :publisher_exchange,
|
4
|
-
:subscriber_exchanges, :queue_name, :queue_options,
|
5
|
-
:
|
6
|
-
:socket_timeout, :heartbeat,
|
4
|
+
:subscriber_exchanges, :queue_name, :queue_options,
|
5
|
+
:redis_url, :redis_stats_url, :stats_interval,
|
6
|
+
:socket_timeout, :heartbeat,
|
7
7
|
:prefetch, :recovery_timeout, :recovery_interval, :logger, :subscriber_threads,
|
8
8
|
:version_field, :error_notifier, :transport_collection,
|
9
9
|
:on_stats, :max_retries, :generation, :destroy_timeout, :destroy_check_interval
|
@@ -44,13 +44,11 @@ module Promiscuous::Config
|
|
44
44
|
self.queue_name ||= "#{self.app}.promiscuous"
|
45
45
|
self.queue_options ||= {:durable => true, :arguments => {'x-ha-policy' => 'all'}}
|
46
46
|
self.redis_url ||= 'redis://localhost/'
|
47
|
-
self.redis_urls ||= [self.redis_url]
|
48
47
|
# TODO self.redis_slave_url ||= nil
|
49
|
-
self.redis_stats_url ||= self.
|
48
|
+
self.redis_stats_url ||= self.redis_url
|
50
49
|
self.stats_interval ||= 0
|
51
50
|
self.socket_timeout ||= 10
|
52
51
|
self.heartbeat ||= 60
|
53
|
-
self.hash_size ||= 2**20 # one million keys ~ 200Mb.
|
54
52
|
self.prefetch ||= 1000
|
55
53
|
self.recovery_timeout ||= 10.seconds
|
56
54
|
self.recovery_interval ||= 5.seconds
|
@@ -9,7 +9,7 @@ module Promiscuous::Publisher::Model::ActiveRecord
|
|
9
9
|
raise <<-help
|
10
10
|
#{self} must include a _v column. Create the following migration:
|
11
11
|
change_table :#{self.table_name} do |t|
|
12
|
-
t.integer :_v, :limit => 8
|
12
|
+
t.integer :_v, :limit => 8, :default => 1
|
13
13
|
end
|
14
14
|
help
|
15
15
|
end
|
@@ -30,7 +30,8 @@ module Promiscuous::Publisher::Model::Mock
|
|
30
30
|
# json dump.
|
31
31
|
batch = op.create_transport_batch([op])
|
32
32
|
|
33
|
-
Promiscuous::Subscriber::Message.new(batch.payload)
|
33
|
+
message = Promiscuous::Subscriber::Message.new(batch.payload)
|
34
|
+
Promiscuous::Subscriber::UnitOfWork.process(message)
|
34
35
|
end
|
35
36
|
|
36
37
|
module ClassMethods
|
@@ -1,17 +1,7 @@
|
|
1
1
|
class Promiscuous::Publisher::Transport::Persistence::ActiveRecord
|
2
|
-
def initialize
|
3
|
-
unless connection.table_exists?(table)
|
4
|
-
raise <<-help
|
5
|
-
Promiscuous requires the following migration to be run:
|
6
|
-
create_table :_promiscuous do |t|
|
7
|
-
t.string :batch
|
8
|
-
t.timestamp :at, :default => :now
|
9
|
-
end
|
10
|
-
help
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
2
|
def save(batch)
|
3
|
+
check_schema
|
4
|
+
|
15
5
|
q = "INSERT INTO #{table} (\"batch\") " +
|
16
6
|
"VALUES ('#{batch.dump}') RETURNING id"
|
17
7
|
|
@@ -21,6 +11,8 @@ class Promiscuous::Publisher::Transport::Persistence::ActiveRecord
|
|
21
11
|
end
|
22
12
|
|
23
13
|
def expired
|
14
|
+
check_schema
|
15
|
+
|
24
16
|
q = "SELECT id, p.batch FROM #{table} p " +
|
25
17
|
"WHERE at < current_timestamp - #{Promiscuous::Config.recovery_timeout} * INTERVAL '1 second'"
|
26
18
|
|
@@ -28,6 +20,8 @@ class Promiscuous::Publisher::Transport::Persistence::ActiveRecord
|
|
28
20
|
end
|
29
21
|
|
30
22
|
def delete(batch)
|
23
|
+
check_schema
|
24
|
+
|
31
25
|
q = "DELETE FROM #{table} WHERE id = #{batch.id}"
|
32
26
|
|
33
27
|
connection.exec_query(q, 'Promiscuous Recovery Delete')
|
@@ -35,6 +29,22 @@ class Promiscuous::Publisher::Transport::Persistence::ActiveRecord
|
|
35
29
|
|
36
30
|
private
|
37
31
|
|
32
|
+
def check_schema
|
33
|
+
return if @schema_checked
|
34
|
+
|
35
|
+
unless connection.table_exists?(table)
|
36
|
+
raise <<-help
|
37
|
+
Promiscuous requires the following migration to be run:
|
38
|
+
create_table :_promiscuous do |t|
|
39
|
+
t.string :batch
|
40
|
+
t.timestamp :at, :default => :now
|
41
|
+
end
|
42
|
+
help
|
43
|
+
end
|
44
|
+
|
45
|
+
@schema_checked = true
|
46
|
+
end
|
47
|
+
|
38
48
|
def connection
|
39
49
|
ActiveRecord::Base.connection
|
40
50
|
end
|
data/lib/promiscuous/redis.rb
CHANGED
@@ -1,228 +1,27 @@
|
|
1
1
|
require 'redis'
|
2
|
-
require 'redis/distributed'
|
3
|
-
require 'digest/sha1'
|
4
2
|
|
5
|
-
|
6
|
-
|
7
|
-
disconnect
|
8
|
-
@master = new_connection
|
9
|
-
end
|
10
|
-
|
11
|
-
def self.master
|
12
|
-
ensure_connected unless @master
|
13
|
-
@master
|
14
|
-
end
|
15
|
-
|
16
|
-
def self.node_for(key)
|
17
|
-
distributed_redis ||= Promiscuous::Redis.master
|
18
|
-
distributed_redis.nodes[FNV.new.fnv1a_32(key) % @master.nodes.size]
|
19
|
-
end
|
20
|
-
|
21
|
-
def self.slave
|
22
|
-
ensure_connected unless @slave
|
23
|
-
@slave
|
24
|
-
end
|
25
|
-
|
26
|
-
def self.ensure_slave
|
27
|
-
# ensure_slave is called on the first publisher declaration.
|
28
|
-
if Promiscuous::Config.redis_slave_url
|
29
|
-
self.slave = new_connection(Promiscuous::Config.redis_slave_url)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def self.disconnect
|
34
|
-
@master.quit if @master
|
35
|
-
@slave.quit if @slave
|
36
|
-
@master = nil
|
37
|
-
@slave = nil
|
38
|
-
end
|
39
|
-
|
40
|
-
def self.new_connection(url=nil)
|
41
|
-
url ||= Promiscuous::Config.redis_urls
|
42
|
-
redis = ::Redis::Distributed.new(url, :tcp_keepalive => 60)
|
3
|
+
class Promiscuous::Redis
|
4
|
+
class_attribute :connection
|
43
5
|
|
44
|
-
|
45
|
-
|
46
|
-
unless Gem::Version.new(version) >= Gem::Version.new('2.6.0')
|
47
|
-
raise "You are using Redis #{version}. Please use Redis 2.6.0 or later."
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
redis
|
52
|
-
end
|
53
|
-
|
54
|
-
def self.new_blocking_connection
|
55
|
-
# This removes the read/select loop in redis, it's weird and unecessary when
|
56
|
-
# blocking on the connection.
|
57
|
-
new_connection.tap do |redis|
|
58
|
-
redis.nodes.each do |node|
|
59
|
-
node.client.connection.instance_eval do
|
60
|
-
@sock.instance_eval do
|
61
|
-
def _read_from_socket(nbytes)
|
62
|
-
readpartial(nbytes)
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
6
|
+
def self.connect
|
7
|
+
self.connection = Redis.new(:url => Promiscuous::Config.redis_url)
|
68
8
|
end
|
69
9
|
|
70
10
|
def self.ensure_connected
|
71
11
|
Promiscuous.ensure_connected
|
72
12
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
raise lost_connection_exception(node, :inner => e)
|
78
|
-
end
|
13
|
+
begin
|
14
|
+
connection.ping
|
15
|
+
rescue Exception => e
|
16
|
+
raise lost_connection_exception(node, :inner => e)
|
79
17
|
end
|
80
18
|
end
|
81
19
|
|
82
|
-
def self.
|
83
|
-
|
84
|
-
end
|
85
|
-
|
86
|
-
class Script
|
87
|
-
def initialize(script)
|
88
|
-
@script = script
|
89
|
-
@sha = Digest::SHA1.hexdigest(@script)
|
90
|
-
end
|
91
|
-
|
92
|
-
def eval(redis, options={})
|
93
|
-
redis.evalsha(@sha, options)
|
94
|
-
rescue ::Redis::CommandError => e
|
95
|
-
if e.message =~ /^NOSCRIPT/
|
96
|
-
redis.script(:load, @script)
|
97
|
-
retry
|
98
|
-
end
|
99
|
-
raise e
|
100
|
-
end
|
101
|
-
|
102
|
-
def to_s
|
103
|
-
@script
|
104
|
-
end
|
20
|
+
def self.disconnect
|
21
|
+
self.connection.quit
|
105
22
|
end
|
106
23
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
def initialize(key, options={})
|
111
|
-
# TODO remove old code with orig_key
|
112
|
-
@orig_key = key.to_s
|
113
|
-
@key = "#{key}:lock"
|
114
|
-
@timeout = options[:timeout].to_i
|
115
|
-
@sleep = options[:sleep].to_f
|
116
|
-
@expire = options[:expire].to_i
|
117
|
-
@lock_set = options[:lock_set]
|
118
|
-
@node = options[:node]
|
119
|
-
raise "Which node?" unless @node
|
120
|
-
end
|
121
|
-
|
122
|
-
def key
|
123
|
-
@orig_key
|
124
|
-
end
|
125
|
-
|
126
|
-
def node
|
127
|
-
@node
|
128
|
-
end
|
129
|
-
|
130
|
-
def lock
|
131
|
-
result = false
|
132
|
-
start_at = Time.now
|
133
|
-
while Time.now - start_at < @timeout
|
134
|
-
break if result = try_lock
|
135
|
-
sleep @sleep
|
136
|
-
end
|
137
|
-
result
|
138
|
-
end
|
139
|
-
|
140
|
-
def try_lock
|
141
|
-
raise "You are trying to lock an already locked mutex" if @token
|
142
|
-
|
143
|
-
now = Time.now.to_i
|
144
|
-
|
145
|
-
# This script loading is not thread safe (touching a class variable), but
|
146
|
-
# that's okay, because the race is harmless.
|
147
|
-
@@lock_script ||= Promiscuous::Redis::Script.new <<-SCRIPT
|
148
|
-
local key = KEYS[1]
|
149
|
-
local token_key = KEYS[2]
|
150
|
-
local lock_set = KEYS[3]
|
151
|
-
local now = tonumber(ARGV[1])
|
152
|
-
local expires_at = tonumber(ARGV[2])
|
153
|
-
local orig_key = ARGV[3]
|
154
|
-
|
155
|
-
local prev_expires_at = tonumber(redis.call('hget', key, 'expires_at'))
|
156
|
-
if prev_expires_at and prev_expires_at > now then
|
157
|
-
return {false, nil}
|
158
|
-
end
|
159
|
-
|
160
|
-
local next_token = redis.call('incr', 'promiscuous:next_token')
|
161
|
-
|
162
|
-
redis.call('hmset', key, 'expires_at', expires_at, 'token', next_token)
|
163
|
-
|
164
|
-
if lock_set then
|
165
|
-
redis.call('zadd', lock_set, now, orig_key)
|
166
|
-
end
|
167
|
-
|
168
|
-
if prev_expires_at then
|
169
|
-
return {'recovered', next_token}
|
170
|
-
else
|
171
|
-
return {true, next_token}
|
172
|
-
end
|
173
|
-
SCRIPT
|
174
|
-
result, @token = @@lock_script.eval(@node, :keys => [@key, 'promiscuous:next_token', @lock_set].compact,
|
175
|
-
:argv => [now, now + @expire, @orig_key])
|
176
|
-
result == 'recovered' ? :recovered : !!result
|
177
|
-
end
|
178
|
-
|
179
|
-
def extend
|
180
|
-
now = Time.now.to_i
|
181
|
-
@@extend_script ||= Promiscuous::Redis::Script.new <<-SCRIPT
|
182
|
-
local key = KEYS[1]
|
183
|
-
local expires_at = tonumber(ARGV[1])
|
184
|
-
local token = ARGV[2]
|
185
|
-
|
186
|
-
if redis.call('hget', key, 'token') == token then
|
187
|
-
redis.call('hset', key, 'expires_at', expires_at)
|
188
|
-
return true
|
189
|
-
else
|
190
|
-
return false
|
191
|
-
end
|
192
|
-
SCRIPT
|
193
|
-
!!@@extend_script.eval(@node, :keys => [@key].compact, :argv => [now + @expire, @token])
|
194
|
-
end
|
195
|
-
|
196
|
-
def unlock
|
197
|
-
raise "You are trying to unlock a non locked mutex" unless @token
|
198
|
-
|
199
|
-
# Since it's possible that the operations in the critical section took a long time,
|
200
|
-
# we can't just simply release the lock. The unlock method checks if the unique @token
|
201
|
-
# remains the same, and do not release if the lock token was overwritten.
|
202
|
-
@@unlock_script ||= Script.new <<-LUA
|
203
|
-
local key = KEYS[1]
|
204
|
-
local lock_set = KEYS[2]
|
205
|
-
local token = ARGV[1]
|
206
|
-
local orig_key = ARGV[2]
|
207
|
-
|
208
|
-
if redis.call('hget', key, 'token') == token then
|
209
|
-
redis.call('del', key)
|
210
|
-
if lock_set then
|
211
|
-
redis.call('zrem', lock_set, orig_key)
|
212
|
-
end
|
213
|
-
return true
|
214
|
-
else
|
215
|
-
return false
|
216
|
-
end
|
217
|
-
LUA
|
218
|
-
result = @@unlock_script.eval(@node, :keys => [@key, @lock_set].compact, :argv => [@token, @orig_key])
|
219
|
-
@token = nil
|
220
|
-
!!result
|
221
|
-
end
|
222
|
-
|
223
|
-
def still_locked?
|
224
|
-
raise "You never locked that mutex" unless @token
|
225
|
-
@node.hget(@key, 'token').to_i == @token
|
226
|
-
end
|
24
|
+
def self.lost_connection_exception(node, options={})
|
25
|
+
Promiscuous::Error::Connection.new("redis://#{connection.location}", options)
|
227
26
|
end
|
228
27
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'fnv'
|
2
|
+
require 'robust-redis-lock'
|
2
3
|
|
3
4
|
class Promiscuous::Subscriber::UnitOfWork
|
4
5
|
attr_accessor :message
|
@@ -58,17 +59,16 @@ class Promiscuous::Subscriber::UnitOfWork
|
|
58
59
|
return yield unless operation.version
|
59
60
|
|
60
61
|
key = "#{app}:#{operation.key}"
|
61
|
-
|
62
|
-
mutex = Promiscuous::Redis::Mutex.new(key, lock_options)
|
62
|
+
lock = Redis::Lock.new(key, LOCK_OPTIONS.merge(:redis => Promiscuous::Redis.connection))
|
63
63
|
|
64
|
-
unless
|
65
|
-
raise Promiscuous::Error::LockUnavailable.new(
|
64
|
+
unless lock.lock
|
65
|
+
raise Promiscuous::Error::LockUnavailable.new(lock.key)
|
66
66
|
end
|
67
67
|
|
68
68
|
begin
|
69
69
|
yield
|
70
70
|
ensure
|
71
|
-
unless
|
71
|
+
unless lock.unlock
|
72
72
|
# TODO Be safe in case we have a duplicate message and lost the lock on it
|
73
73
|
raise "The subscriber lost the lock during its operation. It means that someone else\n"+
|
74
74
|
"received a duplicate message, and we got screwed.\n"
|
@@ -84,7 +84,6 @@ class Promiscuous::Subscriber::UnitOfWork
|
|
84
84
|
end
|
85
85
|
|
86
86
|
def on_message
|
87
|
-
# XXX This needs to be done for each operation
|
88
87
|
with_transaction do
|
89
88
|
self.operations.each { |op| execute_operation(op) if op.model }
|
90
89
|
end
|
data/lib/promiscuous/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: promiscuous
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.
|
4
|
+
version: 1.0.0.beta4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nicolas Viennot
|
@@ -9,104 +9,104 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-
|
12
|
+
date: 2014-06-03 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
requirements:
|
18
|
-
- -
|
18
|
+
- - '>='
|
19
19
|
- !ruby/object:Gem::Version
|
20
20
|
version: '3'
|
21
21
|
type: :runtime
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
|
-
- -
|
25
|
+
- - '>='
|
26
26
|
- !ruby/object:Gem::Version
|
27
27
|
version: '3'
|
28
28
|
- !ruby/object:Gem::Dependency
|
29
29
|
name: activemodel
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
31
31
|
requirements:
|
32
|
-
- -
|
32
|
+
- - '>='
|
33
33
|
- !ruby/object:Gem::Version
|
34
34
|
version: '3'
|
35
35
|
type: :runtime
|
36
36
|
prerelease: false
|
37
37
|
version_requirements: !ruby/object:Gem::Requirement
|
38
38
|
requirements:
|
39
|
-
- -
|
39
|
+
- - '>='
|
40
40
|
- !ruby/object:Gem::Version
|
41
41
|
version: '3'
|
42
42
|
- !ruby/object:Gem::Dependency
|
43
43
|
name: bunny
|
44
44
|
requirement: !ruby/object:Gem::Requirement
|
45
45
|
requirements:
|
46
|
-
- -
|
46
|
+
- - '>='
|
47
47
|
- !ruby/object:Gem::Version
|
48
48
|
version: 0.10.7
|
49
49
|
type: :runtime
|
50
50
|
prerelease: false
|
51
51
|
version_requirements: !ruby/object:Gem::Requirement
|
52
52
|
requirements:
|
53
|
-
- -
|
53
|
+
- - '>='
|
54
54
|
- !ruby/object:Gem::Version
|
55
55
|
version: 0.10.7
|
56
56
|
- !ruby/object:Gem::Dependency
|
57
57
|
name: amq-protocol
|
58
58
|
requirement: !ruby/object:Gem::Requirement
|
59
59
|
requirements:
|
60
|
-
- -
|
60
|
+
- - '>='
|
61
61
|
- !ruby/object:Gem::Version
|
62
62
|
version: 1.8.0
|
63
63
|
type: :runtime
|
64
64
|
prerelease: false
|
65
65
|
version_requirements: !ruby/object:Gem::Requirement
|
66
66
|
requirements:
|
67
|
-
- -
|
67
|
+
- - '>='
|
68
68
|
- !ruby/object:Gem::Version
|
69
69
|
version: 1.8.0
|
70
70
|
- !ruby/object:Gem::Dependency
|
71
71
|
name: ruby-progressbar
|
72
72
|
requirement: !ruby/object:Gem::Requirement
|
73
73
|
requirements:
|
74
|
-
- -
|
74
|
+
- - ~>
|
75
75
|
- !ruby/object:Gem::Version
|
76
76
|
version: 1.2.0
|
77
77
|
type: :runtime
|
78
78
|
prerelease: false
|
79
79
|
version_requirements: !ruby/object:Gem::Requirement
|
80
80
|
requirements:
|
81
|
-
- -
|
81
|
+
- - ~>
|
82
82
|
- !ruby/object:Gem::Version
|
83
83
|
version: 1.2.0
|
84
84
|
- !ruby/object:Gem::Dependency
|
85
85
|
name: redis
|
86
86
|
requirement: !ruby/object:Gem::Requirement
|
87
87
|
requirements:
|
88
|
-
- -
|
88
|
+
- - ~>
|
89
89
|
- !ruby/object:Gem::Version
|
90
90
|
version: 3.0.2
|
91
91
|
type: :runtime
|
92
92
|
prerelease: false
|
93
93
|
version_requirements: !ruby/object:Gem::Requirement
|
94
94
|
requirements:
|
95
|
-
- -
|
95
|
+
- - ~>
|
96
96
|
- !ruby/object:Gem::Version
|
97
97
|
version: 3.0.2
|
98
98
|
- !ruby/object:Gem::Dependency
|
99
99
|
name: algorithms
|
100
100
|
requirement: !ruby/object:Gem::Requirement
|
101
101
|
requirements:
|
102
|
-
- -
|
102
|
+
- - ~>
|
103
103
|
- !ruby/object:Gem::Version
|
104
104
|
version: 0.6.1
|
105
105
|
type: :runtime
|
106
106
|
prerelease: false
|
107
107
|
version_requirements: !ruby/object:Gem::Requirement
|
108
108
|
requirements:
|
109
|
-
- -
|
109
|
+
- - ~>
|
110
110
|
- !ruby/object:Gem::Version
|
111
111
|
version: 0.6.1
|
112
112
|
- !ruby/object:Gem::Dependency
|
@@ -127,16 +127,30 @@ dependencies:
|
|
127
127
|
name: multi_json
|
128
128
|
requirement: !ruby/object:Gem::Requirement
|
129
129
|
requirements:
|
130
|
-
- -
|
130
|
+
- - ~>
|
131
131
|
- !ruby/object:Gem::Version
|
132
132
|
version: 1.8.0
|
133
133
|
type: :runtime
|
134
134
|
prerelease: false
|
135
135
|
version_requirements: !ruby/object:Gem::Requirement
|
136
136
|
requirements:
|
137
|
-
- -
|
137
|
+
- - ~>
|
138
138
|
- !ruby/object:Gem::Version
|
139
139
|
version: 1.8.0
|
140
|
+
- !ruby/object:Gem::Dependency
|
141
|
+
name: robust-redis-lock
|
142
|
+
requirement: !ruby/object:Gem::Requirement
|
143
|
+
requirements:
|
144
|
+
- - ~>
|
145
|
+
- !ruby/object:Gem::Version
|
146
|
+
version: 0.2.2
|
147
|
+
type: :runtime
|
148
|
+
prerelease: false
|
149
|
+
version_requirements: !ruby/object:Gem::Requirement
|
150
|
+
requirements:
|
151
|
+
- - ~>
|
152
|
+
- !ruby/object:Gem::Version
|
153
|
+
version: 0.2.2
|
140
154
|
description: Replicate models across applications
|
141
155
|
email:
|
142
156
|
- nicolas@viennot.biz
|
@@ -225,18 +239,19 @@ require_paths:
|
|
225
239
|
- lib
|
226
240
|
required_ruby_version: !ruby/object:Gem::Requirement
|
227
241
|
requirements:
|
228
|
-
- -
|
242
|
+
- - '>='
|
229
243
|
- !ruby/object:Gem::Version
|
230
244
|
version: '0'
|
231
245
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
232
246
|
requirements:
|
233
|
-
- -
|
247
|
+
- - '>'
|
234
248
|
- !ruby/object:Gem::Version
|
235
249
|
version: 1.3.1
|
236
250
|
requirements: []
|
237
251
|
rubyforge_project:
|
238
|
-
rubygems_version: 2.
|
252
|
+
rubygems_version: 2.0.14
|
239
253
|
signing_key:
|
240
254
|
specification_version: 4
|
241
255
|
summary: Replicate models across applications
|
242
256
|
test_files: []
|
257
|
+
has_rdoc: false
|