redis_object 1.4.9 → 1.5.0
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/.travis.yml +3 -4
- data/Gemfile +1 -2
- data/README.markdown +0 -3
- data/lib/redis_object.rb +2 -0
- data/lib/redis_object/base.rb +0 -233
- data/lib/redis_object/collection.rb +3 -3
- data/lib/redis_object/indices.rb +64 -11
- data/lib/redis_object/matchers.rb +496 -0
- data/lib/redis_object/version.rb +1 -1
- data/spec/benchmark_spec.rb +0 -1
- data/spec/indices_spec.rb +43 -7
- data/spec/spec_helper.rb +1 -7
- data/spec/timestamp_spec.rb +0 -1
- data/spec/{view_caching_spec.rb → view_caching_spec_disabled.rb} +0 -0
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 24c6d1f461aa94f27fe6c79750dc3e1b1fcb8120
|
4
|
+
data.tar.gz: b6739ea7fde6bff76edbed5377882cfe196c46ff
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b4cada8b2e40e5391667c19ae39ce4f7e1131a0e59951bb6d0c665212b78781493d878cb034c60ced5751da3c4d6e7b5bce0c4b86bf7e73dc11f425e9208ba1c
|
7
|
+
data.tar.gz: 995c5f452bc853e98e59f5791b345830d0af7b38d8a09266a83ee9234b12afe53d739744a5c307358232277b523e1a31c6c89151bedbf98f1221b2e56eacb716
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/README.markdown
CHANGED
@@ -3,9 +3,6 @@ RedisObject is a fast and simple-to-use object persistence layer for Ruby.
|
|
3
3
|
|
4
4
|
[](https://rubygems.org/gems/redis_object)
|
5
5
|
[](https://travis-ci.org/remotezygote/RedisObject)
|
6
|
-
[](https://coveralls.io/r/remotezygote/RedisObject?branch=master)
|
7
|
-
[](https://codeclimate.com/github/remotezygote/RedisObject)
|
8
|
-
[](https://gemnasium.com/remotezygote/RedisObject)
|
9
6
|
|
10
7
|
## Prerequisites
|
11
8
|
You'll need [Redis](http://redis.io). Other storage adapters are in the works. Maybe.
|
data/lib/redis_object.rb
CHANGED
@@ -8,6 +8,7 @@ require "redis_object/ext/list_enumerator"
|
|
8
8
|
|
9
9
|
require "redis_object/ext/script_cache"
|
10
10
|
require "redis_object/base"
|
11
|
+
require "redis_object/matchers"
|
11
12
|
require "redis_object/inheritance_tracking"
|
12
13
|
require "redis_object/storage"
|
13
14
|
require "redis_object/keys"
|
@@ -28,6 +29,7 @@ module Seabright
|
|
28
29
|
|
29
30
|
include Seabright::Filters
|
30
31
|
include Seabright::ObjectBase
|
32
|
+
include Seabright::Matchers
|
31
33
|
include Seabright::InheritanceTracking
|
32
34
|
include Seabright::CachedScripts
|
33
35
|
include Seabright::Storage
|
data/lib/redis_object/base.rb
CHANGED
@@ -278,231 +278,6 @@ module Seabright
|
|
278
278
|
end
|
279
279
|
end
|
280
280
|
|
281
|
-
NilPattern = 'nilpattern:'
|
282
|
-
|
283
|
-
RedisObject::ScriptSources::Matcher = "local itms = redis.call('SMEMBERS',KEYS[1])
|
284
|
-
local out = {}
|
285
|
-
local val
|
286
|
-
local pattern
|
287
|
-
for i, v in ipairs(itms) do
|
288
|
-
val = redis.call('HGET',v..'_h',ARGV[1])
|
289
|
-
if val then
|
290
|
-
if ARGV[2]:find('^pattern:') then
|
291
|
-
pattern = ARGV[2]:gsub('^pattern:','')
|
292
|
-
if val:match(pattern) then
|
293
|
-
table.insert(out,itms[i])
|
294
|
-
end
|
295
|
-
elseif ARGV[2]:find('^ipattern:') then
|
296
|
-
pattern = ARGV[2]:gsub('^ipattern:',''):lower()
|
297
|
-
if val:lower():match(pattern) then
|
298
|
-
table.insert(out,itms[i])
|
299
|
-
end
|
300
|
-
else
|
301
|
-
if val == ARGV[2] then
|
302
|
-
table.insert(out,itms[i])
|
303
|
-
end
|
304
|
-
end
|
305
|
-
else
|
306
|
-
if ARGV[2] == '#{NilPattern}' then
|
307
|
-
table.insert(out,itms[i])
|
308
|
-
end
|
309
|
-
end
|
310
|
-
end
|
311
|
-
return out".gsub(/\t/,'').freeze
|
312
|
-
|
313
|
-
RedisObject::ScriptSources::MultiMatcher = "local itms = redis.call('SMEMBERS',KEYS[1])
|
314
|
-
local out = {}
|
315
|
-
local matchers = {}
|
316
|
-
local matcher = {}
|
317
|
-
local mod
|
318
|
-
for i=1,#ARGV do
|
319
|
-
mod = i % 2
|
320
|
-
if mod == 1 then
|
321
|
-
matcher[1] = ARGV[i]
|
322
|
-
else
|
323
|
-
matcher[2] = ARGV[i]
|
324
|
-
table.insert(matchers,matcher)
|
325
|
-
matcher = {}
|
326
|
-
end
|
327
|
-
end
|
328
|
-
local val
|
329
|
-
local good
|
330
|
-
local pattern
|
331
|
-
for i, v in ipairs(itms) do
|
332
|
-
good = true
|
333
|
-
for n=1,#matchers do
|
334
|
-
val = redis.call('HGET',v..'_h',matchers[n][1])
|
335
|
-
if val then
|
336
|
-
if matchers[n][2]:find('^pattern:') then
|
337
|
-
pattern = matchers[n][2]:gsub('^pattern:','')
|
338
|
-
if val:match(pattern) then
|
339
|
-
good = good
|
340
|
-
else
|
341
|
-
good = false
|
342
|
-
break
|
343
|
-
end
|
344
|
-
elseif matchers[n][2]:find('^ipattern:') then
|
345
|
-
pattern = matchers[n][2]:gsub('^ipattern:',''):lower()
|
346
|
-
if val:lower():match(pattern) then
|
347
|
-
good = good
|
348
|
-
else
|
349
|
-
good = false
|
350
|
-
break
|
351
|
-
end
|
352
|
-
else
|
353
|
-
if val ~= matchers[n][2] then
|
354
|
-
good = false
|
355
|
-
break
|
356
|
-
end
|
357
|
-
end
|
358
|
-
else
|
359
|
-
if matchers[n][2] == '#{NilPattern}' then
|
360
|
-
good = good
|
361
|
-
else
|
362
|
-
good = false
|
363
|
-
break
|
364
|
-
end
|
365
|
-
end
|
366
|
-
end
|
367
|
-
if good == true then
|
368
|
-
table.insert(out,itms[i])
|
369
|
-
end
|
370
|
-
end
|
371
|
-
return out".gsub(/\t/,'').freeze
|
372
|
-
|
373
|
-
RedisObject::ScriptSources::OrMatcher = "local itms = redis.call('SMEMBERS',KEYS[1])
|
374
|
-
local out = {}
|
375
|
-
local matchers = {}
|
376
|
-
local matcher = {}
|
377
|
-
local mod
|
378
|
-
for i=1,#ARGV do
|
379
|
-
mod = i % 2
|
380
|
-
if mod == 1 then
|
381
|
-
matcher[1] = ARGV[i]
|
382
|
-
else
|
383
|
-
matcher[2] = ARGV[i]
|
384
|
-
table.insert(matchers,matcher)
|
385
|
-
matcher = {}
|
386
|
-
end
|
387
|
-
end
|
388
|
-
|
389
|
-
local val
|
390
|
-
local good
|
391
|
-
local pattern
|
392
|
-
for i, v in ipairs(itms) do
|
393
|
-
good = false
|
394
|
-
for n=1,#matchers do
|
395
|
-
val = redis.call('HGET',v..'_h',matchers[n][1])
|
396
|
-
if val then
|
397
|
-
if matchers[n][2]:find('^pattern:') then
|
398
|
-
pattern = matchers[n][2]:gsub('^pattern:','')
|
399
|
-
if val:match(pattern) then
|
400
|
-
good = true
|
401
|
-
break
|
402
|
-
else
|
403
|
-
good = good
|
404
|
-
end
|
405
|
-
elseif matchers[n][2]:find('^ipattern:') then
|
406
|
-
pattern = matchers[n][2]:gsub('^ipattern:',''):lower()
|
407
|
-
if val:lower():match(pattern) then
|
408
|
-
good = true
|
409
|
-
break
|
410
|
-
else
|
411
|
-
good = good
|
412
|
-
end
|
413
|
-
else
|
414
|
-
if val == matchers[n][2] then
|
415
|
-
good = true
|
416
|
-
break
|
417
|
-
end
|
418
|
-
end
|
419
|
-
else
|
420
|
-
if matchers[n][2] == '#{NilPattern}' then
|
421
|
-
good = good
|
422
|
-
break
|
423
|
-
else
|
424
|
-
good = false
|
425
|
-
end
|
426
|
-
end
|
427
|
-
end
|
428
|
-
if good == true then
|
429
|
-
table.insert(out,itms[i])
|
430
|
-
end
|
431
|
-
end
|
432
|
-
return out".gsub(/\t/,'').freeze
|
433
|
-
|
434
|
-
def match(pkt, use_or=false)
|
435
|
-
if use_or
|
436
|
-
mtchr = :OrMatcher
|
437
|
-
else
|
438
|
-
mtchr = pkt.keys.count > 1 ? :MultiMatcher : :Matcher
|
439
|
-
end
|
440
|
-
pkt = pkt.flatten.reduce([]) do |i,v|
|
441
|
-
x = case v
|
442
|
-
when Regexp
|
443
|
-
convert_regex_to_lua(v)
|
444
|
-
when Array
|
445
|
-
raise ArgumentError.new("An array can only be used with the find_or method") unless use_or
|
446
|
-
inject_key(i.last, v)
|
447
|
-
when NilClass
|
448
|
-
NilPattern
|
449
|
-
else
|
450
|
-
v.to_s
|
451
|
-
end
|
452
|
-
i << x
|
453
|
-
i
|
454
|
-
end
|
455
|
-
kys = run_script(mtchr,[plname],pkt.flatten)
|
456
|
-
ListEnumerator.new(kys) do |y|
|
457
|
-
kys.each do |k|
|
458
|
-
y << find(k)
|
459
|
-
end
|
460
|
-
end
|
461
|
-
end
|
462
|
-
|
463
|
-
def inject_key(key,list)
|
464
|
-
out = []
|
465
|
-
list.each do |i|
|
466
|
-
if i == list.first
|
467
|
-
out << i
|
468
|
-
else
|
469
|
-
out << key
|
470
|
-
out << i
|
471
|
-
end
|
472
|
-
end
|
473
|
-
out
|
474
|
-
end
|
475
|
-
|
476
|
-
def convert_regex_to_lua(reg)
|
477
|
-
"#{reg.casefold? ? "i" : ""}pattern:#{reg.source.gsub("\\","")}"
|
478
|
-
end
|
479
|
-
|
480
|
-
def grab(ident)
|
481
|
-
case ident
|
482
|
-
when String, Symbol
|
483
|
-
return store.exists(self.hkey(ident.to_s)) ? self.new(ident.to_s) : nil
|
484
|
-
when Hash
|
485
|
-
return match(ident)
|
486
|
-
end
|
487
|
-
nil
|
488
|
-
end
|
489
|
-
|
490
|
-
def or_grab(ident)
|
491
|
-
case ident
|
492
|
-
when Hash
|
493
|
-
return match(ident, true)
|
494
|
-
end
|
495
|
-
nil
|
496
|
-
end
|
497
|
-
|
498
|
-
def find(ident)
|
499
|
-
grab(ident)
|
500
|
-
end
|
501
|
-
|
502
|
-
def or_find(ident)
|
503
|
-
or_grab(ident)
|
504
|
-
end
|
505
|
-
|
506
281
|
def exists?(k)
|
507
282
|
store.exists(self.hkey(k)) || store.exists(self.reserve_key(k))
|
508
283
|
end
|
@@ -513,14 +288,6 @@ module Seabright
|
|
513
288
|
obj
|
514
289
|
end
|
515
290
|
|
516
|
-
# def dump
|
517
|
-
# out = []
|
518
|
-
# each do |obj|
|
519
|
-
# out << obj.dump
|
520
|
-
# end
|
521
|
-
# out.join("\n")
|
522
|
-
# end
|
523
|
-
|
524
291
|
def use_dbnum(db=0)
|
525
292
|
@dbnum = db
|
526
293
|
end
|
@@ -242,7 +242,7 @@ module Seabright
|
|
242
242
|
RedisObject::ScriptSources::RevScript = "redis.call('ZINTERSTORE', KEYS[1], 2, KEYS[2], KEYS[3], 'WEIGHTS', 1, 0)\nlocal keys = redis.call('ZREVRANGE', KEYS[1], 0, KEYS[4])\nredis.call('DEL', KEYS[1])\nreturn keys".freeze
|
243
243
|
|
244
244
|
def keys_by_index(idx,num=-1,reverse=false)
|
245
|
-
keys = run_script(reverse ? :RevScript : :FwdScript, [temp_key,
|
245
|
+
keys = run_script(reverse ? :RevScript : :FwdScript, [temp_key, sort_index_key(idx), key, num])
|
246
246
|
ListEnumerator.new(keys) do |y|
|
247
247
|
keys.each do |member|
|
248
248
|
y << member
|
@@ -250,8 +250,8 @@ module Seabright
|
|
250
250
|
end
|
251
251
|
end
|
252
252
|
|
253
|
-
def
|
254
|
-
class_const.
|
253
|
+
def sort_index_key(idx)
|
254
|
+
class_const.sort_index_key(idx)
|
255
255
|
end
|
256
256
|
|
257
257
|
def item_key(k)
|
data/lib/redis_object/indices.rb
CHANGED
@@ -1,4 +1,15 @@
|
|
1
1
|
module Seabright
|
2
|
+
|
3
|
+
class RedisObject
|
4
|
+
|
5
|
+
def self.reindex_everything!
|
6
|
+
RedisObject.child_classes.each do |cls|
|
7
|
+
cls.reindex_all_indices!
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
12
|
+
|
2
13
|
module Indices
|
3
14
|
|
4
15
|
def index_key(idx)
|
@@ -13,13 +24,27 @@ module Seabright
|
|
13
24
|
|
14
25
|
def indexed_set_method(meth,k,v)
|
15
26
|
ret = send("unindexed_#{meth}".to_sym,k,v)
|
27
|
+
if self.class.has_index?(k)
|
28
|
+
set_index k, v, hkey
|
29
|
+
end
|
16
30
|
if self.class.has_sort_index?(k)
|
17
|
-
|
18
|
-
store.zadd(index_key(k), score_format(k,v), hkey)
|
31
|
+
set_sort_index k, v, hkey
|
19
32
|
end
|
20
33
|
ret
|
21
34
|
end
|
22
35
|
|
36
|
+
def set_index(k,v,hkey)
|
37
|
+
if cur = get(k)
|
38
|
+
store.srem(self.class.index_key(k,cur), hkey)
|
39
|
+
end
|
40
|
+
store.sadd(self.class.index_key(k,v), hkey)
|
41
|
+
end
|
42
|
+
|
43
|
+
def set_sort_index(k,v,hkey)
|
44
|
+
store.zrem(self.class.sort_index_key(k), hkey)
|
45
|
+
store.zadd(self.class.sort_index_key(k), score_format(k,v), hkey)
|
46
|
+
end
|
47
|
+
|
23
48
|
alias_method :unindexed_set, :set unless method_defined?(:unindexed_set)
|
24
49
|
def set(k,v)
|
25
50
|
indexed_set_method(:set,k,v)
|
@@ -32,12 +57,13 @@ module Seabright
|
|
32
57
|
|
33
58
|
alias_method :unindexed_mset, :mset unless method_defined?(:unindexed_mset)
|
34
59
|
def mset(dat)
|
35
|
-
|
60
|
+
dat.select {|k,v| self.class.has_index?(k) }.each do |k,v|
|
61
|
+
set_index k, v, hkey
|
62
|
+
end
|
36
63
|
dat.select {|k,v| self.class.has_sort_index?(k) }.each do |k,v|
|
37
|
-
|
38
|
-
store.zadd(index_key(k), score_format(k,v), hkey)
|
64
|
+
set_sort_index k, v, hkey
|
39
65
|
end
|
40
|
-
|
66
|
+
unindexed_mset(dat)
|
41
67
|
end
|
42
68
|
|
43
69
|
end
|
@@ -45,7 +71,7 @@ module Seabright
|
|
45
71
|
end
|
46
72
|
|
47
73
|
def indexed(idx,num=-1,reverse=false)
|
48
|
-
kys = store.send(reverse ? :zrevrange : :zrange,
|
74
|
+
kys = store.send(reverse ? :zrevrange : :zrange, sort_index_key(idx), 0, num-1)
|
49
75
|
out = ListEnumerator.new(kys) do |yielder|
|
50
76
|
kys.each do |member|
|
51
77
|
if a = self.find_by_key(member)
|
@@ -62,12 +88,25 @@ module Seabright
|
|
62
88
|
end
|
63
89
|
end
|
64
90
|
|
65
|
-
def index_key(
|
91
|
+
def index_key(k,v)
|
92
|
+
"#{self.plname}::field_index::#{k}::#{v}"
|
93
|
+
end
|
94
|
+
|
95
|
+
def sort_index_key(idx)
|
66
96
|
"#{self.plname}::#{idx}"
|
67
97
|
end
|
68
98
|
|
99
|
+
def indices
|
100
|
+
@@indices ||= Set.new
|
101
|
+
end
|
102
|
+
|
69
103
|
def sort_indices
|
70
|
-
@@sort_indices ||=
|
104
|
+
@@sort_indices ||= Set.new
|
105
|
+
end
|
106
|
+
|
107
|
+
def index(k)
|
108
|
+
indices << k.to_sym
|
109
|
+
intercept_sets_for_indices!
|
71
110
|
end
|
72
111
|
|
73
112
|
def sort_by(k)
|
@@ -76,12 +115,26 @@ module Seabright
|
|
76
115
|
end
|
77
116
|
|
78
117
|
def reindex(k)
|
79
|
-
store.
|
118
|
+
store.keys(index_key(k,"*")).each do |ik|
|
119
|
+
store.del ik
|
120
|
+
end
|
80
121
|
all.each do |obj|
|
81
|
-
obj.
|
122
|
+
if v = obj.get(k)
|
123
|
+
obj.set_index(k, v, obj.hkey)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def reindex_all_indices!
|
129
|
+
indices.each do |k|
|
130
|
+
reindex(k)
|
82
131
|
end
|
83
132
|
end
|
84
133
|
|
134
|
+
def has_index?(k)
|
135
|
+
k and indices.include?(k.to_sym)
|
136
|
+
end
|
137
|
+
|
85
138
|
def has_sort_index?(k)
|
86
139
|
k and sort_indices.include?(k.to_sym)
|
87
140
|
end
|
@@ -0,0 +1,496 @@
|
|
1
|
+
module Seabright
|
2
|
+
module Matchers
|
3
|
+
|
4
|
+
module ClassMethods
|
5
|
+
|
6
|
+
NilPattern = 'nilpattern:'
|
7
|
+
|
8
|
+
GetKeyList = "local itms
|
9
|
+
if KEYS[2] then
|
10
|
+
itms = {}
|
11
|
+
for n=2,#KEYS do
|
12
|
+
local kys = redis.call('SMEMBERS',KEYS[n])
|
13
|
+
for k=1,#kys do
|
14
|
+
local rk
|
15
|
+
if kys[k]:match('.*_h') then
|
16
|
+
rk = kys[k]
|
17
|
+
else
|
18
|
+
rk = kys[k]..'_h'
|
19
|
+
end
|
20
|
+
table.insert(itms, rk)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
else
|
24
|
+
itms = {}
|
25
|
+
local kys = redis.call('SMEMBERS',KEYS[1])
|
26
|
+
for k=1,#kys do
|
27
|
+
local rk
|
28
|
+
if kys[k]:match('.*_h') then
|
29
|
+
rk = kys[k]
|
30
|
+
else
|
31
|
+
rk = kys[k]..'_h'
|
32
|
+
end
|
33
|
+
table.insert(itms, rk)
|
34
|
+
end
|
35
|
+
end".gsub(/\t/,'').freeze
|
36
|
+
|
37
|
+
RedisObject::ScriptSources::KeyListFor = GetKeyList + "
|
38
|
+
return itms
|
39
|
+
".gsub(/\t/,'').freeze
|
40
|
+
|
41
|
+
RedisObject::ScriptSources::Matcher = GetKeyList + "
|
42
|
+
local out = {}
|
43
|
+
local val
|
44
|
+
local pattern
|
45
|
+
for i, v in ipairs(itms) do
|
46
|
+
val = redis.call('HGET',v,ARGV[1])
|
47
|
+
if val then
|
48
|
+
if ARGV[2]:find('^pattern:') then
|
49
|
+
pattern = ARGV[2]:gsub('^pattern:','')
|
50
|
+
if val:match(pattern) then
|
51
|
+
table.insert(out,itms[i])
|
52
|
+
end
|
53
|
+
elseif ARGV[2]:find('^ipattern:') then
|
54
|
+
pattern = ARGV[2]:gsub('^ipattern:',''):lower()
|
55
|
+
if val:lower():match(pattern) then
|
56
|
+
table.insert(out,itms[i])
|
57
|
+
end
|
58
|
+
else
|
59
|
+
if val == ARGV[2] then
|
60
|
+
table.insert(out,itms[i])
|
61
|
+
end
|
62
|
+
end
|
63
|
+
else
|
64
|
+
if ARGV[2] == '#{NilPattern}' then
|
65
|
+
table.insert(out,itms[i])
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
return out".gsub(/\t/,'').freeze
|
70
|
+
|
71
|
+
RedisObject::ScriptSources::MultiMatcher = GetKeyList + "
|
72
|
+
local out = {}
|
73
|
+
local matchers = {}
|
74
|
+
local matcher = {}
|
75
|
+
local mod
|
76
|
+
for i=1,#ARGV do
|
77
|
+
mod = i % 2
|
78
|
+
if mod == 1 then
|
79
|
+
matcher[1] = ARGV[i]
|
80
|
+
else
|
81
|
+
matcher[2] = ARGV[i]
|
82
|
+
table.insert(matchers,matcher)
|
83
|
+
matcher = {}
|
84
|
+
end
|
85
|
+
end
|
86
|
+
local val
|
87
|
+
local good
|
88
|
+
local pattern
|
89
|
+
for i, v in ipairs(itms) do
|
90
|
+
good = true
|
91
|
+
for n=1,#matchers do
|
92
|
+
val = redis.call('HGET',v,matchers[n][1])
|
93
|
+
if val then
|
94
|
+
if matchers[n][2]:find('^pattern:') then
|
95
|
+
pattern = matchers[n][2]:gsub('^pattern:','')
|
96
|
+
if val:match(pattern) then
|
97
|
+
good = good
|
98
|
+
else
|
99
|
+
good = false
|
100
|
+
break
|
101
|
+
end
|
102
|
+
elseif matchers[n][2]:find('^ipattern:') then
|
103
|
+
pattern = matchers[n][2]:gsub('^ipattern:',''):lower()
|
104
|
+
if val:lower():match(pattern) then
|
105
|
+
good = good
|
106
|
+
else
|
107
|
+
good = false
|
108
|
+
break
|
109
|
+
end
|
110
|
+
else
|
111
|
+
if val ~= matchers[n][2] then
|
112
|
+
good = false
|
113
|
+
break
|
114
|
+
end
|
115
|
+
end
|
116
|
+
else
|
117
|
+
if matchers[n][2] == '#{NilPattern}' then
|
118
|
+
good = good
|
119
|
+
else
|
120
|
+
good = false
|
121
|
+
break
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
if good == true then
|
126
|
+
table.insert(out,itms[i])
|
127
|
+
end
|
128
|
+
end
|
129
|
+
return out".gsub(/\t/,'').freeze
|
130
|
+
|
131
|
+
RedisObject::ScriptSources::OrMatcher = GetKeyList + "
|
132
|
+
local out = {}
|
133
|
+
local matchers = {}
|
134
|
+
local matcher = {}
|
135
|
+
local mod
|
136
|
+
for i=1,#ARGV do
|
137
|
+
mod = i % 2
|
138
|
+
if mod == 1 then
|
139
|
+
matcher[1] = ARGV[i]
|
140
|
+
else
|
141
|
+
matcher[2] = ARGV[i]
|
142
|
+
table.insert(matchers,matcher)
|
143
|
+
matcher = {}
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
local val
|
148
|
+
local good
|
149
|
+
local pattern
|
150
|
+
for i, v in ipairs(itms) do
|
151
|
+
good = false
|
152
|
+
for n=1,#matchers do
|
153
|
+
val = redis.call('HGET',v,matchers[n][1])
|
154
|
+
if val then
|
155
|
+
if matchers[n][2]:find('^pattern:') then
|
156
|
+
pattern = matchers[n][2]:gsub('^pattern:','')
|
157
|
+
if val:match(pattern) then
|
158
|
+
good = true
|
159
|
+
break
|
160
|
+
else
|
161
|
+
good = good
|
162
|
+
end
|
163
|
+
elseif matchers[n][2]:find('^ipattern:') then
|
164
|
+
pattern = matchers[n][2]:gsub('^ipattern:',''):lower()
|
165
|
+
if val:lower():match(pattern) then
|
166
|
+
good = true
|
167
|
+
break
|
168
|
+
else
|
169
|
+
good = good
|
170
|
+
end
|
171
|
+
else
|
172
|
+
if val == matchers[n][2] then
|
173
|
+
good = true
|
174
|
+
break
|
175
|
+
end
|
176
|
+
end
|
177
|
+
else
|
178
|
+
if matchers[n][2] == '#{NilPattern}' then
|
179
|
+
good = good
|
180
|
+
break
|
181
|
+
else
|
182
|
+
good = false
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
if good == true then
|
187
|
+
table.insert(out,itms[i])
|
188
|
+
end
|
189
|
+
end
|
190
|
+
return out".gsub(/\t/,'').freeze
|
191
|
+
|
192
|
+
def match(pkt, use_or=false)
|
193
|
+
if use_or
|
194
|
+
mtchr = :OrMatcher
|
195
|
+
else
|
196
|
+
mtchr = pkt.keys.count > 1 ? :MultiMatcher : :Matcher
|
197
|
+
end
|
198
|
+
if (ids = pkt[id_sym] || pkt[id_sym.to_s]) and !ids.is_a?(Regexp)
|
199
|
+
return match_by_id(ids, pkt, use_or)
|
200
|
+
end
|
201
|
+
indcs = [plname] + extract_usable_indices(pkt)
|
202
|
+
pkt = pkt.flatten.reduce([]) do |i,v|
|
203
|
+
x = case v
|
204
|
+
when Regexp
|
205
|
+
convert_regex_to_lua(v)
|
206
|
+
when Array
|
207
|
+
raise ArgumentError.new("An array can only be used with the find_or method") unless use_or
|
208
|
+
inject_key(i.last, v)
|
209
|
+
when NilClass
|
210
|
+
NilPattern
|
211
|
+
else
|
212
|
+
v.to_s
|
213
|
+
end
|
214
|
+
i << x
|
215
|
+
i
|
216
|
+
end
|
217
|
+
kys = run_script(mtchr, indcs, pkt.flatten)
|
218
|
+
ListEnumerator.new(kys) do |y|
|
219
|
+
kys.each do |k|
|
220
|
+
y << find(k)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
RedisObject::ScriptSources::FirstMatcher = GetKeyList + "
|
226
|
+
local val
|
227
|
+
local pattern
|
228
|
+
for i, v in ipairs(itms) do
|
229
|
+
val = redis.call('HGET',v,ARGV[1])
|
230
|
+
if val then
|
231
|
+
if ARGV[2]:find('^pattern:') then
|
232
|
+
pattern = ARGV[2]:gsub('^pattern:','')
|
233
|
+
if val:match(pattern) then
|
234
|
+
return itms[i]
|
235
|
+
end
|
236
|
+
elseif ARGV[2]:find('^ipattern:') then
|
237
|
+
pattern = ARGV[2]:gsub('^ipattern:',''):lower()
|
238
|
+
if val:lower():match(pattern) then
|
239
|
+
return itms[i]
|
240
|
+
end
|
241
|
+
else
|
242
|
+
if val == ARGV[2] then
|
243
|
+
return itms[i]
|
244
|
+
end
|
245
|
+
end
|
246
|
+
else
|
247
|
+
if ARGV[2] == '#{NilPattern}' then
|
248
|
+
return itms[i]
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
return ''".gsub(/\t/,'').freeze
|
253
|
+
|
254
|
+
RedisObject::ScriptSources::FirstMultiMatcher = GetKeyList + "
|
255
|
+
local matchers = {}
|
256
|
+
local matcher = {}
|
257
|
+
local mod
|
258
|
+
for i=1,#ARGV do
|
259
|
+
mod = i % 2
|
260
|
+
if mod == 1 then
|
261
|
+
matcher[1] = ARGV[i]
|
262
|
+
else
|
263
|
+
matcher[2] = ARGV[i]
|
264
|
+
table.insert(matchers,matcher)
|
265
|
+
matcher = {}
|
266
|
+
end
|
267
|
+
end
|
268
|
+
local val
|
269
|
+
local good
|
270
|
+
local pattern
|
271
|
+
for i, v in ipairs(itms) do
|
272
|
+
good = true
|
273
|
+
for n=1,#matchers do
|
274
|
+
val = redis.call('HGET',v,matchers[n][1])
|
275
|
+
if val then
|
276
|
+
if matchers[n][2]:find('^pattern:') then
|
277
|
+
pattern = matchers[n][2]:gsub('^pattern:','')
|
278
|
+
if val:match(pattern) then
|
279
|
+
good = good
|
280
|
+
else
|
281
|
+
good = false
|
282
|
+
break
|
283
|
+
end
|
284
|
+
elseif matchers[n][2]:find('^ipattern:') then
|
285
|
+
pattern = matchers[n][2]:gsub('^ipattern:',''):lower()
|
286
|
+
if val:lower():match(pattern) then
|
287
|
+
good = good
|
288
|
+
else
|
289
|
+
good = false
|
290
|
+
break
|
291
|
+
end
|
292
|
+
else
|
293
|
+
if val ~= matchers[n][2] then
|
294
|
+
good = false
|
295
|
+
break
|
296
|
+
end
|
297
|
+
end
|
298
|
+
else
|
299
|
+
if matchers[n][2] == '#{NilPattern}' then
|
300
|
+
good = good
|
301
|
+
else
|
302
|
+
good = false
|
303
|
+
break
|
304
|
+
end
|
305
|
+
end
|
306
|
+
end
|
307
|
+
if good == true then
|
308
|
+
return itms[i]
|
309
|
+
end
|
310
|
+
end
|
311
|
+
return ''".gsub(/\t/,'').freeze
|
312
|
+
|
313
|
+
RedisObject::ScriptSources::FirstOrMatcher = GetKeyList + "
|
314
|
+
local matchers = {}
|
315
|
+
local matcher = {}
|
316
|
+
local mod
|
317
|
+
for i=1,#ARGV do
|
318
|
+
mod = i % 2
|
319
|
+
if mod == 1 then
|
320
|
+
matcher[1] = ARGV[i]
|
321
|
+
else
|
322
|
+
matcher[2] = ARGV[i]
|
323
|
+
table.insert(matchers,matcher)
|
324
|
+
matcher = {}
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
local val
|
329
|
+
local good
|
330
|
+
local pattern
|
331
|
+
for i, v in ipairs(itms) do
|
332
|
+
good = false
|
333
|
+
for n=1,#matchers do
|
334
|
+
val = redis.call('HGET',v,matchers[n][1])
|
335
|
+
if val then
|
336
|
+
if matchers[n][2]:find('^pattern:') then
|
337
|
+
pattern = matchers[n][2]:gsub('^pattern:','')
|
338
|
+
if val:match(pattern) then
|
339
|
+
good = true
|
340
|
+
break
|
341
|
+
else
|
342
|
+
good = good
|
343
|
+
end
|
344
|
+
elseif matchers[n][2]:find('^ipattern:') then
|
345
|
+
pattern = matchers[n][2]:gsub('^ipattern:',''):lower()
|
346
|
+
if val:lower():match(pattern) then
|
347
|
+
good = true
|
348
|
+
break
|
349
|
+
else
|
350
|
+
good = good
|
351
|
+
end
|
352
|
+
else
|
353
|
+
if val == matchers[n][2] then
|
354
|
+
good = true
|
355
|
+
break
|
356
|
+
end
|
357
|
+
end
|
358
|
+
else
|
359
|
+
if matchers[n][2] == '#{NilPattern}' then
|
360
|
+
good = good
|
361
|
+
break
|
362
|
+
else
|
363
|
+
good = false
|
364
|
+
end
|
365
|
+
end
|
366
|
+
end
|
367
|
+
if good == true then
|
368
|
+
return itms[i]
|
369
|
+
end
|
370
|
+
end
|
371
|
+
return ''".gsub(/\t/,'').freeze
|
372
|
+
|
373
|
+
def extract_usable_indices(pkt)
|
374
|
+
pkt.inject([]) do |acc,(k,v)|
|
375
|
+
if self.has_index?(k)
|
376
|
+
acc << index_key(k,v)
|
377
|
+
end
|
378
|
+
acc
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
def match_by_id(ids, pkt, use_or = false)
|
383
|
+
case ids
|
384
|
+
when Array
|
385
|
+
raise ArgumentError.new("An array can only be used with the or_find_first method") unless use_or
|
386
|
+
ids.map do |i|
|
387
|
+
match_by_id(i, pkt, use_or)
|
388
|
+
end.flatten
|
389
|
+
when String, Symbol
|
390
|
+
if obj = find(ids)
|
391
|
+
pkt.each do |k,v|
|
392
|
+
case v
|
393
|
+
when Regexp
|
394
|
+
return nil unless v.match(obj.get(k))
|
395
|
+
when Array
|
396
|
+
raise ArgumentError.new("An array can only be used with the or_find_first method") unless use_or
|
397
|
+
return nil unless v.any? { |val| obj.get(k) == v }
|
398
|
+
when String, Symbol
|
399
|
+
return nil unless obj.get(k) == v
|
400
|
+
when NilClass
|
401
|
+
return nil unless obj.get(k) == nil
|
402
|
+
end
|
403
|
+
end
|
404
|
+
end
|
405
|
+
[obj]
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
def match_first(pkt, use_or=false)
|
410
|
+
if use_or
|
411
|
+
mtchr = :FirstOrMatcher
|
412
|
+
else
|
413
|
+
mtchr = pkt.keys.count > 1 ? :FirstMultiMatcher : :FirstMatcher
|
414
|
+
end
|
415
|
+
if (ids = pkt[id_sym] || pkt[id_sym.to_s]) and !ids.is_a?(Regexp)
|
416
|
+
return match_by_id(ids, pkt, use_or).first
|
417
|
+
end
|
418
|
+
indcs = [plname] + extract_usable_indices(pkt)
|
419
|
+
pkt = pkt.flatten.reduce([]) do |i,v|
|
420
|
+
x = case v
|
421
|
+
when Regexp
|
422
|
+
convert_regex_to_lua(v)
|
423
|
+
when Array
|
424
|
+
raise ArgumentError.new("An array can only be used with the or_find_first method") unless use_or
|
425
|
+
inject_key(i.last, v)
|
426
|
+
when NilClass
|
427
|
+
NilPattern
|
428
|
+
else
|
429
|
+
v.to_s
|
430
|
+
end
|
431
|
+
i << x
|
432
|
+
i
|
433
|
+
end
|
434
|
+
find_by_key(run_script(mtchr,indcs, pkt.flatten))
|
435
|
+
end
|
436
|
+
|
437
|
+
def inject_key(key,list)
|
438
|
+
out = []
|
439
|
+
list.each do |i|
|
440
|
+
if i == list.first
|
441
|
+
out << i
|
442
|
+
else
|
443
|
+
out << key
|
444
|
+
out << i
|
445
|
+
end
|
446
|
+
end
|
447
|
+
out
|
448
|
+
end
|
449
|
+
|
450
|
+
def convert_regex_to_lua(reg)
|
451
|
+
"#{reg.casefold? ? "i" : ""}pattern:#{reg.source.gsub("\\","")}"
|
452
|
+
end
|
453
|
+
|
454
|
+
def grab(ident)
|
455
|
+
case ident
|
456
|
+
when String, Symbol
|
457
|
+
return store.exists(self.hkey(ident.to_s)) ? self.new(ident.to_s) : nil
|
458
|
+
when Hash
|
459
|
+
return match(ident)
|
460
|
+
end
|
461
|
+
nil
|
462
|
+
end
|
463
|
+
|
464
|
+
def or_grab(ident)
|
465
|
+
case ident
|
466
|
+
when Hash
|
467
|
+
return match(ident, true)
|
468
|
+
end
|
469
|
+
nil
|
470
|
+
end
|
471
|
+
|
472
|
+
def find(ident)
|
473
|
+
grab(ident)
|
474
|
+
end
|
475
|
+
|
476
|
+
def find_first(ident)
|
477
|
+
match_first(ident)
|
478
|
+
end
|
479
|
+
|
480
|
+
def or_find(ident)
|
481
|
+
or_grab(ident)
|
482
|
+
end
|
483
|
+
|
484
|
+
def or_find_first(ident)
|
485
|
+
match_first(ident)
|
486
|
+
end
|
487
|
+
|
488
|
+
end
|
489
|
+
|
490
|
+
def self.included(base)
|
491
|
+
base.extend(ClassMethods)
|
492
|
+
end
|
493
|
+
|
494
|
+
end
|
495
|
+
end
|
496
|
+
|
data/lib/redis_object/version.rb
CHANGED
data/spec/benchmark_spec.rb
CHANGED
data/spec/indices_spec.rb
CHANGED
@@ -15,8 +15,7 @@ module IndexSpec
|
|
15
15
|
|
16
16
|
TestData = TestValues.inject({}){|acc,(k,v)| acc["a_#{k}".to_sym] = v; acc }
|
17
17
|
|
18
|
-
|
19
|
-
class IndexedObject < RedisObject
|
18
|
+
class SortedObject < RedisObject
|
20
19
|
|
21
20
|
TestValues.keys.each do |type|
|
22
21
|
send(type.to_sym,"a_#{type}".to_sym)
|
@@ -27,23 +26,60 @@ module IndexSpec
|
|
27
26
|
|
28
27
|
end
|
29
28
|
|
29
|
+
class IndexedObject < RedisObject
|
30
|
+
|
31
|
+
index :some_text
|
32
|
+
|
33
|
+
end
|
34
|
+
|
30
35
|
describe Seabright::Indices do
|
36
|
+
|
31
37
|
before do
|
32
38
|
RedisObject.store.flushdb
|
33
39
|
end
|
34
40
|
|
35
|
-
it "
|
41
|
+
it "sorts on integer field" do
|
36
42
|
|
37
43
|
5.times do
|
38
|
-
obj =
|
44
|
+
obj = SortedObject.create(a_number: Random.rand(100), a_bool: true)
|
39
45
|
end
|
40
46
|
|
41
|
-
|
42
|
-
|
43
|
-
o.should be_a(
|
47
|
+
SortedObject.indexed(:a_number,3,true).count.should eq(3)
|
48
|
+
SortedObject.indexed(:a_number,3,true) do |o|
|
49
|
+
o.should be_a(SortedObject)
|
44
50
|
end
|
45
51
|
|
46
52
|
end
|
47
53
|
|
54
|
+
it "indexes on string field" do
|
55
|
+
|
56
|
+
cnt = 0
|
57
|
+
5.times do
|
58
|
+
obj = IndexedObject.create(some_text: "a" + cnt.to_s)
|
59
|
+
cnt += 1
|
60
|
+
end
|
61
|
+
|
62
|
+
IndexedObject.find_first(some_text: "a0").should be_a(IndexedObject)
|
63
|
+
IndexedObject.find_first(some_text: "a4").should be_a(IndexedObject)
|
64
|
+
IndexedObject.find_first(some_text: "a5").should eq(nil)
|
65
|
+
|
66
|
+
IndexedObject.reindex_all_indices!
|
67
|
+
|
68
|
+
IndexedObject.find_first(some_text: "a0").should be_a(IndexedObject)
|
69
|
+
IndexedObject.find_first(some_text: "a4").should be_a(IndexedObject)
|
70
|
+
IndexedObject.find_first(some_text: "a5").should eq(nil)
|
71
|
+
|
72
|
+
RedisObject.reindex_everything!
|
73
|
+
|
74
|
+
IndexedObject.find_first(some_text: "a0").should be_a(IndexedObject)
|
75
|
+
IndexedObject.find_first(some_text: "a4").should be_a(IndexedObject)
|
76
|
+
IndexedObject.find_first(some_text: "a5").should eq(nil)
|
77
|
+
|
78
|
+
# IndexedObject.indexed(:a_number,3,true) do |o|
|
79
|
+
# o.should be_a(IndexedObject)
|
80
|
+
# end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
48
84
|
end
|
49
85
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -6,15 +6,9 @@ require 'test/unit'
|
|
6
6
|
require 'rspec'
|
7
7
|
|
8
8
|
require 'simplecov'
|
9
|
-
require 'coveralls'
|
10
9
|
|
11
|
-
|
12
|
-
CodeClimate::TestReporter.start
|
10
|
+
SimpleCov.formatter = SimpleCov::Formatter::HTMLFormatter
|
13
11
|
|
14
|
-
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
|
15
|
-
SimpleCov::Formatter::HTMLFormatter,
|
16
|
-
Coveralls::SimpleCov::Formatter
|
17
|
-
]
|
18
12
|
SimpleCov.start do
|
19
13
|
add_filter "_spec.rb"
|
20
14
|
# add_filter "/experimental/"
|
data/spec/timestamp_spec.rb
CHANGED
File without changes
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redis_object
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Bragg
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-11-
|
11
|
+
date: 2013-11-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: utf8_utils
|
@@ -116,6 +116,7 @@ files:
|
|
116
116
|
- lib/redis_object/inheritance_tracking.rb
|
117
117
|
- lib/redis_object/keys.rb
|
118
118
|
- lib/redis_object/logger.rb
|
119
|
+
- lib/redis_object/matchers.rb
|
119
120
|
- lib/redis_object/storage.rb
|
120
121
|
- lib/redis_object/storage/adapter.rb
|
121
122
|
- lib/redis_object/storage/aws.rb
|
@@ -149,7 +150,7 @@ files:
|
|
149
150
|
- spec/timestamp_spec.rb
|
150
151
|
- spec/trigger_spec.rb
|
151
152
|
- spec/types_spec.rb
|
152
|
-
- spec/
|
153
|
+
- spec/view_caching_spec_disabled.rb
|
153
154
|
- spec/views_spec.rb
|
154
155
|
homepage: ''
|
155
156
|
licenses: []
|
@@ -192,6 +193,6 @@ test_files:
|
|
192
193
|
- spec/timestamp_spec.rb
|
193
194
|
- spec/trigger_spec.rb
|
194
195
|
- spec/types_spec.rb
|
195
|
-
- spec/
|
196
|
+
- spec/view_caching_spec_disabled.rb
|
196
197
|
- spec/views_spec.rb
|
197
198
|
has_rdoc:
|