ohm 1.1.2 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
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: