ohm 1.0.0.rc3 → 1.0.0.rc4
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.
- data/lib/ohm.rb +67 -23
- data/test/filtering.rb +69 -0
- metadata +31 -11
data/lib/ohm.rb
CHANGED
@@ -423,10 +423,9 @@ module Ohm
|
|
423
423
|
# set.find(:age => 30)
|
424
424
|
#
|
425
425
|
def find(dict)
|
426
|
-
|
427
|
-
keys.push(key)
|
426
|
+
filters = model.filters(dict).push(key)
|
428
427
|
|
429
|
-
MultiSet.new(
|
428
|
+
MultiSet.new(namespace, model).append(:sinterstore, filters)
|
430
429
|
end
|
431
430
|
|
432
431
|
# Reduce the set using any number of filters.
|
@@ -440,7 +439,7 @@ module Ohm
|
|
440
439
|
# User.find(:name => "John").except(:country => "US")
|
441
440
|
#
|
442
441
|
def except(dict)
|
443
|
-
MultiSet.new(
|
442
|
+
MultiSet.new(namespace, model).append(:sinterstore, key).except(dict)
|
444
443
|
end
|
445
444
|
|
446
445
|
# Do a union to the existing set using any number of filters.
|
@@ -454,7 +453,7 @@ module Ohm
|
|
454
453
|
# User.find(:name => "John").union(:name => "Jane")
|
455
454
|
#
|
456
455
|
def union(dict)
|
457
|
-
MultiSet.new(
|
456
|
+
MultiSet.new(namespace, model).append(:sinterstore, key).union(dict)
|
458
457
|
end
|
459
458
|
|
460
459
|
private
|
@@ -516,7 +515,6 @@ module Ohm
|
|
516
515
|
end
|
517
516
|
end
|
518
517
|
|
519
|
-
|
520
518
|
# Anytime you filter a set with more than one requirement, you
|
521
519
|
# internally use a `MultiSet`. `MutiSet` is a bit slower than just
|
522
520
|
# a `Set` because it has to `SINTERSTORE` all the keys prior to
|
@@ -533,9 +531,15 @@ module Ohm
|
|
533
531
|
# User.find(:name => "John", :age => 30).kind_of?(Ohm::MultiSet)
|
534
532
|
# # => true
|
535
533
|
#
|
536
|
-
class MultiSet < Struct.new(:
|
534
|
+
class MultiSet < Struct.new(:namespace, :model)
|
537
535
|
include Collection
|
538
536
|
|
537
|
+
def append(operation, list)
|
538
|
+
filters.push([operation, list])
|
539
|
+
|
540
|
+
return self
|
541
|
+
end
|
542
|
+
|
539
543
|
# Chain new fiters on an existing set.
|
540
544
|
#
|
541
545
|
# Example:
|
@@ -544,10 +548,9 @@ module Ohm
|
|
544
548
|
# set.find(:status => 'pending')
|
545
549
|
#
|
546
550
|
def find(dict)
|
547
|
-
|
548
|
-
keys.push(*self.keys)
|
551
|
+
filters.push([:sinterstore, model.filters(dict)])
|
549
552
|
|
550
|
-
|
553
|
+
return self
|
551
554
|
end
|
552
555
|
|
553
556
|
# Reduce the set using any number of filters.
|
@@ -561,7 +564,7 @@ module Ohm
|
|
561
564
|
# User.find(:name => "John").except(:country => "US")
|
562
565
|
#
|
563
566
|
def except(dict)
|
564
|
-
|
567
|
+
filters.push([:sdiffstore, model.filters(dict)])
|
565
568
|
|
566
569
|
return self
|
567
570
|
end
|
@@ -577,30 +580,71 @@ module Ohm
|
|
577
580
|
# User.find(:name => "John").union(:name => "Jane")
|
578
581
|
#
|
579
582
|
def union(dict)
|
580
|
-
|
583
|
+
filters.push([:sunionstore, model.filters(dict)])
|
581
584
|
|
582
585
|
return self
|
583
586
|
end
|
584
587
|
|
585
588
|
private
|
586
|
-
def
|
587
|
-
@
|
589
|
+
def filters
|
590
|
+
@filters ||= []
|
588
591
|
end
|
589
592
|
|
590
|
-
def
|
591
|
-
@
|
593
|
+
def temp_keys
|
594
|
+
@temp_keys ||= []
|
592
595
|
end
|
593
596
|
|
594
|
-
def
|
597
|
+
def clean_temp_keys
|
598
|
+
model.db.del(*temp_keys)
|
599
|
+
temp_keys.clear
|
600
|
+
end
|
601
|
+
|
602
|
+
def generate_temp_key
|
595
603
|
key = namespace[:temp][SecureRandom.hex(32)]
|
596
|
-
key
|
597
|
-
key
|
598
|
-
|
604
|
+
temp_keys << key
|
605
|
+
key
|
606
|
+
end
|
607
|
+
|
608
|
+
def execute
|
609
|
+
|
610
|
+
# Hold the final result key for this MultiSet.
|
611
|
+
main = nil
|
612
|
+
|
613
|
+
filters.each do |operation, list|
|
614
|
+
|
615
|
+
# Operation can be sinterstore, sdiffstore, or sunionstore.
|
616
|
+
# each operation we do, i.e. `.union(...)`, will be considered
|
617
|
+
# one intersected set, hence we need to `sinterstore` all
|
618
|
+
# the filters in a temporary set.
|
619
|
+
temp = generate_temp_key
|
620
|
+
temp.sinterstore(*list)
|
621
|
+
|
622
|
+
# If this is the first set, we simply assign the generated
|
623
|
+
# set to main, which could possibly be the return value
|
624
|
+
# for simple filters like one `.find(...)`.
|
625
|
+
if main.nil?
|
626
|
+
main = temp
|
627
|
+
else
|
628
|
+
|
629
|
+
# Append the generated temporary set using the operation.
|
630
|
+
# i.e. if we have (mood=happy & book=1) and we have an
|
631
|
+
# `sunionstore`, we do (mood=happy & book=1) | (mood=sad & book=1)
|
632
|
+
main.send(operation, main, temp)
|
633
|
+
end
|
634
|
+
end
|
599
635
|
|
600
636
|
begin
|
601
|
-
|
637
|
+
|
638
|
+
# At this point, we have the final aggregated set, which we yield
|
639
|
+
# to the caller. the caller can do all the normal set operations,
|
640
|
+
# i.e. SCARD, SMEMBERS, etc.
|
641
|
+
yield main
|
642
|
+
|
602
643
|
ensure
|
603
|
-
|
644
|
+
|
645
|
+
# We have to make sure we clean up the temporary keys to avoid
|
646
|
+
# memory leaks and the unintended explosion of memory usage.
|
647
|
+
clean_temp_keys
|
604
648
|
end
|
605
649
|
end
|
606
650
|
end
|
@@ -789,7 +833,7 @@ module Ohm
|
|
789
833
|
if keys.size == 1
|
790
834
|
Ohm::Set.new(keys.first, key, self)
|
791
835
|
else
|
792
|
-
Ohm::MultiSet.new(
|
836
|
+
Ohm::MultiSet.new(key, self).append(:sinterstore, keys)
|
793
837
|
end
|
794
838
|
end
|
795
839
|
|
data/test/filtering.rb
CHANGED
@@ -84,4 +84,73 @@ test "#union" do |john, jane|
|
|
84
84
|
assert res.include?(john)
|
85
85
|
assert res.include?(jane)
|
86
86
|
assert res.include?(included)
|
87
|
+
|
88
|
+
res = User.find(:status => "active").union(:status => "inactive").find(:lname => "Doe")
|
89
|
+
|
90
|
+
assert res.any? { |e| e.status == "inactive" }
|
91
|
+
end
|
92
|
+
|
93
|
+
# book author thing via @myobie
|
94
|
+
scope do
|
95
|
+
class Book < Ohm::Model
|
96
|
+
collection :authors, :Author
|
97
|
+
end
|
98
|
+
|
99
|
+
class Author < Ohm::Model
|
100
|
+
reference :book, :Book
|
101
|
+
|
102
|
+
attribute :mood
|
103
|
+
index :mood
|
104
|
+
end
|
105
|
+
|
106
|
+
setup do
|
107
|
+
book1 = Book.create
|
108
|
+
book2 = Book.create
|
109
|
+
|
110
|
+
auth1 = Author.create(:book => book1, :mood => "happy")
|
111
|
+
auth2 = Author.create(:book => book1, :mood => "sad")
|
112
|
+
auth3 = Author.create(:book => book2, :mood => "sad")
|
113
|
+
|
114
|
+
[book1, book2]
|
115
|
+
end
|
116
|
+
|
117
|
+
test "straight up intersection + union" do |book1, book2|
|
118
|
+
result = book1.authors.find(:mood => "happy").
|
119
|
+
union(:book_id => book1.id, :mood => "sad")
|
120
|
+
|
121
|
+
assert_equal 2, result.size
|
122
|
+
end
|
123
|
+
|
124
|
+
test "appending an empty set via union" do |book1, book2|
|
125
|
+
res = Author.find(:book_id => book1.id, :mood => "happy").
|
126
|
+
union(:book_id => book2.id, :mood => "sad").
|
127
|
+
union(:book_id => book2.id, :mood => "happy")
|
128
|
+
|
129
|
+
assert_equal 2, res.size
|
130
|
+
end
|
131
|
+
|
132
|
+
test "revert by applying the original intersection" do |book1, book2|
|
133
|
+
res = Author.find(:book_id => book1.id, :mood => "happy").
|
134
|
+
union(:book_id => book2.id, :mood => "sad").
|
135
|
+
find(:book_id => book1.id, :mood => "happy")
|
136
|
+
|
137
|
+
assert_equal 1, res.size
|
138
|
+
end
|
139
|
+
|
140
|
+
test "remove original intersection by doing diff" do |book1, book2|
|
141
|
+
res = Author.find(:book_id => book1.id, :mood => "happy").
|
142
|
+
union(:book_id => book2.id, :mood => "sad").
|
143
|
+
except(:book_id => book1.id, :mood => "happy")
|
144
|
+
|
145
|
+
assert_equal 1, res.size
|
146
|
+
assert res.map(&:mood).include?("sad")
|
147
|
+
assert res.map(&:book_id).include?(book2.id)
|
148
|
+
end
|
149
|
+
|
150
|
+
test "@myobie usecase" do |book1, book2|
|
151
|
+
res = book1.authors.find(:mood => "happy").
|
152
|
+
union(:mood => "sad", :book_id => book1.id)
|
153
|
+
|
154
|
+
assert_equal 2, res.size
|
155
|
+
end
|
87
156
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ohm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.
|
4
|
+
version: 1.0.0.rc4
|
5
5
|
prerelease: 6
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,11 +10,11 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2012-04-
|
13
|
+
date: 2012-04-24 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: nest
|
17
|
-
requirement:
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
18
|
none: false
|
19
19
|
requirements:
|
20
20
|
- - ~>
|
@@ -22,10 +22,15 @@ dependencies:
|
|
22
22
|
version: '1.0'
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
|
-
version_requirements:
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ~>
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: '1.0'
|
26
31
|
- !ruby/object:Gem::Dependency
|
27
32
|
name: scrivener
|
28
|
-
requirement:
|
33
|
+
requirement: !ruby/object:Gem::Requirement
|
29
34
|
none: false
|
30
35
|
requirements:
|
31
36
|
- - ~>
|
@@ -33,10 +38,15 @@ dependencies:
|
|
33
38
|
version: 0.0.3
|
34
39
|
type: :runtime
|
35
40
|
prerelease: false
|
36
|
-
version_requirements:
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ~>
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 0.0.3
|
37
47
|
- !ruby/object:Gem::Dependency
|
38
48
|
name: cutest
|
39
|
-
requirement:
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
40
50
|
none: false
|
41
51
|
requirements:
|
42
52
|
- - ~>
|
@@ -44,10 +54,15 @@ dependencies:
|
|
44
54
|
version: '0.1'
|
45
55
|
type: :development
|
46
56
|
prerelease: false
|
47
|
-
version_requirements:
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ~>
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0.1'
|
48
63
|
- !ruby/object:Gem::Dependency
|
49
64
|
name: batch
|
50
|
-
requirement:
|
65
|
+
requirement: !ruby/object:Gem::Requirement
|
51
66
|
none: false
|
52
67
|
requirements:
|
53
68
|
- - ~>
|
@@ -55,7 +70,12 @@ dependencies:
|
|
55
70
|
version: 0.0.1
|
56
71
|
type: :development
|
57
72
|
prerelease: false
|
58
|
-
version_requirements:
|
73
|
+
version_requirements: !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ~>
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: 0.0.1
|
59
79
|
description: Ohm is a library that allows to store an object in Redis, a persistent
|
60
80
|
key-value database. It includes an extensible list of validations and has very good
|
61
81
|
performance.
|
@@ -117,7 +137,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
117
137
|
version: 1.3.1
|
118
138
|
requirements: []
|
119
139
|
rubyforge_project: ohm
|
120
|
-
rubygems_version: 1.8.
|
140
|
+
rubygems_version: 1.8.23
|
121
141
|
signing_key:
|
122
142
|
specification_version: 3
|
123
143
|
summary: Object-hash mapping library for Redis.
|