redis_object 1.4.9 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Gem Version](https://badge.fury.io/rb/redis_object.png)](https://rubygems.org/gems/redis_object)
|
5
5
|
[![Build Status](https://travis-ci.org/remotezygote/RedisObject.png?branch=master)](https://travis-ci.org/remotezygote/RedisObject)
|
6
|
-
[![Coverage Status](https://coveralls.io/repos/remotezygote/RedisObject/badge.png?branch=master)](https://coveralls.io/r/remotezygote/RedisObject?branch=master)
|
7
|
-
[![Code Climate](https://codeclimate.com/github/remotezygote/RedisObject.png)](https://codeclimate.com/github/remotezygote/RedisObject)
|
8
|
-
[![Dependency Status](https://gemnasium.com/remotezygote/RedisObject.png)](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:
|