ohm 2.0.0.alpha5 → 2.0.0.rc1
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.
- checksums.yaml +4 -4
- data/.gitignore +1 -2
- data/CHANGELOG +60 -0
- data/README.md +57 -205
- data/Rakefile +0 -2
- data/lib/ohm.rb +60 -23
- data/lib/ohm/json.rb +0 -7
- data/lib/ohm/lua/save.lua +4 -3
- data/ohm.gemspec +2 -2
- data/test/enumerable.rb +4 -4
- data/test/filtering.rb +18 -142
- data/test/helper.rb +0 -1
- data/test/json.rb +32 -39
- data/test/model.rb +6 -10
- data/test/set.rb +20 -0
- data/test/to_hash.rb +29 -0
- metadata +5 -5
- data/test/extensibility.rb +0 -48
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 60d1fadf47380356ffbc44627e28f9b15ff8528a
|
4
|
+
data.tar.gz: f911d9c462235f7c56f5216b10f1b2d48f3ab287
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 67eea41a25fd17691a9cc79700abf248438a7bccefc7624053212d6439d6252bfc2850e0a7905fe8a2ad46eadac6d9d11d150978cec4f937a2bbba89ea05e90b
|
7
|
+
data.tar.gz: 1e17aace64652f6c96cd79fa6ff071a67fbed614951af15e8487d8fee78b21ec69f10313b7e8b1004292d1f97a22c3d8be74d5f892172742fe634fea84989ef2
|
data/.gitignore
CHANGED
data/CHANGELOG
CHANGED
@@ -1,3 +1,63 @@
|
|
1
|
+
(unreleased)
|
2
|
+
|
3
|
+
- Include Ohm::BasicSet#exists? in the public API. This makes possible
|
4
|
+
to check if an id is included in a set. Check Ohm::BasicSet#exists?
|
5
|
+
documentation for more details.
|
6
|
+
|
7
|
+
- Change Ohm::MultiSet#except to union keys instead of intersect them
|
8
|
+
when passing an array.
|
9
|
+
|
10
|
+
class User < Ohm::Model
|
11
|
+
attribute :name
|
12
|
+
end
|
13
|
+
|
14
|
+
john = User.create(:name => "John")
|
15
|
+
jane = User.create(:name => "Jane")
|
16
|
+
|
17
|
+
res = User.all.except(:name => [john.name, jane.name])
|
18
|
+
|
19
|
+
# before
|
20
|
+
res.size # => 2
|
21
|
+
|
22
|
+
# now
|
23
|
+
res.size # => 0
|
24
|
+
|
25
|
+
- Move ID generation to Lua. With this change, it's no longer possible
|
26
|
+
to generate custom ids. All ids are autoincremented.
|
27
|
+
|
28
|
+
- Ohm::Model#reference accepts strings as model references. For example:
|
29
|
+
|
30
|
+
class Bar < Ohm::Model
|
31
|
+
reference :foo, "SomeNamespace::Foo"
|
32
|
+
end
|
33
|
+
|
34
|
+
- Nest dependency has been removed. Now, Ohm uses Nido
|
35
|
+
(https://github.com/soveran/nido) to generate the keys that hold
|
36
|
+
the data.
|
37
|
+
|
38
|
+
- Scrivener dependency has been removed.
|
39
|
+
|
40
|
+
- Ohm no longer supports model validations and favors filter validation
|
41
|
+
on the boundary layer. Check Scrivener project
|
42
|
+
(https://github.com/soveran/scrivener) for more information.
|
43
|
+
|
44
|
+
- Redis dependency has been removed. Now, Ohm uses Redic
|
45
|
+
(https://github.com/amakawa/redic), a lightweight Redis client.
|
46
|
+
Redic uses the hiredis gem for the connection and for parsing
|
47
|
+
the replies. Check Redic's README for more details.
|
48
|
+
|
49
|
+
- Ohm::Model#transaction has been removed.
|
50
|
+
|
51
|
+
- Ohm::Transaction has been removed.
|
52
|
+
|
53
|
+
1.3.2
|
54
|
+
|
55
|
+
- Fetching a batch of objects is now done in batches of 1000 objects at
|
56
|
+
a time. If you are iterating over large collections, this change should
|
57
|
+
provide a significant performance boost both in used memory and total
|
58
|
+
execution time.
|
59
|
+
- MutableSet#<< is now an alias for #add.
|
60
|
+
|
1
61
|
1.3.1
|
2
62
|
|
3
63
|
- Improve memory consumption when indexing persisted attributes.
|
data/README.md
CHANGED
@@ -7,8 +7,7 @@ Description
|
|
7
7
|
-----------
|
8
8
|
|
9
9
|
Ohm is a library for storing objects in [Redis][redis], a persistent key-value
|
10
|
-
database. It
|
11
|
-
performance.
|
10
|
+
database. It has very good performance.
|
12
11
|
|
13
12
|
Community
|
14
13
|
---------
|
@@ -17,7 +16,6 @@ Join the mailing list: [http://groups.google.com/group/ohm-ruby](http://groups.g
|
|
17
16
|
|
18
17
|
Meet us on IRC: [#ohm](irc://chat.freenode.net/#ohm) on [freenode.net](http://freenode.net/)
|
19
18
|
|
20
|
-
|
21
19
|
Related projects
|
22
20
|
----------------
|
23
21
|
|
@@ -58,61 +56,55 @@ Now, in an irb session you can test the Redis adapter directly:
|
|
58
56
|
|
59
57
|
>> require "ohm"
|
60
58
|
=> true
|
61
|
-
>> Ohm.
|
62
|
-
=> []
|
63
|
-
>> Ohm.redis.set "Foo", "Bar"
|
59
|
+
>> Ohm.redis.call "SET", "Foo", "Bar"
|
64
60
|
=> "OK"
|
65
|
-
>> Ohm.redis.
|
61
|
+
>> Ohm.redis.call "GET", "Foo"
|
66
62
|
=> "Bar"
|
67
63
|
|
68
|
-
## Connecting to
|
69
|
-
|
70
|
-
There are a couple of different strategies for connecting to your Redis
|
71
|
-
database. The first is to explicitly set the `:host`, `:port`, `:db` and
|
72
|
-
`:timeout` options. You can also set only a few of them, and let the other
|
73
|
-
options fall back to the default.
|
74
|
-
|
75
|
-
The other noteworthy style of connecting is by just doing `Ohm.connect` and
|
76
|
-
set the environment variable `REDIS_URL`.
|
64
|
+
## Connecting to a Redis database
|
77
65
|
|
78
|
-
|
66
|
+
Ohm uses a lightweight Redis client called [Redic][redic]. To connect
|
67
|
+
to a Redis database, you will need to set an instance of `Redic`, with
|
68
|
+
an URL of the form `redis://:<passwd>@<host>:<port>/<db>`, through the
|
69
|
+
`Ohm.redis=` method, e.g.
|
79
70
|
|
80
|
-
|
81
|
-
|
82
|
-
A Redis URL of the form `redis://:<passwd>@<host>:<port>/<db>`.
|
83
|
-
Note that if you specify a URL and one of the other options at
|
84
|
-
the same time, the other options will take precedence. Also, if
|
85
|
-
you try and do `Ohm.connect` without any arguments, it will check
|
86
|
-
if `ENV["REDIS_URL"]` is set, and will use it as the argument for
|
87
|
-
`:url`.
|
71
|
+
```ruby
|
72
|
+
require "ohm"
|
88
73
|
|
89
|
-
|
74
|
+
Ohm.redis = Redic.new("redis://127.0.0.1:6379")
|
90
75
|
|
91
|
-
|
76
|
+
Ohm.redis.call "SET", "Foo", "Bar"
|
92
77
|
|
93
|
-
|
78
|
+
Ohm.redis.call "GET", "Foo"
|
79
|
+
# => "Bar"
|
80
|
+
```
|
94
81
|
|
95
|
-
|
82
|
+
Ohm defaults to a Redic connection to "redis://127.0.0.1:6379". The
|
83
|
+
example above could be rewritten as:
|
96
84
|
|
97
|
-
|
85
|
+
```ruby
|
86
|
+
require "ohm"
|
98
87
|
|
99
|
-
|
88
|
+
Ohm.redis.call "SET", "Foo", "Bar"
|
100
89
|
|
101
|
-
|
90
|
+
Ohm.redis.call "GET", "Foo"
|
91
|
+
# => "Bar"
|
92
|
+
```
|
102
93
|
|
103
|
-
|
104
|
-
|
94
|
+
All Ohm models inherit the same connection settings from `Ohm.redis`.
|
95
|
+
For cases where certain models need to connect to different databases,
|
96
|
+
they simple have to override that, i.e.
|
105
97
|
|
106
|
-
|
98
|
+
```ruby
|
99
|
+
require "ohm"
|
107
100
|
|
108
|
-
|
101
|
+
Ohm.redis = Redic.new(ENV["REDIS_URL1"])
|
109
102
|
|
110
|
-
|
103
|
+
class User < Ohm::Model
|
104
|
+
end
|
111
105
|
|
112
|
-
|
113
|
-
|
114
|
-
instance per thread. I you have no choice, then pass `:thread_safe => true`
|
115
|
-
when connecting.
|
106
|
+
User.redis = Redic.new(ENV["REDIS_URL2"])
|
107
|
+
```
|
116
108
|
|
117
109
|
Models
|
118
110
|
------
|
@@ -131,10 +123,6 @@ class Event < Ohm::Model
|
|
131
123
|
counter :votes
|
132
124
|
|
133
125
|
index :name
|
134
|
-
|
135
|
-
def validate
|
136
|
-
assert_present :name
|
137
|
-
end
|
138
126
|
end
|
139
127
|
|
140
128
|
class Venue < Ohm::Model
|
@@ -165,12 +153,12 @@ Event[2]
|
|
165
153
|
# => nil
|
166
154
|
|
167
155
|
# Finding all the events
|
168
|
-
Event.all
|
169
|
-
# => [
|
156
|
+
Event.all.to_a
|
157
|
+
# => [<Event:1 name='Ohm Worldwide Conference 2031'>]
|
170
158
|
```
|
171
159
|
|
172
160
|
This example shows some basic features, like attribute declarations and
|
173
|
-
|
161
|
+
querying. Keep reading to find out what you can do with models.
|
174
162
|
|
175
163
|
Attribute types
|
176
164
|
---------------
|
@@ -223,31 +211,20 @@ Persistence strategy
|
|
223
211
|
--------------------
|
224
212
|
|
225
213
|
The attributes declared with `attribute` are only persisted after
|
226
|
-
calling `save`.
|
227
|
-
to Redis (see the section on **Validations** below).
|
214
|
+
calling `save`.
|
228
215
|
|
229
216
|
Operations on attributes of type `list`, `set` and `counter` are
|
230
217
|
possible only after the object is created (when it has an assigned
|
231
218
|
`id`). Any operation on these kinds of attributes is performed
|
232
|
-
immediately
|
233
|
-
|
234
|
-
buffering the operations and waiting for a call to `save`.
|
219
|
+
immediately. This design yields better performance than buffering
|
220
|
+
the operations and waiting for a call to `save`.
|
235
221
|
|
236
222
|
For most use cases, this pattern doesn't represent a problem.
|
237
|
-
If you need to check for validity before operating on lists, sets or
|
238
|
-
counters, you can use this pattern:
|
239
|
-
|
240
|
-
```ruby
|
241
|
-
if event.valid?
|
242
|
-
event.comments.add(Comment.create(:body => "Great event!"))
|
243
|
-
end
|
244
|
-
```
|
245
|
-
|
246
223
|
If you are saving the object, this will suffice:
|
247
224
|
|
248
225
|
```ruby
|
249
226
|
if event.save
|
250
|
-
event.comments.add(Comment.create(:
|
227
|
+
event.comments.add(Comment.create(body: "Wonderful event!"))
|
251
228
|
end
|
252
229
|
```
|
253
230
|
|
@@ -267,7 +244,7 @@ You can add instances of `Person` to the set of attendees with the
|
|
267
244
|
`add` method:
|
268
245
|
|
269
246
|
```ruby
|
270
|
-
event.attendees.add(Person.create(:
|
247
|
+
event.attendees.add(Person.create(name: "Albert"))
|
271
248
|
|
272
249
|
# And now...
|
273
250
|
event.attendees.each do |person|
|
@@ -317,7 +294,7 @@ and it's within the current model you are sorting.
|
|
317
294
|
|
318
295
|
```ruby
|
319
296
|
Post.all.sort_by(:title) # SORT Post:all BY Post:*->title
|
320
|
-
Post.all.sort(:
|
297
|
+
Post.all.sort(by: :title) # SORT Post:all BY title
|
321
298
|
```
|
322
299
|
|
323
300
|
__Tip:__ Unless you absolutely know what you're doing, use `sort`
|
@@ -332,10 +309,10 @@ the `:by` option, using {Ohm::Model::Collection#sort sort} and
|
|
332
309
|
that `sort_by` does much of the hand-coding for you.
|
333
310
|
|
334
311
|
```ruby
|
335
|
-
Post.all.sort_by(:title, :
|
312
|
+
Post.all.sort_by(:title, get: :title)
|
336
313
|
# SORT Post:all BY Post:*->title GET Post:*->title
|
337
314
|
|
338
|
-
Post.all.sort(:
|
315
|
+
Post.all.sort(by: :title, get: :title)
|
339
316
|
# SORT Post:all BY title GET title
|
340
317
|
```
|
341
318
|
|
@@ -360,7 +337,7 @@ end
|
|
360
337
|
|
361
338
|
After this, every time you refer to `post.comments` you will be talking
|
362
339
|
about instances of the model `Comment`. If you want to get a list of IDs
|
363
|
-
you can use `post.comments.
|
340
|
+
you can use `post.comments.ids`.
|
364
341
|
|
365
342
|
### References explained
|
366
343
|
|
@@ -391,7 +368,7 @@ The net effect here is we can conveniently set and retrieve `Post` objects,
|
|
391
368
|
and also search comments using the `post_id` index.
|
392
369
|
|
393
370
|
```ruby
|
394
|
-
Comment.find(:
|
371
|
+
Comment.find(post_id: 1)
|
395
372
|
```
|
396
373
|
|
397
374
|
### Collections explained
|
@@ -409,7 +386,7 @@ class Post < Ohm::Model
|
|
409
386
|
attribute :body
|
410
387
|
|
411
388
|
def comments
|
412
|
-
Comment.find(:
|
389
|
+
Comment.find(post_id: self.id)
|
413
390
|
end
|
414
391
|
end
|
415
392
|
```
|
@@ -441,7 +418,7 @@ any index declared, Ohm maintains different sets of objects IDs for quick
|
|
441
418
|
lookups.
|
442
419
|
|
443
420
|
In the `Event` example, the index on the name attribute will
|
444
|
-
allow for searches like `Event.find(:
|
421
|
+
allow for searches like `Event.find(name: "some value")`.
|
445
422
|
|
446
423
|
Note that the methods {Ohm::Model::Set#find find} and
|
447
424
|
{Ohm::Model::Set#except except} need a corresponding index in order to work.
|
@@ -452,23 +429,23 @@ You can find a collection of records with the `find` method:
|
|
452
429
|
|
453
430
|
```ruby
|
454
431
|
# This returns a collection of users with the username "Albert"
|
455
|
-
User.find(:
|
432
|
+
User.find(username: "Albert")
|
456
433
|
```
|
457
434
|
|
458
435
|
### Filtering results
|
459
436
|
|
460
437
|
```ruby
|
461
438
|
# Find all users from Argentina
|
462
|
-
User.find(:
|
439
|
+
User.find(country: "Argentina")
|
463
440
|
|
464
441
|
# Find all activated users from Argentina
|
465
|
-
User.find(:
|
442
|
+
User.find(country: "Argentina", status: "activated")
|
466
443
|
|
467
444
|
# Find all users from Argentina, except those with a suspended account.
|
468
|
-
User.find(:
|
445
|
+
User.find(country: "Argentina").except(status: "suspended")
|
469
446
|
|
470
447
|
# Find all users both from Argentina and Uruguay
|
471
|
-
User.find(:
|
448
|
+
User.find(country: "Argentina").union(country: "Uruguay")
|
472
449
|
```
|
473
450
|
|
474
451
|
Note that calling these methods results in new sets being created
|
@@ -499,131 +476,6 @@ User.create(email: "foo@bar.com")
|
|
499
476
|
# => raises Ohm::UniqueIndexViolation
|
500
477
|
```
|
501
478
|
|
502
|
-
Validations
|
503
|
-
-----------
|
504
|
-
|
505
|
-
Before every save, the `validate` method is called by Ohm. In the method
|
506
|
-
definition you can use assertions that will determine if the attributes
|
507
|
-
are valid. Nesting assertions is a good practice, and you are also
|
508
|
-
encouraged to create your own assertions. You can trigger validations at
|
509
|
-
any point by calling `valid?` on a model instance.
|
510
|
-
|
511
|
-
Assertions
|
512
|
-
-----------
|
513
|
-
|
514
|
-
Ohm ships with some basic assertions. Check Ohm::Validations to see
|
515
|
-
the method definitions.
|
516
|
-
|
517
|
-
### assert
|
518
|
-
|
519
|
-
The `assert` method is used by all the other assertions. It pushes the
|
520
|
-
second parameter to the list of errors if the first parameter evaluates
|
521
|
-
to false.
|
522
|
-
|
523
|
-
```ruby
|
524
|
-
def assert(value, error)
|
525
|
-
value or errors.push(error) && false
|
526
|
-
end
|
527
|
-
```
|
528
|
-
|
529
|
-
### assert_present
|
530
|
-
|
531
|
-
Checks that the given field is not nil or empty. The error code for this
|
532
|
-
assertion is `:not_present`.
|
533
|
-
|
534
|
-
```ruby
|
535
|
-
assert_present :name
|
536
|
-
```
|
537
|
-
|
538
|
-
### assert_format
|
539
|
-
|
540
|
-
Checks that the given field matches the provided format. The error code
|
541
|
-
for this assertion is :format.
|
542
|
-
|
543
|
-
```ruby
|
544
|
-
assert_format :username, /^\w+$/
|
545
|
-
```
|
546
|
-
|
547
|
-
### assert_numeric
|
548
|
-
|
549
|
-
Checks that the given field holds a number as a Fixnum or as a string
|
550
|
-
representation. The error code for this assertion is :not_numeric.
|
551
|
-
|
552
|
-
```ruby
|
553
|
-
assert_numeric :votes
|
554
|
-
```
|
555
|
-
|
556
|
-
### assert_url
|
557
|
-
|
558
|
-
Provides a pretty general URL regular expression match. An important
|
559
|
-
point to make is that this assumes that the URL should start with
|
560
|
-
`http://` or `https://`. The error code for this assertion is
|
561
|
-
`:not_url`.
|
562
|
-
|
563
|
-
### assert_email
|
564
|
-
|
565
|
-
In this current day and age, almost all web applications need to
|
566
|
-
validate an email address. This pretty much matches 99% of the emails
|
567
|
-
out there. The error code for this assertion is `:not_email`.
|
568
|
-
|
569
|
-
### assert_member
|
570
|
-
|
571
|
-
Checks that a given field is contained within a set of values (i.e.
|
572
|
-
like an `ENUM`).
|
573
|
-
|
574
|
-
``` ruby
|
575
|
-
def validate
|
576
|
-
assert_member :state, %w{pending paid delivered}
|
577
|
-
end
|
578
|
-
```
|
579
|
-
|
580
|
-
The error code for this assertion is `:not_valid`
|
581
|
-
|
582
|
-
### assert_length
|
583
|
-
|
584
|
-
Checks that a given field's length falls under a specified range.
|
585
|
-
|
586
|
-
``` ruby
|
587
|
-
def validate
|
588
|
-
assert_length :username, 3..20
|
589
|
-
end
|
590
|
-
```
|
591
|
-
|
592
|
-
The error code for this assertion is `:not_in_range`.
|
593
|
-
|
594
|
-
### assert_decimal
|
595
|
-
|
596
|
-
Checks that a given field looks like a number in the human sense
|
597
|
-
of the word. Valid numbers are: 0.1, .1, 1, 1.1, 3.14159, etc.
|
598
|
-
|
599
|
-
The error code for this assertion is `:not_decimal`.
|
600
|
-
|
601
|
-
Errors
|
602
|
-
------
|
603
|
-
|
604
|
-
When an assertion fails, the error report is added to the errors array.
|
605
|
-
Each error report contains two elements: the field where the assertion
|
606
|
-
was issued and the error code.
|
607
|
-
|
608
|
-
### Validation example
|
609
|
-
|
610
|
-
Given the following example:
|
611
|
-
|
612
|
-
```ruby
|
613
|
-
def validate
|
614
|
-
assert_present :foo
|
615
|
-
assert_numeric :bar
|
616
|
-
assert_format :baz, /^\d{2}$/
|
617
|
-
end
|
618
|
-
```
|
619
|
-
|
620
|
-
If all the assertions fail, the following errors will be present:
|
621
|
-
|
622
|
-
```ruby
|
623
|
-
obj.errors
|
624
|
-
# => { foo: [:not_present], bar: [:not_numeric], baz: [:format] }
|
625
|
-
```
|
626
|
-
|
627
479
|
Ohm Extensions
|
628
480
|
==============
|
629
481
|
|
@@ -637,10 +489,10 @@ make sure to check them if you need to extend Ohm's functionality.
|
|
637
489
|
Upgrading
|
638
490
|
=========
|
639
491
|
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
for new projects.
|
492
|
+
Ohm 2 breaks the compatibility with previous versions. If you're upgrading an
|
493
|
+
existing application, it's nice to have a good test coverage before going in.
|
494
|
+
To know about fixes and changes, please refer to the CHANGELOG file.
|
644
495
|
|
645
496
|
[redis]: http://redis.io
|
646
497
|
[ohm]: http://github.com/soveran/ohm
|
498
|
+
[redic]: https://github.com/amakawa/redic
|
data/Rakefile
CHANGED
data/lib/ohm.rb
CHANGED
@@ -56,11 +56,12 @@ module Ohm
|
|
56
56
|
# reference :user, User # NameError undefined constant User.
|
57
57
|
# end
|
58
58
|
#
|
59
|
-
# Instead of relying on some clever `const_missing` hack, we can
|
60
|
-
# simply use a
|
59
|
+
# # Instead of relying on some clever `const_missing` hack, we can
|
60
|
+
# # simply use a symbol or a string.
|
61
61
|
#
|
62
62
|
# class Comment < Ohm::Model
|
63
63
|
# reference :user, :User
|
64
|
+
# reference :post, "Post"
|
64
65
|
# end
|
65
66
|
#
|
66
67
|
def self.const(context, name)
|
@@ -166,7 +167,6 @@ module Ohm
|
|
166
167
|
def size
|
167
168
|
redis.call("LLEN", key)
|
168
169
|
end
|
169
|
-
alias :count :size
|
170
170
|
|
171
171
|
# Returns the first element of the list using LINDEX.
|
172
172
|
def first
|
@@ -342,7 +342,6 @@ module Ohm
|
|
342
342
|
def size
|
343
343
|
execute { |key| redis.call("SCARD", key) }
|
344
344
|
end
|
345
|
-
alias :count :size
|
346
345
|
|
347
346
|
# Syntactic sugar for `sort_by` or `sort` when you only need the
|
348
347
|
# first element.
|
@@ -385,11 +384,29 @@ module Ohm
|
|
385
384
|
model[id] if exists?(id)
|
386
385
|
end
|
387
386
|
|
388
|
-
|
387
|
+
# Returns +true+ if +id+ is included in the set. Otherwise, returns +false+.
|
388
|
+
#
|
389
|
+
# Example:
|
390
|
+
#
|
391
|
+
# class Post < Ohm::Model
|
392
|
+
# end
|
393
|
+
#
|
394
|
+
# class User < Ohm::Model
|
395
|
+
# set :posts, :Post
|
396
|
+
# end
|
397
|
+
#
|
398
|
+
# user = User.create
|
399
|
+
# post = Post.create
|
400
|
+
# user.posts.add(post)
|
401
|
+
#
|
402
|
+
# user.posts.exists?('nonexistent') # => false
|
403
|
+
# user.posts.exists?(post.id) # => true
|
404
|
+
#
|
389
405
|
def exists?(id)
|
390
406
|
execute { |key| redis.call("SISMEMBER", key, id) == 1 }
|
391
407
|
end
|
392
408
|
|
409
|
+
private
|
393
410
|
def to_key(att)
|
394
411
|
if model.counters.include?(att)
|
395
412
|
namespace["*:counters->%s" % att]
|
@@ -569,7 +586,7 @@ module Ohm
|
|
569
586
|
#
|
570
587
|
def except(dict)
|
571
588
|
MultiSet.new(
|
572
|
-
namespace, model, Command[:sdiffstore, command,
|
589
|
+
namespace, model, Command[:sdiffstore, command, unioned(dict)]
|
573
590
|
)
|
574
591
|
end
|
575
592
|
|
@@ -598,6 +615,10 @@ module Ohm
|
|
598
615
|
Command[:sinterstore, *model.filters(dict)]
|
599
616
|
end
|
600
617
|
|
618
|
+
def unioned(dict)
|
619
|
+
Command[:sunionstore, *model.filters(dict)]
|
620
|
+
end
|
621
|
+
|
601
622
|
def execute
|
602
623
|
# namespace[:tmp] is where all the temp keys should be stored in.
|
603
624
|
# redis will be where all the commands are executed against.
|
@@ -677,7 +698,7 @@ module Ohm
|
|
677
698
|
@redis ||= Redic.new(Ohm.redis.url)
|
678
699
|
end
|
679
700
|
|
680
|
-
#
|
701
|
+
# Returns the namespace for all the keys generated using this model.
|
681
702
|
#
|
682
703
|
# Example:
|
683
704
|
#
|
@@ -1018,7 +1039,7 @@ module Ohm
|
|
1018
1039
|
# u = User.create
|
1019
1040
|
# u.incr :points
|
1020
1041
|
#
|
1021
|
-
#
|
1042
|
+
# u.points
|
1022
1043
|
# # => 1
|
1023
1044
|
#
|
1024
1045
|
# Note: You can't use counters until you save the model. If you
|
@@ -1049,21 +1070,8 @@ module Ohm
|
|
1049
1070
|
new(atts).save
|
1050
1071
|
end
|
1051
1072
|
|
1052
|
-
#
|
1053
|
-
#
|
1054
|
-
# Example:
|
1055
|
-
#
|
1056
|
-
# class User < Ohm::Model
|
1057
|
-
# attribute :name
|
1058
|
-
# end
|
1059
|
-
#
|
1060
|
-
# u = User.create(:name => "John")
|
1061
|
-
# u.key.hget(:name)
|
1062
|
-
# # => John
|
1063
|
-
#
|
1064
|
-
# For more details see
|
1065
|
-
# http://github.com/soveran/nest
|
1066
|
-
#
|
1073
|
+
# Returns the namespace for the keys generated using this model.
|
1074
|
+
# Check `Ohm::Model.key` documentation for more details.
|
1067
1075
|
def key
|
1068
1076
|
model.key[id]
|
1069
1077
|
end
|
@@ -1153,6 +1161,21 @@ module Ohm
|
|
1153
1161
|
@attributes[att] = val
|
1154
1162
|
end
|
1155
1163
|
|
1164
|
+
# Returns +true+ if the model is not persisted. Otherwise, returns +false+.
|
1165
|
+
#
|
1166
|
+
# Example:
|
1167
|
+
#
|
1168
|
+
# class User < Ohm::Model
|
1169
|
+
# attribute :name
|
1170
|
+
# end
|
1171
|
+
#
|
1172
|
+
# u = User.new(:name => "John")
|
1173
|
+
# u.new?
|
1174
|
+
# # => true
|
1175
|
+
#
|
1176
|
+
# u.save
|
1177
|
+
# u.new?
|
1178
|
+
# # => false
|
1156
1179
|
def new?
|
1157
1180
|
!defined?(@id)
|
1158
1181
|
end
|
@@ -1184,6 +1207,20 @@ module Ohm
|
|
1184
1207
|
end
|
1185
1208
|
alias :eql? :==
|
1186
1209
|
|
1210
|
+
# Returns a hash of the attributes with their names as keys
|
1211
|
+
# and the values of the attributes as values. It doesn't
|
1212
|
+
# include the ID of the model.
|
1213
|
+
#
|
1214
|
+
# Example:
|
1215
|
+
#
|
1216
|
+
# class User < Ohm::Model
|
1217
|
+
# attribute :name
|
1218
|
+
# end
|
1219
|
+
#
|
1220
|
+
# u = User.create(:name => "John")
|
1221
|
+
# u.attributes
|
1222
|
+
# # => { :name => "John" }
|
1223
|
+
#
|
1187
1224
|
def attributes
|
1188
1225
|
@attributes
|
1189
1226
|
end
|
data/lib/ohm/json.rb
CHANGED
data/lib/ohm/lua/save.lua
CHANGED
@@ -6,10 +6,11 @@
|
|
6
6
|
--
|
7
7
|
-- # model
|
8
8
|
--
|
9
|
-
-- Table with
|
10
|
-
-- id (model instance id)
|
11
|
-
-- key (hash where the attributes will be saved)
|
9
|
+
-- Table with one or two attributes:
|
12
10
|
-- name (model name)
|
11
|
+
-- id (model instance id, optional)
|
12
|
+
--
|
13
|
+
-- If the id is not provided, it is treated as a new record.
|
13
14
|
--
|
14
15
|
-- # attrs
|
15
16
|
--
|
data/ohm.gemspec
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "ohm"
|
3
|
-
s.version = "2.0.0.
|
3
|
+
s.version = "2.0.0.rc1"
|
4
4
|
s.summary = %{Object-hash mapping library for Redis.}
|
5
|
-
s.description = %Q{Ohm is a library that allows to store an object in Redis, a persistent key-value database. It
|
5
|
+
s.description = %Q{Ohm is a library that allows to store an object in Redis, a persistent key-value database. It has very good performance.}
|
6
6
|
s.authors = ["Michel Martens", "Damian Janowski", "Cyril David"]
|
7
7
|
s.email = ["michel@soveran.com", "djanowski@dimaion.com", "me@cyrildavid.com"]
|
8
8
|
s.homepage = "http://soveran.github.io/ohm/"
|
data/test/enumerable.rb
CHANGED
@@ -12,14 +12,14 @@ scope do
|
|
12
12
|
[john, jane]
|
13
13
|
end
|
14
14
|
|
15
|
-
test "Set#
|
15
|
+
test "Set#size doesn't do each" do
|
16
16
|
set = Contact.all
|
17
17
|
|
18
18
|
def set.each
|
19
19
|
raise "Failed"
|
20
20
|
end
|
21
21
|
|
22
|
-
assert_equal 2, set.
|
22
|
+
assert_equal 2, set.size
|
23
23
|
end
|
24
24
|
|
25
25
|
test "Set#each as an Enumerator" do |john, jane|
|
@@ -67,13 +67,13 @@ scope do
|
|
67
67
|
end
|
68
68
|
end
|
69
69
|
|
70
|
-
test "List#
|
70
|
+
test "List#size doesn't do each" do |post, c1, c2|
|
71
71
|
list = post.comments
|
72
72
|
|
73
73
|
def list.each
|
74
74
|
raise "Failed"
|
75
75
|
end
|
76
76
|
|
77
|
-
assert_equal 2, list.
|
77
|
+
assert_equal 2, list.size
|
78
78
|
end
|
79
79
|
end
|
data/test/filtering.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
__END__
|
2
1
|
require File.expand_path("./helper", File.dirname(__FILE__))
|
3
2
|
|
4
3
|
class User < Ohm::Model
|
5
4
|
attribute :fname
|
6
5
|
attribute :lname
|
7
6
|
attribute :status
|
7
|
+
|
8
8
|
index :fname
|
9
9
|
index :lname
|
10
10
|
index :status
|
@@ -64,9 +64,24 @@ test "#except" do |john, jane|
|
|
64
64
|
res = User.all.except(:status => "inactive")
|
65
65
|
|
66
66
|
assert_equal 2, res.size
|
67
|
+
assert res.include?(john)
|
67
68
|
assert res.include?(jane)
|
68
69
|
end
|
69
70
|
|
71
|
+
test "#except unions keys when passing an array" do |john, jane|
|
72
|
+
expected = User.create(:fname => "Jean", :status => "inactive")
|
73
|
+
|
74
|
+
res = User.find(:status => "inactive").except(:fname => [john.fname, jane.fname])
|
75
|
+
|
76
|
+
assert_equal 1, res.size
|
77
|
+
assert res.include?(expected)
|
78
|
+
|
79
|
+
res = User.all.except(:fname => [john.fname, jane.fname])
|
80
|
+
|
81
|
+
assert_equal 1, res.size
|
82
|
+
assert res.include?(expected)
|
83
|
+
end
|
84
|
+
|
70
85
|
test "indices bug related to a nil attribute" do |john, jane|
|
71
86
|
# First we create a record with a nil attribute
|
72
87
|
out = User.create(:status => nil, :lname => "Doe")
|
@@ -77,7 +92,7 @@ test "indices bug related to a nil attribute" do |john, jane|
|
|
77
92
|
|
78
93
|
# At this point, the index for the nil attribute should
|
79
94
|
# have been cleared.
|
80
|
-
assert_equal 0, User.
|
95
|
+
assert_equal 0, User.redis.call("SCARD", "User:indices:status:")
|
81
96
|
end
|
82
97
|
|
83
98
|
test "#union" do |john, jane|
|
@@ -150,7 +165,7 @@ scope do
|
|
150
165
|
|
151
166
|
assert_equal 1, res.size
|
152
167
|
assert res.map(&:mood).include?("sad")
|
153
|
-
assert res.map(&:book_id).include?(book2.id)
|
168
|
+
assert res.map(&:book_id).include?(book2.id.to_s)
|
154
169
|
end
|
155
170
|
|
156
171
|
test "@myobie usecase" do |book1, book2|
|
@@ -160,142 +175,3 @@ scope do
|
|
160
175
|
assert_equal 2, res.size
|
161
176
|
end
|
162
177
|
end
|
163
|
-
|
164
|
-
# test precision of filtering commands
|
165
|
-
require "logger"
|
166
|
-
require "stringio"
|
167
|
-
scope do
|
168
|
-
class Post < Ohm::Model
|
169
|
-
attribute :author
|
170
|
-
index :author
|
171
|
-
|
172
|
-
attribute :mood
|
173
|
-
index :mood
|
174
|
-
end
|
175
|
-
|
176
|
-
setup do
|
177
|
-
io = StringIO.new
|
178
|
-
|
179
|
-
Post.connect(:logger => Logger.new(io))
|
180
|
-
|
181
|
-
Post.create(author: "matz", mood: "happy")
|
182
|
-
Post.create(author: "rich", mood: "mad")
|
183
|
-
|
184
|
-
io
|
185
|
-
end
|
186
|
-
|
187
|
-
def read(io)
|
188
|
-
io.rewind
|
189
|
-
io.read
|
190
|
-
end
|
191
|
-
|
192
|
-
test "SINTERSTORE a b" do |io|
|
193
|
-
Post.find(author: "matz").find(mood: "happy").to_a
|
194
|
-
|
195
|
-
# This is the simple case. We should only do one SINTERSTORE
|
196
|
-
# given two direct keys. Anything more and we're performing badly.
|
197
|
-
expected = "SINTERSTORE Post:tmp:[a-f0-9]{64} " +
|
198
|
-
"Post:indices:author:matz Post:indices:mood:happy"
|
199
|
-
|
200
|
-
assert(read(io) =~ Regexp.new(expected))
|
201
|
-
end
|
202
|
-
|
203
|
-
test "SUNIONSTORE a b" do |io|
|
204
|
-
Post.find(author: "matz").union(mood: "happy").to_a
|
205
|
-
|
206
|
-
# Another simple case where we must only do one operation at maximum.
|
207
|
-
expected = "SUNIONSTORE Post:tmp:[a-f0-9]{64} " +
|
208
|
-
"Post:indices:author:matz Post:indices:mood:happy"
|
209
|
-
|
210
|
-
assert(read(io) =~ Regexp.new(expected))
|
211
|
-
end
|
212
|
-
|
213
|
-
test "SUNIONSTORE c (SINTERSTORE a b)" do |io|
|
214
|
-
Post.find(author: "matz").find(mood: "happy").union(author: "rich").to_a
|
215
|
-
|
216
|
-
# For this case we need an intermediate key. This will
|
217
|
-
# contain the intersection of matz + happy.
|
218
|
-
expected = "SINTERSTORE (Post:tmp:[a-f0-9]{64}) " +
|
219
|
-
"Post:indices:author:matz Post:indices:mood:happy"
|
220
|
-
|
221
|
-
assert(read(io) =~ Regexp.new(expected))
|
222
|
-
|
223
|
-
# The next operation is simply doing a UNION of the previously
|
224
|
-
# generated intermediate key and the additional single key.
|
225
|
-
expected = "SUNIONSTORE (Post:tmp:[a-f0-9]{64}) " +
|
226
|
-
"%s Post:indices:author:rich" % $1
|
227
|
-
|
228
|
-
assert(read(io) =~ Regexp.new(expected))
|
229
|
-
end
|
230
|
-
|
231
|
-
test "SUNIONSTORE (SINTERSTORE c d) (SINTERSTORE a b)" do |io|
|
232
|
-
Post.find(author: "matz").find(mood: "happy").
|
233
|
-
union(author: "rich", mood: "sad").to_a
|
234
|
-
|
235
|
-
# Similar to the previous case, we need to do an intermediate
|
236
|
-
# operation.
|
237
|
-
expected = "SINTERSTORE (Post:tmp:[a-f0-9]{64}) " +
|
238
|
-
"Post:indices:author:matz Post:indices:mood:happy"
|
239
|
-
|
240
|
-
match1 = read(io).match(Regexp.new(expected))
|
241
|
-
assert match1
|
242
|
-
|
243
|
-
# But now, we need to also hold another intermediate key for the
|
244
|
-
# condition of author: rich AND mood: sad.
|
245
|
-
expected = "SINTERSTORE (Post:tmp:[a-f0-9]{64}) " +
|
246
|
-
"Post:indices:author:rich Post:indices:mood:sad"
|
247
|
-
|
248
|
-
match2 = read(io).match(Regexp.new(expected))
|
249
|
-
assert match2
|
250
|
-
|
251
|
-
# Now we expect that it does a UNION of those two previous
|
252
|
-
# intermediate keys.
|
253
|
-
expected = sprintf(
|
254
|
-
"SUNIONSTORE (Post:tmp:[a-f0-9]{64}) %s %s",
|
255
|
-
match1[1], match2[1]
|
256
|
-
)
|
257
|
-
|
258
|
-
assert(read(io) =~ Regexp.new(expected))
|
259
|
-
end
|
260
|
-
|
261
|
-
test do |io|
|
262
|
-
Post.create(author: "kent", mood: "sad")
|
263
|
-
|
264
|
-
Post.find(author: "kent", mood: "sad").
|
265
|
-
union(author: "matz", mood: "happy").
|
266
|
-
except(mood: "sad", author: "rich").to_a
|
267
|
-
|
268
|
-
expected = "SINTERSTORE (Post:tmp:[a-f0-9]{64}) " +
|
269
|
-
"Post:indices:author:kent Post:indices:mood:sad"
|
270
|
-
|
271
|
-
match1 = read(io).match(Regexp.new(expected))
|
272
|
-
assert match1
|
273
|
-
|
274
|
-
expected = "SINTERSTORE (Post:tmp:[a-f0-9]{64}) " +
|
275
|
-
"Post:indices:author:matz Post:indices:mood:happy"
|
276
|
-
|
277
|
-
match2 = read(io).match(Regexp.new(expected))
|
278
|
-
assert match2
|
279
|
-
|
280
|
-
expected = sprintf(
|
281
|
-
"SUNIONSTORE (Post:tmp:[a-f0-9]{64}) %s %s",
|
282
|
-
match1[1], match2[1]
|
283
|
-
)
|
284
|
-
|
285
|
-
match3 = read(io).match(Regexp.new(expected))
|
286
|
-
assert match3
|
287
|
-
|
288
|
-
expected = "SINTERSTORE (Post:tmp:[a-f0-9]{64}) " +
|
289
|
-
"Post:indices:mood:sad Post:indices:author:rich"
|
290
|
-
|
291
|
-
match4 = read(io).match(Regexp.new(expected))
|
292
|
-
assert match4
|
293
|
-
|
294
|
-
expected = sprintf(
|
295
|
-
"SDIFFSTORE (Post:tmp:[a-f0-9]{64}) %s %s",
|
296
|
-
match3[1], match4[1]
|
297
|
-
)
|
298
|
-
|
299
|
-
assert(read(io) =~ Regexp.new(expected))
|
300
|
-
end
|
301
|
-
end
|
data/test/helper.rb
CHANGED
data/test/json.rb
CHANGED
@@ -1,70 +1,63 @@
|
|
1
|
-
|
1
|
+
require_relative 'helper'
|
2
2
|
|
3
|
-
require File.expand_path("./helper", File.dirname(__FILE__))
|
4
|
-
|
5
|
-
require "json"
|
6
3
|
require "ohm/json"
|
7
4
|
|
8
5
|
class Venue < Ohm::Model
|
9
6
|
attribute :name
|
10
7
|
list :programmers, :Programmer
|
11
|
-
|
12
|
-
def validate
|
13
|
-
assert_present :name
|
14
|
-
end
|
15
8
|
end
|
16
9
|
|
17
10
|
class Programmer < Ohm::Model
|
18
11
|
attribute :language
|
19
12
|
|
20
|
-
|
21
|
-
assert_present :language
|
22
|
-
end
|
13
|
+
index :language
|
23
14
|
|
24
15
|
def to_hash
|
25
|
-
super.merge(:
|
16
|
+
super.merge(language: language)
|
26
17
|
end
|
27
18
|
end
|
28
19
|
|
29
|
-
test "
|
30
|
-
|
31
|
-
assert Hash.new == person.to_hash
|
32
|
-
end
|
20
|
+
test "exports model.to_hash to json" do
|
21
|
+
assert_equal Hash.new, JSON.parse(Venue.new.to_json)
|
33
22
|
|
34
|
-
|
35
|
-
|
36
|
-
assert_equal
|
37
|
-
|
23
|
+
venue = Venue.create(name: "foo")
|
24
|
+
json = JSON.parse(venue.to_json)
|
25
|
+
assert_equal venue.id, json["id"]
|
26
|
+
assert_equal nil, json["name"]
|
38
27
|
|
39
|
-
|
40
|
-
|
41
|
-
expected_hash = { :id => 1, :language => 'Ruby' }
|
28
|
+
programmer = Programmer.create(language: "Ruby")
|
29
|
+
json = JSON.parse(programmer.to_json)
|
42
30
|
|
43
|
-
|
31
|
+
assert_equal programmer.id, json["id"]
|
32
|
+
assert_equal programmer.language, json["language"]
|
44
33
|
end
|
45
34
|
|
46
|
-
test "
|
47
|
-
|
35
|
+
test "exports a set to json" do
|
36
|
+
Programmer.create(language: "Ruby")
|
37
|
+
Programmer.create(language: "Python")
|
48
38
|
|
49
|
-
|
50
|
-
|
51
|
-
|
39
|
+
expected = [{ id: "1", language: "Ruby" }, { id: "2", language: "Python"}].to_json
|
40
|
+
|
41
|
+
assert_equal expected, Programmer.all.to_json
|
52
42
|
end
|
53
43
|
|
54
|
-
test "
|
55
|
-
Programmer.create(:
|
56
|
-
Programmer.create(:
|
44
|
+
test "exports a multiset to json" do
|
45
|
+
Programmer.create(language: "Ruby")
|
46
|
+
Programmer.create(language: "Python")
|
57
47
|
|
58
|
-
expected = [{ :
|
59
|
-
|
48
|
+
expected = [{ id: "1", language: "Ruby" }, { id: "2", language: "Python"}].to_json
|
49
|
+
result = Programmer.find(language: "Ruby").union(language: "Python").to_json
|
50
|
+
|
51
|
+
assert_equal expected, result
|
60
52
|
end
|
61
53
|
|
62
|
-
test "
|
63
|
-
venue = Venue.create(:
|
54
|
+
test "exports a list to json" do
|
55
|
+
venue = Venue.create(name: "Foo")
|
56
|
+
|
57
|
+
venue.programmers.push(Programmer.create(language: "Ruby"))
|
58
|
+
venue.programmers.push(Programmer.create(language: "Python"))
|
64
59
|
|
65
|
-
|
66
|
-
venue.programmers.push(Programmer.create(:language => "Python"))
|
60
|
+
expected = [{ id: "1", language: "Ruby" }, { id: "2", language: "Python"}].to_json
|
67
61
|
|
68
|
-
expected = [{ :id => "1", :language => "Ruby" }, { :id => "2", :language => "Python"}].to_json
|
69
62
|
assert_equal expected, venue.programmers.to_json
|
70
63
|
end
|
data/test/model.rb
CHANGED
@@ -7,12 +7,12 @@ require "ostruct"
|
|
7
7
|
class Post < Ohm::Model
|
8
8
|
attribute :body
|
9
9
|
attribute :published
|
10
|
-
set :related, Post
|
10
|
+
set :related, :Post
|
11
11
|
end
|
12
12
|
|
13
13
|
class User < Ohm::Model
|
14
14
|
attribute :email
|
15
|
-
set :posts, Post
|
15
|
+
set :posts, :Post
|
16
16
|
end
|
17
17
|
|
18
18
|
class Person < Ohm::Model
|
@@ -28,7 +28,7 @@ end
|
|
28
28
|
class Event < Ohm::Model
|
29
29
|
attribute :name
|
30
30
|
counter :votes
|
31
|
-
set :attendees, Person
|
31
|
+
set :attendees, :Person
|
32
32
|
|
33
33
|
attribute :slug
|
34
34
|
|
@@ -51,10 +51,6 @@ end
|
|
51
51
|
class Meetup < Ohm::Model
|
52
52
|
attribute :name
|
53
53
|
attribute :location
|
54
|
-
|
55
|
-
def validate
|
56
|
-
assert_present :name
|
57
|
-
end
|
58
54
|
end
|
59
55
|
|
60
56
|
test "booleans" do
|
@@ -311,8 +307,8 @@ end
|
|
311
307
|
test "delete an existing model" do
|
312
308
|
class ModelToBeDeleted < Ohm::Model
|
313
309
|
attribute :name
|
314
|
-
set :foos, Post
|
315
|
-
set :bars, Post
|
310
|
+
set :foos, :Post
|
311
|
+
set :bars, :Post
|
316
312
|
end
|
317
313
|
|
318
314
|
@model = ModelToBeDeleted.create(:name => "Lorem")
|
@@ -774,7 +770,7 @@ test "be persisted" do
|
|
774
770
|
|
775
771
|
assert "foo" == SomeNamespace::Foo[1].name
|
776
772
|
assert "foo" == SomeNamespace::Bar[1].foo.name
|
777
|
-
end
|
773
|
+
end if RUBY_VERSION >= "2.0.0"
|
778
774
|
|
779
775
|
test "typecast attributes" do
|
780
776
|
class Option < Ohm::Model
|
data/test/set.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require_relative 'helper'
|
2
|
+
|
3
|
+
class Post < Ohm::Model
|
4
|
+
end
|
5
|
+
|
6
|
+
class User < Ohm::Model
|
7
|
+
set :posts, :Post
|
8
|
+
end
|
9
|
+
|
10
|
+
test '#exists? returns false if the given id is not included in the set' do
|
11
|
+
assert !User.create.posts.exists?('nonexistent')
|
12
|
+
end
|
13
|
+
|
14
|
+
test '#exists? returns true if the given id is included in the set' do
|
15
|
+
user = User.create
|
16
|
+
post = Post.create
|
17
|
+
user.posts.add(post)
|
18
|
+
|
19
|
+
assert user.posts.exists?(post.id)
|
20
|
+
end
|
data/test/to_hash.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require_relative 'helper'
|
2
|
+
|
3
|
+
class User < Ohm::Model
|
4
|
+
end
|
5
|
+
|
6
|
+
test "returns an empty hash if model doesn't have set attributes" do
|
7
|
+
assert_equal Hash.new, User.new.to_hash
|
8
|
+
end
|
9
|
+
|
10
|
+
test "returns a hash with its id if model is persisted" do
|
11
|
+
user = User.create
|
12
|
+
|
13
|
+
assert_equal Hash[id: user.id], user.to_hash
|
14
|
+
end
|
15
|
+
|
16
|
+
class Person < Ohm::Model
|
17
|
+
attribute :name
|
18
|
+
|
19
|
+
def to_hash
|
20
|
+
super.merge(name: name)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
test "returns additional attributes if the method is overrided" do
|
25
|
+
person = Person.create(name: "John")
|
26
|
+
expected = { id: person.id, name: person.name }
|
27
|
+
|
28
|
+
assert_equal expected, person.to_hash
|
29
|
+
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: 2.0.0.
|
4
|
+
version: 2.0.0.rc1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michel Martens
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2013-
|
13
|
+
date: 2013-12-18 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: redic
|
@@ -69,8 +69,7 @@ dependencies:
|
|
69
69
|
- !ruby/object:Gem::Version
|
70
70
|
version: '0'
|
71
71
|
description: Ohm is a library that allows to store an object in Redis, a persistent
|
72
|
-
key-value database. It
|
73
|
-
performance.
|
72
|
+
key-value database. It has very good performance.
|
74
73
|
email:
|
75
74
|
- michel@soveran.com
|
76
75
|
- djanowski@dimaion.com
|
@@ -110,7 +109,6 @@ files:
|
|
110
109
|
- test/counters.rb
|
111
110
|
- test/db/.gitignore
|
112
111
|
- test/enumerable.rb
|
113
|
-
- test/extensibility.rb
|
114
112
|
- test/filtering.rb
|
115
113
|
- test/hash_key.rb
|
116
114
|
- test/helper.rb
|
@@ -119,7 +117,9 @@ files:
|
|
119
117
|
- test/json.rb
|
120
118
|
- test/list.rb
|
121
119
|
- test/model.rb
|
120
|
+
- test/set.rb
|
122
121
|
- test/test.conf
|
122
|
+
- test/to_hash.rb
|
123
123
|
- test/uniques.rb
|
124
124
|
homepage: http://soveran.github.io/ohm/
|
125
125
|
licenses:
|
data/test/extensibility.rb
DELETED
@@ -1,48 +0,0 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
|
3
|
-
require File.expand_path("./helper", File.dirname(__FILE__))
|
4
|
-
|
5
|
-
if defined?(Ohm::Model::PureRuby)
|
6
|
-
class User < Ohm::Model
|
7
|
-
attribute :email
|
8
|
-
|
9
|
-
attr_accessor :foo
|
10
|
-
|
11
|
-
def save
|
12
|
-
super do |t|
|
13
|
-
t.before do
|
14
|
-
self.email = email.downcase
|
15
|
-
end
|
16
|
-
|
17
|
-
t.after do
|
18
|
-
if @foo
|
19
|
-
key[:foos].sadd(@foo)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def delete
|
26
|
-
super do |t|
|
27
|
-
foos = nil
|
28
|
-
|
29
|
-
t.before do
|
30
|
-
foos = key[:foos].smembers
|
31
|
-
end
|
32
|
-
|
33
|
-
t.after do
|
34
|
-
foos.each { |foo| key[:foos].srem(foo) }
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
test do
|
41
|
-
u = User.create(:email => "FOO@BAR.COM", :foo => "bar")
|
42
|
-
assert_equal "foo@bar.com", u.email
|
43
|
-
assert_equal ["bar"], u.key[:foos].smembers
|
44
|
-
|
45
|
-
u.delete
|
46
|
-
assert_equal [], User.key[u.id][:foos].smembers
|
47
|
-
end
|
48
|
-
end
|