ruote-redis 2.1.10 → 2.1.11

Sign up to get free protection for your applications and to get access to all the features.
@@ -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