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.
Files changed (3) hide show
  1. data/lib/ohm.rb +67 -23
  2. data/test/filtering.rb +69 -0
  3. 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
- keys = model.filters(dict)
427
- keys.push(key)
426
+ filters = model.filters(dict).push(key)
428
427
 
429
- MultiSet.new(keys, namespace, model)
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([key], namespace, model).except(dict)
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([key], namespace, model).union(dict)
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(:keys, :namespace, :model)
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
- keys = model.filters(dict)
548
- keys.push(*self.keys)
551
+ filters.push([:sinterstore, model.filters(dict)])
549
552
 
550
- MultiSet.new(keys, namespace, model)
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
- sdiff.push(*model.filters(dict)).uniq!
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
- sunion.push(*model.filters(dict)).uniq!
583
+ filters.push([:sunionstore, model.filters(dict)])
581
584
 
582
585
  return self
583
586
  end
584
587
 
585
588
  private
586
- def sunion
587
- @sunion ||= []
589
+ def filters
590
+ @filters ||= []
588
591
  end
589
592
 
590
- def sdiff
591
- @sdiff ||= []
593
+ def temp_keys
594
+ @temp_keys ||= []
592
595
  end
593
596
 
594
- def execute
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.sinterstore(*keys)
597
- key.sdiffstore(key, *sdiff) if sdiff.any?
598
- key.sunionstore(key, *sunion) if sunion.any?
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
- yield key
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
- key.del
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(keys, key, self)
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.rc3
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-20 00:00:00.000000000 Z
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: &70347702087860 !ruby/object:Gem::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: *70347702087860
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: &70347702087160 !ruby/object:Gem::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: *70347702087160
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: &70347702086580 !ruby/object:Gem::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: *70347702086580
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: &70347702086060 !ruby/object:Gem::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: *70347702086060
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.11
140
+ rubygems_version: 1.8.23
121
141
  signing_key:
122
142
  specification_version: 3
123
143
  summary: Object-hash mapping library for Redis.