zermelo 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -0
  3. data/README.md +76 -52
  4. data/lib/zermelo/associations/association_data.rb +4 -3
  5. data/lib/zermelo/associations/class_methods.rb +37 -50
  6. data/lib/zermelo/associations/index.rb +3 -1
  7. data/lib/zermelo/associations/multiple.rb +247 -0
  8. data/lib/zermelo/associations/range_index.rb +44 -0
  9. data/lib/zermelo/associations/singular.rb +193 -0
  10. data/lib/zermelo/associations/unique_index.rb +4 -3
  11. data/lib/zermelo/backend.rb +120 -0
  12. data/lib/zermelo/backends/{influxdb_backend.rb → influxdb.rb} +87 -31
  13. data/lib/zermelo/backends/{redis_backend.rb → redis.rb} +53 -58
  14. data/lib/zermelo/backends/stub.rb +43 -0
  15. data/lib/zermelo/filter.rb +194 -0
  16. data/lib/zermelo/filters/index_range.rb +22 -0
  17. data/lib/zermelo/filters/{influxdb_filter.rb → influxdb.rb} +12 -11
  18. data/lib/zermelo/filters/redis.rb +173 -0
  19. data/lib/zermelo/filters/steps/list_step.rb +48 -30
  20. data/lib/zermelo/filters/steps/set_step.rb +148 -89
  21. data/lib/zermelo/filters/steps/sort_step.rb +2 -2
  22. data/lib/zermelo/record.rb +53 -0
  23. data/lib/zermelo/records/attributes.rb +32 -0
  24. data/lib/zermelo/records/class_methods.rb +12 -25
  25. data/lib/zermelo/records/{influxdb_record.rb → influxdb.rb} +3 -4
  26. data/lib/zermelo/records/instance_methods.rb +9 -8
  27. data/lib/zermelo/records/key.rb +3 -1
  28. data/lib/zermelo/records/redis.rb +17 -0
  29. data/lib/zermelo/records/stub.rb +17 -0
  30. data/lib/zermelo/version.rb +1 -1
  31. data/spec/lib/zermelo/associations/index_spec.rb +70 -1
  32. data/spec/lib/zermelo/associations/multiple_spec.rb +1084 -0
  33. data/spec/lib/zermelo/associations/range_index_spec.rb +77 -0
  34. data/spec/lib/zermelo/associations/singular_spec.rb +149 -0
  35. data/spec/lib/zermelo/associations/unique_index_spec.rb +58 -2
  36. data/spec/lib/zermelo/filter_spec.rb +363 -0
  37. data/spec/lib/zermelo/locks/redis_lock_spec.rb +3 -3
  38. data/spec/lib/zermelo/records/instance_methods_spec.rb +206 -0
  39. data/spec/spec_helper.rb +9 -1
  40. data/spec/support/mock_logger.rb +48 -0
  41. metadata +31 -46
  42. data/lib/zermelo/associations/belongs_to.rb +0 -115
  43. data/lib/zermelo/associations/has_and_belongs_to_many.rb +0 -128
  44. data/lib/zermelo/associations/has_many.rb +0 -120
  45. data/lib/zermelo/associations/has_one.rb +0 -109
  46. data/lib/zermelo/associations/has_sorted_set.rb +0 -124
  47. data/lib/zermelo/backends/base.rb +0 -115
  48. data/lib/zermelo/filters/base.rb +0 -212
  49. data/lib/zermelo/filters/redis_filter.rb +0 -111
  50. data/lib/zermelo/filters/steps/sorted_set_step.rb +0 -156
  51. data/lib/zermelo/records/base.rb +0 -62
  52. data/lib/zermelo/records/redis_record.rb +0 -27
  53. data/spec/lib/zermelo/associations/belongs_to_spec.rb +0 -6
  54. data/spec/lib/zermelo/associations/has_many_spec.rb +0 -6
  55. data/spec/lib/zermelo/associations/has_one_spec.rb +0 -6
  56. data/spec/lib/zermelo/associations/has_sorted_set.spec.rb +0 -6
  57. data/spec/lib/zermelo/backends/influxdb_backend_spec.rb +0 -0
  58. data/spec/lib/zermelo/backends/moneta_backend_spec.rb +0 -0
  59. data/spec/lib/zermelo/filters/influxdb_filter_spec.rb +0 -0
  60. data/spec/lib/zermelo/filters/redis_filter_spec.rb +0 -0
  61. data/spec/lib/zermelo/records/influxdb_record_spec.rb +0 -434
  62. data/spec/lib/zermelo/records/key_spec.rb +0 -6
  63. data/spec/lib/zermelo/records/redis_record_spec.rb +0 -1461
  64. data/spec/lib/zermelo/records/type_validator_spec.rb +0 -6
  65. data/spec/lib/zermelo/version_spec.rb +0 -6
  66. data/spec/lib/zermelo_spec.rb +0 -6
@@ -1,1461 +0,0 @@
1
- require 'spec_helper'
2
- require 'zermelo/records/redis_record'
3
-
4
- # NB: also covers associations.rb, which is mixed in to Zermelo::Record
5
-
6
- describe Zermelo::Records::RedisRecord, :redis => true do
7
-
8
- module Zermelo
9
- class RedisExample
10
- include Zermelo::Records::RedisRecord
11
-
12
- define_attributes :name => :string,
13
- :email => :string,
14
- :active => :boolean
15
-
16
- validates :name, :presence => true
17
-
18
- has_many :children, :class_name => 'Zermelo::RedisExampleChild',
19
- :inverse_of => :example, :before_add => :fail_if_roger
20
-
21
- has_sorted_set :data, :class_name => 'Zermelo::RedisExampleDatum',
22
- :key => :timestamp, :inverse_of => :example
23
-
24
- has_and_belongs_to_many :templates, :class_name => 'Zermelo::Template',
25
- :inverse_of => :examples
26
-
27
- index_by :active
28
- unique_index_by :name
29
-
30
- def fail_if_roger(*childs)
31
- raise "Not adding child" if childs.any? {|c| 'Roger'.eql?(c.name) }
32
- end
33
- end
34
-
35
- class RedisUnsavable
36
- include Zermelo::Records::RedisRecord
37
-
38
- define_attributes :name => :string
39
-
40
- before_create :block_create
41
-
42
- def block_create
43
- false
44
- end
45
- end
46
-
47
- class RedisExampleChild
48
- include Zermelo::Records::RedisRecord
49
-
50
- define_attributes :name => :string,
51
- :important => :boolean
52
-
53
- index_by :important
54
-
55
- belongs_to :example, :class_name => 'Zermelo::RedisExample', :inverse_of => :children
56
-
57
- validates :name, :presence => true
58
- end
59
-
60
- class RedisExampleDatum
61
- include Zermelo::Records::RedisRecord
62
-
63
- define_attributes :timestamp => :timestamp,
64
- :summary => :string,
65
- :emotion => :string
66
-
67
- belongs_to :example, :class_name => 'Zermelo::RedisExample', :inverse_of => :data
68
-
69
- index_by :emotion
70
-
71
- validates :timestamp, :presence => true
72
- end
73
-
74
- class Template
75
- include Zermelo::Records::RedisRecord
76
-
77
- define_attributes :name => :string
78
-
79
- has_and_belongs_to_many :examples, :class_name => 'Zermelo::RedisExample',
80
- :inverse_of => :templates
81
-
82
- validates :name, :presence => true
83
- end
84
- end
85
-
86
- let(:redis) { Zermelo.redis }
87
-
88
- def create_example(attrs = {})
89
- redis.hmset("redis_example:#{attrs[:id]}:attrs",
90
- {'name' => attrs[:name], 'email' => attrs[:email], 'active' => attrs[:active]}.to_a.flatten)
91
- redis.sadd("redis_example::indices:by_active:boolean:#{!!attrs[:active]}", attrs[:id])
92
- name = attrs[:name].gsub(/%/, '%%').gsub(/ /, '%20').gsub(/:/, '%3A')
93
- redis.hset('redis_example::indices:by_name', "string:#{name}", attrs[:id])
94
- redis.sadd('redis_example::attrs:ids', attrs[:id])
95
- end
96
-
97
- it "is invalid without a name" do
98
- example = Zermelo::RedisExample.new(:id => '1', :email => 'jsmith@example.com')
99
- expect(example).not_to be_valid
100
-
101
- errs = example.errors
102
- expect(errs).not_to be_nil
103
- expect(errs[:name]).to eq(["can't be blank"])
104
- end
105
-
106
- it "adds a record's attributes to redis" do
107
- example = Zermelo::RedisExample.new(:id => '1', :name => 'John Smith',
108
- :email => 'jsmith@example.com', :active => true)
109
- expect(example).to be_valid
110
- expect(example.save).to be_truthy
111
-
112
- expect(redis.keys('*')).to match_array(['redis_example::attrs:ids',
113
- 'redis_example:1:attrs',
114
- 'redis_example::indices:by_name',
115
- 'redis_example::indices:by_active:boolean:true'])
116
- expect(redis.smembers('redis_example::attrs:ids')).to eq(['1'])
117
- expect(redis.hgetall('redis_example:1:attrs')).to eq(
118
- {'name' => 'John Smith', 'email' => 'jsmith@example.com', 'active' => 'true'}
119
- )
120
- expect(redis.hgetall('redis_example::indices:by_name')).to eq({'string:John%20Smith' => '1'})
121
- expect(redis.smembers('redis_example::indices:by_active:boolean:true')).to eq(
122
- ['1']
123
- )
124
- end
125
-
126
- it 'raises an RecordInvalid exception if validation fails while saving' do
127
- example = Zermelo::RedisExample.new(:id => '1', :email => 'jsmith@example.com')
128
-
129
- expect {
130
- example.save!
131
- }.to raise_error(Zermelo::Records::Errors::RecordInvalid)
132
- end
133
-
134
- it 'raises a RecordNotSaved exception if a callback blocks saving' do
135
- example = Zermelo::RedisUnsavable.new(:id => '1', :name => 'not saving')
136
-
137
- expect {
138
- example.save!
139
- }.to raise_error(Zermelo::Records::Errors::RecordNotSaved)
140
- end
141
-
142
- it "finds a record by id in redis" do
143
- create_example(:id => '8', :name => 'John Jones',
144
- :email => 'jjones@example.com', :active => 'true')
145
-
146
- example = Zermelo::RedisExample.find_by_id('8')
147
- expect(example).not_to be_nil
148
- expect(example.id).to eq('8')
149
- expect(example.name).to eq('John Jones')
150
- expect(example.email).to eq('jjones@example.com')
151
- end
152
-
153
- it "finds records by a uniquely indexed value in redis" do
154
- create_example(:id => '8', :name => 'John Jones',
155
- :email => 'jjones@example.com', :active => 'true')
156
-
157
- examples = Zermelo::RedisExample.intersect(:name => 'John Jones').all
158
- expect(examples).not_to be_nil
159
- expect(examples).to be_an(Array)
160
- expect(examples.size).to eq(1)
161
- example = examples.first
162
- expect(example.id).to eq('8')
163
- expect(example.name).to eq('John Jones')
164
- expect(example.email).to eq('jjones@example.com')
165
- end
166
-
167
- it 'finds records by regex match against an indexed value in redis'
168
-
169
- it 'finds records by regex match against a uniquely indexed value in redis' do
170
- create_example(:id => '8', :name => 'John Jones',
171
- :email => 'jjones@example.com', :active => 'true')
172
-
173
- examples = Zermelo::RedisExample.intersect(:name => /hn Jones/).all
174
- expect(examples).not_to be_nil
175
- expect(examples).to be_an(Array)
176
- expect(examples.size).to eq(1)
177
- example = examples.first
178
- expect(example.id).to eq('8')
179
- expect(example.name).to eq('John Jones')
180
- expect(example.email).to eq('jjones@example.com')
181
- end
182
-
183
- it 'cannot find records by regex match against non-string values' do
184
- create_example(:id => '8', :name => 'John Jones',
185
- :email => 'jjones@example.com', :active => true)
186
- create_example(:id => '9', :name => 'James Brown',
187
- :email => 'jbrown@example.com', :active => false)
188
-
189
- expect {
190
- Zermelo::RedisExample.intersect(:active => /alse/).all
191
- }.to raise_error
192
- end
193
-
194
- it "updates a record's attributes in redis" do
195
- create_example(:id => '8', :name => 'John Jones',
196
- :email => 'jjones@example.com', :active => 'true')
197
-
198
- example = Zermelo::RedisExample.find_by_id('8')
199
- example.name = 'Jane Janes'
200
- example.email = 'jjanes@example.com'
201
- expect(example.save).to be_truthy
202
-
203
- expect(redis.keys('*')).to match_array(['redis_example::attrs:ids',
204
- 'redis_example:8:attrs',
205
- 'redis_example::indices:by_name',
206
- 'redis_example::indices:by_active:boolean:true'])
207
- expect(redis.smembers('redis_example::attrs:ids')).to eq(['8'])
208
- expect(redis.hgetall('redis_example:8:attrs')).to eq(
209
- {'name' => 'Jane Janes', 'email' => 'jjanes@example.com', 'active' => 'true'}
210
- )
211
- expect(redis.smembers('redis_example::indices:by_active:boolean:true')).to eq(
212
- ['8']
213
- )
214
- end
215
-
216
- it "deletes a record's attributes from redis" do
217
- create_example(:id => '8', :name => 'John Jones',
218
- :email => 'jjones@example.com', :active => 'true')
219
-
220
- expect(redis.keys('*')).to match_array(['redis_example::attrs:ids',
221
- 'redis_example:8:attrs',
222
- 'redis_example::indices:by_name',
223
- 'redis_example::indices:by_active:boolean:true'])
224
-
225
- example = Zermelo::RedisExample.find_by_id('8')
226
- example.destroy
227
-
228
- expect(redis.keys('*')).to eq([])
229
- end
230
-
231
- it "resets changed state on refresh" do
232
- create_example(:id => '8', :name => 'John Jones',
233
- :email => 'jjones@example.com', :active => 'true')
234
- example = Zermelo::RedisExample.find_by_id('8')
235
-
236
- example.name = "King Henry VIII"
237
- expect(example.changed).to include('name')
238
- expect(example.changes).to eq({'name' => ['John Jones', 'King Henry VIII']})
239
-
240
- example.refresh
241
- expect(example.changed).to be_empty
242
- expect(example.changes).to be_empty
243
- end
244
-
245
- it "stores a string as an attribute value"
246
- it "stores an integer as an attribute value"
247
- it "stores a timestamp as an attribute value"
248
- it "stores a boolean as an attribute value"
249
-
250
- it "stores a list as an attribute value"
251
- it "stores a set as an attribute value"
252
- it "stores a hash as an attribute value"
253
-
254
- context 'pagination' do
255
-
256
- before do
257
- create_example(:id => '1', :name => 'mno')
258
- create_example(:id => '2', :name => 'abc')
259
- create_example(:id => '3', :name => 'jkl')
260
- create_example(:id => '4', :name => 'ghi')
261
- create_example(:id => '5', :name => 'def')
262
- end
263
-
264
- it "returns paginated query responses" do
265
- expect(Zermelo::RedisExample.sort(:id).page(1, :per_page => 3).map(&:id)).to eq(['1','2', '3'])
266
- expect(Zermelo::RedisExample.sort(:id).page(2, :per_page => 2).map(&:id)).to eq(['3','4'])
267
- expect(Zermelo::RedisExample.sort(:id).page(3, :per_page => 2).map(&:id)).to eq(['5'])
268
- expect(Zermelo::RedisExample.sort(:id).page(3, :per_page => 3).map(&:id)).to eq([])
269
-
270
- expect(Zermelo::RedisExample.sort(:name).page(1, :per_page => 3).map(&:id)).to eq(['2','5', '4'])
271
- expect(Zermelo::RedisExample.sort(:name).page(2, :per_page => 2).map(&:id)).to eq(['4','3'])
272
- expect(Zermelo::RedisExample.sort(:name).page(3, :per_page => 2).map(&:id)).to eq(['1'])
273
- expect(Zermelo::RedisExample.sort(:name).page(3, :per_page => 3).map(&:id)).to eq([])
274
- end
275
-
276
- end
277
-
278
- context 'filters' do
279
-
280
- let(:active) {
281
- create_example(:id => '8', :name => 'John Jones',
282
- :email => 'jjones@example.com', :active => true)
283
- }
284
-
285
- let(:inactive) {
286
- create_example(:id => '9', :name => 'James Brown',
287
- :email => 'jbrown@example.com', :active => false)
288
- }
289
-
290
- before do
291
- active; inactive
292
- end
293
-
294
- it 'can append to a filter chain fragment more than once' do
295
- inter = Zermelo::RedisExample.intersect(:active => true)
296
- expect(inter.ids).to eq(['8'])
297
-
298
- union = inter.union(:name => 'James Brown')
299
- expect(union.ids).to eq(['8', '9'])
300
-
301
- diff = inter.diff(:id => ['8'])
302
- expect(diff.ids).to eq([])
303
- end
304
-
305
- it "filters all class records by indexed attribute values" do
306
- example = Zermelo::RedisExample.intersect(:active => true).all
307
- expect(example).not_to be_nil
308
- expect(example).to be_an(Array)
309
- expect(example.size).to eq(1)
310
- expect(example.map(&:id)).to eq(['8'])
311
- end
312
-
313
- it 'filters by id attribute values' do
314
- example = Zermelo::RedisExample.intersect(:id => '9').all
315
- expect(example).not_to be_nil
316
- expect(example).to be_an(Array)
317
- expect(example.size).to eq(1)
318
- expect(example.map(&:id)).to eq(['9'])
319
- end
320
-
321
- it 'supports sequential intersection and union operations' do
322
- examples = Zermelo::RedisExample.intersect(:active => true).union(:active => false).all
323
- expect(examples).not_to be_nil
324
- expect(examples).to be_an(Array)
325
- expect(examples.size).to eq(2)
326
- expect(examples.map(&:id)).to match_array(['8', '9'])
327
- end
328
-
329
- it "ANDs multiple union arguments, not ORs them" do
330
- create_example(:id => '10', :name => 'Jay Johns',
331
- :email => 'jjohns@example.com', :active => true)
332
- examples = Zermelo::RedisExample.intersect(:id => ['8']).union(:id => ['9', '10'], :active => true).all
333
- expect(examples).not_to be_nil
334
- expect(examples).to be_an(Array)
335
- expect(examples.size).to eq(2)
336
- expect(examples.map(&:id)).to match_array(['8', '10'])
337
- end
338
-
339
- it 'supports a regex as argument in union after intersect' do
340
- create_example(:id => '10', :name => 'Jay Johns',
341
- :email => 'jjohns@example.com', :active => true)
342
- examples = Zermelo::RedisExample.intersect(:id => ['8']).union(:id => ['9', '10'], :name => [nil, /^Jam/]).all
343
- expect(examples).not_to be_nil
344
- expect(examples).to be_an(Array)
345
- expect(examples.size).to eq(2)
346
- expect(examples.map(&:id)).to match_array(['8', '9'])
347
- end
348
-
349
- it 'allows intersection operations across multiple values for an attribute' do
350
- create_example(:id => '10', :name => 'Jay Johns',
351
- :email => 'jjohns@example.com', :active => true)
352
-
353
- examples = Zermelo::RedisExample.intersect(:name => ['Jay Johns', 'James Brown']).all
354
- expect(examples).not_to be_nil
355
- expect(examples).to be_an(Array)
356
- expect(examples.size).to eq(2)
357
- expect(examples.map(&:id)).to match_array(['9', '10'])
358
- end
359
-
360
- it 'allows union operations across multiple values for an attribute' do
361
- create_example(:id => '10', :name => 'Jay Johns',
362
- :email => 'jjohns@example.com', :active => true)
363
-
364
- examples = Zermelo::RedisExample.intersect(:active => false).union(:name => ['Jay Johns', 'James Brown']).all
365
- expect(examples).not_to be_nil
366
- expect(examples).to be_an(Array)
367
- expect(examples.size).to eq(2)
368
- expect(examples.map(&:id)).to match_array(['9', '10'])
369
- end
370
-
371
- it 'filters by multiple id attribute values' do
372
- create_example(:id => '10', :name => 'Jay Johns',
373
- :email => 'jjohns@example.com', :active => true)
374
-
375
- example = Zermelo::RedisExample.intersect(:id => ['8', '10']).all
376
- expect(example).not_to be_nil
377
- expect(example).to be_an(Array)
378
- expect(example.size).to eq(2)
379
- expect(example.map(&:id)).to eq(['8', '10'])
380
- end
381
-
382
- it 'excludes particular records' do
383
- example = Zermelo::RedisExample.diff(:active => true).all
384
- expect(example).not_to be_nil
385
- expect(example).to be_an(Array)
386
- expect(example.size).to eq(1)
387
- expect(example.map(&:id)).to eq(['9'])
388
- end
389
-
390
- it 'sorts records by an attribute' do
391
- example = Zermelo::RedisExample.sort(:name, :order => 'alpha').all
392
- expect(example).not_to be_nil
393
- expect(example).to be_an(Array)
394
- expect(example.size).to eq(2)
395
- expect(example.map(&:id)).to eq(['9', '8'])
396
- end
397
-
398
- it "does not return a spurious record count when records don't exist" do
399
- scope = Zermelo::RedisExample.intersect(:id => ['3000', '5000'])
400
- expect(scope.all).to be_empty
401
- expect(scope.count).to eq 0
402
- end
403
-
404
- end
405
-
406
-
407
- context "has_many" do
408
-
409
- def create_child(parent, attrs = {})
410
- redis.sadd("redis_example:#{parent.id}:assocs:children_ids", attrs[:id]) unless parent.nil?
411
-
412
- redis.hmset("redis_example_child:#{attrs[:id]}:attrs",
413
- {'name' => attrs[:name], 'important' => !!attrs[:important]}.to_a.flatten)
414
-
415
- redis.hmset("redis_example_child:#{attrs[:id]}:assocs:belongs_to",
416
- {'example_id' => parent.id}.to_a.flatten) unless parent.nil?
417
-
418
- redis.sadd("redis_example_child::indices:by_important:boolean:#{!!attrs[:important]}", attrs[:id])
419
-
420
- redis.sadd('redis_example_child::attrs:ids', attrs[:id])
421
- end
422
-
423
- it "sets a parent/child has_many relationship between two records in redis" do
424
- create_example(:id => '8', :name => 'John Jones',
425
- :email => 'jjones@example.com', :active => 'true')
426
-
427
- child = Zermelo::RedisExampleChild.new(:id => '3', :name => 'Abel Tasman')
428
- expect(child.save).to be_truthy
429
-
430
- example = Zermelo::RedisExample.find_by_id('8')
431
- example.children << child
432
-
433
- expect(redis.keys('*')).to match_array(['redis_example::attrs:ids',
434
- 'redis_example::indices:by_name',
435
- 'redis_example::indices:by_active:boolean:true',
436
- 'redis_example:8:attrs',
437
- 'redis_example:8:assocs:children_ids',
438
- 'redis_example_child::attrs:ids',
439
- 'redis_example_child::indices:by_important:null:null',
440
- 'redis_example_child:3:attrs',
441
- 'redis_example_child:3:assocs:belongs_to'])
442
-
443
- expect(redis.smembers('redis_example::attrs:ids')).to eq(['8'])
444
- expect(redis.smembers('redis_example::indices:by_active:boolean:true')).to eq(
445
- ['8']
446
- )
447
- expect(redis.hgetall('redis_example:8:attrs')).to eq(
448
- {'name' => 'John Jones', 'email' => 'jjones@example.com', 'active' => 'true'}
449
- )
450
- expect(redis.smembers('redis_example:8:assocs:children_ids')).to eq(['3'])
451
-
452
- expect(redis.smembers('redis_example_child::attrs:ids')).to eq(['3'])
453
- expect(redis.hgetall('redis_example_child:3:attrs')).to eq(
454
- {'name' => 'Abel Tasman'}
455
- )
456
- end
457
-
458
- it "loads a child from a parent's has_many relationship" do
459
- create_example(:id => '8', :name => 'John Jones',
460
- :email => 'jjones@example.com', :active => 'true')
461
- example = Zermelo::RedisExample.find_by_id('8')
462
- create_child(example, :id => '3', :name => 'Abel Tasman')
463
-
464
- children = example.children.all
465
-
466
- expect(children).to be_an(Array)
467
- expect(children.size).to eq(1)
468
- child = children.first
469
- expect(child).to be_a(Zermelo::RedisExampleChild)
470
- expect(child.name).to eq('Abel Tasman')
471
- end
472
-
473
- it "loads a parent from a child's belongs_to relationship" do
474
- create_example(:id => '8', :name => 'John Jones',
475
- :email => 'jjones@example.com', :active => 'true')
476
- example = Zermelo::RedisExample.find_by_id('8')
477
- create_child(example, :id => '3', :name => 'Abel Tasman')
478
- child = Zermelo::RedisExampleChild.find_by_id('3')
479
-
480
- other_example = child.example
481
- expect(other_example).not_to be_nil
482
- expect(other_example).to be_a(Zermelo::RedisExample)
483
- expect(other_example.name).to eq('John Jones')
484
- end
485
-
486
- it "removes a parent/child has_many relationship between two records in redis" do
487
- create_example(:id => '8', :name => 'John Jones',
488
- :email => 'jjones@example.com', :active => 'true')
489
- example = Zermelo::RedisExample.find_by_id('8')
490
-
491
- create_child(example, :id => '3', :name => 'Abel Tasman')
492
- child = Zermelo::RedisExampleChild.find_by_id('3')
493
-
494
- expect(redis.smembers('redis_example_child::attrs:ids')).to eq(['3'])
495
- expect(redis.smembers('redis_example:8:assocs:children_ids')).to eq(['3'])
496
-
497
- example.children.delete(child)
498
-
499
- expect(redis.smembers('redis_example_child::attrs:ids')).to eq(['3']) # child not deleted
500
- expect(redis.smembers('redis_example:8:assocs:children_ids')).to eq([]) # but association is
501
- end
502
-
503
- it "filters has_many records by indexed attribute values" do
504
- create_example(:id => '8', :name => 'John Jones',
505
- :email => 'jjones@example.com', :active => 'true')
506
- example = Zermelo::RedisExample.find_by_id('8')
507
-
508
- create_child(example, :id => '3', :name => 'Martin Luther King', :important => true)
509
- create_child(example, :id => '4', :name => 'Julius Caesar', :important => true)
510
- create_child(example, :id => '5', :name => 'John Smith', :important => false)
511
-
512
- important_kids = example.children.intersect(:important => true).all
513
- expect(important_kids).not_to be_nil
514
- expect(important_kids).to be_an(Array)
515
- expect(important_kids.size).to eq(2)
516
- expect(important_kids.map(&:id)).to match_array(['3', '4'])
517
- end
518
-
519
- it "filters has_many records by intersecting ids" do
520
- create_example(:id => '8', :name => 'John Jones',
521
- :email => 'jjones@example.com', :active => 'true')
522
- example = Zermelo::RedisExample.find_by_id('8')
523
-
524
- create_child(example, :id => '3', :name => 'Martin Luther King', :important => true)
525
- create_child(example, :id => '4', :name => 'Julius Caesar', :important => true)
526
- create_child(example, :id => '5', :name => 'John Smith', :important => false)
527
-
528
- important_kids = example.children.intersect(:important => true, :id => ['4', '5']).all
529
- expect(important_kids).not_to be_nil
530
- expect(important_kids).to be_an(Array)
531
- expect(important_kids.size).to eq(1)
532
- expect(important_kids.map(&:id)).to match_array(['4'])
533
- end
534
-
535
- it "checks whether a record id exists through a has_many filter" do
536
- create_example(:id => '8', :name => 'John Jones',
537
- :email => 'jjones@example.com', :active => 'true')
538
- example = Zermelo::RedisExample.find_by_id('8')
539
-
540
- create_child(example, :id => '3', :name => 'Martin Luther King', :important => true)
541
- create_child(example, :id => '4', :name => 'Julius Caesar', :important => true)
542
- create_child(example, :id => '5', :name => 'John Smith', :important => false)
543
-
544
- expect(example.children.intersect(:important => true).exists?('3')).to be_truthy
545
- expect(example.children.intersect(:important => true).exists?('5')).to be_falsey
546
- end
547
-
548
- it "finds a record through a has_many filter" do
549
- create_example(:id => '8', :name => 'John Jones',
550
- :email => 'jjones@example.com', :active => 'true')
551
- example = Zermelo::RedisExample.find_by_id('8')
552
-
553
- create_child(example, :id => '3', :name => 'Martin Luther King', :important => true)
554
- create_child(example, :id => '4', :name => 'Julius Caesar', :important => true)
555
- create_child(example, :id => '5', :name => 'John Smith', :important => false)
556
-
557
- martin = example.children.intersect(:important => true).find_by_id('3')
558
- expect(martin).not_to be_nil
559
- expect(martin).to be_a(Zermelo::RedisExampleChild)
560
- expect(martin.id).to eq('3')
561
- end
562
-
563
- it "does not add a child if the before_add callback raises an exception" do
564
- create_example(:id => '8', :name => 'John Jones',
565
- :email => 'jjones@example.com', :active => 'true')
566
- example = Zermelo::RedisExample.find_by_id('8')
567
-
568
- create_child(nil, :id => '6', :name => 'Roger', :important => true)
569
- child = Zermelo::RedisExampleChild.find_by_id('6')
570
-
571
- expect(example.children).to be_empty
572
- expect {
573
- example.children << child
574
- }.to raise_error
575
- expect(example.children).to be_empty
576
- end
577
-
578
- it 'clears the belongs_to association when the child record is deleted' do
579
- create_example(:id => '8', :name => 'John Jones',
580
- :email => 'jjones@example.com', :active => 'true')
581
- example = Zermelo::RedisExample.find_by_id('8')
582
-
583
- time = Time.now
584
-
585
- create_child(example, :id => '6', :name => 'Martin Luther King', :important => true)
586
- child = Zermelo::RedisExampleChild.find_by_id('6')
587
-
588
- expect(redis.keys).to match_array(['redis_example::attrs:ids',
589
- 'redis_example::indices:by_name',
590
- 'redis_example::indices:by_active:boolean:true',
591
- 'redis_example:8:attrs',
592
- 'redis_example:8:assocs:children_ids',
593
- 'redis_example_child::attrs:ids',
594
- 'redis_example_child::indices:by_important:boolean:true',
595
- 'redis_example_child:6:attrs',
596
- 'redis_example_child:6:assocs:belongs_to'])
597
-
598
- child.destroy
599
-
600
- expect(redis.keys).to match_array(['redis_example::attrs:ids',
601
- 'redis_example::indices:by_name',
602
- 'redis_example::indices:by_active:boolean:true',
603
- 'redis_example:8:attrs'])
604
- end
605
-
606
- it "clears the belongs_to association when the parent record is deleted" do
607
- create_example(:id => '8', :name => 'John Jones',
608
- :email => 'jjones@example.com', :active => 'true')
609
- example = Zermelo::RedisExample.find_by_id('8')
610
-
611
- time = Time.now
612
-
613
- create_child(example, :id => '6', :name => 'Martin Luther King', :important => true)
614
- child = Zermelo::RedisExampleChild.find_by_id('6')
615
-
616
- expect(redis.keys).to match_array(['redis_example::attrs:ids',
617
- 'redis_example::indices:by_name',
618
- 'redis_example::indices:by_active:boolean:true',
619
- 'redis_example:8:attrs',
620
- 'redis_example:8:assocs:children_ids',
621
- 'redis_example_child::attrs:ids',
622
- 'redis_example_child::indices:by_important:boolean:true',
623
- 'redis_example_child:6:attrs',
624
- 'redis_example_child:6:assocs:belongs_to'])
625
-
626
- example.destroy
627
-
628
- expect(redis.keys).to match_array(['redis_example_child::attrs:ids',
629
- 'redis_example_child::indices:by_important:boolean:true',
630
- 'redis_example_child:6:attrs'])
631
- end
632
-
633
- it 'returns associated ids for multiple parent ids' do
634
- create_example(:id => '8', :name => 'John Jones',
635
- :email => 'jjones@example.com', :active => 'true')
636
- example_8 = Zermelo::RedisExample.find_by_id('8')
637
-
638
- create_example(:id => '9', :name => 'Jane Johnson',
639
- :email => 'jjohnson@example.com', :active => 'true')
640
- example_9 = Zermelo::RedisExample.find_by_id('9')
641
-
642
- create_example(:id => '10', :name => 'Jim Smith',
643
- :email => 'jsmith@example.com', :active => 'true')
644
-
645
- create_child(example_8, :id => '3', :name => 'abc', :important => false)
646
- create_child(example_9, :id => '4', :name => 'abc', :important => false)
647
- create_child(example_9, :id => '5', :name => 'abc', :important => false)
648
-
649
- assoc_ids = Zermelo::RedisExample.intersect(:id => [ '8', '9', '10']).
650
- associated_ids_for(:children)
651
- expect(assoc_ids).to eq('8' => Set.new(['3']),
652
- '9' => Set.new(['4', '5']),
653
- '10' => Set.new())
654
-
655
- assoc_parent_ids = Zermelo::RedisExampleChild.intersect(:id => ['3', '4', '5']).
656
- associated_ids_for(:example)
657
- expect(assoc_parent_ids).to eq('3' => '8',
658
- '4' => '9',
659
- '5' => '9')
660
- end
661
-
662
- end
663
-
664
- context "has_sorted_set" do
665
-
666
- def create_datum(parent, attrs = {})
667
- redis.zadd("redis_example:#{parent.id}:assocs:data_ids", attrs[:timestamp].to_i.to_f, attrs[:id])
668
-
669
- redis.hmset("redis_example_datum:#{attrs[:id]}:attrs",
670
- {'summary' => attrs[:summary], 'timestamp' => attrs[:timestamp].to_i.to_f,
671
- 'emotion' => attrs[:emotion]}.to_a.flatten)
672
-
673
- redis.sadd("redis_example_datum::indices:by_emotion:string:#{attrs[:emotion]}", attrs[:id])
674
- redis.hset("redis_example_datum:#{attrs[:id]}:assocs:belongs_to", 'example_id', parent.id)
675
-
676
- redis.sadd('redis_example_datum::attrs:ids', attrs[:id])
677
- end
678
-
679
- it "sets a parent/child has_sorted_set relationship between two records in redis" do
680
- create_example(:id => '8', :name => 'John Jones',
681
- :email => 'jjones@example.com', :active => 'true')
682
-
683
- time = Time.now
684
-
685
- data = Zermelo::RedisExampleDatum.new(:id => '4', :timestamp => time,
686
- :summary => "hello!")
687
- expect(data.save).to be_truthy
688
-
689
- example = Zermelo::RedisExample.find_by_id('8')
690
- example.data << data
691
-
692
- expect(redis.keys('*')).to match_array(['redis_example::attrs:ids',
693
- 'redis_example::indices:by_name',
694
- 'redis_example::indices:by_active:boolean:true',
695
- 'redis_example:8:attrs',
696
- 'redis_example:8:assocs:data_ids',
697
- 'redis_example_datum::attrs:ids',
698
- 'redis_example_datum::indices:by_emotion:null:null',
699
- 'redis_example_datum:4:attrs',
700
- 'redis_example_datum:4:assocs:belongs_to'])
701
-
702
- expect(redis.smembers('redis_example_datum::attrs:ids')).to eq(['4'])
703
- expect(redis.hgetall('redis_example_datum:4:attrs')).to eq(
704
- {'summary' => 'hello!', 'timestamp' => time.to_f.to_s}
705
- )
706
- expect(redis.hgetall('redis_example_datum:4:assocs:belongs_to')).to eq(
707
- {'example_id' => '8'}
708
- )
709
-
710
- result = redis.zrange('redis_example:8:assocs:data_ids', 0, -1,
711
- :with_scores => true) # .should == [['4', time.to_f]]
712
- expect(result.size).to eq(1)
713
- expect(result.first.first).to eq('4')
714
- expect(result.first.last).to be_within(0.001).of(time.to_f)
715
- end
716
-
717
- it "loads a child from a parent's has_sorted_set relationship" do
718
- create_example(:id => '8', :name => 'John Jones',
719
- :email => 'jjones@example.com', :active => 'true')
720
- example = Zermelo::RedisExample.find_by_id('8')
721
-
722
- time = Time.now
723
-
724
- create_datum(example, :id => '4', :summary => 'well then', :timestamp => time)
725
- datum = Zermelo::RedisExampleDatum.find_by_id('4')
726
-
727
- data = example.data.all
728
-
729
- expect(data).to be_an(Array)
730
- expect(data.size).to eq(1)
731
- datum = data.first
732
- expect(datum).to be_a(Zermelo::RedisExampleDatum)
733
- expect(datum.summary).to eq('well then')
734
- expect(datum.timestamp).to be_within(1).of(time) # ignore fractional differences
735
- end
736
-
737
- it "removes a parent/child has_sorted_set relationship between two records in redis" do
738
- create_example(:id => '8', :name => 'John Jones',
739
- :email => 'jjones@example.com', :active => 'true')
740
- example = Zermelo::RedisExample.find_by_id('8')
741
-
742
- time = Time.now
743
-
744
- create_datum(example, :id => '4', :summary => 'well then', :timestamp => time)
745
- datum = Zermelo::RedisExampleDatum.find_by_id('4')
746
-
747
- expect(redis.smembers('redis_example_datum::attrs:ids')).to eq(['4'])
748
- expect(redis.zrange('redis_example:8:assocs:data_ids', 0, -1)).to eq(['4'])
749
-
750
- example.data.delete(datum)
751
-
752
- expect(redis.smembers('redis_example_datum::attrs:ids')).to eq(['4']) # child not deleted
753
- expect(redis.zrange('redis_example:8:assocs.data_ids', 0, -1)).to eq([]) # but association is
754
- end
755
-
756
- it "filters has_sorted_set records by indexed attribute values" do
757
- create_example(:id => '8', :name => 'John Jones',
758
- :email => 'jjones@example.com', :active => 'true')
759
- example = Zermelo::RedisExample.find_by_id('8')
760
-
761
- time = Time.now
762
-
763
- create_datum(example, :id => '4', :summary => 'well then', :timestamp => time,
764
- :emotion => 'upset')
765
- create_datum(example, :id => '5', :summary => 'ok', :timestamp => time.to_i + 10,
766
- :emotion => 'happy')
767
- create_datum(example, :id => '6', :summary => 'aaargh', :timestamp => time.to_i + 20,
768
- :emotion => 'upset')
769
-
770
- upset_data = example.data.intersect(:emotion => 'upset').all
771
- expect(upset_data).not_to be_nil
772
- expect(upset_data).to be_an(Array)
773
- expect(upset_data.size).to eq(2)
774
- expect(upset_data.map(&:id)).to eq(['4', '6'])
775
- end
776
-
777
-
778
- it "filters has_sorted_set records by indexed attribute values with a regex search" do
779
- create_example(:id => '8', :name => 'John Jones',
780
- :email => 'jjones@example.com', :active => 'true')
781
- example = Zermelo::RedisExample.find_by_id('8')
782
-
783
- time = Time.now
784
-
785
- create_datum(example, :id => '4', :summary => 'well then', :timestamp => time,
786
- :emotion => 'upset')
787
- create_datum(example, :id => '5', :summary => 'ok', :timestamp => time.to_i + 10,
788
- :emotion => 'happy')
789
- create_datum(example, :id => '6', :summary => 'aaargh', :timestamp => time.to_i + 20,
790
- :emotion => 'upset')
791
-
792
- upset_data = example.data.intersect(:emotion => /^ups/).all
793
- expect(upset_data).not_to be_nil
794
- expect(upset_data).to be_an(Array)
795
- expect(upset_data.size).to eq(2)
796
- expect(upset_data.map(&:id)).to eq(['4', '6'])
797
- end
798
-
799
- it "retrieves a subset of a sorted set by index" do
800
- create_example(:id => '8', :name => 'John Jones',
801
- :email => 'jjones@example.com', :active => 'true')
802
- example = Zermelo::RedisExample.find_by_id('8')
803
-
804
- time = Time.now
805
-
806
- create_datum(example, :id => '4', :summary => 'well then', :timestamp => time,
807
- :emotion => 'upset')
808
- create_datum(example, :id => '5', :summary => 'ok', :timestamp => time.to_i + 10,
809
- :emotion => 'happy')
810
- create_datum(example, :id => '6', :summary => 'aaargh', :timestamp => time.to_i + 20,
811
- :emotion => 'upset')
812
-
813
- data = example.data.intersect_range(0, 1).all
814
- expect(data).not_to be_nil
815
- expect(data).to be_an(Array)
816
- expect(data.size).to eq(2)
817
- expect(data.map(&:id)).to eq(['4', '5'])
818
- end
819
-
820
- it "retrieves a reversed subset of a sorted set by index" do
821
- create_example(:id => '8', :name => 'John Jones',
822
- :email => 'jjones@example.com', :active => 'true')
823
- example = Zermelo::RedisExample.find_by_id('8')
824
-
825
- time = Time.now
826
-
827
- create_datum(example, :id => '4', :summary => 'well then', :timestamp => time.to_i,
828
- :emotion => 'upset')
829
- create_datum(example, :id => '5', :summary => 'ok', :timestamp => time.to_i + 10,
830
- :emotion => 'happy')
831
- create_datum(example, :id => '6', :summary => 'aaargh', :timestamp => time.to_i + 20,
832
- :emotion => 'upset')
833
-
834
- data = example.data.intersect_range(1, 2).sort(:id, :desc => true).all
835
-
836
- expect(data).not_to be_nil
837
- expect(data).to be_an(Array)
838
- expect(data.size).to eq(2)
839
- expect(data.map(&:id)).to eq(['6', '5'])
840
- end
841
-
842
- it "retrieves a subset of a sorted set by score" do
843
- create_example(:id => '8', :name => 'John Jones',
844
- :email => 'jjones@example.com', :active => 'true')
845
- example = Zermelo::RedisExample.find_by_id('8')
846
-
847
- time = Time.now
848
-
849
- create_datum(example, :id => '4', :summary => 'well then', :timestamp => time,
850
- :emotion => 'upset')
851
- create_datum(example, :id => '5', :summary => 'ok', :timestamp => time.to_i + 10,
852
- :emotion => 'happy')
853
- create_datum(example, :id => '6', :summary => 'aaargh', :timestamp => time.to_i + 20,
854
- :emotion => 'upset')
855
-
856
- data = example.data.intersect_range(time.to_i - 1, time.to_i + 15, :by_score => true).all
857
- expect(data).not_to be_nil
858
- expect(data).to be_an(Array)
859
- expect(data.size).to eq(2)
860
- expect(data.map(&:id)).to eq(['4', '5'])
861
- end
862
-
863
- it "retrieves a reversed subset of a sorted set by score" do
864
- create_example(:id => '8', :name => 'John Jones',
865
- :email => 'jjones@example.com', :active => 'true')
866
- example = Zermelo::RedisExample.find_by_id('8')
867
-
868
- time = Time.now
869
-
870
- create_datum(example, :id => '4', :summary => 'well then', :timestamp => time,
871
- :emotion => 'upset')
872
- create_datum(example, :id => '5', :summary => 'ok', :timestamp => time.to_i + 10,
873
- :emotion => 'happy')
874
- create_datum(example, :id => '6', :summary => 'aaargh', :timestamp => time.to_i + 20,
875
- :emotion => 'upset')
876
-
877
- data = example.data.intersect_range(time.to_i - 1, time.to_i + 15,
878
- :by_score => true).sort(:timestamp, :desc => true).all
879
- expect(data).not_to be_nil
880
- expect(data).to be_an(Array)
881
- expect(data.size).to eq(2)
882
- expect(data.map(&:id)).to eq(['5', '4'])
883
- end
884
-
885
- it "checks whether a record exists through a has_sorted_set filter" do
886
- create_example(:id => '8', :name => 'John Jones',
887
- :email => 'jjones@example.com', :active => 'true')
888
- example = Zermelo::RedisExample.find_by_id('8')
889
-
890
- time = Time.now
891
-
892
- create_datum(example, :id => '4', :summary => 'well then', :timestamp => time,
893
- :emotion => 'upset')
894
- create_datum(example, :id => '5', :summary => 'ok', :timestamp => time.to_i + 10,
895
- :emotion => 'happy')
896
- create_datum(example, :id => '6', :summary => 'aaargh', :timestamp => time.to_i + 20,
897
- :emotion => 'upset')
898
-
899
- expect(example.data.intersect(:emotion => 'upset').exists?('4')).to be_truthy
900
- expect(example.data.intersect(:emotion => 'upset').exists?('5')).to be_falsey
901
- end
902
-
903
- it "retrieves the union of a sorted set by index"
904
- it "retrieves a reversed union of a sorted set by index"
905
-
906
- it "retrieves the union of a sorted set by score"
907
- it "retrieves a reversed union of a sorted set by score"
908
-
909
- it "retrieves the exclusion of a sorted set by index" do
910
- create_example(:id => '8', :name => 'John Jones',
911
- :email => 'jjones@example.com', :active => 'true')
912
- example = Zermelo::RedisExample.find_by_id('8')
913
-
914
- time = Time.now
915
-
916
- create_datum(example, :id => '4', :summary => 'well then', :timestamp => time,
917
- :emotion => 'upset')
918
- create_datum(example, :id => '5', :summary => 'ok', :timestamp => time.to_i + 10,
919
- :emotion => 'happy')
920
- create_datum(example, :id => '6', :summary => 'aaargh', :timestamp => time.to_i + 20,
921
- :emotion => 'upset')
922
-
923
- data = example.data.diff_range(0, 1).all
924
- expect(data).not_to be_nil
925
- expect(data).to be_an(Array)
926
- expect(data.size).to eq(1)
927
- expect(data.map(&:id)).to eq(['6'])
928
- end
929
-
930
- it "retrieves a reversed exclusion of a sorted set by index" do
931
- create_example(:id => '8', :name => 'John Jones',
932
- :email => 'jjones@example.com', :active => 'true')
933
- example = Zermelo::RedisExample.find_by_id('8')
934
-
935
- time = Time.now
936
-
937
- create_datum(example, :id => '4', :summary => 'well then', :timestamp => time,
938
- :emotion => 'upset')
939
- create_datum(example, :id => '5', :summary => 'ok', :timestamp => time.to_i + 10,
940
- :emotion => 'happy')
941
- create_datum(example, :id => '6', :summary => 'aaargh', :timestamp => time.to_i + 20,
942
- :emotion => 'upset')
943
-
944
- data = example.data.diff_range(2, 2).sort(:id, :desc => true).all
945
- expect(data).not_to be_nil
946
- expect(data).to be_an(Array)
947
- expect(data.size).to eq(2)
948
- expect(data.map(&:id)).to eq(['5', '4'])
949
- end
950
-
951
- it "retrieves the exclusion of a sorted set by score" do
952
- create_example(:id => '8', :name => 'John Jones',
953
- :email => 'jjones@example.com', :active => 'true')
954
- example = Zermelo::RedisExample.find_by_id('8')
955
-
956
- time = Time.now
957
-
958
- create_datum(example, :id => '4', :summary => 'well then', :timestamp => time,
959
- :emotion => 'upset')
960
- create_datum(example, :id => '5', :summary => 'ok', :timestamp => time.to_i + 10,
961
- :emotion => 'happy')
962
- create_datum(example, :id => '6', :summary => 'aaargh', :timestamp => time.to_i + 20,
963
- :emotion => 'upset')
964
-
965
- data = example.data.diff_range(time.to_i - 1, time.to_i + 15, :by_score => true).all
966
- expect(data).not_to be_nil
967
- expect(data).to be_an(Array)
968
- expect(data.size).to eq(1)
969
- expect(data.map(&:id)).to eq(['6'])
970
- end
971
-
972
- it "retrieves a reversed exclusion of a sorted set by score" do
973
- create_example(:id => '8', :name => 'John Jones',
974
- :email => 'jjones@example.com', :active => 'true')
975
- example = Zermelo::RedisExample.find_by_id('8')
976
-
977
- time = Time.now
978
-
979
- create_datum(example, :id => '4', :summary => 'well then', :timestamp => time,
980
- :emotion => 'upset')
981
- create_datum(example, :id => '5', :summary => 'ok', :timestamp => time.to_i + 10,
982
- :emotion => 'happy')
983
- create_datum(example, :id => '6', :summary => 'aaargh', :timestamp => time.to_i + 20,
984
- :emotion => 'upset')
985
-
986
- data = example.data.diff_range(time.to_i - 1, time.to_i + 8, :by_score => true).
987
- sort(:timestamp, :desc => true).all
988
- expect(data).not_to be_nil
989
- expect(data).to be_an(Array)
990
- expect(data.size).to eq(2)
991
- expect(data.map(&:id)).to eq(['6', '5'])
992
- end
993
-
994
- it "finds a record through a has_sorted_set filter" do
995
- create_example(:id => '8', :name => 'John Jones',
996
- :email => 'jjones@example.com', :active => 'true')
997
- example = Zermelo::RedisExample.find_by_id('8')
998
-
999
- time = Time.now
1000
-
1001
- create_datum(example, :id => '4', :summary => 'well then', :timestamp => time,
1002
- :emotion => 'upset')
1003
- create_datum(example, :id => '5', :summary => 'ok', :timestamp => time.to_i + 10,
1004
- :emotion => 'happy')
1005
- create_datum(example, :id => '6', :summary => 'aaargh', :timestamp => time.to_i + 20,
1006
- :emotion => 'upset')
1007
-
1008
- wellthen = upset_data = example.data.intersect(:emotion => 'upset').find_by_id('4')
1009
- expect(wellthen).not_to be_nil
1010
- expect(wellthen).to be_a(Zermelo::RedisExampleDatum)
1011
- expect(wellthen.id).to eq('4')
1012
- end
1013
-
1014
- it 'clears the belongs_to association when the child record is deleted' do
1015
- create_example(:id => '8', :name => 'John Jones',
1016
- :email => 'jjones@example.com', :active => 'true')
1017
- example = Zermelo::RedisExample.find_by_id('8')
1018
-
1019
- time = Time.now
1020
-
1021
- create_datum(example, :id => '6', :summary => 'aaargh', :timestamp => time.to_i + 20,
1022
- :emotion => 'upset')
1023
- datum = Zermelo::RedisExampleDatum.find_by_id('6')
1024
-
1025
- expect(redis.keys).to match_array(['redis_example::attrs:ids',
1026
- 'redis_example::indices:by_name',
1027
- 'redis_example::indices:by_active:boolean:true',
1028
- 'redis_example:8:attrs',
1029
- 'redis_example:8:assocs:data_ids',
1030
- 'redis_example_datum::attrs:ids',
1031
- 'redis_example_datum::indices:by_emotion:string:upset',
1032
- 'redis_example_datum:6:attrs',
1033
- 'redis_example_datum:6:assocs:belongs_to'])
1034
-
1035
- datum.destroy
1036
-
1037
- expect(redis.keys).to match_array(['redis_example::attrs:ids',
1038
- 'redis_example::indices:by_name',
1039
- 'redis_example::indices:by_active:boolean:true',
1040
- 'redis_example:8:attrs'])
1041
- end
1042
-
1043
- it "clears the belongs_to association when the parent record is deleted" do
1044
- create_example(:id => '8', :name => 'John Jones',
1045
- :email => 'jjones@example.com', :active => 'true')
1046
- example = Zermelo::RedisExample.find_by_id('8')
1047
-
1048
- time = Time.now
1049
-
1050
- create_datum(example, :id => '6', :summary => 'aaargh', :timestamp => time.to_i + 20,
1051
- :emotion => 'upset')
1052
-
1053
- expect(redis.keys).to match_array(['redis_example::attrs:ids',
1054
- 'redis_example::indices:by_name',
1055
- 'redis_example::indices:by_active:boolean:true',
1056
- 'redis_example:8:attrs',
1057
- 'redis_example:8:assocs:data_ids',
1058
- 'redis_example_datum::attrs:ids',
1059
- 'redis_example_datum::indices:by_emotion:string:upset',
1060
- 'redis_example_datum:6:attrs',
1061
- 'redis_example_datum:6:assocs:belongs_to'])
1062
-
1063
- example.destroy
1064
-
1065
- expect(redis.keys).to match_array(['redis_example_datum::attrs:ids',
1066
- 'redis_example_datum::indices:by_emotion:string:upset',
1067
- 'redis_example_datum:6:attrs'])
1068
- end
1069
-
1070
- it 'returns associated ids for multiple parent ids' do
1071
- create_example(:id => '8', :name => 'John Jones',
1072
- :email => 'jjones@example.com', :active => 'true')
1073
- example_8 = Zermelo::RedisExample.find_by_id('8')
1074
-
1075
- create_example(:id => '9', :name => 'Jane Johnson',
1076
- :email => 'jjohnson@example.com', :active => 'true')
1077
-
1078
- create_example(:id => '10', :name => 'Jim Smith',
1079
- :email => 'jsmith@example.com', :active => 'true')
1080
- example_10 = Zermelo::RedisExample.find_by_id('10')
1081
-
1082
- time = Time.now.to_i
1083
-
1084
- create_datum(example_8, :id => '3', :summary => 'aaargh', :timestamp => time.to_i + 20,
1085
- :emotion => 'ok')
1086
- create_datum(example_8, :id => '4', :summary => 'aaargh', :timestamp => time.to_i + 30,
1087
- :emotion => 'ok')
1088
- create_datum(example_10, :id => '5', :summary => 'aaargh', :timestamp => time.to_i + 40,
1089
- :emotion => 'not_ok')
1090
-
1091
- assoc_ids = Zermelo::RedisExample.intersect(:id => ['8', '9', '10']).
1092
- associated_ids_for(:data)
1093
- expect(assoc_ids).to eq('8' => Set.new(['3', '4']),
1094
- '9' => Set.new(),
1095
- '10' => Set.new(['5']))
1096
- end
1097
-
1098
- end
1099
-
1100
- context "has_one" do
1101
-
1102
- class Zermelo::RedisExampleSpecial
1103
- include Zermelo::Records::RedisRecord
1104
-
1105
- define_attributes :name => :string
1106
-
1107
- belongs_to :example, :class_name => 'Zermelo::RedisExample', :inverse_of => :special
1108
-
1109
- validates :name, :presence => true
1110
- end
1111
-
1112
- class Zermelo::RedisExample
1113
- has_one :special, :class_name => 'Zermelo::RedisExampleSpecial', :inverse_of => :example
1114
- end
1115
-
1116
- it "sets and retrieves a record via a has_one association" do
1117
- create_example(:id => '8', :name => 'John Jones',
1118
- :email => 'jjones@example.com', :active => 'true')
1119
-
1120
- special = Zermelo::RedisExampleSpecial.new(:id => '22', :name => 'Bill Smith')
1121
- expect(special.save).to be_truthy
1122
-
1123
- example = Zermelo::RedisExample.find_by_id('8')
1124
- example.special = special
1125
-
1126
- expect(redis.keys('*')).to match_array(['redis_example::attrs:ids',
1127
- 'redis_example::indices:by_name',
1128
- 'redis_example::indices:by_active:boolean:true',
1129
- 'redis_example:8:attrs',
1130
- 'redis_example:8:assocs',
1131
- 'redis_example_special::attrs:ids',
1132
- 'redis_example_special:22:attrs',
1133
- 'redis_example_special:22:assocs:belongs_to'])
1134
-
1135
- expect(redis.hgetall('redis_example:8:assocs')).to eq("special_id" => "22")
1136
-
1137
- expect(redis.smembers('redis_example_special::attrs:ids')).to eq(['22'])
1138
- expect(redis.hgetall('redis_example_special:22:attrs')).to eq(
1139
- {'name' => 'Bill Smith'}
1140
- )
1141
-
1142
- expect(redis.hgetall('redis_example_special:22:assocs:belongs_to')).to eq(
1143
- {'example_id' => '8'}
1144
- )
1145
-
1146
- example2 = Zermelo::RedisExample.find_by_id('8')
1147
- special2 = example2.special
1148
- expect(special2).not_to be_nil
1149
-
1150
- expect(special2.id).to eq('22')
1151
- expect(special2.example.id).to eq('8')
1152
- end
1153
-
1154
- def create_special(parent, attrs = {})
1155
- redis.hmset("redis_example_special:#{attrs[:id]}:attrs", {'name' => attrs[:name]}.to_a.flatten)
1156
-
1157
- redis.hset("redis_example_special:#{attrs[:id]}:assocs:belongs_to", 'example_id', parent.id)
1158
- redis.hset("redis_example:#{parent.id}:assocs", 'special_id', attrs[:id])
1159
-
1160
- redis.sadd('redis_example_special::attrs:ids', attrs[:id])
1161
- end
1162
-
1163
- it 'clears the belongs_to association when the child record is deleted' do
1164
- create_example(:id => '8', :name => 'John Jones',
1165
- :email => 'jjones@example.com', :active => 'true')
1166
- example = Zermelo::RedisExample.find_by_id('8')
1167
- create_special(example, :id => '3', :name => 'Another Jones')
1168
- special = Zermelo::RedisExampleSpecial.find_by_id('3')
1169
-
1170
- expect(redis.keys).to match_array(['redis_example::attrs:ids',
1171
- 'redis_example::indices:by_name',
1172
- 'redis_example::indices:by_active:boolean:true',
1173
- 'redis_example:8:attrs',
1174
- 'redis_example:8:assocs',
1175
- 'redis_example_special::attrs:ids',
1176
- 'redis_example_special:3:attrs',
1177
- 'redis_example_special:3:assocs:belongs_to'])
1178
-
1179
- special.destroy
1180
-
1181
- expect(redis.keys).to match_array(['redis_example::attrs:ids',
1182
- 'redis_example::indices:by_name',
1183
- 'redis_example::indices:by_active:boolean:true',
1184
- 'redis_example:8:attrs'])
1185
- end
1186
-
1187
- it "clears the belongs_to association when the parent record is deleted" do
1188
- create_example(:id => '8', :name => 'John Jones',
1189
- :email => 'jjones@example.com', :active => 'true')
1190
- example = Zermelo::RedisExample.find_by_id('8')
1191
- create_special(example, :id => '3', :name => 'Another Jones')
1192
-
1193
- expect(redis.keys).to match_array(['redis_example::attrs:ids',
1194
- 'redis_example::indices:by_name',
1195
- 'redis_example::indices:by_active:boolean:true',
1196
- 'redis_example:8:attrs',
1197
- 'redis_example:8:assocs',
1198
- 'redis_example_special::attrs:ids',
1199
- 'redis_example_special:3:attrs',
1200
- 'redis_example_special:3:assocs:belongs_to'])
1201
-
1202
- example.destroy
1203
-
1204
- expect(redis.keys).to match_array(['redis_example_special::attrs:ids',
1205
- 'redis_example_special:3:attrs'])
1206
- end
1207
-
1208
- it 'returns associated ids for multiple parent ids' do
1209
- create_example(:id => '8', :name => 'John Jones',
1210
- :email => 'jjones@example.com', :active => 'true')
1211
-
1212
- create_example(:id => '9', :name => 'Jane Johnson',
1213
- :email => 'jjohnson@example.com', :active => 'true')
1214
- example_9 = Zermelo::RedisExample.find_by_id('9')
1215
-
1216
- create_example(:id => '10', :name => 'Jim Smith',
1217
- :email => 'jsmith@example.com', :active => 'true')
1218
- example_10 = Zermelo::RedisExample.find_by_id('10')
1219
-
1220
- time = Time.now.to_i
1221
-
1222
- create_special(example_9, :id => '3', :name => 'jkl')
1223
- create_special(example_10, :id => '4', :name => 'pqr')
1224
-
1225
- assoc_ids = Zermelo::RedisExample.intersect(:id => ['8', '9', '10']).
1226
- associated_ids_for(:special)
1227
- expect(assoc_ids).to eq('8' => nil,
1228
- '9' => '3',
1229
- '10' => '4')
1230
- end
1231
-
1232
- end
1233
-
1234
-
1235
- context 'sorting by multiple keys' do
1236
-
1237
- def create_template(attrs = {})
1238
- redis.hmset("template:#{attrs[:id]}:attrs", {'name' => attrs[:name]}.to_a.flatten)
1239
- redis.sadd('template::attrs:ids', attrs[:id])
1240
- end
1241
-
1242
- before do
1243
- create_template(:id => '1', :name => 'abc')
1244
- create_template(:id => '2', :name => 'def')
1245
- create_template(:id => '3', :name => 'abc')
1246
- create_template(:id => '4', :name => 'def')
1247
- end
1248
-
1249
- it 'sorts by multiple fields' do
1250
- expect(Zermelo::Template.sort(:name => :asc, :id => :desc).map(&:id)).to eq(['3', '1', '4', '2'])
1251
- end
1252
-
1253
- end
1254
-
1255
- context "has_and_belongs_to_many" do
1256
-
1257
- def create_template(attrs = {})
1258
- redis.hmset("template:#{attrs[:id]}:attrs", {'name' => attrs[:name]}.to_a.flatten)
1259
- redis.sadd('template::attrs:ids', attrs[:id])
1260
- end
1261
-
1262
- before(:each) do
1263
- create_example(:id => '8', :name => 'John Jones',
1264
- :email => 'jjones@example.com', :active => true)
1265
- create_template(:id => '2', :name => 'Template 1')
1266
- end
1267
-
1268
- it "sets a has_and_belongs_to_many relationship between two records in redis" do
1269
- example = Zermelo::RedisExample.find_by_id('8')
1270
- template = Zermelo::Template.find_by_id('2')
1271
-
1272
- example.templates << template
1273
-
1274
- expect(redis.keys('*')).to match_array(['redis_example::attrs:ids',
1275
- 'redis_example::indices:by_name',
1276
- 'redis_example::indices:by_active:boolean:true',
1277
- 'redis_example:8:attrs',
1278
- 'redis_example:8:assocs:templates_ids',
1279
- 'template::attrs:ids',
1280
- 'template:2:attrs',
1281
- 'template:2:assocs:examples_ids'])
1282
-
1283
- expect(redis.smembers('redis_example::attrs:ids')).to eq(['8'])
1284
- expect(redis.smembers('redis_example::indices:by_active:boolean:true')).to eq(['8'])
1285
- expect(redis.hgetall('redis_example:8:attrs')).to eq(
1286
- {'name' => 'John Jones', 'email' => 'jjones@example.com', 'active' => 'true'}
1287
- )
1288
- expect(redis.smembers('redis_example:8:assocs:templates_ids')).to eq(['2'])
1289
-
1290
- expect(redis.smembers('template::attrs:ids')).to eq(['2'])
1291
- expect(redis.hgetall('template:2:attrs')).to eq({'name' => 'Template 1'})
1292
- expect(redis.smembers('template:2:assocs:examples_ids')).to eq(['8'])
1293
- end
1294
-
1295
- it "loads a record from a has_and_belongs_to_many relationship" do
1296
- example = Zermelo::RedisExample.find_by_id('8')
1297
- template = Zermelo::Template.find_by_id('2')
1298
-
1299
- template.examples << example
1300
-
1301
- templates = example.templates.all
1302
-
1303
- expect(templates).to be_an(Array)
1304
- expect(templates.size).to eq(1)
1305
- other_template = templates.first
1306
- expect(other_template).to be_a(Zermelo::Template)
1307
- expect(other_template.id).to eq(template.id)
1308
- end
1309
-
1310
- it "removes a has_and_belongs_to_many relationship between two records in redis" do
1311
- example = Zermelo::RedisExample.find_by_id('8')
1312
- template = Zermelo::Template.find_by_id('2')
1313
-
1314
- template.examples << example
1315
-
1316
- expect(redis.smembers('template::attrs:ids')).to eq(['2'])
1317
- expect(redis.smembers('redis_example:8:assocs:templates_ids')).to eq(['2'])
1318
-
1319
- example.templates.delete(template)
1320
-
1321
- expect(redis.smembers('template::attrs:ids')).to eq(['2']) # template not deleted
1322
- expect(redis.smembers('redis_example:8:assocs:templates_ids')).to eq([]) # but association is
1323
- end
1324
-
1325
- it "filters has_and_belongs_to_many records by indexed attribute values" do
1326
- create_example(:id => '9', :name => 'James Smith',
1327
- :email => 'jsmith@example.com', :active => false)
1328
- create_example(:id => '10', :name => 'Alpha Beta',
1329
- :email => 'abc@example.com', :active => true)
1330
-
1331
- example = Zermelo::RedisExample.find_by_id('8')
1332
- example_2 = Zermelo::RedisExample.find_by_id('9')
1333
- example_3 = Zermelo::RedisExample.find_by_id('10')
1334
- template = Zermelo::Template.find_by_id('2')
1335
-
1336
- example.templates << template
1337
- example_2.templates << template
1338
- example_3.templates << template
1339
-
1340
- examples = template.examples.intersect(:active => true).all
1341
- expect(examples).not_to be_nil
1342
- expect(examples).to be_an(Array)
1343
- expect(examples.size).to eq(2)
1344
- expect(examples.map(&:id)).to match_array(['8', '10'])
1345
- end
1346
-
1347
- it "checks whether a record id exists through a has_and_belongs_to_many filter" do
1348
- create_example(:id => '9', :name => 'James Smith',
1349
- :email => 'jsmith@example.com', :active => false)
1350
-
1351
- example = Zermelo::RedisExample.find_by_id('8')
1352
- example_2 = Zermelo::RedisExample.find_by_id('9')
1353
- template = Zermelo::Template.find_by_id('2')
1354
-
1355
- example.templates << template
1356
- example_2.templates << template
1357
-
1358
- expect(template.examples.intersect(:active => false).exists?('9')).to be_truthy
1359
- expect(template.examples.intersect(:active => false).exists?('8')).to be_falsey
1360
- end
1361
-
1362
- it "finds a record through a has_and_belongs_to_many filter" do
1363
- create_example(:id => '9', :name => 'James Smith',
1364
- :email => 'jsmith@example.com', :active => false)
1365
-
1366
- example = Zermelo::RedisExample.find_by_id('8')
1367
- example_2 = Zermelo::RedisExample.find_by_id('9')
1368
- template = Zermelo::Template.find_by_id('2')
1369
-
1370
- example.templates << template
1371
- example_2.templates << template
1372
-
1373
- james = template.examples.intersect(:active => false).find_by_id('9')
1374
- expect(james).not_to be_nil
1375
- expect(james).to be_a(Zermelo::RedisExample)
1376
- expect(james.id).to eq(example_2.id)
1377
- end
1378
-
1379
- it 'clears a has_and_belongs_to_many association when a record is deleted'
1380
-
1381
- it 'returns associated ids for multiple parent ids' do
1382
- create_example(:id => '9', :name => 'Jane Johnson',
1383
- :email => 'jjohnson@example.com', :active => 'true')
1384
- example_9 = Zermelo::RedisExample.find_by_id('9')
1385
-
1386
- create_example(:id => '10', :name => 'Jim Smith',
1387
- :email => 'jsmith@example.com', :active => 'true')
1388
- example_10 = Zermelo::RedisExample.find_by_id('10')
1389
-
1390
- create_template(:id => '3', :name => 'Template 3')
1391
- create_template(:id => '4', :name => 'Template 4')
1392
-
1393
- template_2 = Zermelo::Template.find_by_id('2')
1394
- template_3 = Zermelo::Template.find_by_id('3')
1395
- template_4 = Zermelo::Template.find_by_id('4')
1396
-
1397
- example_9.templates.add(template_2)
1398
- example_10.templates.add(template_3, template_4)
1399
-
1400
- assoc_ids = Zermelo::RedisExample.intersect(:id => ['8', '9', '10']).
1401
- associated_ids_for(:templates)
1402
- expect(assoc_ids).to eq('8' => Set.new([]),
1403
- '9' => Set.new(['2']),
1404
- '10' => Set.new(['3', '4']))
1405
- end
1406
-
1407
- end
1408
-
1409
- context 'bad parameters' do
1410
-
1411
- let(:example) { Zermelo::RedisExample.find_by_id('8') }
1412
-
1413
- before(:each) do
1414
- create_example(:id => '8', :name => 'John Jones',
1415
- :email => 'jjones@example.com', :active => true)
1416
- end
1417
-
1418
- it 'raises an error when calling add on has_many without an argument' do
1419
- expect {
1420
- example.children.add
1421
- }.to raise_error
1422
- end
1423
-
1424
- it 'raises an error when calling delete on has_many without an argument' do
1425
- expect {
1426
- example.children.delete
1427
- }.to raise_error
1428
- end
1429
-
1430
- it 'raises an error when calling add on has_sorted_set without an argument' do
1431
- expect {
1432
- example.data.add
1433
- }.to raise_error
1434
- end
1435
-
1436
- it 'raises an error when calling delete on has_sorted_set without an argument' do
1437
- expect {
1438
- example.data.delete
1439
- }.to raise_error
1440
- end
1441
-
1442
- it 'raises an error when calling add on has_and_belongs_to_many without an argument' do
1443
- expect {
1444
- example.templates.add
1445
- }.to raise_error
1446
- end
1447
-
1448
- it 'raises an error when calling delete on has_and_belongs_to_many without an argument' do
1449
- expect {
1450
- example.templates.delete
1451
- }.to raise_error
1452
- end
1453
-
1454
- it 'raises an error when trying to filter on a non-indexed value' do
1455
- expect {
1456
- Zermelo::RedisExample.intersect(:email => 'jjones@example.com').all
1457
- }.to raise_error
1458
- end
1459
- end
1460
-
1461
- end