ohm 1.1.2 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. data/lib/ohm.rb +153 -134
  2. data/test/enumerable.rb +79 -0
  3. metadata +31 -11
data/lib/ohm.rb CHANGED
@@ -140,8 +140,27 @@ module Ohm
140
140
  redis.flushdb
141
141
  end
142
142
 
143
- # Wraps the whole pipelining functionality.
144
- module PipelinedFetch
143
+ module Collection
144
+ include Enumerable
145
+
146
+ def each
147
+ if block_given?
148
+ to_a.each { |element| yield element }
149
+ else
150
+ Enumerator.new(self, :each)
151
+ end
152
+ end
153
+
154
+ # Fetch the data from Redis in one go.
155
+ def to_a
156
+ fetch(ids)
157
+ end
158
+
159
+ def empty?
160
+ size == 0
161
+ end
162
+
163
+ # Wraps the whole pipelining functionality.
145
164
  def fetch(ids)
146
165
  arr = db.pipelined do
147
166
  ids.each { |id| db.hgetall(namespace[id]) }
@@ -159,24 +178,126 @@ module Ohm
159
178
  end
160
179
  end
161
180
 
162
- # Defines most of the methods used by `Set` and `MultiSet`.
163
- module Collection
164
- include PipelinedFetch
165
- include Enumerable
181
+ class List
182
+ include Collection
166
183
 
167
- # Fetch the data from Redis in one go.
168
- def to_a
169
- fetch(ids)
184
+ attr :key
185
+ attr :namespace
186
+ attr :model
187
+
188
+ def initialize(key, namespace, model)
189
+ @key = key
190
+ @namespace = namespace
191
+ @model = model
170
192
  end
171
193
 
172
- def each
173
- to_a.each { |e| yield e }
194
+ # Returns the total size of the list using LLEN.
195
+ def size
196
+ db.llen(key)
174
197
  end
198
+ alias :count :size
175
199
 
176
- def empty?
177
- size == 0
200
+ # Returns the first element of the list using LINDEX.
201
+ def first
202
+ model[db.lindex(key, 0)]
203
+ end
204
+
205
+ # Returns the last element of the list using LINDEX.
206
+ def last
207
+ model[db.lindex(key, -1)]
208
+ end
209
+
210
+ # Checks if the model is part of this List.
211
+ #
212
+ # An important thing to note is that this method loads all of the
213
+ # elements of the List since there is no command in Redis that
214
+ # allows you to actually check the list contents efficiently.
215
+ #
216
+ # You may want to avoid doing this if your list has say, 10K entries.
217
+ def include?(model)
218
+ ids.include?(model.id.to_s)
219
+ end
220
+
221
+ # Replace all the existing elements of a list with a different
222
+ # collection of models. This happens atomically in a MULTI-EXEC
223
+ # block.
224
+ #
225
+ # Example:
226
+ #
227
+ # user = User.create
228
+ # p1 = Post.create
229
+ # user.posts.push(p1)
230
+ #
231
+ # p2, p3 = Post.create, Post.create
232
+ # user.posts.replace([p2, p3])
233
+ #
234
+ # user.posts.include?(p1)
235
+ # # => false
236
+ #
237
+ def replace(models)
238
+ ids = models.map { |model| model.id }
239
+
240
+ model.db.multi do
241
+ db.del(key)
242
+ ids.each { |id| db.rpush(key, id) }
243
+ end
244
+ end
245
+
246
+ # Pushes the model to the _end_ of the list using RPUSH.
247
+ def push(model)
248
+ db.rpush(key, model.id)
249
+ end
250
+
251
+ # Pushes the model to the _beginning_ of the list using LPUSH.
252
+ def unshift(model)
253
+ db.lpush(key, model.id)
254
+ end
255
+
256
+ # Delete a model from the list.
257
+ #
258
+ # Note: If your list contains the model multiple times, this method
259
+ # will delete all instances of that model in one go.
260
+ #
261
+ # Example:
262
+ #
263
+ # class Comment < Ohm::Model
264
+ # end
265
+ #
266
+ # class Post < Ohm::Model
267
+ # list :comments, Comment
268
+ # end
269
+ #
270
+ # p = Post.create
271
+ # c = Comment.create
272
+ #
273
+ # p.comments.push(c)
274
+ # p.comments.push(c)
275
+ #
276
+ # p.comments.delete(c)
277
+ #
278
+ # p.comments.size == 0
279
+ # # => true
280
+ #
281
+ def delete(model)
282
+ # LREM key 0 <id> means remove all elements matching <id>
283
+ # @see http://redis.io/commands/lrem
284
+ db.lrem(key, 0, model.id)
178
285
  end
179
286
 
287
+ private
288
+ def ids
289
+ db.lrange(key, 0, -1)
290
+ end
291
+
292
+ def db
293
+ model.db
294
+ end
295
+ end
296
+
297
+ # Defines most of the methods used by `Set` and `MultiSet`.
298
+ class BasicSet
299
+ include Collection
300
+
180
301
  # Allows you to sort by any field in your model.
181
302
  #
182
303
  # Example:
@@ -249,6 +370,7 @@ module Ohm
249
370
  def size
250
371
  execute { |key| db.scard(key) }
251
372
  end
373
+ alias :count :size
252
374
 
253
375
  # Syntactic sugar for `sort_by` or `sort` when you only need the
254
376
  # first element.
@@ -305,128 +427,17 @@ module Ohm
305
427
  end
306
428
  end
307
429
 
308
- class List < Struct.new(:key, :namespace, :model)
309
- include PipelinedFetch
310
- include Enumerable
311
-
312
- # Returns the total size of the list using LLEN.
313
- def size
314
- db.llen(key)
315
- end
316
-
317
- # Returns the first element of the list using LINDEX.
318
- def first
319
- model[db.lindex(key, 0)]
320
- end
321
-
322
- # Returns the last element of the list using LINDEX.
323
- def last
324
- model[db.lindex(key, -1)]
325
- end
326
-
327
- # Checks if the model is part of this List.
328
- #
329
- # An important thing to note is that this method loads all of the
330
- # elements of the List since there is no command in Redis that
331
- # allows you to actually check the list contents efficiently.
332
- #
333
- # You may want to avoid doing this if your list has say, 10K entries.
334
- def include?(model)
335
- ids.include?(model.id.to_s)
336
- end
337
-
338
- # Replace all the existing elements of a list with a different
339
- # collection of models. This happens atomically in a MULTI-EXEC
340
- # block.
341
- #
342
- # Example:
343
- #
344
- # user = User.create
345
- # p1 = Post.create
346
- # user.posts.push(p1)
347
- #
348
- # p2, p3 = Post.create, Post.create
349
- # user.posts.replace([p2, p3])
350
- #
351
- # user.posts.include?(p1)
352
- # # => false
353
- #
354
- def replace(models)
355
- ids = models.map { |model| model.id }
430
+ class Set < BasicSet
431
+ attr :key
432
+ attr :namespace
433
+ attr :model
356
434
 
357
- model.db.multi do
358
- db.del(key)
359
- ids.each { |id| db.rpush(key, id) }
360
- end
435
+ def initialize(key, namespace, model)
436
+ @key = key
437
+ @namespace = namespace
438
+ @model = model
361
439
  end
362
440
 
363
- # Fetch the data from Redis in one go.
364
- def to_a
365
- fetch(ids)
366
- end
367
-
368
- def each
369
- to_a.each { |element| yield element }
370
- end
371
-
372
- def empty?
373
- size == 0
374
- end
375
-
376
- # Pushes the model to the _end_ of the list using RPUSH.
377
- def push(model)
378
- db.rpush(key, model.id)
379
- end
380
-
381
- # Pushes the model to the _beginning_ of the list using LPUSH.
382
- def unshift(model)
383
- db.lpush(key, model.id)
384
- end
385
-
386
- # Delete a model from the list.
387
- #
388
- # Note: If your list contains the model multiple times, this method
389
- # will delete all instances of that model in one go.
390
- #
391
- # Example:
392
- #
393
- # class Comment < Ohm::Model
394
- # end
395
- #
396
- # class Post < Ohm::Model
397
- # list :comments, Comment
398
- # end
399
- #
400
- # p = Post.create
401
- # c = Comment.create
402
- #
403
- # p.comments.push(c)
404
- # p.comments.push(c)
405
- #
406
- # p.comments.delete(c)
407
- #
408
- # p.comments.size == 0
409
- # # => true
410
- #
411
- def delete(model)
412
- # LREM key 0 <id> means remove all elements matching <id>
413
- # @see http://redis.io/commands/lrem
414
- db.lrem(key, 0, model.id)
415
- end
416
-
417
- private
418
- def ids
419
- db.lrange(key, 0, -1)
420
- end
421
-
422
- def db
423
- model.db
424
- end
425
- end
426
-
427
- class Set < Struct.new(:key, :namespace, :model)
428
- include Collection
429
-
430
441
  # Chain new fiters on an existing set.
431
442
  #
432
443
  # Example:
@@ -547,8 +558,16 @@ module Ohm
547
558
  # User.find(:name => "John", :age => 30).kind_of?(Ohm::MultiSet)
548
559
  # # => true
549
560
  #
550
- class MultiSet < Struct.new(:namespace, :model, :command)
551
- include Collection
561
+ class MultiSet < BasicSet
562
+ attr :namespace
563
+ attr :model
564
+ attr :command
565
+
566
+ def initialize(namespace, model, command)
567
+ @namespace = namespace
568
+ @model = model
569
+ @command = command
570
+ end
552
571
 
553
572
  # Chain new fiters on an existing set.
554
573
  #
@@ -0,0 +1,79 @@
1
+ require File.expand_path("./helper", File.dirname(__FILE__))
2
+
3
+ scope do
4
+ class Contact < Ohm::Model
5
+ attribute :name
6
+ end
7
+
8
+ setup do
9
+ john = Contact.create(name: "John Doe")
10
+ jane = Contact.create(name: "Jane Doe")
11
+
12
+ [john, jane]
13
+ end
14
+
15
+ test "Set#count doesn't do each" do
16
+ set = Contact.all
17
+
18
+ def set.each
19
+ raise "Failed"
20
+ end
21
+
22
+ assert_equal 2, set.count
23
+ end
24
+
25
+ test "Set#each as an Enumerator" do |john, jane|
26
+ enum = Contact.all.each
27
+
28
+ enum.each do |c|
29
+ assert c == john || c == jane
30
+ end
31
+ end
32
+
33
+ test "select" do |john, jane|
34
+ assert_equal 2, Contact.all.count
35
+ assert_equal [john], Contact.all.select { |c| c.id == john.id }
36
+ end
37
+ end
38
+
39
+ scope do
40
+ class Comment < Ohm::Model
41
+ end
42
+
43
+ class Post < Ohm::Model
44
+ list :comments, :Comment
45
+ end
46
+
47
+ setup do
48
+ c1 = Comment.create
49
+ c2 = Comment.create
50
+
51
+ post = Post.create
52
+ post.comments.push(c1)
53
+ post.comments.push(c2)
54
+
55
+ [post, c1, c2]
56
+ end
57
+
58
+ test "List#select" do |post, c1, c2|
59
+ assert_equal [c1], post.comments.select { |comment| comment == c1 }
60
+ end
61
+
62
+ test "List#each as Enumerator" do |post, c1, c2|
63
+ enum = post.comments.each
64
+
65
+ enum.each do |comment|
66
+ assert comment == c1 || comment == c2
67
+ end
68
+ end
69
+
70
+ test "List#count doesn't do each" do |post, c1, c2|
71
+ list = post.comments
72
+
73
+ def list.each
74
+ raise "Failed"
75
+ end
76
+
77
+ assert_equal 2, list.count
78
+ end
79
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ohm
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.2
4
+ version: 1.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -15,7 +15,7 @@ date: 2012-08-29 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: redis
18
- requirement: &2152402160 !ruby/object:Gem::Requirement
18
+ requirement: !ruby/object:Gem::Requirement
19
19
  none: false
20
20
  requirements:
21
21
  - - ! '>='
@@ -23,10 +23,15 @@ dependencies:
23
23
  version: '0'
24
24
  type: :runtime
25
25
  prerelease: false
26
- version_requirements: *2152402160
26
+ version_requirements: !ruby/object:Gem::Requirement
27
+ none: false
28
+ requirements:
29
+ - - ! '>='
30
+ - !ruby/object:Gem::Version
31
+ version: '0'
27
32
  - !ruby/object:Gem::Dependency
28
33
  name: nest
29
- requirement: &2152401480 !ruby/object:Gem::Requirement
34
+ requirement: !ruby/object:Gem::Requirement
30
35
  none: false
31
36
  requirements:
32
37
  - - ~>
@@ -34,10 +39,15 @@ dependencies:
34
39
  version: '1.0'
35
40
  type: :runtime
36
41
  prerelease: false
37
- version_requirements: *2152401480
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '1.0'
38
48
  - !ruby/object:Gem::Dependency
39
49
  name: scrivener
40
- requirement: &2152400640 !ruby/object:Gem::Requirement
50
+ requirement: !ruby/object:Gem::Requirement
41
51
  none: false
42
52
  requirements:
43
53
  - - ~>
@@ -45,10 +55,15 @@ dependencies:
45
55
  version: 0.0.3
46
56
  type: :runtime
47
57
  prerelease: false
48
- version_requirements: *2152400640
58
+ version_requirements: !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ~>
62
+ - !ruby/object:Gem::Version
63
+ version: 0.0.3
49
64
  - !ruby/object:Gem::Dependency
50
65
  name: cutest
51
- requirement: &2152400100 !ruby/object:Gem::Requirement
66
+ requirement: !ruby/object:Gem::Requirement
52
67
  none: false
53
68
  requirements:
54
69
  - - ~>
@@ -56,7 +71,12 @@ dependencies:
56
71
  version: '1.1'
57
72
  type: :development
58
73
  prerelease: false
59
- version_requirements: *2152400100
74
+ version_requirements: !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ~>
78
+ - !ruby/object:Gem::Version
79
+ version: '1.1'
60
80
  description: Ohm is a library that allows to store an object in Redis, a persistent
61
81
  key-value database. It includes an extensible list of validations and has very good
62
82
  performance.
@@ -81,6 +101,7 @@ files:
81
101
  - test/connection.rb
82
102
  - test/core.rb
83
103
  - test/counters.rb
104
+ - test/enumerable.rb
84
105
  - test/extensibility.rb
85
106
  - test/filtering.rb
86
107
  - test/hash_key.rb
@@ -115,9 +136,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
115
136
  version: '0'
116
137
  requirements: []
117
138
  rubyforge_project: ohm
118
- rubygems_version: 1.8.11
139
+ rubygems_version: 1.8.23
119
140
  signing_key:
120
141
  specification_version: 3
121
142
  summary: Object-hash mapping library for Redis.
122
143
  test_files: []
123
- has_rdoc: