ruote-redis 2.1.10 → 2.1.11

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.
@@ -2,6 +2,14 @@
2
2
  = ruote-redis - CHANGELOG.txt
3
3
 
4
4
 
5
+ == ruote-redis 2.1.11 released 2010/10/01
6
+
7
+ - storage format change (thanks @wwkeyboard)
8
+ - get_many(type, keys) support (array of keys instead of lonely regex key)
9
+ - get_many(type, nil, :skip => 10) support
10
+ - get_many(type, nil, :count => true) support
11
+
12
+
5
13
  == ruote-redis 2.1.10 released 2010/06/15
6
14
 
7
15
  - works with redis-rb 1.0.2 and 2.0
@@ -0,0 +1,18 @@
1
+
2
+ = ruote-redis CREDITS.txt
3
+
4
+
5
+ == authors
6
+
7
+ - John Mettraux http://jmettraux.wordpress.com
8
+
9
+
10
+ == contributors
11
+
12
+ - Aaron Lee - http://github.com/wwkeyboard
13
+
14
+
15
+ == many thanks to
16
+
17
+ - the Redis team for their great project
18
+
@@ -38,13 +38,13 @@ start a redis server instance (port 6379) and then
38
38
 
39
39
  get into ruote/ and do
40
40
 
41
- ruby test/unit/storage.rb --redis
41
+ ruby test/unit/storage.rb -- --redis
42
42
 
43
43
  * functional tests :
44
44
 
45
45
  get into ruote/ and do
46
46
 
47
- ruby test/functional/test.rb --redis
47
+ ruby test/functional/test.rb -- --redis
48
48
 
49
49
 
50
50
  == license
data/Rakefile CHANGED
@@ -33,7 +33,7 @@ Redis storage for ruote (a ruby workflow engine)
33
33
  #gem.test_file = 'test/test.rb'
34
34
 
35
35
  gem.add_dependency 'ruote', ">= #{Ruote::Redis::VERSION}"
36
- gem.add_dependency 'redis', '>= 2.0.1'
36
+ gem.add_dependency 'redis', '>= 2.0.5'
37
37
  gem.add_development_dependency 'yard'
38
38
  gem.add_development_dependency 'rake'
39
39
  gem.add_development_dependency 'jeweler'
@@ -46,33 +46,30 @@ Jeweler::GemcutterTasks.new
46
46
  #
47
47
  # DOC
48
48
 
49
- begin
50
-
51
- require 'yard'
49
+ #
50
+ # make sure to have rdoc 2.5.x to run that
51
+ #
52
+ require 'rake/rdoctask'
53
+ Rake::RDocTask.new do |rd|
52
54
 
53
- YARD::Rake::YardocTask.new do |doc|
54
- doc.options = [
55
- '-o', 'html/ruote-redis', '--title',
56
- "ruote-redis #{Ruote::Redis::VERSION}"
57
- ]
58
- end
55
+ rd.main = 'README.rdoc'
56
+ rd.rdoc_dir = 'rdoc/ruote-redis_rdoc'
59
57
 
60
- rescue LoadError
58
+ rd.rdoc_files.include(
59
+ 'README.rdoc', 'CHANGELOG.txt', 'CREDITS.txt', 'lib/**/*.rb')
61
60
 
62
- task :yard do
63
- abort "YARD is not available : sudo gem install yard"
64
- end
61
+ rd.title = "ruote-redis #{Ruote::Redis::VERSION}"
65
62
  end
66
63
 
67
64
 
68
65
  #
69
66
  # TO THE WEB
70
67
 
71
- task :upload_website => [ :clean, :yard ] do
68
+ task :upload_rdoc => [ :clean, :rdoc ] do
72
69
 
73
70
  account = 'jmettraux@rubyforge.org'
74
71
  webdir = '/var/www/gforge-projects/ruote'
75
72
 
76
- sh "rsync -azv -e ssh html/ruote-redis #{account}:#{webdir}/"
73
+ sh "rsync -azv -e ssh rdoc/ruote-redis_rdoc #{account}:#{webdir}/"
77
74
  end
78
75
 
@@ -1,8 +1,8 @@
1
1
  #--
2
- # Copyright (c) 2005-2010, John Mettraux, jmettraux@gmail.com
2
+ # Copyright(c) 2005-2010, John Mettraux, jmettraux@gmail.com
3
3
  #
4
4
  # Permission is hereby granted, free of charge, to any person obtaining a copy
5
- # of this software and associated documentation files (the "Software"), to deal
5
+ # of this software and associated documentation files(the "Software"), to deal
6
6
  # in the Software without restriction, including without limitation the rights
7
7
  # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
8
  # copies of the Software, and to permit persons to whom the Software is
@@ -37,8 +37,8 @@ module Redis
37
37
  # A Redis storage for ruote.
38
38
  #
39
39
  # The constructor accepts two arguments, the first one is a Redis instance
40
- # ( see http://github.com/ezmobius/redis-rb ), the second one is the classic
41
- # ruote engine options ( see
40
+ #( see http://github.com/ezmobius/redis-rb ), the second one is the classic
41
+ # ruote engine options( see
42
42
  # http://ruote.rubyforge.org/configuration.html#engine )
43
43
  #
44
44
  # require 'redis' # gem install redis
@@ -66,12 +66,14 @@ module Redis
66
66
 
67
67
  attr_reader :redis
68
68
 
69
- def initialize (redis, options={})
69
+ # A Redis storage for ruote.
70
+ #
71
+ def initialize(redis, options={})
70
72
 
71
73
  @redis = redis
72
74
  @options = options
73
75
 
74
- def @redis.keys_to_a (opt)
76
+ def @redis.keys_to_a(opt)
75
77
  r = keys(opt)
76
78
  r.is_a?(Array) ? r : r.split(' ')
77
79
  end
@@ -79,132 +81,167 @@ module Redis
79
81
  put_configuration
80
82
  end
81
83
 
82
- def reserve (doc)
84
+ def reserve(doc)
83
85
 
84
86
  @redis.del(key_for(doc))
85
87
  end
86
88
 
87
- def put_msg (action, options)
89
+ def put_msg(action, options)
88
90
 
89
91
  doc = prepare_msg_doc(action, options)
90
92
 
93
+ puts "XXX" if @redis.nil?
94
+
91
95
  @redis.set(key_for(doc), to_json(doc))
92
96
 
93
97
  nil
94
98
  end
95
99
 
96
- def put_schedule (flavour, owner_fei, s, msg)
100
+ def put_schedule(flavour, owner_fei, s, msg)
97
101
 
98
- if doc = prepare_schedule_doc(flavour, owner_fei, s, msg)
99
- @redis.set(key_for(doc), to_json(doc))
100
- return doc['_id']
101
- end
102
+ doc = prepare_schedule_doc(flavour, owner_fei, s, msg)
102
103
 
103
- nil
104
+ return nil unless doc
105
+
106
+ @redis.set(key_for(doc), to_json(doc))
107
+
108
+ doc['_id']
104
109
  end
105
110
 
106
- def delete_schedule (schedule_id)
111
+ def delete_schedule(schedule_id)
107
112
 
108
113
  @redis.del(key_for('schedules', schedule_id))
109
114
  end
110
115
 
111
- def put (doc, opts={})
116
+ def put(doc, opts={})
112
117
 
113
- rev = doc['_rev'].to_i
114
118
  key = key_for(doc)
119
+ rev = doc['_rev']
120
+
121
+ lock(key) do
122
+
123
+ current_doc = do_get(key)
124
+ current_rev = current_doc ? current_doc['_rev'] : nil
125
+
126
+ if current_rev && rev != current_rev
127
+ #
128
+ # version in storage is newer than version being put,
129
+ # (eturn version in storage)
130
+ #
131
+ current_doc
132
+
133
+ elsif rev && current_rev.nil?
134
+ #
135
+ # document deleted, put fails (return true)
136
+ #
137
+ true
138
+
139
+ else
140
+ #
141
+ # put is successful (return nil)
142
+ #
143
+ nrev = (rev.to_i + 1).to_s
144
+ @redis.set(key, to_json(doc.merge('_rev' => nrev)))
145
+ doc['_rev'] = nrev if opts[:update_rev]
146
+
147
+ nil
148
+ end
149
+ end
150
+ end
115
151
 
116
- current_rev = @redis.get(key).to_i
152
+ def get(type, key)
117
153
 
118
- return true if current_rev == 0 && rev > 0
119
- return do_get(doc, current_rev) if rev != current_rev
154
+ do_get(key_for(type, key))
155
+ end
120
156
 
121
- nrev = rev + 1
157
+ def delete(doc)
122
158
 
123
- # the setnx here is crucial in multiple workers env...
159
+ rev = doc['_rev']
124
160
 
125
- r = @redis.setnx(
126
- key_rev_for(doc, nrev),
127
- to_json(doc.merge('_rev' => nrev), opts))
161
+ raise ArgumentError.new("can't delete doc without _rev") unless rev
128
162
 
129
- return get(doc['type'], doc['_id']) if r == false
163
+ key = key_for(doc)
130
164
 
131
- @redis.set(key, nrev)
132
- @redis.del(key_rev_for(doc, rev)) if rev > 0
165
+ lock(key) do
133
166
 
134
- doc['_rev'] = nrev if opts[:update_rev]
167
+ current_doc = do_get(key)
135
168
 
136
- nil
137
- end
169
+ if current_doc.nil?
170
+ #
171
+ # document is [already] gone, delete fails (return true)
172
+ #
173
+ true
138
174
 
139
- def get (type, key)
175
+ elsif current_doc['_rev'] != rev
176
+ #
177
+ # version in storage doesn't match version to delete
178
+ # (return version in storage)
179
+ #
180
+ current_doc
140
181
 
141
- do_get(type, key, @redis.get(key_for(type, key)))
142
- end
182
+ else
183
+ #
184
+ # delete is successful (return nil)
185
+ #
186
+ @redis.del(key)
143
187
 
144
- def delete (doc)
145
-
146
- raise ArgumentError.new(
147
- "can't delete doc without _rev") unless doc['_rev']
148
-
149
- r = put(doc, :delete => true)
188
+ nil
189
+ end
190
+ end
191
+ end
150
192
 
151
- return r if r != nil
193
+ def get_many(type, key=nil, opts={})
152
194
 
153
- @redis.keys_to_a("#{key_for(doc)}*").sort.each { |k|
154
- Thread.pass # lingering a bit...
155
- @redis.del(k)
156
- }
157
- # deleting the key_rev last and making 1 'keys' call preliminarily
195
+ keys = key ? Array(key) : nil
158
196
 
159
- nil
160
- end
197
+ #ids = if type == 'msgs' || type == 'schedules'
198
+ # @redis.keys_to_a("#{type}/*")
161
199
 
162
- def get_many (type, key=nil, opts={})
200
+ ids = if keys == nil
163
201
 
164
- keys = "#{type}/*"
202
+ @redis.keys_to_a("#{type}/*")
165
203
 
166
- ids = if type == 'msgs' || type == 'schedules'
204
+ elsif keys.first.is_a?(String)
167
205
 
168
- @redis.keys_to_a(keys)
206
+ keys.collect { |k| @redis.keys_to_a("#{type}/*!#{k}") }.flatten
169
207
 
170
- else
208
+ else #if keys.first.is_a?(Regexp)
171
209
 
172
- @redis.keys_to_a(keys).inject({}) { |h, k|
210
+ @redis.keys_to_a("#{type}/*").select { |i|
173
211
 
174
- if m = k.match(/^[^\/]+\/([^\/]+)\/(\d+)$/)
212
+ i = i[type.length + 1..-1]
213
+ # removing "^type/"
175
214
 
176
- if ( ! key) || m[1].match(key)
215
+ keys.find { |k| k.match(i) }
216
+ }
217
+ end
177
218
 
178
- o = h[m[1]]
179
- n = [ m[2].to_i, k ]
180
- h[m[1]] = [ m[2].to_i, k ] if ( ! o) || o.first < n.first
181
- end
182
- end
219
+ ids = ids.reject { |i| i.match(LOCK_KEY) }
220
+ ids = ids.sort
221
+ ids = ids.reverse if opts[:descending]
183
222
 
184
- h
185
- }.values.collect { |i| i[1] }
186
- end
223
+ skip = opts[:skip] || 0
224
+ limit = opts[:limit] || ids.length
225
+ ids = ids[skip, limit]
187
226
 
188
- if l = opts[:limit]
189
- ids = ids[0, l]
227
+ docs = ids.length > 0 ? @redis.mget(*ids) : []
228
+ docs = docs.inject({}) do |h, doc|
229
+ if doc
230
+ doc = Rufus::Json.decode(doc)
231
+ h[doc['_id']] = doc
232
+ end
233
+ h
190
234
  end
191
235
 
192
- ids.inject([]) do |a, i|
193
- v = @redis.get(i)
194
- a << Rufus::Json.decode(v) if v
195
- a
196
- end
236
+ opts[:count] ? docs.size : docs.values
197
237
  end
198
238
 
199
- def ids (type)
200
-
201
- @redis.keys_to_a("#{type}/*").inject([]) { |a, k|
202
-
203
- if m = k.match(/^[^\/]+\/([^\/]+)$/)
204
- a << m[1]
205
- end
239
+ def ids(type)
206
240
 
207
- a
241
+ @redis.keys_to_a("#{type}/*").reject { |i|
242
+ i.match(LOCK_KEY)
243
+ }.collect { |i|
244
+ i.split('/').last
208
245
  }.sort
209
246
  end
210
247
 
@@ -213,77 +250,100 @@ module Redis
213
250
  @redis.keys_to_a('*').each { |k| @redis.del(k) }
214
251
  end
215
252
 
216
- #def dump (type)
217
- # @dbs[type].dump
218
- #end
253
+ # Returns a String containing a representation of the current content of
254
+ # in this Redis storage.
255
+ #
256
+ def dump(type)
257
+
258
+ @redis.keys_to_a("#{type}/*").sort.join("\n")
259
+ end
260
+
261
+ def close
219
262
 
220
- def shutdown
263
+ @redis.quit
221
264
  end
222
265
 
223
266
  # Mainly used by ruote's test/unit/ut_17_storage.rb
224
267
  #
225
- def add_type (type)
268
+ def add_type(type)
226
269
  end
227
270
 
228
- # Nukes a db type and reputs it (losing all the documents that were in it).
271
+ # Nukes a db type and reputs it(losing all the documents that were in it).
229
272
  #
230
- def purge_type! (type)
273
+ def purge_type!(type)
231
274
 
232
275
  @redis.keys_to_a("#{type}/*").each { |k| @redis.del(k) }
233
276
  end
234
277
 
235
278
  protected
236
279
 
237
- # key_for(doc)
238
- # key_for(type, key)
280
+ LOCK_KEY = /-lock$/
281
+
282
+ # A locking mecha.
283
+ #
284
+ # Mostly inspired from http://code.google.com/p/redis/wiki/SetnxCommand
239
285
  #
240
- def key_for (*args)
286
+ def lock(key, &block)
241
287
 
242
- a = args.first
288
+ kl = "#{key}-lock"
243
289
 
244
- (a.is_a?(Hash) ? [ a['type'], a['_id'] ] : args[0, 2]).join('/')
245
- end
290
+ loop do
246
291
 
247
- # key_rev_for(doc)
248
- # key_rev_for(doc, rev)
249
- # key_rev_for(type, key, rev)
250
- #
251
- def key_rev_for (*args)
292
+ r = @redis.setnx(kl, Time.now.to_f.to_s)
252
293
 
253
- as = nil
254
- a = args.first
294
+ if r == false
295
+
296
+ t = @redis.get(kl)
297
+
298
+ @redis.del(kl) if t && Time.now.to_f - t.to_f > 60.0
299
+ # after 1 minute, locks time out
300
+
301
+ sleep 0.007 # let's try to lock again after a while
302
+ else
255
303
 
256
- if a.is_a?(Hash)
257
- as = [ a['type'], a['_id'], a['_rev'] ] if a.is_a?(Hash)
258
- as[2] = args[1] if args[1]
259
- else
260
- as = args[0, 3]
304
+ break # lock acquired
305
+ end
261
306
  end
262
307
 
263
- as.join('/')
308
+ #@redis.expire(kl, 2)
309
+ # this doesn't work, it makes the next call to setnx succeed
310
+
311
+ result = block.call
312
+
313
+ @redis.del(kl)
314
+
315
+ result
264
316
  end
265
317
 
266
- def do_get (*args)
318
+ # key_for(doc)
319
+ # key_for(type, key)
320
+ #
321
+ def key_for(*args)
322
+
323
+ a = args.first
324
+
325
+ (a.is_a?(Hash) ? [ a['type'], a['_id'] ] : args[0, 2]).join('/')
326
+ end
267
327
 
268
- d = @redis.get(key_rev_for(*args))
328
+ def do_get(key)
269
329
 
270
- d ? Rufus::Json.decode(d) : nil
330
+ from_json(@redis.get(key))
271
331
  end
272
332
 
273
- def to_json (doc, opts={})
333
+ def from_json(s)
274
334
 
275
- doc = if opts[:delete]
276
- nil
277
- else
278
- doc.merge('put_at' => Ruote.now_to_utc_s)
279
- end
335
+ s ? Rufus::Json.decode(s) : nil
336
+ end
337
+
338
+ def to_json(doc, opts={})
280
339
 
281
- Rufus::Json.encode(doc)
340
+ Rufus::Json.encode(
341
+ opts[:delete] ? nil : doc.merge('put_at' => Ruote.now_to_utc_s))
282
342
  end
283
343
 
284
344
  # Don't put configuration if it's already in
285
345
  #
286
- # (avoid storages from trashing configuration...)
346
+ # (prevent storages from trashing configuration...)
287
347
  #
288
348
  def put_configuration
289
349
 
@@ -1,7 +1,7 @@
1
1
 
2
2
  module Ruote
3
3
  module Redis
4
- VERSION = '2.1.10'
4
+ VERSION = '2.1.11'
5
5
  end
6
6
  end
7
7
 
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{ruote-redis}
8
- s.version = "2.1.10"
8
+ s.version = "2.1.11"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["John Mettraux"]
12
- s.date = %q{2010-06-15}
12
+ s.date = %q{2010-10-01}
13
13
  s.description = %q{Redis storage for ruote (a ruby workflow engine)}
14
14
  s.email = %q{jmettraux@gmail.com}
15
15
  s.extra_rdoc_files = [
@@ -18,6 +18,7 @@ Gem::Specification.new do |s|
18
18
  ]
19
19
  s.files = [
20
20
  "CHANGELOG.txt",
21
+ "CREDITS.txt",
21
22
  "LICENSE.txt",
22
23
  "README.rdoc",
23
24
  "Rakefile",
@@ -44,21 +45,21 @@ Gem::Specification.new do |s|
44
45
  s.specification_version = 3
45
46
 
46
47
  if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
47
- s.add_runtime_dependency(%q<ruote>, [">= 2.1.10"])
48
- s.add_runtime_dependency(%q<redis>, [">= 2.0.1"])
48
+ s.add_runtime_dependency(%q<ruote>, [">= 2.1.11"])
49
+ s.add_runtime_dependency(%q<redis>, [">= 2.0.5"])
49
50
  s.add_development_dependency(%q<yard>, [">= 0"])
50
51
  s.add_development_dependency(%q<rake>, [">= 0"])
51
52
  s.add_development_dependency(%q<jeweler>, [">= 0"])
52
53
  else
53
- s.add_dependency(%q<ruote>, [">= 2.1.10"])
54
- s.add_dependency(%q<redis>, [">= 2.0.1"])
54
+ s.add_dependency(%q<ruote>, [">= 2.1.11"])
55
+ s.add_dependency(%q<redis>, [">= 2.0.5"])
55
56
  s.add_dependency(%q<yard>, [">= 0"])
56
57
  s.add_dependency(%q<rake>, [">= 0"])
57
58
  s.add_dependency(%q<jeweler>, [">= 0"])
58
59
  end
59
60
  else
60
- s.add_dependency(%q<ruote>, [">= 2.1.10"])
61
- s.add_dependency(%q<redis>, [">= 2.0.1"])
61
+ s.add_dependency(%q<ruote>, [">= 2.1.11"])
62
+ s.add_dependency(%q<redis>, [">= 2.0.5"])
62
63
  s.add_dependency(%q<yard>, [">= 0"])
63
64
  s.add_dependency(%q<rake>, [">= 0"])
64
65
  s.add_dependency(%q<jeweler>, [">= 0"])
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 2
7
7
  - 1
8
- - 10
9
- version: 2.1.10
8
+ - 11
9
+ version: 2.1.11
10
10
  platform: ruby
11
11
  authors:
12
12
  - John Mettraux
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-06-15 00:00:00 +09:00
17
+ date: 2010-10-01 00:00:00 +09:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -27,8 +27,8 @@ dependencies:
27
27
  segments:
28
28
  - 2
29
29
  - 1
30
- - 10
31
- version: 2.1.10
30
+ - 11
31
+ version: 2.1.11
32
32
  type: :runtime
33
33
  version_requirements: *id001
34
34
  - !ruby/object:Gem::Dependency
@@ -41,8 +41,8 @@ dependencies:
41
41
  segments:
42
42
  - 2
43
43
  - 0
44
- - 1
45
- version: 2.0.1
44
+ - 5
45
+ version: 2.0.5
46
46
  type: :runtime
47
47
  version_requirements: *id002
48
48
  - !ruby/object:Gem::Dependency
@@ -92,6 +92,7 @@ extra_rdoc_files:
92
92
  - README.rdoc
93
93
  files:
94
94
  - CHANGELOG.txt
95
+ - CREDITS.txt
95
96
  - LICENSE.txt
96
97
  - README.rdoc
97
98
  - Rakefile