zermelo 1.2.1 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/README.md +22 -21
- data/lib/zermelo/associations/multiple.rb +1 -1
- data/lib/zermelo/backends/influxdb.rb +3 -1
- data/lib/zermelo/backends/redis.rb +2 -1
- data/lib/zermelo/filter.rb +19 -1
- data/lib/zermelo/filters/influxdb.rb +11 -3
- data/lib/zermelo/filters/redis.rb +21 -24
- data/lib/zermelo/filters/steps/set_step.rb +54 -21
- data/lib/zermelo/ordered_set.rb +11 -0
- data/lib/zermelo/records/class_methods.rb +1 -1
- data/lib/zermelo/version.rb +1 -1
- data/spec/lib/zermelo/associations/multiple_spec.rb +165 -41
- data/spec/lib/zermelo/filter_spec.rb +21 -21
- data/spec/support/mock_logger.rb +9 -6
- data/spec/support/profile_all_formatter.rb +12 -11
- data/spec/support/uncolored_doc_formatter.rb +6 -4
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 69911858d3394494076bade1249037f111eb5e7b
|
4
|
+
data.tar.gz: 5b2c9e463d7fb0b71a7135bad76b481e4e18629a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b2b62aabc2f4421d99e53bc298e260550d2124cb957786c6ef1bce894cb0fd43c0cd704a6378e6a6acfbc2cfebffda52412984756fc314f98322a2ea63cb7a23
|
7
|
+
data.tar.gz: 69d7a165a1da92e759c6923828d12340d793eead6ee20681a0e22c9f7dde901b0421323ce0fd4c960bc708f5d84e9ebfab79bf166f638a3768032c8b75214074
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -256,21 +256,21 @@ Classes that include `Zermelo::Record` have the following class methods made ava
|
|
256
256
|
|
257
257
|
|Name | Arguments | Returns |
|
258
258
|
|-------------------------|---------------|---------|
|
259
|
-
|`all` | | Returns
|
260
|
-
|`each` | | Yields all records to the provided block, returns the same
|
261
|
-
|`collect` / `map` | | Yields all records to the provided block, returns an Array with the values returned from the block: [
|
262
|
-
|`select` / `find_all` | | Yields all records to the provided block, returns an Array with each record where the block returned true: [
|
263
|
-
|`reject` | | Yields all records to the provided block, returns an Array with each record where the block returned false: [
|
264
|
-
|`ids` | | Returns
|
259
|
+
|`all` | | Returns a Set of all the records stored for this class |
|
260
|
+
|`each` | | Yields all records to the provided block, returns the same Set as .all(): [Enumerable#each](http://ruby-doc.org/core-2.2.2/Enumerable.html#method-i-each) |
|
261
|
+
|`collect` / `map` | | Yields all records to the provided block, returns an Array with the values returned from the block: [Enumerable#collect](http://ruby-doc.org/core-2.2.2/Enumerable.html#method-i-collect) |
|
262
|
+
|`select` / `find_all` | | Yields all records to the provided block, returns an Array with each record where the block returned true: [Enumerable#select](http://ruby-doc.org/core-2.2.2/Enumerable.html#method-i-select) |
|
263
|
+
|`reject` | | Yields all records to the provided block, returns an Array with each record where the block returned false: [Enumerable#reject](http://ruby-doc.org/core-2.2.2/Enumerable.html#method-i-reject) |
|
264
|
+
|`ids` | | Returns a Set with the ids of all stored records |
|
265
265
|
|`count` | | Returns an Integer count of the number of stored records |
|
266
266
|
|`empty?` | | Returns true if no records are stored, false otherwise |
|
267
267
|
|`destroy_all` | | Removes all stored records |
|
268
268
|
|`exists?` | ID | Returns true if the record with the id is present, false if not |
|
269
269
|
|`find_by_id` | ID | Returns the instantiated record for the id, or nil if not present |
|
270
|
-
|`find_by_ids` | ID, ID, ... | Returns
|
270
|
+
|`find_by_ids` | ID, ID, ... | Returns a Set of instantiated records for the ids, with nils if the respective record is not present |
|
271
271
|
|`find_by_id!` | ID | Returns the instantiated record for the id, or raises a Zermelo::Records::RecordNotFound exception if not present |
|
272
|
-
|`find_by_ids!` | ID, ID, ... | Returns
|
273
|
-
|`associated_ids_for`
|
272
|
+
|`find_by_ids!` | ID, ID, ... | Returns a Set of instantiated records for the ids, or raises a Zermelo::Records::RecordsNotFound exception if any are not present |
|
273
|
+
|`associated_ids_for` & `associations_for` | association | (Defined in the `Associations` section below) |
|
274
274
|
|
275
275
|
### Instance methods
|
276
276
|
|
@@ -373,16 +373,16 @@ comment1.save
|
|
373
373
|
comment2 = Comment.new(:id => '2')
|
374
374
|
comment2.save
|
375
375
|
|
376
|
-
p post.comments.ids # ==
|
377
|
-
p Comment.ids # ==
|
376
|
+
p post.comments.ids # == #<Set: {}>
|
377
|
+
p Comment.ids # == #<Set: {'1', '2'}>
|
378
378
|
post.comments << comment1
|
379
|
-
p post.comments.ids # ==
|
379
|
+
p post.comments.ids # == #<Set: {'1'}>
|
380
380
|
```
|
381
381
|
|
382
382
|
`.associated_ids_for` is somewhat of a special case; it uses the simplest queries possible to get the ids of the associated records of a set of records, e.g. for the data directly above:
|
383
383
|
|
384
384
|
```ruby
|
385
|
-
Post.associated_ids_for(:comments) # => {'a' =>
|
385
|
+
Post.associated_ids_for(:comments) # => {'a' => #<Set: {'1'}>}
|
386
386
|
|
387
387
|
post_b = Post.new(:id => 'b')
|
388
388
|
post_b.save
|
@@ -391,16 +391,18 @@ comment3 = Comment.new(:id => '3')
|
|
391
391
|
comment3.save
|
392
392
|
post.comments << comment3
|
393
393
|
|
394
|
-
Post.associated_ids_for(:comments) # => {'a' =>
|
394
|
+
Post.associated_ids_for(:comments) # => {'a' => #<Set: {'1', '3'}>, 'b' => #<Set: {'2'}>}
|
395
395
|
```
|
396
396
|
|
397
397
|
For `belongs to` associations, you may pass an extra option to `associated_ids_for`, `:inversed => true`, and you'll get the data back as if it were applied from the inverse side; however the data will only cover that used as the query root. Again, assuming the data from the last two code blocks, e.g.
|
398
398
|
|
399
399
|
```ruby
|
400
400
|
Comment.associated_ids_for(:post) # => {'1' => 'a', '2' => 'b', '3' => 'a'}
|
401
|
-
Comment.associated_ids_for(:post, :inversed => true) # => {'a' =>
|
401
|
+
Comment.associated_ids_for(:post, :inversed => true) # => {'a' => #<Set: {'1', '3'}>, 'b' => #<Set: {'2'}>}
|
402
402
|
```
|
403
403
|
|
404
|
+
`.associations_for` returns chainable Zermelo association proxy objects, rather than sets of ids, as the Hash values. Please note, `.associations_for` only works with multiple associations (`has_many`, `has_and_belongs_to_many`, `has_sorted_set`).
|
405
|
+
|
404
406
|
### Class data indexing
|
405
407
|
|
406
408
|
Simple instance attributes, as defined above, can be indexed by value (and those indices can be queried).
|
@@ -466,7 +468,7 @@ are both valid, and the `Comment` instances returned by the first query would be
|
|
466
468
|
The chained queries are only executed when the results are invoked (lazy evaluation) by the addition of one of the class methods listed above; e.g.
|
467
469
|
|
468
470
|
```ruby
|
469
|
-
Comment.intersect(:title => 'Interesting').all # ->
|
471
|
+
Comment.intersect(:title => 'Interesting').all # -> #<Set: {Comment, Comment, ...}>
|
470
472
|
Comment.intersect(:title => 'Interesting', :promoted => true).count # -> Integer
|
471
473
|
```
|
472
474
|
|
@@ -477,7 +479,7 @@ SINTER comment::attrs:ids comment::indices:by_title:string:Interesting
|
|
477
479
|
HGET comment:ca9e427d-4d81-47f8-bcfe-bb614d40528c:attrs title
|
478
480
|
```
|
479
481
|
|
480
|
-
with the result being
|
482
|
+
with the result being a Set with one member, a Comment record with `{:id => 'ca9e427d-4d81-47f8-bcfe-bb614d40528c', :title => 'Interesting'}`
|
481
483
|
|
482
484
|
and the second (`.count`) will execute these Redis commands.
|
483
485
|
|
@@ -507,9 +509,8 @@ comment2 = Comment.new(:id => '2', :created_at => t - 60)
|
|
507
509
|
comment2.save
|
508
510
|
|
509
511
|
range = Zermelo::Filters::IndexRange.new(t - 90, t, :by_score => true)
|
510
|
-
Comment.ids #
|
511
|
-
Comment.intersect(:created_at => range).ids #
|
512
|
-
|
512
|
+
Comment.ids # #<Set: {'1', '2'}>
|
513
|
+
Comment.intersect(:created_at => range).ids # #<Set: {'2'}>
|
513
514
|
```
|
514
515
|
|
515
516
|
### Future
|
@@ -519,7 +520,7 @@ Some possible changes:
|
|
519
520
|
* pluggable id generation strategies
|
520
521
|
* pluggable key naming strategies
|
521
522
|
* instrumentation for benchmarking etc.
|
522
|
-
* multiple data backends
|
523
|
+
* multiple data backends
|
523
524
|
|
524
525
|
## License
|
525
526
|
|
@@ -12,7 +12,7 @@ module Zermelo
|
|
12
12
|
:all, :each, :collect, :map,
|
13
13
|
:select, :find_all, :reject, :destroy_all,
|
14
14
|
:ids, :count, :empty?, :exists?,
|
15
|
-
:associated_ids_for
|
15
|
+
:associated_ids_for, :associations_for
|
16
16
|
|
17
17
|
def initialize(type, parent_klass, parent_id, name)
|
18
18
|
@type = type
|
@@ -2,6 +2,8 @@ require 'zermelo/backend'
|
|
2
2
|
|
3
3
|
require 'zermelo/filters/influxdb'
|
4
4
|
|
5
|
+
require 'zermelo/ordered_set'
|
6
|
+
|
5
7
|
# NB influxdb doesn't support individually addressable deletes, so
|
6
8
|
# this backend only works to write new records
|
7
9
|
# (it could just write the new state of the record, and query id by newest limit 1,
|
@@ -59,7 +61,7 @@ module Zermelo
|
|
59
61
|
when :hash
|
60
62
|
{}
|
61
63
|
when :set
|
62
|
-
Set.new
|
64
|
+
Set.new
|
63
65
|
else nil
|
64
66
|
end
|
65
67
|
else
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'zermelo/backend'
|
2
2
|
|
3
3
|
require 'zermelo/filters/redis'
|
4
|
+
require 'zermelo/ordered_set'
|
4
5
|
require 'zermelo/locks/redis_lock'
|
5
6
|
|
6
7
|
module Zermelo
|
@@ -54,7 +55,7 @@ module Zermelo
|
|
54
55
|
end
|
55
56
|
when :sorted_set
|
56
57
|
if attr_key.accessor.nil?
|
57
|
-
Zermelo.redis.zrange(redis_attr_key, 0, -1)
|
58
|
+
Zermelo::OrderedSet.new(Zermelo.redis.zrange(redis_attr_key, 0, -1))
|
58
59
|
else
|
59
60
|
# TODO
|
60
61
|
end
|
data/lib/zermelo/filter.rb
CHANGED
@@ -168,6 +168,24 @@ module Zermelo
|
|
168
168
|
}
|
169
169
|
end
|
170
170
|
|
171
|
+
def associations_for(name)
|
172
|
+
data_type, type_klass = @associated_class.send(:with_association_data, name.to_sym) do |data|
|
173
|
+
[data.data_type, data.type_klass]
|
174
|
+
end
|
175
|
+
|
176
|
+
lock {
|
177
|
+
case data_type
|
178
|
+
when :belongs_to, :has_one
|
179
|
+
raise "'associations_for' only supports multiple associations"
|
180
|
+
else
|
181
|
+
_ids.each_with_object({}) do |this_id, memo|
|
182
|
+
memo[this_id] = ::Zermelo::Associations::Multiple.new(data_type,
|
183
|
+
@associated_class, this_id, name)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
}
|
187
|
+
end
|
188
|
+
|
171
189
|
protected
|
172
190
|
|
173
191
|
def lock(when_steps_empty = true, *klasses, &block)
|
@@ -193,7 +211,7 @@ module Zermelo
|
|
193
211
|
end
|
194
212
|
|
195
213
|
def _all
|
196
|
-
_ids.map {|id| _load(id) }
|
214
|
+
_ids.map! {|id| _load(id) }
|
197
215
|
end
|
198
216
|
|
199
217
|
end
|
@@ -64,7 +64,15 @@ module Zermelo
|
|
64
64
|
initial_id_data = nil
|
65
65
|
end
|
66
66
|
|
67
|
-
|
67
|
+
if initial_id_data.nil?
|
68
|
+
ret = case result_type
|
69
|
+
when :ids
|
70
|
+
Set.new
|
71
|
+
when :count
|
72
|
+
0
|
73
|
+
end
|
74
|
+
return ret
|
75
|
+
end
|
68
76
|
|
69
77
|
initial_ids = initial_id_data.first[@initial_key.name]
|
70
78
|
|
@@ -105,9 +113,9 @@ module Zermelo
|
|
105
113
|
|
106
114
|
case result_type
|
107
115
|
when :ids
|
108
|
-
data_keys.empty? ?
|
116
|
+
data_keys.empty? ? Set.new : Set.new(data_keys.collect {|k| k =~ /^#{class_key}\/(.+)$/; $1 })
|
109
117
|
when :count
|
110
|
-
data_keys.empty? ?
|
118
|
+
data_keys.empty? ? 0 : data_keys.size
|
111
119
|
end
|
112
120
|
end
|
113
121
|
end
|
@@ -2,6 +2,8 @@ require 'zermelo/filter'
|
|
2
2
|
|
3
3
|
require 'zermelo/filters/index_range'
|
4
4
|
|
5
|
+
require 'zermelo/ordered_set'
|
6
|
+
|
5
7
|
# TODO check escaping of ids and index_keys -- shouldn't allow bare :
|
6
8
|
|
7
9
|
module Zermelo
|
@@ -14,20 +16,20 @@ module Zermelo
|
|
14
16
|
|
15
17
|
SHORTCUTS = {
|
16
18
|
:list => {
|
17
|
-
:ids => proc {|key| Zermelo.redis.lrange(key, 0, -1) },
|
19
|
+
:ids => proc {|key| Zermelo::OrderedSet.new(Zermelo.redis.lrange(key, 0, -1)) },
|
18
20
|
:count => proc {|key| Zermelo.redis.llen(key) },
|
19
21
|
:exists? => proc {|key, id| Zermelo.redis.lrange(key, 0, -1).include?(id) },
|
20
22
|
:first => proc {|key| Zermelo.redis.lrange(key, 0, 0).first },
|
21
23
|
:last => proc {|key| Zermelo.redis.lrevrange(key, 0, 0).first }
|
22
24
|
},
|
23
25
|
:set => {
|
24
|
-
:ids => proc {|key| Zermelo.redis.smembers(key) },
|
26
|
+
:ids => proc {|key| Set.new(Zermelo.redis.smembers(key)) },
|
25
27
|
:count => proc {|key| Zermelo.redis.scard(key) },
|
26
28
|
:exists? => proc {|key, id| Zermelo.redis.sismember(key, id) }
|
27
29
|
},
|
28
30
|
:sorted_set => {
|
29
31
|
:ids => proc {|key, order|
|
30
|
-
Zermelo.redis.send((:desc.eql?(order) ? :zrevrange : :zrange), key, 0, -1)
|
32
|
+
Zermelo::OrderedSet.new(Zermelo.redis.send((:desc.eql?(order) ? :zrevrange : :zrange), key, 0, -1))
|
31
33
|
},
|
32
34
|
:count => proc {|key, order| Zermelo.redis.zcard(key) },
|
33
35
|
:exists? => proc {|key, order, id| !Zermelo.redis.zscore(key, id).nil? },
|
@@ -73,17 +75,12 @@ module Zermelo
|
|
73
75
|
resolve_steps(:exists?, id)
|
74
76
|
end
|
75
77
|
|
76
|
-
# If called with a
|
77
|
-
#
|
78
|
-
|
79
|
-
# If called with any arguments -- treats them as a hash of shortcuts
|
80
|
-
|
81
|
-
# If not called with any arguments -- returns two values, the first is
|
82
|
-
# the name of a set containing the filtered ids, the second is a boolean
|
83
|
-
# for whether or not to clear up that set once it's been used
|
84
|
-
|
85
|
-
def resolve_steps(shortcut, *args)
|
78
|
+
# If not called with a shortcut, return value is the name of a set
|
79
|
+
# containing the filtered ids
|
80
|
+
def resolve_steps(shortcut = nil, *args)
|
86
81
|
if @steps.empty?
|
82
|
+
raise "Shortcut must be provided if no steps" if shortcut.nil?
|
83
|
+
|
87
84
|
unless @callback_target_class.nil? || @callbacks.nil?
|
88
85
|
br = @callbacks[:before_read]
|
89
86
|
if !br.nil? && @callback_target_class.respond_to?(br)
|
@@ -92,17 +89,13 @@ module Zermelo
|
|
92
89
|
end
|
93
90
|
|
94
91
|
sc = Zermelo::Filters::Redis::SHORTCUTS[@initial_key.type][shortcut]
|
95
|
-
|
96
|
-
|
92
|
+
r_key = backend.key_to_redis_key(@initial_key)
|
93
|
+
shortcut_params = if @initial_key.type == :sorted_set
|
94
|
+
[r_key, @sort_order] + args
|
97
95
|
else
|
98
|
-
r_key
|
99
|
-
shortcut_params = if @initial_key.type == :sorted_set
|
100
|
-
[r_key, @sort_order] + args
|
101
|
-
else
|
102
|
-
[r_key] + args
|
103
|
-
end
|
104
|
-
sc.call(*shortcut_params)
|
96
|
+
[r_key] + args
|
105
97
|
end
|
98
|
+
ret = sc.call(*shortcut_params)
|
106
99
|
|
107
100
|
unless @callback_target_class.nil? || @callbacks.nil?
|
108
101
|
ar = @callbacks[:after_read]
|
@@ -140,7 +133,7 @@ module Zermelo
|
|
140
133
|
raise "'#{step.class.name}' does not accept input type #{step_opts[:source].type}"
|
141
134
|
end
|
142
135
|
|
143
|
-
if step == last_step
|
136
|
+
if step == last_step && !shortcut.nil?
|
144
137
|
step_opts.update(:shortcut => shortcut, :shortcut_args => args)
|
145
138
|
end
|
146
139
|
|
@@ -160,7 +153,11 @@ module Zermelo
|
|
160
153
|
end
|
161
154
|
end
|
162
155
|
|
163
|
-
|
156
|
+
if step == last_step
|
157
|
+
temp_keys.delete(result) if shortcut.nil?
|
158
|
+
else
|
159
|
+
step_opts[:source] = result
|
160
|
+
end
|
164
161
|
end
|
165
162
|
|
166
163
|
result
|
@@ -25,25 +25,42 @@ module Zermelo
|
|
25
25
|
order = opts[:sort_order]
|
26
26
|
|
27
27
|
source_keys = @attributes.each_with_object([]) do |(att, value), memo|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
if :id.eql?(att)
|
32
|
-
ts = associated_class.send(:temp_key, :set)
|
33
|
-
temp_keys << ts
|
34
|
-
Zermelo.redis.sadd(backend.key_to_redis_key(ts), val)
|
35
|
-
memo << ts
|
36
|
-
else
|
28
|
+
idx_class = nil
|
29
|
+
unless :id.eql?(att)
|
37
30
|
idx_class = idx_attrs[att.to_s]
|
38
31
|
raise "'#{att}' property is not indexed" if idx_class.nil?
|
32
|
+
end
|
33
|
+
|
34
|
+
if [Set, Array].any? {|t| value.is_a?(t) }
|
35
|
+
conditions_set = associated_class.send(:temp_key, source.type)
|
36
|
+
temp_keys << conditions_set
|
37
|
+
r_conditions_set = backend.key_to_redis_key(conditions_set)
|
39
38
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
39
|
+
backend.temp_key_wrap do |conditions_temp_keys|
|
40
|
+
if idx_class.nil?
|
41
|
+
cond_objects, cond_ids = value.partition do |v|
|
42
|
+
[Zermelo::Filter, Zermelo::Associations::Multiple].any? {|c| v.is_a?(c)}
|
43
|
+
end
|
44
44
|
|
45
|
-
|
46
|
-
|
45
|
+
unless cond_objects.empty?
|
46
|
+
cond_keys = cond_objects.collect do |co|
|
47
|
+
k = case co
|
48
|
+
when Zermelo::Filter
|
49
|
+
co.send(:resolve_steps)
|
50
|
+
when Zermelo::Associations::Multiple
|
51
|
+
co.instance_variable_get('@record_ids_key')
|
52
|
+
end
|
53
|
+
backend.key_to_redis_key(k)
|
54
|
+
end
|
55
|
+
|
56
|
+
Zermelo.redis.sunionstore(r_conditions_set, *cond_keys)
|
57
|
+
end
|
58
|
+
unless cond_ids.empty?
|
59
|
+
cond_ids.map! {|ci| ci.is_a?(Zermelo::Associations::Singular) ? ci.id : ci }
|
60
|
+
Zermelo.redis.sadd(r_conditions_set, cond_ids)
|
61
|
+
end
|
62
|
+
else
|
63
|
+
index_keys = value.to_a.collect {|v|
|
47
64
|
il = backend.index_lookup(att, associated_class,
|
48
65
|
idx_class, v, attr_types[att], conditions_temp_keys)
|
49
66
|
backend.key_to_redis_key(il)
|
@@ -56,11 +73,27 @@ module Zermelo
|
|
56
73
|
Zermelo.redis.zunionstore(r_conditions_set, index_keys)
|
57
74
|
end
|
58
75
|
end
|
59
|
-
|
76
|
+
end
|
77
|
+
memo << conditions_set
|
78
|
+
elsif idx_class.nil?
|
79
|
+
case value
|
80
|
+
when Zermelo::Filter
|
81
|
+
ts = value.send(:resolve_steps)
|
82
|
+
temp_keys << ts
|
83
|
+
memo << ts
|
84
|
+
when Zermelo::Associations::Multiple
|
85
|
+
memo << value.instance_variable_get('@record_ids_key')
|
60
86
|
else
|
61
|
-
|
62
|
-
|
87
|
+
ts = associated_class.send(:temp_key, :set)
|
88
|
+
temp_keys << ts
|
89
|
+
r_ts = backend.key_to_redis_key(ts)
|
90
|
+
Zermelo.redis.sadd(r_ts,
|
91
|
+
value.is_a?(Zermelo::Associations::Singular) ? value.id : value)
|
92
|
+
memo << ts
|
63
93
|
end
|
94
|
+
else
|
95
|
+
memo << backend.index_lookup(att, associated_class,
|
96
|
+
idx_class, value, attr_types[att], temp_keys)
|
64
97
|
end
|
65
98
|
end
|
66
99
|
|
@@ -79,10 +112,10 @@ module Zermelo
|
|
79
112
|
r_dest_set = backend.key_to_redis_key(dest_set)
|
80
113
|
|
81
114
|
Zermelo.redis.sinterstore(r_dest_set, *r_source_keys)
|
82
|
-
Zermelo.redis.sunion(r_source_key, r_dest_set)
|
115
|
+
Set.new(Zermelo.redis.sunion(r_source_key, r_dest_set))
|
83
116
|
end
|
84
117
|
when :intersect
|
85
|
-
Zermelo.redis.sinter(r_source_key, *r_source_keys)
|
118
|
+
Set.new(Zermelo.redis.sinter(r_source_key, *r_source_keys))
|
86
119
|
when :diff
|
87
120
|
backend.temp_key_wrap do |shortcut_temp_keys|
|
88
121
|
dest_set = associated_class.send(:temp_key, :set)
|
@@ -90,7 +123,7 @@ module Zermelo
|
|
90
123
|
r_dest_set = backend.key_to_redis_key(dest_set)
|
91
124
|
|
92
125
|
Zermelo.redis.sinterstore(r_dest_set, *r_source_keys)
|
93
|
-
Zermelo.redis.sdiff(r_source_key, r_dest_set)
|
126
|
+
Set.new(Zermelo.redis.sdiff(r_source_key, r_dest_set))
|
94
127
|
end
|
95
128
|
end
|
96
129
|
else
|
@@ -26,7 +26,7 @@ module Zermelo
|
|
26
26
|
:all, :each, :collect, :map,
|
27
27
|
:select, :find_all, :reject, :destroy_all,
|
28
28
|
:ids, :count, :empty?, :exists?,
|
29
|
-
:associated_ids_for
|
29
|
+
:associated_ids_for, :associations_for
|
30
30
|
|
31
31
|
def generate_id
|
32
32
|
return SecureRandom.uuid if SecureRandom.respond_to?(:uuid)
|
data/lib/zermelo/version.rb
CHANGED
@@ -20,14 +20,14 @@ describe Zermelo::Associations::Multiple do
|
|
20
20
|
|
21
21
|
children = parent.children.all
|
22
22
|
|
23
|
-
expect(children).to
|
23
|
+
expect(children).to be_a(Set)
|
24
24
|
expect(children).to be_empty
|
25
25
|
|
26
26
|
parent.children << child
|
27
27
|
|
28
28
|
children = parent.children.all
|
29
29
|
|
30
|
-
expect(children).to
|
30
|
+
expect(children).to be_a(Set)
|
31
31
|
expect(children.size).to eq(1)
|
32
32
|
end
|
33
33
|
|
@@ -36,7 +36,7 @@ describe Zermelo::Associations::Multiple do
|
|
36
36
|
|
37
37
|
children = parent.children.all
|
38
38
|
|
39
|
-
expect(children).to
|
39
|
+
expect(children).to be_a(Set)
|
40
40
|
expect(children.size).to eq(1)
|
41
41
|
child = children.first
|
42
42
|
expect(child).to be_a(child_class)
|
@@ -61,7 +61,7 @@ describe Zermelo::Associations::Multiple do
|
|
61
61
|
child = child_class.find_by_id('3')
|
62
62
|
parent.children.remove(child)
|
63
63
|
expect(parent.children.count).to eq(1)
|
64
|
-
expect(parent.children.ids).to eq(['4'])
|
64
|
+
expect(parent.children.ids).to eq(Set.new(['4']))
|
65
65
|
end
|
66
66
|
|
67
67
|
it "deletes a record from the set by id" do
|
@@ -71,7 +71,7 @@ describe Zermelo::Associations::Multiple do
|
|
71
71
|
expect(parent.children.count).to eq(2)
|
72
72
|
parent.children.remove_ids('3')
|
73
73
|
expect(parent.children.count).to eq(1)
|
74
|
-
expect(parent.children.ids).to eq(['4'])
|
74
|
+
expect(parent.children.ids).to eq(Set.new(['4']))
|
75
75
|
end
|
76
76
|
|
77
77
|
it "clears all records from the set" do
|
@@ -82,7 +82,7 @@ describe Zermelo::Associations::Multiple do
|
|
82
82
|
child = child_class.find_by_id('3')
|
83
83
|
parent.children.clear
|
84
84
|
expect(parent.children.count).to eq(0)
|
85
|
-
expect(parent.children.ids).to eq(
|
85
|
+
expect(parent.children.ids).to eq(Set.new)
|
86
86
|
end
|
87
87
|
|
88
88
|
it "does not add a child if the before_add callback raises an exception" # do
|
@@ -125,15 +125,15 @@ describe Zermelo::Associations::Multiple do
|
|
125
125
|
it "by indexed attribute values" do
|
126
126
|
important_kids = parent.children.intersect(:important => true).all
|
127
127
|
expect(important_kids).not_to be_nil
|
128
|
-
expect(important_kids).to
|
128
|
+
expect(important_kids).to be_a(Set)
|
129
129
|
expect(important_kids.size).to eq(2)
|
130
|
-
expect(important_kids.map(&:id)).to
|
130
|
+
expect(important_kids.map(&:id)).to eq(['3', '4'])
|
131
131
|
end
|
132
132
|
|
133
133
|
it "by intersecting ids" do
|
134
134
|
important_kids = parent.children.intersect(:important => true, :id => ['4', '5']).all
|
135
135
|
expect(important_kids).not_to be_nil
|
136
|
-
expect(important_kids).to
|
136
|
+
expect(important_kids).to be_a(Set)
|
137
137
|
expect(important_kids.size).to eq(1)
|
138
138
|
expect(important_kids.map(&:id)).to match_array(['4'])
|
139
139
|
end
|
@@ -143,7 +143,7 @@ describe Zermelo::Associations::Multiple do
|
|
143
143
|
create_child(parent, :id => '4', :important => false)
|
144
144
|
|
145
145
|
result = parent.children.intersect(:important => true).union(:id => '4').all
|
146
|
-
expect(result).to
|
146
|
+
expect(result).to be_a(Set)
|
147
147
|
expect(result.size).to eq(2)
|
148
148
|
expect(result.map(&:id)).to eq(['3', '4'])
|
149
149
|
end
|
@@ -172,7 +172,7 @@ describe Zermelo::Associations::Multiple do
|
|
172
172
|
associated_ids_for(:children)
|
173
173
|
expect(assoc_ids).to eq('8' => Set.new(['3', '4', '5']),
|
174
174
|
'9' => Set.new(['6']),
|
175
|
-
'10' => Set.new
|
175
|
+
'10' => Set.new)
|
176
176
|
|
177
177
|
assoc_parent_ids = child_class.intersect(:id => ['3', '4', '5', '6']).
|
178
178
|
associated_ids_for(:parent)
|
@@ -182,6 +182,27 @@ describe Zermelo::Associations::Multiple do
|
|
182
182
|
'6' => '9')
|
183
183
|
end
|
184
184
|
|
185
|
+
it 'returns associations for multiple parent ids' do
|
186
|
+
create_parent(:id => '9')
|
187
|
+
parent_9 = parent_class.find_by_id('9')
|
188
|
+
|
189
|
+
create_child(parent_9, :id => '6', :important => false)
|
190
|
+
|
191
|
+
create_parent(:id => '10')
|
192
|
+
|
193
|
+
assocs = parent_class.intersect(:id => [ '8', '9', '10']).
|
194
|
+
associations_for(:children)
|
195
|
+
expect(assocs).to be_a(Hash)
|
196
|
+
expect(assocs.keys).to match_array(['8', '9', '10'])
|
197
|
+
expect(assocs.values.all? {|r| r.is_a?(Zermelo::Associations::Multiple)}).to be true
|
198
|
+
|
199
|
+
expect(assocs['8'].count).to eq(3)
|
200
|
+
expect(assocs['8'].ids).to eq(Set.new(['3', '4', '5']))
|
201
|
+
expect(assocs['9'].count).to eq(1)
|
202
|
+
expect(assocs['9'].ids).to eq(Set.new(['6']))
|
203
|
+
expect(assocs['10'].count).to eq(0)
|
204
|
+
expect(assocs['10'].ids).to eq(Set.new)
|
205
|
+
end
|
185
206
|
end
|
186
207
|
end
|
187
208
|
|
@@ -310,6 +331,106 @@ describe Zermelo::Associations::Multiple do
|
|
310
331
|
"#{ck}:6:attrs"])
|
311
332
|
end
|
312
333
|
|
334
|
+
it 'queries using association objects' do
|
335
|
+
create_parent(:id => '8')
|
336
|
+
parent_8 = parent_class.find_by_id('8')
|
337
|
+
create_child(parent_8, :id => '5')
|
338
|
+
create_child(parent_8, :id => '6')
|
339
|
+
|
340
|
+
create_parent(:id => '9')
|
341
|
+
parent_9 = parent_class.find_by_id('9')
|
342
|
+
create_child(parent_9, :id => '7')
|
343
|
+
|
344
|
+
create_parent(:id => '10')
|
345
|
+
|
346
|
+
assocs = parent_class.intersect(:id => ['8', '10']).
|
347
|
+
associations_for(:children).values
|
348
|
+
|
349
|
+
children = child_class.intersect(:id => assocs)
|
350
|
+
expect(children.count).to eq(2)
|
351
|
+
expect(children.ids).to eq(Set.new(['5', '6']))
|
352
|
+
end
|
353
|
+
|
354
|
+
it 'queries using multiple association objects' do
|
355
|
+
create_parent(:id => '8')
|
356
|
+
parent_8 = parent_class.find_by_id('8')
|
357
|
+
create_child(parent_8, :id => '5')
|
358
|
+
create_child(parent_8, :id => '6')
|
359
|
+
|
360
|
+
create_parent(:id => '9')
|
361
|
+
parent_9 = parent_class.find_by_id('9')
|
362
|
+
create_child(parent_9, :id => '7')
|
363
|
+
|
364
|
+
create_parent(:id => '10')
|
365
|
+
parent_10 = parent_class.find_by_id('10')
|
366
|
+
create_child(parent_10, :id => '4')
|
367
|
+
|
368
|
+
children = child_class.intersect(:id => [parent_8.children, parent_9.children])
|
369
|
+
expect(children.count).to eq(3)
|
370
|
+
expect(children.ids).to eq(Set.new(['5', '6', '7']))
|
371
|
+
end
|
372
|
+
|
373
|
+
it 'queries using a single filter object' do
|
374
|
+
create_parent(:id => '8')
|
375
|
+
parent_8 = parent_class.find_by_id('8')
|
376
|
+
create_child(parent_8, :id => '5')
|
377
|
+
create_child(parent_8, :id => '6')
|
378
|
+
|
379
|
+
create_parent(:id => '9')
|
380
|
+
parent_9 = parent_class.find_by_id('9')
|
381
|
+
create_child(parent_9, :id => '7')
|
382
|
+
|
383
|
+
create_parent(:id => '10')
|
384
|
+
|
385
|
+
par = parent_class.intersect(:id => ['8', '10'])
|
386
|
+
|
387
|
+
parent_ids = parent_class.intersect(:id => par).ids
|
388
|
+
expect(parent_ids).to eq(Set.new(['8', '10']))
|
389
|
+
end
|
390
|
+
|
391
|
+
it 'queries using multiple filter objects' do
|
392
|
+
create_parent(:id => '8')
|
393
|
+
parent_8 = parent_class.find_by_id('8')
|
394
|
+
create_child(parent_8, :id => '5')
|
395
|
+
create_child(parent_8, :id => '6')
|
396
|
+
|
397
|
+
create_parent(:id => '9')
|
398
|
+
parent_9 = parent_class.find_by_id('9')
|
399
|
+
create_child(parent_9, :id => '7')
|
400
|
+
|
401
|
+
create_parent(:id => '10')
|
402
|
+
|
403
|
+
par_1 = parent_class.intersect(:id => ['8'])
|
404
|
+
par_2 = parent_class.intersect(:id => ['10'])
|
405
|
+
|
406
|
+
parent_ids = parent_class.intersect(:id => [par_1, par_2]).ids
|
407
|
+
expect(parent_ids).to eq(Set.new(['8', '10']))
|
408
|
+
end
|
409
|
+
|
410
|
+
it 'queries using a combination of bare value, association and filter object' do
|
411
|
+
create_parent(:id => '8')
|
412
|
+
parent_8 = parent_class.find_by_id('8')
|
413
|
+
create_child(parent_8, :id => '5')
|
414
|
+
create_child(parent_8, :id => '6')
|
415
|
+
|
416
|
+
create_parent(:id => '9')
|
417
|
+
parent_9 = parent_class.find_by_id('9')
|
418
|
+
create_child(parent_9, :id => '7')
|
419
|
+
|
420
|
+
create_parent(:id => '10')
|
421
|
+
parent_10 = parent_class.find_by_id('10')
|
422
|
+
create_child(parent_10, :id => '4')
|
423
|
+
|
424
|
+
assocs = parent_class.intersect(:id => ['8']).
|
425
|
+
associations_for(:children).values
|
426
|
+
|
427
|
+
children = child_class.intersect(:id => assocs + [
|
428
|
+
parent_9.children.intersect(:id => '7'), '4'
|
429
|
+
])
|
430
|
+
expect(children.count).to eq(4)
|
431
|
+
expect(children.ids).to eq(Set.new(['4', '5', '6', '7']))
|
432
|
+
end
|
433
|
+
|
313
434
|
end
|
314
435
|
|
315
436
|
context 'influxdb', :influxdb => true, :has_many => true do
|
@@ -349,6 +470,9 @@ describe Zermelo::Associations::Multiple do
|
|
349
470
|
par.children.add(child_class.find_by_id!(attrs[:id]))
|
350
471
|
end
|
351
472
|
|
473
|
+
# FIXME not implemented yet for InfluxDB, see SetStep
|
474
|
+
it 'queries associated filters transparently'
|
475
|
+
|
352
476
|
end
|
353
477
|
|
354
478
|
end
|
@@ -372,7 +496,7 @@ describe Zermelo::Associations::Multiple do
|
|
372
496
|
|
373
497
|
secondaries = primary.secondaries.all
|
374
498
|
|
375
|
-
expect(secondaries).to
|
499
|
+
expect(secondaries).to be_a(Set)
|
376
500
|
expect(secondaries.size).to eq(1)
|
377
501
|
other_secondary = secondaries.first
|
378
502
|
expect(other_secondary).to be_a(secondary_class)
|
@@ -412,9 +536,9 @@ describe Zermelo::Associations::Multiple do
|
|
412
536
|
expect(secondary.primaries.count).to eq(1)
|
413
537
|
expect(primary.secondaries.count).to eq(0)
|
414
538
|
expect(primary_2.secondaries.count).to eq(1)
|
415
|
-
expect(secondary.primaries.ids).to eq(['9'])
|
416
|
-
expect(primary.secondaries.ids).to eq(
|
417
|
-
expect(primary_2.secondaries.ids).to eq(['2'])
|
539
|
+
expect(secondary.primaries.ids).to eq(Set.new(['9']))
|
540
|
+
expect(primary.secondaries.ids).to eq(Set.new)
|
541
|
+
expect(primary_2.secondaries.ids).to eq(Set.new(['2']))
|
418
542
|
end
|
419
543
|
|
420
544
|
it "deletes a record from the set by id" do
|
@@ -434,9 +558,9 @@ describe Zermelo::Associations::Multiple do
|
|
434
558
|
expect(secondary.primaries.count).to eq(1)
|
435
559
|
expect(primary.secondaries.count).to eq(0)
|
436
560
|
expect(primary_2.secondaries.count).to eq(1)
|
437
|
-
expect(secondary.primaries.ids).to eq(['9'])
|
438
|
-
expect(primary.secondaries.ids).to eq(
|
439
|
-
expect(primary_2.secondaries.ids).to eq(['2'])
|
561
|
+
expect(secondary.primaries.ids).to eq(Set.new(['9']))
|
562
|
+
expect(primary.secondaries.ids).to eq(Set.new)
|
563
|
+
expect(primary_2.secondaries.ids).to eq(Set.new(['2']))
|
440
564
|
end
|
441
565
|
|
442
566
|
it "clears all records from the set" do
|
@@ -456,9 +580,9 @@ describe Zermelo::Associations::Multiple do
|
|
456
580
|
expect(secondary.primaries.count).to eq(0)
|
457
581
|
expect(primary.secondaries.count).to eq(0)
|
458
582
|
expect(primary_2.secondaries.count).to eq(0)
|
459
|
-
expect(secondary.primaries.ids).to eq(
|
460
|
-
expect(primary.secondaries.ids).to eq(
|
461
|
-
expect(primary_2.secondaries.ids).to eq(
|
583
|
+
expect(secondary.primaries.ids).to eq(Set.new)
|
584
|
+
expect(primary.secondaries.ids).to eq(Set.new)
|
585
|
+
expect(primary_2.secondaries.ids).to eq(Set.new)
|
462
586
|
end
|
463
587
|
|
464
588
|
context 'filters' do
|
@@ -478,7 +602,7 @@ describe Zermelo::Associations::Multiple do
|
|
478
602
|
|
479
603
|
primaries = secondary.primaries.intersect(:active => true).all
|
480
604
|
expect(primaries).not_to be_nil
|
481
|
-
expect(primaries).to
|
605
|
+
expect(primaries).to be_a(Set)
|
482
606
|
expect(primaries.size).to eq(2)
|
483
607
|
expect(primaries.map(&:id)).to match_array(['8', '10'])
|
484
608
|
end
|
@@ -747,7 +871,7 @@ describe Zermelo::Associations::Multiple do
|
|
747
871
|
|
748
872
|
children = parent.children.all
|
749
873
|
|
750
|
-
expect(children).to
|
874
|
+
expect(children).to be_a(Zermelo::OrderedSet)
|
751
875
|
expect(children.size).to eq(1)
|
752
876
|
child = children.first
|
753
877
|
expect(child).to be_a(child_class)
|
@@ -866,9 +990,9 @@ describe Zermelo::Associations::Multiple do
|
|
866
990
|
|
867
991
|
assoc_ids = parent_class.intersect(:id => ['8', '9', '10']).
|
868
992
|
associated_ids_for(:children)
|
869
|
-
expect(assoc_ids).to eq('8' => ['3', '4'],
|
870
|
-
'9' =>
|
871
|
-
'10' => ['5'])
|
993
|
+
expect(assoc_ids).to eq('8' => Zermelo::OrderedSet.new(['3', '4']),
|
994
|
+
'9' => Zermelo::OrderedSet.new,
|
995
|
+
'10' => Zermelo::OrderedSet.new(['5']))
|
872
996
|
end
|
873
997
|
|
874
998
|
it "deletes a record from the set" do
|
@@ -881,7 +1005,7 @@ describe Zermelo::Associations::Multiple do
|
|
881
1005
|
child = child_class.find_by_id('3')
|
882
1006
|
parent.children.remove(child)
|
883
1007
|
expect(parent.children.count).to eq(1)
|
884
|
-
expect(parent.children.ids).to eq(['4'])
|
1008
|
+
expect(parent.children.ids).to eq(Set.new(['4']))
|
885
1009
|
end
|
886
1010
|
|
887
1011
|
it "deletes a record from the set by id" do
|
@@ -893,7 +1017,7 @@ describe Zermelo::Associations::Multiple do
|
|
893
1017
|
expect(parent.children.count).to eq(2)
|
894
1018
|
parent.children.remove_ids('3')
|
895
1019
|
expect(parent.children.count).to eq(1)
|
896
|
-
expect(parent.children.ids).to eq(['4'])
|
1020
|
+
expect(parent.children.ids).to eq(Zermelo::OrderedSet.new(['4']))
|
897
1021
|
end
|
898
1022
|
|
899
1023
|
it "clears all records from the set" do
|
@@ -906,7 +1030,7 @@ describe Zermelo::Associations::Multiple do
|
|
906
1030
|
child = child_class.find_by_id('3')
|
907
1031
|
parent.children.clear
|
908
1032
|
expect(parent.children.count).to eq(0)
|
909
|
-
expect(parent.children.ids).to eq(
|
1033
|
+
expect(parent.children.ids).to eq(Zermelo::OrderedSet.new)
|
910
1034
|
end
|
911
1035
|
|
912
1036
|
context 'filters' do
|
@@ -922,7 +1046,7 @@ describe Zermelo::Associations::Multiple do
|
|
922
1046
|
it "by indexed attribute values" do
|
923
1047
|
upset_children = parent.children.intersect(:emotion => 'upset').all
|
924
1048
|
expect(upset_children).not_to be_nil
|
925
|
-
expect(upset_children).to
|
1049
|
+
expect(upset_children).to be_a(Zermelo::OrderedSet)
|
926
1050
|
expect(upset_children.size).to eq(2)
|
927
1051
|
expect(upset_children.map(&:id)).to eq(['4', '6'])
|
928
1052
|
end
|
@@ -930,7 +1054,7 @@ describe Zermelo::Associations::Multiple do
|
|
930
1054
|
it "by indexed attribute values with a regex search" do
|
931
1055
|
upset_children = parent.children.intersect(:emotion => /^ups/).all
|
932
1056
|
expect(upset_children).not_to be_nil
|
933
|
-
expect(upset_children).to
|
1057
|
+
expect(upset_children).to be_a(Zermelo::OrderedSet)
|
934
1058
|
expect(upset_children.size).to eq(2)
|
935
1059
|
expect(upset_children.map(&:id)).to eq(['4', '6'])
|
936
1060
|
end
|
@@ -939,7 +1063,7 @@ describe Zermelo::Associations::Multiple do
|
|
939
1063
|
range = Zermelo::Filters::IndexRange.new(0, 1)
|
940
1064
|
children = parent.children.intersect(:timestamp => range).all
|
941
1065
|
expect(children).not_to be_nil
|
942
|
-
expect(children).to
|
1066
|
+
expect(children).to be_a(Zermelo::OrderedSet)
|
943
1067
|
expect(children.size).to eq(2)
|
944
1068
|
expect(children.map(&:id)).to eq(['4', '5'])
|
945
1069
|
end
|
@@ -948,7 +1072,7 @@ describe Zermelo::Associations::Multiple do
|
|
948
1072
|
range = Zermelo::Filters::IndexRange.new(1, 2)
|
949
1073
|
children = parent.children.intersect(:timestamp => range).sort(:id, :desc => true).all
|
950
1074
|
expect(children).not_to be_nil
|
951
|
-
expect(children).to
|
1075
|
+
expect(children).to be_a(Zermelo::OrderedSet)
|
952
1076
|
expect(children.size).to eq(2)
|
953
1077
|
expect(children.map(&:id)).to eq(['6', '5'])
|
954
1078
|
end
|
@@ -957,7 +1081,7 @@ describe Zermelo::Associations::Multiple do
|
|
957
1081
|
range = Zermelo::Filters::IndexRange.new(time - 25, time - 5, :by_score => true)
|
958
1082
|
children = parent.children.intersect(:timestamp => range).all
|
959
1083
|
expect(children).not_to be_nil
|
960
|
-
expect(children).to
|
1084
|
+
expect(children).to be_a(Zermelo::OrderedSet)
|
961
1085
|
expect(children.size).to eq(2)
|
962
1086
|
expect(children.map(&:id)).to eq(['4', '5'])
|
963
1087
|
end
|
@@ -967,7 +1091,7 @@ describe Zermelo::Associations::Multiple do
|
|
967
1091
|
children = parent.children.intersect(:timestamp => range).
|
968
1092
|
sort(:timestamp, :desc => true).all
|
969
1093
|
expect(children).not_to be_nil
|
970
|
-
expect(children).to
|
1094
|
+
expect(children).to be_a(Zermelo::OrderedSet)
|
971
1095
|
expect(children.size).to eq(2)
|
972
1096
|
expect(children.map(&:id)).to eq(['5', '4'])
|
973
1097
|
end
|
@@ -994,7 +1118,7 @@ describe Zermelo::Associations::Multiple do
|
|
994
1118
|
children = parent.children.intersect(:id => ['4']).
|
995
1119
|
union(:emotion => 'upset', :id => ['4', '6']).all
|
996
1120
|
expect(children).not_to be_nil
|
997
|
-
expect(children).to
|
1121
|
+
expect(children).to be_a(Zermelo::OrderedSet)
|
998
1122
|
expect(children.size).to eq(2)
|
999
1123
|
expect(children.map(&:id)).to eq(['4', '6'])
|
1000
1124
|
end
|
@@ -1002,7 +1126,7 @@ describe Zermelo::Associations::Multiple do
|
|
1002
1126
|
it "ANDs multiple diff arguments, not ORs them" do
|
1003
1127
|
children = parent.children.diff(:emotion => 'upset', :id => ['4', '5']).all
|
1004
1128
|
expect(children).not_to be_nil
|
1005
|
-
expect(children).to
|
1129
|
+
expect(children).to be_a(Zermelo::OrderedSet)
|
1006
1130
|
expect(children.size).to eq(2)
|
1007
1131
|
expect(children.map(&:id)).to eq(['5', '6'])
|
1008
1132
|
end
|
@@ -1011,7 +1135,7 @@ describe Zermelo::Associations::Multiple do
|
|
1011
1135
|
range = Zermelo::Filters::IndexRange.new(0, 1)
|
1012
1136
|
children = parent.children.diff(:timestamp => range).all
|
1013
1137
|
expect(children).not_to be_nil
|
1014
|
-
expect(children).to
|
1138
|
+
expect(children).to be_a(Zermelo::OrderedSet)
|
1015
1139
|
expect(children.size).to eq(1)
|
1016
1140
|
expect(children.map(&:id)).to eq(['6'])
|
1017
1141
|
end
|
@@ -1020,7 +1144,7 @@ describe Zermelo::Associations::Multiple do
|
|
1020
1144
|
range = Zermelo::Filters::IndexRange.new(2, 2)
|
1021
1145
|
children = parent.children.diff(:timestamp => range).sort(:id, :desc => true).all
|
1022
1146
|
expect(children).not_to be_nil
|
1023
|
-
expect(children).to
|
1147
|
+
expect(children).to be_a(Zermelo::OrderedSet)
|
1024
1148
|
expect(children.size).to eq(2)
|
1025
1149
|
expect(children.map(&:id)).to eq(['5', '4'])
|
1026
1150
|
end
|
@@ -1029,7 +1153,7 @@ describe Zermelo::Associations::Multiple do
|
|
1029
1153
|
range = Zermelo::Filters::IndexRange.new(time - 25, time - 5, :by_score => true)
|
1030
1154
|
children = parent.children.diff(:timestamp => range).all
|
1031
1155
|
expect(children).not_to be_nil
|
1032
|
-
expect(children).to
|
1156
|
+
expect(children).to be_a(Zermelo::OrderedSet)
|
1033
1157
|
expect(children.size).to eq(1)
|
1034
1158
|
expect(children.map(&:id)).to eq(['6'])
|
1035
1159
|
end
|
@@ -1038,7 +1162,7 @@ describe Zermelo::Associations::Multiple do
|
|
1038
1162
|
range = Zermelo::Filters::IndexRange.new(time - 5, time, :by_score => true)
|
1039
1163
|
children = parent.children.diff(:timestamp => range).sort(:timestamp, :desc => true).all
|
1040
1164
|
expect(children).not_to be_nil
|
1041
|
-
expect(children).to
|
1165
|
+
expect(children).to be_a(Zermelo::OrderedSet)
|
1042
1166
|
expect(children.size).to eq(2)
|
1043
1167
|
expect(children.map(&:id)).to eq(['5', '4'])
|
1044
1168
|
end
|
@@ -27,7 +27,7 @@ describe Zermelo::Filter do
|
|
27
27
|
it "returns all record ids" do
|
28
28
|
examples = example_class.ids
|
29
29
|
expect(examples).not_to be_nil
|
30
|
-
expect(examples).to
|
30
|
+
expect(examples).to be_a(Set)
|
31
31
|
expect(examples.size).to eq(2)
|
32
32
|
expect(examples).to contain_exactly('8', '9')
|
33
33
|
end
|
@@ -42,7 +42,7 @@ describe Zermelo::Filter do
|
|
42
42
|
it "returns all records" do
|
43
43
|
examples = example_class.all
|
44
44
|
expect(examples).not_to be_nil
|
45
|
-
expect(examples).to
|
45
|
+
expect(examples).to be_a(Set)
|
46
46
|
expect(examples.size).to eq(2)
|
47
47
|
expect(examples.map(&:id)).to contain_exactly('9', '8')
|
48
48
|
end
|
@@ -57,7 +57,7 @@ describe Zermelo::Filter do
|
|
57
57
|
it "finds records by a uniquely indexed value" do
|
58
58
|
examples = example_class.intersect(:name => 'John Jones').all
|
59
59
|
expect(examples).not_to be_nil
|
60
|
-
expect(examples).to be_an(
|
60
|
+
expect(examples).to be_an(Set)
|
61
61
|
expect(examples.size).to eq(1)
|
62
62
|
example = examples.first
|
63
63
|
expect(example.id).to eq('8')
|
@@ -67,7 +67,7 @@ describe Zermelo::Filter do
|
|
67
67
|
it "filters all class records by indexed attribute values" do
|
68
68
|
example = example_class.intersect(:active => true).all
|
69
69
|
expect(example).not_to be_nil
|
70
|
-
expect(example).to
|
70
|
+
expect(example).to be_a(Set)
|
71
71
|
expect(example.size).to eq(1)
|
72
72
|
expect(example.map(&:id)).to eq(['8'])
|
73
73
|
end
|
@@ -75,7 +75,7 @@ describe Zermelo::Filter do
|
|
75
75
|
it 'filters by id attribute values' do
|
76
76
|
example = example_class.intersect(:id => '9').all
|
77
77
|
expect(example).not_to be_nil
|
78
|
-
expect(example).to
|
78
|
+
expect(example).to be_a(Set)
|
79
79
|
expect(example.size).to eq(1)
|
80
80
|
expect(example.map(&:id)).to eq(['9'])
|
81
81
|
end
|
@@ -86,7 +86,7 @@ describe Zermelo::Filter do
|
|
86
86
|
|
87
87
|
examples = example_class.intersect(:id => ['8', '10']).all
|
88
88
|
expect(examples).not_to be_nil
|
89
|
-
expect(examples).to
|
89
|
+
expect(examples).to be_a(Set)
|
90
90
|
expect(examples.size).to eq(2)
|
91
91
|
expect(examples.map(&:id)).to match_array(['8', '10'])
|
92
92
|
end
|
@@ -95,7 +95,7 @@ describe Zermelo::Filter do
|
|
95
95
|
examples = example_class.intersect(:active => true).
|
96
96
|
union(:active => false).all
|
97
97
|
expect(examples).not_to be_nil
|
98
|
-
expect(examples).to
|
98
|
+
expect(examples).to be_a(Set)
|
99
99
|
expect(examples.size).to eq(2)
|
100
100
|
expect(examples.map(&:id)).to match_array(['8', '9'])
|
101
101
|
end
|
@@ -104,7 +104,7 @@ describe Zermelo::Filter do
|
|
104
104
|
example = example_class.intersect(:active => true).
|
105
105
|
intersect(:name => 'John Jones').all
|
106
106
|
expect(example).not_to be_nil
|
107
|
-
expect(example).to
|
107
|
+
expect(example).to be_a(Set)
|
108
108
|
expect(example.size).to eq(1)
|
109
109
|
expect(example.map(&:id)).to eq(['8'])
|
110
110
|
end
|
@@ -113,7 +113,7 @@ describe Zermelo::Filter do
|
|
113
113
|
example = example_class.intersect(:active => true,
|
114
114
|
:name => 'John Jones').all
|
115
115
|
expect(example).not_to be_nil
|
116
|
-
expect(example).to
|
116
|
+
expect(example).to be_a(Set)
|
117
117
|
expect(example.size).to eq(1)
|
118
118
|
expect(example.map(&:id)).to eq(['8'])
|
119
119
|
end
|
@@ -124,7 +124,7 @@ describe Zermelo::Filter do
|
|
124
124
|
|
125
125
|
example = example_class.intersect(:active => true).diff(:name => 'Fred Bloggs').all
|
126
126
|
expect(example).not_to be_nil
|
127
|
-
expect(example).to
|
127
|
+
expect(example).to be_a(Set)
|
128
128
|
expect(example.size).to eq(1)
|
129
129
|
expect(example.map(&:id)).to eq(['8'])
|
130
130
|
end
|
@@ -138,7 +138,7 @@ describe Zermelo::Filter do
|
|
138
138
|
it 'finds records by regex match against a uniquely indexed value' do
|
139
139
|
examples = example_class.intersect(:name => /hn Jones/).all
|
140
140
|
expect(examples).not_to be_nil
|
141
|
-
expect(examples).to
|
141
|
+
expect(examples).to be_a(Set)
|
142
142
|
expect(examples.size).to eq(1)
|
143
143
|
example = examples.first
|
144
144
|
expect(example.id).to eq('8')
|
@@ -153,13 +153,13 @@ describe Zermelo::Filter do
|
|
153
153
|
|
154
154
|
it 'can append to a filter chain fragment more than once' do
|
155
155
|
inter = example_class.intersect(:active => true)
|
156
|
-
expect(inter.ids).to eq(['8'])
|
156
|
+
expect(inter.ids).to eq(Set.new(['8']))
|
157
157
|
|
158
158
|
union = inter.union(:name => 'James Brown')
|
159
|
-
expect(union.ids).to
|
159
|
+
expect(union.ids).to eq(Set.new(['8', '9']))
|
160
160
|
|
161
161
|
diff = inter.diff(:id => ['8'])
|
162
|
-
expect(diff.ids).to eq(
|
162
|
+
expect(diff.ids).to eq(Set.new)
|
163
163
|
end
|
164
164
|
|
165
165
|
it "ANDs multiple union arguments, not ORs them" do
|
@@ -167,7 +167,7 @@ describe Zermelo::Filter do
|
|
167
167
|
examples = example_class.intersect(:id => ['8']).
|
168
168
|
union(:id => ['9', '10'], :active => true).all
|
169
169
|
expect(examples).not_to be_nil
|
170
|
-
expect(examples).to
|
170
|
+
expect(examples).to be_a(Set)
|
171
171
|
expect(examples.size).to eq(2)
|
172
172
|
expect(examples.map(&:id)).to match_array(['8', '10'])
|
173
173
|
end
|
@@ -177,7 +177,7 @@ describe Zermelo::Filter do
|
|
177
177
|
examples = example_class.intersect(:id => ['8', '9', '10']).
|
178
178
|
diff(:id => ['9', '10'], :active => false).all
|
179
179
|
expect(examples).not_to be_nil
|
180
|
-
expect(examples).to
|
180
|
+
expect(examples).to be_a(Set)
|
181
181
|
expect(examples.size).to eq(2)
|
182
182
|
expect(examples.map(&:id)).to match_array(['8', '10'])
|
183
183
|
end
|
@@ -187,7 +187,7 @@ describe Zermelo::Filter do
|
|
187
187
|
examples = example_class.intersect(:id => ['8']).
|
188
188
|
union(:id => ['9', '10'], :name => [nil, /^Jam/]).all
|
189
189
|
expect(examples).not_to be_nil
|
190
|
-
expect(examples).to
|
190
|
+
expect(examples).to be_a(Set)
|
191
191
|
expect(examples.size).to eq(2)
|
192
192
|
expect(examples.map(&:id)).to match_array(['8', '9'])
|
193
193
|
end
|
@@ -197,7 +197,7 @@ describe Zermelo::Filter do
|
|
197
197
|
|
198
198
|
examples = example_class.intersect(:name => ['Jay Johns', 'James Brown']).all
|
199
199
|
expect(examples).not_to be_nil
|
200
|
-
expect(examples).to
|
200
|
+
expect(examples).to be_a(Set)
|
201
201
|
expect(examples.size).to eq(2)
|
202
202
|
expect(examples.map(&:id)).to match_array(['9', '10'])
|
203
203
|
end
|
@@ -208,7 +208,7 @@ describe Zermelo::Filter do
|
|
208
208
|
examples = example_class.intersect(:active => false).
|
209
209
|
union(:name => ['Jay Johns', 'James Brown']).all
|
210
210
|
expect(examples).not_to be_nil
|
211
|
-
expect(examples).to
|
211
|
+
expect(examples).to be_a(Set)
|
212
212
|
expect(examples.size).to eq(2)
|
213
213
|
expect(examples.map(&:id)).to match_array(['9', '10'])
|
214
214
|
end
|
@@ -216,7 +216,7 @@ describe Zermelo::Filter do
|
|
216
216
|
it 'excludes particular records' do
|
217
217
|
example = example_class.diff(:active => true).all
|
218
218
|
expect(example).not_to be_nil
|
219
|
-
expect(example).to
|
219
|
+
expect(example).to be_a(Set)
|
220
220
|
expect(example.size).to eq(1)
|
221
221
|
expect(example.map(&:id)).to eq(['9'])
|
222
222
|
end
|
@@ -260,7 +260,7 @@ describe Zermelo::Filter do
|
|
260
260
|
it 'sorts records by an attribute' do
|
261
261
|
example = example_class.sort(:name, :order => 'alpha').all
|
262
262
|
expect(example).not_to be_nil
|
263
|
-
expect(example).to
|
263
|
+
expect(example).to be_a(Set)
|
264
264
|
expect(example.size).to eq(2)
|
265
265
|
expect(example.map(&:id)).to eq(['9', '8'])
|
266
266
|
end
|
data/spec/support/mock_logger.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
|
+
# Store logger messages in specs for possible later output
|
1
2
|
class MockLogger
|
2
3
|
attr_accessor :messages, :errors
|
3
4
|
|
4
|
-
def self.configure_log(name,
|
5
|
+
def self.configure_log(name, _config = {})
|
5
6
|
@name = name
|
6
7
|
end
|
7
8
|
|
@@ -20,7 +21,8 @@ class MockLogger
|
|
20
21
|
def #{level}(msg = nil, &block)
|
21
22
|
msg = yield if msg.nil? && block_given?
|
22
23
|
@messages << '[#{level.upcase}] :: ' +
|
23
|
-
(self.class.instance_variable_get('@name') || 'flapjack') + ' :: ' +
|
24
|
+
(self.class.instance_variable_get('@name') || 'flapjack') + ' :: ' +
|
25
|
+
msg
|
24
26
|
end
|
25
27
|
RUBY
|
26
28
|
end
|
@@ -30,9 +32,11 @@ class MockLogger
|
|
30
32
|
def #{level}(msg = nil, &block)
|
31
33
|
msg = yield if msg.nil? && block_given?
|
32
34
|
@messages << '[#{level.upcase}] :: ' +
|
33
|
-
(self.class.instance_variable_get('@name') || 'flapjack') + ' :: ' +
|
35
|
+
(self.class.instance_variable_get('@name') || 'flapjack') + ' :: ' +
|
36
|
+
msg
|
34
37
|
@errors << '[#{level.upcase}] :: ' +
|
35
|
-
(self.class.instance_variable_get('@name') || 'flapjack') + ' :: ' +
|
38
|
+
(self.class.instance_variable_get('@name') || 'flapjack') + ' :: ' +
|
39
|
+
msg
|
36
40
|
end
|
37
41
|
ERRORS
|
38
42
|
end
|
@@ -44,5 +48,4 @@ class MockLogger
|
|
44
48
|
end
|
45
49
|
LEVELS
|
46
50
|
end
|
47
|
-
|
48
|
-
end
|
51
|
+
end
|
@@ -1,9 +1,10 @@
|
|
1
1
|
require 'rspec/core/formatters/base_formatter'
|
2
2
|
|
3
|
+
# Outputs how log each spec takes to run
|
3
4
|
class ProfileAllFormatter < RSpec::Core::Formatters::BaseFormatter
|
4
|
-
|
5
5
|
RSpec::Core::Formatters.register self,
|
6
|
-
|
6
|
+
:example_started, :example_passed,
|
7
|
+
:start_dump
|
7
8
|
|
8
9
|
def initialize(output)
|
9
10
|
super(output)
|
@@ -12,33 +13,33 @@ class ProfileAllFormatter < RSpec::Core::Formatters::BaseFormatter
|
|
12
13
|
|
13
14
|
def start(notification)
|
14
15
|
super(notification)
|
15
|
-
@output.puts
|
16
|
+
@output.puts 'Profiling enabled.'
|
16
17
|
end
|
17
18
|
|
18
|
-
def example_started(
|
19
|
-
@time = (
|
19
|
+
def example_started(_notification)
|
20
|
+
@time = (Time.respond_to?(:zone) && Time.zone) ? Time.zone.now : Time.now
|
20
21
|
end
|
21
22
|
|
22
23
|
def example_passed(notification)
|
24
|
+
now = (Time.respond_to?(:zone) && Time.zone) ? Time.zone.now : Time.now
|
23
25
|
@example_times << [
|
24
26
|
notification.example.example_group.description,
|
25
27
|
notification.example.description,
|
26
|
-
|
28
|
+
now - @time
|
27
29
|
]
|
28
30
|
end
|
29
31
|
|
30
|
-
def start_dump(
|
32
|
+
def start_dump(_notification)
|
31
33
|
@output.puts "\n\nExample times:\n"
|
32
34
|
|
33
|
-
@example_times = @example_times.sort_by do |
|
35
|
+
@example_times = @example_times.sort_by do |_description, _example, time|
|
34
36
|
time
|
35
37
|
end.reverse
|
36
38
|
|
37
39
|
@example_times.each do |description, example, time|
|
38
|
-
@output.print
|
40
|
+
@output.print format('%.7f', time)
|
39
41
|
@output.puts " #{description} #{example}"
|
40
42
|
end
|
41
43
|
@output.flush
|
42
44
|
end
|
43
|
-
|
44
|
-
end
|
45
|
+
end
|
@@ -1,11 +1,13 @@
|
|
1
1
|
require 'rspec/core/formatters/base_text_formatter'
|
2
2
|
|
3
|
+
# Output text witout colourising it
|
3
4
|
module NoColorizer
|
4
|
-
def self.wrap(text,
|
5
|
+
def self.wrap(text, _)
|
5
6
|
text
|
6
7
|
end
|
7
8
|
end
|
8
9
|
|
10
|
+
# Re-implements RSpec's doc formatter without ANSI text colours
|
9
11
|
class UncoloredDocFormatter < RSpec::Core::Formatters::BaseTextFormatter
|
10
12
|
RSpec::Core::Formatters.register(self, :example_group_started,
|
11
13
|
:example_group_finished,
|
@@ -25,7 +27,7 @@ class UncoloredDocFormatter < RSpec::Core::Formatters::BaseTextFormatter
|
|
25
27
|
@group_level += 1
|
26
28
|
end
|
27
29
|
|
28
|
-
def example_group_finished(
|
30
|
+
def example_group_finished(_)
|
29
31
|
@group_level -= 1
|
30
32
|
end
|
31
33
|
|
@@ -34,12 +36,12 @@ class UncoloredDocFormatter < RSpec::Core::Formatters::BaseTextFormatter
|
|
34
36
|
end
|
35
37
|
|
36
38
|
def example_pending(pending)
|
37
|
-
output.puts "#{current_indentation}#{pending.example.description.strip} "
|
39
|
+
output.puts "#{current_indentation}#{pending.example.description.strip} " \
|
38
40
|
"(PENDING: #{pending.example.execution_result.pending_message})"
|
39
41
|
end
|
40
42
|
|
41
43
|
def example_failed(failure)
|
42
|
-
output.puts "#{current_indentation}#{failure.example.description.strip} "
|
44
|
+
output.puts "#{current_indentation}#{failure.example.description.strip} " \
|
43
45
|
"(FAILED - #{next_failure_index})"
|
44
46
|
end
|
45
47
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: zermelo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ali Graham
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-08-
|
11
|
+
date: 2015-08-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|
@@ -91,6 +91,7 @@ files:
|
|
91
91
|
- lib/zermelo/filters/steps/sort_step.rb
|
92
92
|
- lib/zermelo/locks/no_lock.rb
|
93
93
|
- lib/zermelo/locks/redis_lock.rb
|
94
|
+
- lib/zermelo/ordered_set.rb
|
94
95
|
- lib/zermelo/record.rb
|
95
96
|
- lib/zermelo/records/attributes.rb
|
96
97
|
- lib/zermelo/records/class_methods.rb
|