ohm 2.0.1 → 2.1.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.
- checksums.yaml +4 -4
- data/README.md +17 -7
- data/examples/chaining.rb +48 -89
- data/examples/tagging.rb +7 -4
- data/lib/ohm.rb +30 -0
- data/ohm.gemspec +1 -1
- data/test/filtering.rb +8 -0
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 879b753fd8424a547c5cf83753b2f4d7dd817a14
|
4
|
+
data.tar.gz: fab1f4b6971328047ed17e37d589ebc47c32d082
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3538cccef143743083a6a31c672e4e5bacec42f743196fbcf52f1c68ca4ca3bcc3169f9633a40cdfc1633d48428c60e53e999641e252b640767d85b6b8db3117
|
7
|
+
data.tar.gz: b51d4ad91623b2357492837a37d1f0f03928e4a2e526849afe4cd558987089fab2a930233065d1230e6dd82c70c6c0341435c0fbbf7f9a467953480e09ec99c4
|
data/README.md
CHANGED
@@ -23,6 +23,7 @@ These are libraries in other languages that were inspired by Ohm.
|
|
23
23
|
|
24
24
|
* [JOhm](https://github.com/xetorthio/johm) for Java, created by xetorthio
|
25
25
|
* [Lohm](https://github.com/slact/lua-ohm) for Lua, created by slact
|
26
|
+
* [ohm.lua](https://github.com/amakawa/ohm.lua) for Lua, created by amakawa
|
26
27
|
* [Nohm](https://github.com/maritz/nohm) for Node.js, created by maritz
|
27
28
|
* [Redisco](https://github.com/iamteem/redisco) for Python, created by iamteem
|
28
29
|
* [redis3m](https://github.com/luca3m/redis3m) for C++, created by luca3m
|
@@ -164,11 +165,17 @@ querying. Keep reading to find out what you can do with models.
|
|
164
165
|
Attribute types
|
165
166
|
---------------
|
166
167
|
|
167
|
-
Ohm::Model provides
|
168
|
-
|
169
|
-
|
170
|
-
{Ohm::Model.
|
171
|
-
|
168
|
+
Ohm::Model provides 4 attribute types:
|
169
|
+
|
170
|
+
* {Ohm::Model.attribute attribute},
|
171
|
+
* {Ohm::Model.set set}
|
172
|
+
* {Ohm::Model.list list}
|
173
|
+
* {Ohm::Model.counter counter}
|
174
|
+
|
175
|
+
and 2 meta types:
|
176
|
+
|
177
|
+
* {Ohm::Model.reference reference}
|
178
|
+
* {Ohm::Model.collection collection}.
|
172
179
|
|
173
180
|
### attribute
|
174
181
|
|
@@ -439,8 +446,11 @@ User.find(username: "Albert")
|
|
439
446
|
# Find all users from Argentina
|
440
447
|
User.find(country: "Argentina")
|
441
448
|
|
442
|
-
# Find all
|
443
|
-
User.find(country: "Argentina", status: "
|
449
|
+
# Find all active users from Argentina
|
450
|
+
User.find(country: "Argentina", status: "active")
|
451
|
+
|
452
|
+
# Find all active users from Argentina and Uruguay
|
453
|
+
User.find(status: "active").combine(country: ["Argentina", "Uruguay"])
|
444
454
|
|
445
455
|
# Find all users from Argentina, except those with a suspended account.
|
446
456
|
User.find(country: "Argentina").except(status: "suspended")
|
data/examples/chaining.rb
CHANGED
@@ -16,12 +16,12 @@ require "ohm"
|
|
16
16
|
# A `User` has a `collection` of *orders*. Note that a collection
|
17
17
|
# is actually just a convenience, which implemented simply will look like:
|
18
18
|
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
19
|
+
# def orders
|
20
|
+
# Order.find(user_id: self.id)
|
21
|
+
# end
|
22
22
|
#
|
23
23
|
class User < Ohm::Model
|
24
|
-
collection :orders, Order
|
24
|
+
collection :orders, :Order
|
25
25
|
end
|
26
26
|
|
27
27
|
# The product for our purposes will only contain a name.
|
@@ -29,7 +29,7 @@ class Product < Ohm::Model
|
|
29
29
|
attribute :name
|
30
30
|
end
|
31
31
|
|
32
|
-
# We define an `Order` with just a single `attribute` called state
|
32
|
+
# We define an `Order` with just a single `attribute` called `state`, and
|
33
33
|
# also add an `index` so we can search an order given its state.
|
34
34
|
#
|
35
35
|
# The `reference` to the `User` is actually required for the `collection`
|
@@ -58,15 +58,15 @@ prepare { Ohm.flush }
|
|
58
58
|
setup do
|
59
59
|
@user = User.create
|
60
60
|
|
61
|
-
@ipod = Product.create(:
|
62
|
-
@ipad = Product.create(:
|
61
|
+
@ipod = Product.create(name: "iPod")
|
62
|
+
@ipad = Product.create(name: "iPad")
|
63
63
|
|
64
|
-
@pending = Order.create(:
|
65
|
-
:
|
66
|
-
@authorized = Order.create(:
|
67
|
-
:
|
68
|
-
@captured = Order.create(:
|
69
|
-
:
|
64
|
+
@pending = Order.create(user: @user, state: "pending",
|
65
|
+
product: @ipod)
|
66
|
+
@authorized = Order.create(user: @user, state: "authorized",
|
67
|
+
product: @ipad)
|
68
|
+
@captured = Order.create(user: @user, state: "captured",
|
69
|
+
product: @ipad)
|
70
70
|
end
|
71
71
|
|
72
72
|
# Now let's try and grab all pending orders, and also pending
|
@@ -74,31 +74,22 @@ end
|
|
74
74
|
test "finding pending orders" do
|
75
75
|
assert @user.orders.find(state: "pending").include?(@pending)
|
76
76
|
|
77
|
-
assert @user.orders.find(:
|
78
|
-
:
|
77
|
+
assert @user.orders.find(state: "pending",
|
78
|
+
product_id: @ipod.id).include?(@pending)
|
79
79
|
|
80
|
-
assert @user.orders.find(:
|
81
|
-
:
|
80
|
+
assert @user.orders.find(state: "pending",
|
81
|
+
product_id: @ipad.id).empty?
|
82
82
|
end
|
83
83
|
|
84
|
-
# Now we try and find captured and authorized orders.
|
85
|
-
# is trying to find an order that is either *captured* or *authorized*,
|
86
|
-
# since `Ohm` as of this writing doesn't support unions in its
|
87
|
-
# finder syntax.
|
84
|
+
# Now we try and find captured and/or authorized orders.
|
88
85
|
test "finding authorized and/or captured orders" do
|
89
|
-
assert @user.orders.find(:
|
90
|
-
assert @user.orders.find(:
|
86
|
+
assert @user.orders.find(state: "authorized").include?(@authorized)
|
87
|
+
assert @user.orders.find(state: "captured").include?(@captured)
|
91
88
|
|
92
|
-
|
89
|
+
results = @user.orders.find(state: "authorized").union(state: "captured")
|
93
90
|
|
94
|
-
|
95
|
-
|
96
|
-
@user.orders.find(:state => "authorized").key,
|
97
|
-
@user.orders.find(:state => "captured").key
|
98
|
-
)
|
99
|
-
|
100
|
-
assert auth_or_capt.smembers.include?(@authorized.id)
|
101
|
-
assert auth_or_capt.smembers.include?(@captured.id)
|
91
|
+
assert results.include?(@authorized)
|
92
|
+
assert results.include?(@captured)
|
102
93
|
end
|
103
94
|
|
104
95
|
#### Creating shortcuts
|
@@ -106,11 +97,11 @@ end
|
|
106
97
|
# You can of course define methods to make that code more readable.
|
107
98
|
class User < Ohm::Model
|
108
99
|
def authorized_orders
|
109
|
-
orders.find(:
|
100
|
+
orders.find(state: "authorized")
|
110
101
|
end
|
111
102
|
|
112
103
|
def captured_orders
|
113
|
-
orders.find(:
|
104
|
+
orders.find(state: "captured")
|
114
105
|
end
|
115
106
|
end
|
116
107
|
|
@@ -121,62 +112,40 @@ test "finding authorized and/or captured orders" do
|
|
121
112
|
end
|
122
113
|
|
123
114
|
# In most cases this is fine, but if you want to have a little fun,
|
124
|
-
# then we can play around with some chainability.
|
115
|
+
# then we can play around with some chainability using scopes.
|
125
116
|
|
126
|
-
|
117
|
+
# Let's require `ohm-contrib`, which we will be using for scopes.
|
118
|
+
require "ohm/contrib"
|
127
119
|
|
128
|
-
#
|
129
|
-
|
130
|
-
|
131
|
-
# We can simply subclass it and define the monad to always be an
|
132
|
-
# `Order` so we don't have to manually set it everytime.
|
133
|
-
class UserOrders < Ohm::Model::Set
|
134
|
-
def initialize(key)
|
135
|
-
super key, Ohm::Model::Wrapper.wrap(Order)
|
136
|
-
end
|
120
|
+
# Include `Ohm::Scope` module and desired scopes.
|
121
|
+
class Order
|
122
|
+
include Ohm::Scope
|
137
123
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
self.class.new(model.index_key_for(:state, "pending"))
|
143
|
-
end
|
124
|
+
scope do
|
125
|
+
def pending
|
126
|
+
find(state: "pending")
|
127
|
+
end
|
144
128
|
|
145
|
-
|
146
|
-
|
147
|
-
|
129
|
+
def authorized
|
130
|
+
find(state: "authorized")
|
131
|
+
end
|
148
132
|
|
149
|
-
|
150
|
-
|
151
|
-
|
133
|
+
def captured
|
134
|
+
find(state: "captured")
|
135
|
+
end
|
152
136
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
# NOTE: `volatile` just returns the key prepended with a `~:`, so in
|
157
|
-
# this case it would be `~:Order:accepted`.
|
158
|
-
def accepted
|
159
|
-
model.key.volatile[:accepted].sunionstore(
|
160
|
-
authorized.key, captured.key
|
161
|
-
)
|
162
|
-
|
163
|
-
self.class.new(model.key.volatile[:accepted])
|
164
|
-
end
|
165
|
-
end
|
166
|
-
|
167
|
-
# Now let's re-open the `User` class and add a customized `orders` method.
|
168
|
-
class User < Ohm::Model
|
169
|
-
def orders
|
170
|
-
UserOrders.new(Order.index_key_for(:user_id, id))
|
137
|
+
def accepted
|
138
|
+
find(state: "authorized").union(state: "captured")
|
139
|
+
end
|
171
140
|
end
|
172
141
|
end
|
173
142
|
|
174
143
|
# Ok! Let's put all of that chaining code to good use.
|
175
144
|
test "finding pending orders using a chainable style" do
|
176
145
|
assert @user.orders.pending.include?(@pending)
|
177
|
-
assert @user.orders.pending.find(:
|
146
|
+
assert @user.orders.pending.find(product_id: @ipod.id).include?(@pending)
|
178
147
|
|
179
|
-
assert @user.orders.pending.find(:
|
148
|
+
assert @user.orders.pending.find(product_id: @ipad.id).empty?
|
180
149
|
end
|
181
150
|
|
182
151
|
test "finding authorized and/or captured orders using a chainable style" do
|
@@ -188,16 +157,6 @@ test "finding authorized and/or captured orders using a chainable style" do
|
|
188
157
|
|
189
158
|
accepted = @user.orders.accepted
|
190
159
|
|
191
|
-
assert accepted.find(:
|
192
|
-
assert accepted.find(:
|
160
|
+
assert accepted.find(product_id: @ipad.id).include?(@authorized)
|
161
|
+
assert accepted.find(product_id: @ipad.id).include?(@captured)
|
193
162
|
end
|
194
|
-
|
195
|
-
#### Conclusion
|
196
|
-
|
197
|
-
# This design pattern is something that really depends upon the situation. In
|
198
|
-
# the example above, you can add more complicated querying on the `UserOrders`
|
199
|
-
# class.
|
200
|
-
#
|
201
|
-
# The most important takeaway here is the ease of which we can weild the
|
202
|
-
# different components of Ohm, and mold it accordingly to our preferences,
|
203
|
-
# without having to monkey-patch anything.
|
data/examples/tagging.rb
CHANGED
@@ -156,11 +156,13 @@ class Post
|
|
156
156
|
|
157
157
|
# For our scenario, we only need a `before_update` and `after_save`.
|
158
158
|
# The idea for our `before_update` is to decrement the `total` of
|
159
|
-
# all existing tags. We use `
|
160
|
-
#
|
159
|
+
# all existing tags. We use `get(:tags)` the original tags for the
|
160
|
+
# record and use assigned one on save.
|
161
161
|
protected
|
162
162
|
def before_update
|
163
|
-
|
163
|
+
assigned_tags = tags
|
164
|
+
tag(get(:tags)).map(&Tag).each { |t| t.decr :total }
|
165
|
+
self.tags = assigned_tags
|
164
166
|
end
|
165
167
|
|
166
168
|
# And of course, we increment all new tags for a particular record
|
@@ -186,7 +188,8 @@ class Tag < Ohm::Model
|
|
186
188
|
# though is that we need to encode the tag name, so special characters
|
187
189
|
# and spaces won't produce an invalid key.
|
188
190
|
def self.[](id)
|
189
|
-
|
191
|
+
encoded_id = id.encode
|
192
|
+
super(encoded_id) || create(:id => encoded_id)
|
190
193
|
end
|
191
194
|
end
|
192
195
|
|
data/lib/ohm.rb
CHANGED
@@ -528,6 +528,20 @@ module Ohm
|
|
528
528
|
MultiSet.new(namespace, model, key).except(dict)
|
529
529
|
end
|
530
530
|
|
531
|
+
# Perform an intersection between the existent set and
|
532
|
+
# the new set created by the union of the passed filters.
|
533
|
+
#
|
534
|
+
# Example:
|
535
|
+
#
|
536
|
+
# set = User.find(:status => "active")
|
537
|
+
# set.combine(:name => ["John", "Jane"])
|
538
|
+
#
|
539
|
+
# # The result will include all users with active status
|
540
|
+
# # and with names "John" or "Jane".
|
541
|
+
def combine(dict)
|
542
|
+
MultiSet.new(namespace, model, key).combine(dict)
|
543
|
+
end
|
544
|
+
|
531
545
|
# Do a union to the existing set using any number of filters.
|
532
546
|
#
|
533
547
|
# Example:
|
@@ -666,6 +680,22 @@ module Ohm
|
|
666
680
|
)
|
667
681
|
end
|
668
682
|
|
683
|
+
# Perform an intersection between the existent set and
|
684
|
+
# the new set created by the union of the passed filters.
|
685
|
+
#
|
686
|
+
# Example:
|
687
|
+
#
|
688
|
+
# set = User.find(:status => "active")
|
689
|
+
# set.combine(:name => ["John", "Jane"])
|
690
|
+
#
|
691
|
+
# # The result will include all users with active status
|
692
|
+
# # and with names "John" or "Jane".
|
693
|
+
def combine(dict)
|
694
|
+
MultiSet.new(
|
695
|
+
namespace, model, Command[:sinterstore, command, unioned(dict)]
|
696
|
+
)
|
697
|
+
end
|
698
|
+
|
669
699
|
# Do a union to the existing set using any number of filters.
|
670
700
|
#
|
671
701
|
# Example:
|
data/ohm.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "ohm"
|
3
|
-
s.version = "2.0
|
3
|
+
s.version = "2.1.0"
|
4
4
|
s.summary = %{Object-hash mapping library for Redis.}
|
5
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"]
|
data/test/filtering.rb
CHANGED
@@ -111,6 +111,14 @@ test "#union" do |john, jane|
|
|
111
111
|
assert res.any? { |e| e.status == "inactive" }
|
112
112
|
end
|
113
113
|
|
114
|
+
test "#combine" do |john, jane|
|
115
|
+
res = User.find(:status => "active").combine(fname: ["John", "Jane"])
|
116
|
+
|
117
|
+
assert_equal 2, res.size
|
118
|
+
assert res.include?(john)
|
119
|
+
assert res.include?(jane)
|
120
|
+
end
|
121
|
+
|
114
122
|
# book author thing via @myobie
|
115
123
|
scope do
|
116
124
|
class Book < Ohm::Model
|
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
|
4
|
+
version: 2.1.0
|
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:
|
13
|
+
date: 2015-01-22 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: redic
|
@@ -138,7 +138,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
138
138
|
version: '0'
|
139
139
|
requirements: []
|
140
140
|
rubyforge_project: ohm
|
141
|
-
rubygems_version: 2.0.
|
141
|
+
rubygems_version: 2.0.14
|
142
142
|
signing_key:
|
143
143
|
specification_version: 4
|
144
144
|
summary: Object-hash mapping library for Redis.
|