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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c8442ff692747d2d56fc983ec85bc6665656748a
4
- data.tar.gz: a2ef423d8095fb2d9e7f12f5cf84ca2dadc9c528
3
+ metadata.gz: 69911858d3394494076bade1249037f111eb5e7b
4
+ data.tar.gz: 5b2c9e463d7fb0b71a7135bad76b481e4e18629a
5
5
  SHA512:
6
- metadata.gz: 5364e3f2a9ea70de9f7ec5142909736952d4ab3d3167823877ed40c49759ddbffa002756cbe730d60357776768707f92f0557120b1fe3877439d30a56833ef45
7
- data.tar.gz: d15a22b19592f45711c46b684d0fe0d943c971c87a21843a2d291692c5af51756cb8542545e55e7a88042429c45c5021158e35034bf9b98916ecf3dca006072c
6
+ metadata.gz: b2b62aabc2f4421d99e53bc298e260550d2124cb957786c6ef1bce894cb0fd43c0cd704a6378e6a6acfbc2cfebffda52412984756fc314f98322a2ea63cb7a23
7
+ data.tar.gz: 69d7a165a1da92e759c6923828d12340d793eead6ee20681a0e22c9f7dde901b0421323ce0fd4c960bc708f5d84e9ebfab79bf166f638a3768032c8b75214074
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  ## Zermelo Changelog
2
2
 
3
+ # 1.3.0 - 2015-08-15
4
+
5
+ * handle some Zermelo objects as query values, less data back-and-forth
6
+ * return Set/OrderedSet objects as appropriate (58318c25)
7
+
3
8
  # 1.2.1 - 2015-08-07
4
9
 
5
10
  * 'empty' filter method (637d0c9c)
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 an Array of all the records stored for this class |
260
- |`each` | | Yields all records to the provided block, returns the same Array as .all(): [Array#each](http://ruby-doc.org/core-2.1.2/Array.html#method-i-each) |
261
- |`collect` / `map` | | Yields all records to the provided block, returns an Array with the values returned from the block: [Array#collect](http://ruby-doc.org/core-2.1.2/Array.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: [Array#select](http://ruby-doc.org/core-2.1.2/Array.html#method-i-select) |
263
- |`reject` | | Yields all records to the provided block, returns an Array with each record where the block returned false: [Array#reject](http://ruby-doc.org/core-2.1.2/Array.html#method-i-reject) |
264
- |`ids` | | Returns an Array with the ids of all stored records |
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 an Array of instantiated records for the ids, with nils if the respective record is not present |
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 an Array of instantiated records for the ids, or raises a Zermelo::Records::RecordsNotFound exception if any are not present |
273
- |`associated_ids_for` | association | (Defined in the `Associations` section below) |
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 # == [1, 2]
376
+ p post.comments.ids # == #<Set: {}>
377
+ p Comment.ids # == #<Set: {'1', '2'}>
378
378
  post.comments << comment1
379
- p post.comments.ids # == [1]
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' => ['1']}
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' => ['1', '3'], 'b' => ['2']}
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' => ['1', '3'], 'b' => ['2']}
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 # -> [Comment, Comment, ...]
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 an Array with one member, a Comment record with `{:id => 'ca9e427d-4d81-47f8-bcfe-bb614d40528c', :title => 'Interesting'}`
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 # ['1', '2']
511
- Comment.intersect(:created_at => range).ids # ['2']
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; there's currently an experimental InfluxDB 0.8 backend (waiting for the Ruby driver to update for 0.9 support).
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
@@ -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
- return [] if initial_id_data.nil?
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? ? [] : data_keys.collect {|k| k =~ /^#{class_key}\/(.+)$/; $1 }
116
+ data_keys.empty? ? Set.new : Set.new(data_keys.collect {|k| k =~ /^#{class_key}\/(.+)$/; $1 })
109
117
  when :count
110
- data_keys.empty? ? 0 : data_keys.size
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 block -- takes a block and passes the name of a set to
77
- # it; deletes all temporary sets once done
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
- ret = if sc.nil?
96
- yield(@initial_key)
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 = backend.key_to_redis_key(@initial_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
- step_opts[:source] = result unless step == last_step
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
- val = value.is_a?(Set) ? value.to_a : value
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
- if val.is_a?(Enumerable)
41
- conditions_set = associated_class.send(:temp_key, source.type)
42
- temp_keys << conditions_set
43
- r_conditions_set = backend.key_to_redis_key(conditions_set)
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
- backend.temp_key_wrap do |conditions_temp_keys|
46
- index_keys = val.collect {|v|
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
- memo << conditions_set
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
- memo << backend.index_lookup(att, associated_class,
62
- idx_class, val, attr_types[att], temp_keys)
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
@@ -0,0 +1,11 @@
1
+ require 'active_support/ordered_hash'
2
+
3
+ module Zermelo
4
+ class OrderedSet < Set
5
+
6
+ def initialize(enum = nil, &block)
7
+ @hash = ActiveSupport::OrderedHash.new
8
+ super
9
+ end
10
+ end
11
+ end
@@ -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)
@@ -1,3 +1,3 @@
1
1
  module Zermelo
2
- VERSION = '1.2.1'
2
+ VERSION = '1.3.0'
3
3
  end
@@ -20,14 +20,14 @@ describe Zermelo::Associations::Multiple do
20
20
 
21
21
  children = parent.children.all
22
22
 
23
- expect(children).to be_an(Array)
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 be_an(Array)
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 be_an(Array)
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 be_an(Array)
128
+ expect(important_kids).to be_a(Set)
129
129
  expect(important_kids.size).to eq(2)
130
- expect(important_kids.map(&:id)).to match_array(['3', '4'])
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 be_an(Array)
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 be_an(Array)
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 be_an(Array)
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 be_an(Array)
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 be_an(Array)
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 be_an(Array)
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 be_an(Array)
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 be_an(Array)
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 be_an(Array)
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 be_an(Array)
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 be_an(Array)
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 be_an(Array)
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 be_an(Array)
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 be_an(Array)
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 be_an(Array)
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 be_an(Array)
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 be_an(Array)
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 be_an(Array)
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 be_an(Array)
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(Array)
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 be_an(Array)
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 be_an(Array)
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 be_an(Array)
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 be_an(Array)
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 be_an(Array)
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 be_an(Array)
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 be_an(Array)
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 be_an(Array)
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 match_array(['8', '9'])
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 be_an(Array)
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 be_an(Array)
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 be_an(Array)
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 be_an(Array)
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 be_an(Array)
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 be_an(Array)
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 be_an(Array)
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
@@ -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, config = {})
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') + ' :: ' + msg
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') + ' :: ' + msg
35
+ (self.class.instance_variable_get('@name') || 'flapjack') + ' :: ' +
36
+ msg
34
37
  @errors << '[#{level.upcase}] :: ' +
35
- (self.class.instance_variable_get('@name') || 'flapjack') + ' :: ' + msg
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
- :example_started, :example_passed, :start_dump
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 "Profiling enabled."
16
+ @output.puts 'Profiling enabled.'
16
17
  end
17
18
 
18
- def example_started(notification)
19
- @time = ((Time.respond_to?(:zone) && Time.zone) ? Time.zone.now : Time.now)
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
- ((Time.respond_to?(:zone) && Time.zone) ? Time.zone.now : Time.now) - @time
28
+ now - @time
27
29
  ]
28
30
  end
29
31
 
30
- def start_dump(notification)
32
+ def start_dump(_notification)
31
33
  @output.puts "\n\nExample times:\n"
32
34
 
33
- @example_times = @example_times.sort_by do |description, example, time|
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 sprintf("%.7f", time)
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, color)
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(notification)
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.2.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-07 00:00:00.000000000 Z
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