ohm 0.1.5 → 1.0.0.alpha1
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.
- data/Rakefile +19 -1
- data/lib/ohm.rb +893 -1662
- data/lib/ohm/transaction.rb +129 -0
- data/test/association.rb +33 -0
- data/test/connection.rb +72 -0
- data/test/core.rb +26 -0
- data/test/counters.rb +67 -0
- data/test/extensibility.rb +48 -0
- data/test/filtering.rb +42 -0
- data/test/{hash_key_test.rb → hash_key.rb} +0 -0
- data/test/indices.rb +97 -0
- data/test/{json_test.rb → json.rb} +10 -3
- data/test/lua-save.rb +193 -0
- data/test/lua.rb +47 -0
- data/test/{model_test.rb → model.rb} +325 -439
- data/test/pipeline-performance.rb +65 -0
- data/test/transactions.rb +241 -0
- data/test/uniques.rb +87 -0
- data/test/{associations_test.rb → unused/associations_test.rb} +3 -3
- data/test/{pattern_test.rb → unused/pattern_test.rb} +0 -0
- data/test/{upgrade_script_test.rb → unused/upgrade_script_test.rb} +0 -0
- data/test/{wrapper_test.rb → unused/wrapper_test.rb} +0 -0
- data/test/{validations_test.rb → validations.rb} +20 -70
- metadata +44 -29
- data/lib/ohm/compat-1.8.6.rb +0 -39
- data/lib/ohm/key.rb +0 -35
- data/lib/ohm/pattern.rb +0 -37
- data/lib/ohm/validations.rb +0 -213
- data/lib/ohm/version.rb +0 -5
- data/test/connection_test.rb +0 -101
- data/test/errors_test.rb +0 -120
- data/test/indices_test.rb +0 -213
- data/test/mutex_test.rb +0 -84
@@ -33,7 +33,7 @@ test "export a hash with the errors" do
|
|
33
33
|
person = Venue.new
|
34
34
|
person.valid?
|
35
35
|
|
36
|
-
|
36
|
+
assert_equal({ errors: { name: [:not_present] }}, person.to_hash)
|
37
37
|
end
|
38
38
|
|
39
39
|
test "export a hash with the its id" do
|
@@ -46,8 +46,7 @@ test "export a hash with its id and the errors" do
|
|
46
46
|
person.name = nil
|
47
47
|
person.valid?
|
48
48
|
|
49
|
-
expected_hash = { :
|
50
|
-
|
49
|
+
expected_hash = { id: '1', errors: { name: [:not_present] }}
|
51
50
|
assert expected_hash == person.to_hash
|
52
51
|
end
|
53
52
|
|
@@ -65,3 +64,11 @@ test "just be the to_hash of a model" do
|
|
65
64
|
assert "1" == json["id"]
|
66
65
|
assert "Ruby" == json["language"]
|
67
66
|
end
|
67
|
+
|
68
|
+
test "export an array of records to json" do
|
69
|
+
Programmer.create(language: "Ruby")
|
70
|
+
Programmer.create(language: "Python")
|
71
|
+
|
72
|
+
expected = [{ id: "1", language: "Ruby" }, { id: "2", language: "Python"}].to_json
|
73
|
+
assert_equal expected, Programmer.all.to_a.to_json
|
74
|
+
end
|
data/test/lua-save.rb
ADDED
@@ -0,0 +1,193 @@
|
|
1
|
+
require File.expand_path("./helper", File.dirname(__FILE__))
|
2
|
+
|
3
|
+
def redis
|
4
|
+
Ohm.redis
|
5
|
+
end
|
6
|
+
|
7
|
+
begin
|
8
|
+
Ohm.redis.script("flush")
|
9
|
+
rescue RuntimeError
|
10
|
+
# We're running on Redis < 2.6, so we
|
11
|
+
# skip all the test.
|
12
|
+
else
|
13
|
+
setup do
|
14
|
+
Ohm.redis.script("flush")
|
15
|
+
|
16
|
+
redis.sadd("User:uniques", "email")
|
17
|
+
redis.sadd("User:indices", "fname")
|
18
|
+
redis.sadd("User:indices", "lname")
|
19
|
+
redis.hset("User:uniques:email", "foo@bar.com", 1)
|
20
|
+
|
21
|
+
Ohm::Lua.new("./test/lua", redis)
|
22
|
+
end
|
23
|
+
|
24
|
+
test "empty email doesn't choke" do |lua|
|
25
|
+
res = lua.run_file("save",
|
26
|
+
keys: ["User"],
|
27
|
+
argv: ["email", nil])
|
28
|
+
|
29
|
+
assert_equal [200, ["id", "1"]], res
|
30
|
+
assert_equal "1", redis.hget("User:uniques:email", nil)
|
31
|
+
end
|
32
|
+
|
33
|
+
test "empty fname / lname doesn't choke" do |lua|
|
34
|
+
res = lua.run_file("save",
|
35
|
+
keys: ["User"],
|
36
|
+
argv: ["email", nil, "fname", nil, "lname", nil])
|
37
|
+
|
38
|
+
assert_equal [200, ["id", "1"]], res
|
39
|
+
assert redis.sismember("User:indices:fname:", 1)
|
40
|
+
assert redis.sismember("User:indices:lname:", 1)
|
41
|
+
end
|
42
|
+
|
43
|
+
test "returns the unique constraint error" do |lua|
|
44
|
+
res = lua.run_file("save",
|
45
|
+
keys: ["User"],
|
46
|
+
argv: ["email", "foo@bar.com"])
|
47
|
+
|
48
|
+
assert_equal [500, ["email", "not_unique"]], res
|
49
|
+
end
|
50
|
+
|
51
|
+
test "persists the unique entry properly" do |lua|
|
52
|
+
lua.run_file("save",
|
53
|
+
keys: ["User"],
|
54
|
+
argv: ["email", "bar@baz.com"])
|
55
|
+
|
56
|
+
assert_equal "1", redis.hget("User:uniques:email", "bar@baz.com")
|
57
|
+
end
|
58
|
+
|
59
|
+
test "adds the entry to User:all" do |lua|
|
60
|
+
lua.run_file("save",
|
61
|
+
keys: ["User"],
|
62
|
+
argv: ["email", "bar@baz.com"])
|
63
|
+
|
64
|
+
assert_equal 1, redis.scard("User:all")
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
test "saves the attributes" do |lua|
|
69
|
+
lua.run_file("save",
|
70
|
+
keys: ["User"],
|
71
|
+
argv: ["email", "bar@baz.com", "fname", "John", "lname", "Doe"])
|
72
|
+
|
73
|
+
assert_equal "bar@baz.com", redis.hget("User:1", "email")
|
74
|
+
assert_equal "John", redis.hget("User:1", "fname")
|
75
|
+
assert_equal "Doe", redis.hget("User:1", "lname")
|
76
|
+
end
|
77
|
+
|
78
|
+
test "indexes fname / lname" do |lua|
|
79
|
+
lua.run_file("save",
|
80
|
+
keys: ["User"],
|
81
|
+
argv: ["email", "bar@baz.com", "fname", "John", "lname", "Doe"])
|
82
|
+
|
83
|
+
assert redis.sismember("User:indices:fname:John", 1)
|
84
|
+
assert redis.sismember("User:indices:lname:Doe", 1)
|
85
|
+
end
|
86
|
+
|
87
|
+
test "unique constraint during update" do |lua|
|
88
|
+
lua.run_file("save",
|
89
|
+
keys: ["User"],
|
90
|
+
argv: ["email", "bar@baz.com", "fname", "John", "lname", "Doe"])
|
91
|
+
|
92
|
+
res = lua.run_file("save",
|
93
|
+
keys: ["User", "User:1"],
|
94
|
+
argv: ["email", "bar@baz.com", "fname", "John", "lname", "Doe"])
|
95
|
+
|
96
|
+
assert_equal [200, ["id", "1"]], res
|
97
|
+
|
98
|
+
res = lua.run_file("save",
|
99
|
+
keys: ["User", "User:1"],
|
100
|
+
argv: ["email", "foo@bar.com", "fname", "Jane", "lname", "Doe"])
|
101
|
+
|
102
|
+
assert_equal [200, ["id", "1"]], res
|
103
|
+
end
|
104
|
+
|
105
|
+
test "cleanup of existing indices during update" do |lua|
|
106
|
+
lua.run_file("save",
|
107
|
+
keys: ["User"],
|
108
|
+
argv: ["email", "bar@baz.com", "fname", "John", "lname", "Doe"])
|
109
|
+
|
110
|
+
res = lua.run_file("save",
|
111
|
+
keys: ["User", "User:1"],
|
112
|
+
argv: ["email", "foo@bar.com", "fname", "Jane", "lname", "Smith"])
|
113
|
+
|
114
|
+
assert ! redis.sismember("User:indices:fname:John", 1)
|
115
|
+
assert ! redis.sismember("User:indices:fname:Doe", 1)
|
116
|
+
end
|
117
|
+
|
118
|
+
test "cleanup of existing uniques during update" do |lua|
|
119
|
+
lua.run_file("save",
|
120
|
+
keys: ["User"],
|
121
|
+
argv: ["email", "bar@baz.com", "fname", "John", "lname", "Doe"])
|
122
|
+
|
123
|
+
res = lua.run_file("save",
|
124
|
+
keys: ["User", "User:1"],
|
125
|
+
argv: ["email", "foo@bar.com", "fname", "Jane", "lname", "Smith"])
|
126
|
+
|
127
|
+
assert_equal nil, redis.hget("User:uniques:email", "bar@baz.com")
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
__END__
|
132
|
+
$VERBOSE = false
|
133
|
+
|
134
|
+
test "stress test for lua scripting" do |lua|
|
135
|
+
require "benchmark"
|
136
|
+
|
137
|
+
class User < Ohm::Model
|
138
|
+
attribute :email
|
139
|
+
attribute :fname
|
140
|
+
attribute :lname
|
141
|
+
|
142
|
+
index :email
|
143
|
+
index :fname
|
144
|
+
index :lname
|
145
|
+
end
|
146
|
+
|
147
|
+
t = Benchmark.measure do
|
148
|
+
threads = 100.times.map do |i|
|
149
|
+
Thread.new do
|
150
|
+
User.create(email: "foo#{i}@bar.com",
|
151
|
+
fname: "Jane#{i}",
|
152
|
+
lname: "Smith#{i}")
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
threads.each(&:join)
|
157
|
+
end
|
158
|
+
|
159
|
+
puts t
|
160
|
+
end
|
161
|
+
|
162
|
+
test "stress test for postgres + sequel (as a comparison)" do
|
163
|
+
require "sequel"
|
164
|
+
|
165
|
+
DB = Sequel.connect("postgres://cyx@localhost/postgres")
|
166
|
+
DB[:users].truncate
|
167
|
+
t = Benchmark.measure do
|
168
|
+
threads = 100.times.map do |i|
|
169
|
+
Thread.new do
|
170
|
+
DB[:users].insert(email: "foo#{i}@bar.com",
|
171
|
+
fname: "John#{i}",
|
172
|
+
lname: "Doe#{i}")
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
threads.each(&:join)
|
177
|
+
end
|
178
|
+
|
179
|
+
puts t
|
180
|
+
end
|
181
|
+
|
182
|
+
## Result for 100 threads:
|
183
|
+
# 0.040000 0.010000 0.050000 ( 0.061512) - lua script
|
184
|
+
# 0.150000 0.180000 0.330000 ( 0.259676) - postgres
|
185
|
+
#
|
186
|
+
## Result for 100 linear executions:
|
187
|
+
#
|
188
|
+
# 0.010000 0.010000 0.020000 ( 0.032064) - lua script
|
189
|
+
# 0.010000 0.010000 0.020000 ( 0.059540) - postgres
|
190
|
+
#
|
191
|
+
## It's also important to note that with 1K concurrent threads,
|
192
|
+
# postgres throws a Sequel::PoolTimeout
|
193
|
+
end
|
data/test/lua.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require File.expand_path("./helper", File.dirname(__FILE__))
|
4
|
+
|
5
|
+
begin
|
6
|
+
Ohm.redis.script("flush")
|
7
|
+
rescue RuntimeError
|
8
|
+
# We're running on Redis < 2.6, so we
|
9
|
+
# skip all the test.
|
10
|
+
else
|
11
|
+
setup do
|
12
|
+
Ohm::Lua.new("./test/lua", Ohm.redis)
|
13
|
+
end
|
14
|
+
|
15
|
+
test do |lua|
|
16
|
+
lua.redis.set("foo", "baz")
|
17
|
+
|
18
|
+
res = lua.run_file("getset", keys: ["foo"], argv: ["bar"])
|
19
|
+
assert_equal ["baz", "bar"], res
|
20
|
+
end
|
21
|
+
|
22
|
+
test do |lua|
|
23
|
+
res = lua.run_file("ohm-save",
|
24
|
+
keys: ["User"],
|
25
|
+
argv: ["fname", "John", "lname", "Doe"])
|
26
|
+
|
27
|
+
assert lua.redis.sismember("User:all", 1)
|
28
|
+
assert_equal({ "fname" => "John", "lname" => "Doe" },
|
29
|
+
lua.redis.hgetall("User:1"))
|
30
|
+
end
|
31
|
+
|
32
|
+
test do |lua|
|
33
|
+
lua.redis.sadd("User:indices", "fname")
|
34
|
+
lua.redis.sadd("User:indices", "lname")
|
35
|
+
|
36
|
+
res = lua.run_file("save-with-indices",
|
37
|
+
keys: ["User:1", "User:all", "User:indices"],
|
38
|
+
argv: ["fname", "John", "lname", "Doe"])
|
39
|
+
|
40
|
+
assert lua.redis.sismember("User:all", 1)
|
41
|
+
|
42
|
+
assert lua.redis.sismember("User:fname:John", 1)
|
43
|
+
assert lua.redis.sismember("User:lname:Doe", 1)
|
44
|
+
assert lua.redis.sismember("User:1:_indices", "User:fname:John")
|
45
|
+
assert lua.redis.sismember("User:1:_indices", "User:lname:Doe")
|
46
|
+
end
|
47
|
+
end
|
@@ -6,7 +6,7 @@ require "ostruct"
|
|
6
6
|
|
7
7
|
class Post < Ohm::Model
|
8
8
|
attribute :body
|
9
|
-
|
9
|
+
set :related, Post
|
10
10
|
end
|
11
11
|
|
12
12
|
class User < Ohm::Model
|
@@ -18,12 +18,8 @@ class Person < Ohm::Model
|
|
18
18
|
attribute :name
|
19
19
|
index :initial
|
20
20
|
|
21
|
-
def validate
|
22
|
-
assert_present :name
|
23
|
-
end
|
24
|
-
|
25
21
|
def initial
|
26
|
-
name[0, 1].upcase
|
22
|
+
name[0, 1].upcase if name
|
27
23
|
end
|
28
24
|
end
|
29
25
|
|
@@ -34,7 +30,7 @@ class Event < Ohm::Model
|
|
34
30
|
|
35
31
|
attribute :slug
|
36
32
|
|
37
|
-
def
|
33
|
+
def save
|
38
34
|
self.slug = name.to_s.downcase
|
39
35
|
super
|
40
36
|
end
|
@@ -55,6 +51,48 @@ class Meetup < Ohm::Model
|
|
55
51
|
end
|
56
52
|
end
|
57
53
|
|
54
|
+
test "counters are cleaned up during deletion" do
|
55
|
+
e = Event.create(name: "Foo")
|
56
|
+
e.incr :votes, 10
|
57
|
+
|
58
|
+
assert_equal 10, e.votes
|
59
|
+
|
60
|
+
e.delete
|
61
|
+
assert ! e.key[:counters].exists
|
62
|
+
end
|
63
|
+
|
64
|
+
test "return the unsaved object if validation fails" do
|
65
|
+
assert Person.create(:name => nil).kind_of?(Person)
|
66
|
+
end
|
67
|
+
|
68
|
+
test "return false if the validation fails" do
|
69
|
+
event = Meetup.create(:name => "Ruby Tuesday")
|
70
|
+
assert !event.update(:name => nil)
|
71
|
+
end
|
72
|
+
|
73
|
+
test "get" do
|
74
|
+
m = Meetup.create(name: "Foo")
|
75
|
+
m.name = "Bar"
|
76
|
+
|
77
|
+
assert_equal "Foo", m.get(:name)
|
78
|
+
assert_equal "Foo", m.name
|
79
|
+
end
|
80
|
+
|
81
|
+
test "set" do
|
82
|
+
m = Meetup.create(name: "Foo")
|
83
|
+
|
84
|
+
m.set :name, "Bar"
|
85
|
+
assert_equal "Bar", m.name
|
86
|
+
|
87
|
+
m = Meetup[m.id]
|
88
|
+
assert_equal "Bar", m.name
|
89
|
+
|
90
|
+
# Deletes when value is nil.
|
91
|
+
m.set :name, nil
|
92
|
+
m = Meetup[m.id]
|
93
|
+
assert ! m.key.hexists(:name)
|
94
|
+
end
|
95
|
+
|
58
96
|
test "assign attributes from the hash" do
|
59
97
|
event = Event.new(:name => "Ruby Tuesday")
|
60
98
|
assert event.name == "Ruby Tuesday"
|
@@ -68,21 +106,12 @@ test "assign an ID and save the object" do
|
|
68
106
|
assert "2" == event2.id
|
69
107
|
end
|
70
108
|
|
71
|
-
test "return the unsaved object if validation fails" do
|
72
|
-
assert Person.create(:name => nil).kind_of?(Person)
|
73
|
-
end
|
74
|
-
|
75
109
|
test "updates attributes" do
|
76
110
|
event = Meetup.create(:name => "Ruby Tuesday")
|
77
111
|
event.update(:name => "Ruby Meetup")
|
78
112
|
assert "Ruby Meetup" == event.name
|
79
113
|
end
|
80
114
|
|
81
|
-
test "return false if the validation fails" do
|
82
|
-
event = Meetup.create(:name => "Ruby Tuesday")
|
83
|
-
assert !event.update(:name => nil)
|
84
|
-
end
|
85
|
-
|
86
115
|
test "save the attributes in UTF8" do
|
87
116
|
event = Meetup.create(:name => "32° Kisei-sen")
|
88
117
|
assert "32° Kisei-sen" == Meetup[event.id].name
|
@@ -92,7 +121,7 @@ test "delete the attribute if set to nil" do
|
|
92
121
|
event = Meetup.create(:name => "Ruby Tuesday", :location => "Los Angeles")
|
93
122
|
assert "Los Angeles" == Meetup[event.id].location
|
94
123
|
assert event.update(:location => nil)
|
95
|
-
|
124
|
+
assert_equal nil, Meetup[event.id].location
|
96
125
|
end
|
97
126
|
|
98
127
|
test "delete the attribute if set to an empty string" do
|
@@ -122,16 +151,6 @@ test "not raise if a counter is redefined" do
|
|
122
151
|
end
|
123
152
|
end
|
124
153
|
|
125
|
-
test "not raise if a list is redefined" do
|
126
|
-
class RedefinedModel < Ohm::Model
|
127
|
-
list :todo, lambda { }
|
128
|
-
|
129
|
-
silence_warnings do
|
130
|
-
list :todo, lambda { }
|
131
|
-
end
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
154
|
test "not raise if a set is redefined" do
|
136
155
|
class RedefinedModel < Ohm::Model
|
137
156
|
set :friends, lambda { }
|
@@ -144,7 +163,7 @@ end
|
|
144
163
|
|
145
164
|
test "not raise if a collection is redefined" do
|
146
165
|
class RedefinedModel < Ohm::Model
|
147
|
-
|
166
|
+
set :toys, lambda { }
|
148
167
|
|
149
168
|
silence_warnings do
|
150
169
|
set :toys, lambda { }
|
@@ -225,10 +244,10 @@ end
|
|
225
244
|
|
226
245
|
test "assign a new id to the event" do
|
227
246
|
event1 = Event.new
|
228
|
-
event1.
|
247
|
+
event1.save
|
229
248
|
|
230
249
|
event2 = Event.new
|
231
|
-
event2.
|
250
|
+
event2.save
|
232
251
|
|
233
252
|
assert !event1.new?
|
234
253
|
assert !event2.new?
|
@@ -246,7 +265,7 @@ end
|
|
246
265
|
test "save it only if it was previously created" do
|
247
266
|
event = Event.new
|
248
267
|
event.name = "Lorem ipsum"
|
249
|
-
event.
|
268
|
+
event.save
|
250
269
|
|
251
270
|
event.name = "Lorem"
|
252
271
|
event.save
|
@@ -254,7 +273,7 @@ test "save it only if it was previously created" do
|
|
254
273
|
assert "Lorem" == Event[event.id].name
|
255
274
|
end
|
256
275
|
|
257
|
-
test "allow to hook into
|
276
|
+
test "allow to hook into save" do
|
258
277
|
event = Event.create(:name => "Foo")
|
259
278
|
|
260
279
|
assert "foo" == event.slug
|
@@ -266,7 +285,7 @@ test "save counters" do
|
|
266
285
|
event.incr(:votes)
|
267
286
|
event.save
|
268
287
|
|
269
|
-
|
288
|
+
assert_equal 1, Event[event.id].votes
|
270
289
|
end
|
271
290
|
|
272
291
|
# Delete
|
@@ -274,13 +293,13 @@ test "delete an existing model" do
|
|
274
293
|
class ModelToBeDeleted < Ohm::Model
|
275
294
|
attribute :name
|
276
295
|
set :foos, Post
|
277
|
-
|
296
|
+
set :bars, Post
|
278
297
|
end
|
279
298
|
|
280
299
|
@model = ModelToBeDeleted.create(:name => "Lorem")
|
281
300
|
|
282
|
-
@model.foos
|
283
|
-
@model.bars
|
301
|
+
@model.foos.add(Post.create)
|
302
|
+
@model.bars.add(Post.create)
|
284
303
|
|
285
304
|
id = @model.id
|
286
305
|
|
@@ -303,14 +322,13 @@ test "be no leftover keys" do
|
|
303
322
|
index :name
|
304
323
|
end
|
305
324
|
|
306
|
-
|
325
|
+
assert_equal [], Ohm.redis.keys("*")
|
307
326
|
|
308
327
|
Foo.create(:name => "Bar")
|
309
|
-
|
310
|
-
assert
|
328
|
+
expected = %w[Foo:1 Foo:all Foo:id Foo:indices:name:Bar]
|
329
|
+
assert expected.sort == Ohm.redis.keys("*").sort
|
311
330
|
|
312
331
|
Foo[1].delete
|
313
|
-
|
314
332
|
assert ["Foo:id"] == Ohm.redis.keys("*")
|
315
333
|
end
|
316
334
|
|
@@ -318,14 +336,13 @@ end
|
|
318
336
|
test "find all" do
|
319
337
|
event1 = Event.new
|
320
338
|
event1.name = "Ruby Meetup"
|
321
|
-
event1.
|
339
|
+
event1.save
|
322
340
|
|
323
341
|
event2 = Event.new
|
324
342
|
event2.name = "Ruby Tuesday"
|
325
|
-
event2.
|
343
|
+
event2.save
|
326
344
|
|
327
345
|
all = Event.all
|
328
|
-
|
329
346
|
assert all.detect {|e| e.name == "Ruby Meetup" }
|
330
347
|
assert all.detect {|e| e.name == "Ruby Tuesday" }
|
331
348
|
end
|
@@ -337,7 +354,8 @@ test "sort all" do
|
|
337
354
|
Person.create :name => "B"
|
338
355
|
Person.create :name => "A"
|
339
356
|
|
340
|
-
|
357
|
+
names = Person.all.sort_by(:name, :order => "ALPHA").map { |p| p.name }
|
358
|
+
assert %w[A B C D] == names
|
341
359
|
end
|
342
360
|
|
343
361
|
test "return an empty array if there are no elements to sort" do
|
@@ -360,60 +378,58 @@ test "return attribute values when the get parameter is specified" do
|
|
360
378
|
Person.create :name => "B"
|
361
379
|
Person.create :name => "A"
|
362
380
|
|
363
|
-
|
381
|
+
res = Person.all.sort_by(:name, :get => :name, :order => "ALPHA")
|
382
|
+
|
383
|
+
assert_equal ["A", "B"], res
|
364
384
|
end
|
365
385
|
|
366
386
|
test "work on lists" do
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
387
|
+
post = Post.create :body => "Hello world!"
|
388
|
+
post.related.key.rpush(Post.create(:body => "C").id)
|
389
|
+
post.related.key.rpush(Post.create(:body => "B").id)
|
390
|
+
post.related.key.rpush(Post.create(:body => "A").id)
|
371
391
|
|
372
|
-
|
392
|
+
res = post.related.sort_by(:body, order: "ALPHA ASC").map { |r| r.body }
|
393
|
+
assert_equal ["A", "B", "C"], res
|
373
394
|
end
|
374
395
|
|
375
396
|
# Loading attributes
|
376
397
|
setup do
|
377
398
|
event = Event.new
|
378
399
|
event.name = "Ruby Tuesday"
|
379
|
-
|
400
|
+
event.save.id
|
380
401
|
end
|
381
402
|
|
382
|
-
|
383
|
-
|
403
|
+
test "load attributes as a strings" do
|
404
|
+
event = Event.create(:name => 1)
|
384
405
|
|
385
|
-
|
386
|
-
|
387
|
-
break if line =~ /ping/
|
388
|
-
log << line
|
389
|
-
end
|
390
|
-
end
|
406
|
+
assert "1" == Event[event.id].name
|
407
|
+
end
|
391
408
|
|
392
|
-
|
409
|
+
# Enumerable indices
|
410
|
+
class Entry < Ohm::Model
|
411
|
+
attribute :tags
|
412
|
+
index :tag
|
393
413
|
|
394
|
-
|
395
|
-
|
396
|
-
Ohm.redis.ping
|
397
|
-
monitor.join
|
414
|
+
def tag
|
415
|
+
tags.split(/\s+/)
|
398
416
|
end
|
399
417
|
end
|
400
418
|
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
log = monitor { event.name }
|
405
|
-
|
406
|
-
assert !log.empty?
|
407
|
-
|
408
|
-
log = monitor { event.name }
|
409
|
-
|
410
|
-
assert log.empty?
|
419
|
+
setup do
|
420
|
+
Entry.create(tags: "foo bar baz")
|
411
421
|
end
|
412
422
|
|
413
|
-
test "
|
414
|
-
|
423
|
+
test "finding by one entry in the enumerable" do |entry|
|
424
|
+
assert Entry.find(tag: "foo").include?(entry)
|
425
|
+
assert Entry.find(tag: "bar").include?(entry)
|
426
|
+
assert Entry.find(tag: "baz").include?(entry)
|
427
|
+
end
|
415
428
|
|
416
|
-
|
429
|
+
test "finding by multiple entries in the enumerable" do |entry|
|
430
|
+
assert Entry.find(tag: ["foo", "bar"]).include?(entry)
|
431
|
+
assert Entry.find(tag: ["bar", "baz"]).include?(entry)
|
432
|
+
assert Entry.find(tag: ["baz", "oof"]).empty?
|
417
433
|
end
|
418
434
|
|
419
435
|
# Attributes of type Set
|
@@ -426,314 +442,84 @@ setup do
|
|
426
442
|
@event.name = "Ruby Tuesday"
|
427
443
|
end
|
428
444
|
|
445
|
+
test "filter elements" do
|
446
|
+
@event.save
|
447
|
+
@event.attendees.add(@person1)
|
448
|
+
@event.attendees.add(@person2)
|
449
|
+
|
450
|
+
assert [@person1] == @event.attendees.find(:initial => "A").to_a
|
451
|
+
assert [@person2] == @event.attendees.find(:initial => "B").to_a
|
452
|
+
assert [] == @event.attendees.find(:initial => "Z").to_a
|
453
|
+
end
|
454
|
+
|
429
455
|
test "not be available if the model is new" do
|
430
|
-
assert_raise Ohm::
|
431
|
-
@event.attendees
|
456
|
+
assert_raise Ohm::MissingID do
|
457
|
+
@event.attendees
|
432
458
|
end
|
433
459
|
end
|
434
460
|
|
435
461
|
test "remove an element if sent delete" do
|
436
|
-
@event.
|
437
|
-
@event.attendees
|
438
|
-
@event.attendees
|
439
|
-
@event.attendees
|
440
|
-
|
441
|
-
@event.attendees.
|
442
|
-
|
462
|
+
@event.save
|
463
|
+
@event.attendees.add(@person1)
|
464
|
+
@event.attendees.add(@person2)
|
465
|
+
@event.attendees.add(@person3)
|
466
|
+
|
467
|
+
assert_equal ["1", "2", "3"], @event.attendees.key.sort
|
468
|
+
|
469
|
+
@event.attendees.key.srem(@person2.id)
|
470
|
+
assert_equal ["1", "3"], Event[@event.id].attendees.key.sort
|
443
471
|
end
|
444
472
|
|
445
473
|
test "return true if the set includes some member" do
|
446
|
-
@event.
|
447
|
-
@event.attendees
|
448
|
-
@event.attendees
|
474
|
+
@event.save
|
475
|
+
@event.attendees.add(@person1)
|
476
|
+
@event.attendees.add(@person2)
|
449
477
|
assert @event.attendees.include?(@person2)
|
450
478
|
assert !@event.attendees.include?(@person3)
|
451
479
|
end
|
452
480
|
|
453
481
|
test "return instances of the passed model" do
|
454
|
-
@event.
|
455
|
-
@event.attendees
|
482
|
+
@event.save
|
483
|
+
@event.attendees.add(@person1)
|
456
484
|
|
457
|
-
assert [@person1] == @event.attendees.
|
485
|
+
assert [@person1] == @event.attendees.to_a
|
458
486
|
assert @person1 == @event.attendees[@person1.id]
|
459
487
|
end
|
460
488
|
|
461
489
|
test "return the size of the set" do
|
462
|
-
@event.
|
463
|
-
@event.attendees
|
464
|
-
@event.attendees
|
465
|
-
@event.attendees
|
490
|
+
@event.save
|
491
|
+
@event.attendees.add(@person1)
|
492
|
+
@event.attendees.add(@person2)
|
493
|
+
@event.attendees.add(@person3)
|
466
494
|
assert 3 == @event.attendees.size
|
467
495
|
end
|
468
496
|
|
469
497
|
test "empty the set" do
|
470
|
-
@event.
|
471
|
-
@event.attendees
|
472
|
-
|
473
|
-
@event.attendees.clear
|
498
|
+
@event.save
|
499
|
+
@event.attendees.add(@person1)
|
500
|
+
@event.attendees.key.del
|
474
501
|
|
475
502
|
assert @event.attendees.empty?
|
476
503
|
end
|
477
504
|
|
478
505
|
test "replace the values in the set" do
|
479
|
-
@event.
|
480
|
-
@event.attendees << @person1
|
481
|
-
|
482
|
-
assert [@person1] == @event.attendees.all
|
483
|
-
|
484
|
-
@event.attendees.replace([@person2, @person3])
|
485
|
-
|
486
|
-
assert [@person2, @person3] == @event.attendees.all.sort_by(&:id)
|
487
|
-
end
|
488
|
-
|
489
|
-
test "filter elements" do
|
490
|
-
@event.create
|
506
|
+
@event.save
|
491
507
|
@event.attendees.add(@person1)
|
492
|
-
@event.attendees.add(@person2)
|
493
508
|
|
494
|
-
assert [@person1] == @event.attendees.
|
495
|
-
assert [@person2] == @event.attendees.find(:initial => "B").all
|
496
|
-
assert [] == @event.attendees.find(:initial => "Z").all
|
497
|
-
end
|
498
|
-
|
499
|
-
# Attributes of type List
|
500
|
-
setup do
|
501
|
-
@post = Post.new
|
502
|
-
@post.body = "Hello world!"
|
503
|
-
@post.create
|
504
|
-
end
|
505
|
-
|
506
|
-
test "return an array" do
|
507
|
-
assert @post.related.all.kind_of?(Array)
|
508
|
-
end
|
509
|
+
assert [@person1] == @event.attendees.to_a
|
509
510
|
|
510
|
-
|
511
|
-
@post.related.push Post.create
|
512
|
-
@post.related << Post.create
|
513
|
-
|
514
|
-
assert ["2", "3"] == @post.related.all.map { |model| model.id }
|
515
|
-
end
|
516
|
-
|
517
|
-
test "keep the inserting order" do
|
518
|
-
@post.related << Post.create
|
519
|
-
@post.related << Post.create
|
520
|
-
@post.related << Post.create
|
521
|
-
assert ["2", "3", "4"] == @post.related.all.map { |model| model.id }
|
522
|
-
end
|
523
|
-
|
524
|
-
test "keep the inserting order after saving" do
|
525
|
-
@post.related << Post.create
|
526
|
-
@post.related << Post.create
|
527
|
-
@post.related << Post.create
|
528
|
-
@post.save
|
529
|
-
assert ["2", "3", "4"] == Post[@post.id].related.map { |model| model.id }
|
530
|
-
end
|
531
|
-
|
532
|
-
test "allow slicing the list" do
|
533
|
-
post1 = Post.create
|
534
|
-
post2 = Post.create
|
535
|
-
post3 = Post.create
|
536
|
-
|
537
|
-
@post.related << post1
|
538
|
-
@post.related << post2
|
539
|
-
@post.related << post3
|
540
|
-
|
541
|
-
assert post1 == @post.related[0]
|
542
|
-
assert post2 == @post.related[1]
|
543
|
-
assert post3 == @post.related[-1]
|
544
|
-
|
545
|
-
assert nil == @post.related[3]
|
546
|
-
|
547
|
-
assert [post2, post3] == @post.related[1, 2]
|
548
|
-
assert [post2, post3] == @post.related[1, -1]
|
549
|
-
|
550
|
-
assert [] == @post.related[4, 5]
|
551
|
-
|
552
|
-
assert [post2, post3] == @post.related[1..2]
|
553
|
-
assert [post2, post3] == @post.related[1..5]
|
554
|
-
|
555
|
-
assert [] == @post.related[4..5]
|
556
|
-
end
|
557
|
-
|
558
|
-
test "respond to each" do
|
559
|
-
@post.related << Post.create
|
560
|
-
@post.related << Post.create
|
561
|
-
@post.related << Post.create
|
562
|
-
|
563
|
-
i = 2
|
564
|
-
@post.related.each do |c|
|
565
|
-
assert i == c.id.to_i
|
566
|
-
i += 1
|
567
|
-
end
|
568
|
-
end
|
569
|
-
|
570
|
-
test "return the size of the list" do
|
571
|
-
@post.related << Post.create
|
572
|
-
@post.related << Post.create
|
573
|
-
@post.related << Post.create
|
574
|
-
assert 3 == @post.related.size
|
575
|
-
end
|
576
|
-
|
577
|
-
test "return the last element with pop" do
|
578
|
-
@post.related << Post.create
|
579
|
-
@post.related << Post.create
|
580
|
-
assert "3" == @post.related.pop.id
|
581
|
-
assert "2" == @post.related.pop.id
|
582
|
-
assert @post.related.empty?
|
583
|
-
end
|
584
|
-
|
585
|
-
test "return the first element with shift" do
|
586
|
-
@post.related << Post.create
|
587
|
-
@post.related << Post.create
|
588
|
-
assert "2" == @post.related.shift.id
|
589
|
-
assert "3" == @post.related.shift.id
|
590
|
-
assert @post.related.empty?
|
591
|
-
end
|
592
|
-
|
593
|
-
test "push to the head of the list with unshift" do
|
594
|
-
@post.related.unshift Post.create
|
595
|
-
@post.related.unshift Post.create
|
596
|
-
assert "2" == @post.related.pop.id
|
597
|
-
assert "3" == @post.related.pop.id
|
598
|
-
assert @post.related.empty?
|
599
|
-
end
|
600
|
-
|
601
|
-
test "empty the list" do
|
602
|
-
@post.related.unshift Post.create
|
603
|
-
@post.related.clear
|
604
|
-
|
605
|
-
assert @post.related.empty?
|
606
|
-
end
|
607
|
-
|
608
|
-
test "replace the values in the list" do
|
609
|
-
@post.related.replace([Post.create, Post.create])
|
610
|
-
|
611
|
-
assert ["2", "3"] == @post.related.map { |model| model.id }
|
612
|
-
end
|
613
|
-
|
614
|
-
test "add models" do
|
615
|
-
@post.related.add(Post.create(:body => "Hello"))
|
616
|
-
assert ["2"] == @post.related.map { |model| model.id }
|
617
|
-
end
|
618
|
-
|
619
|
-
test "find elements in the list" do
|
620
|
-
another_post = Post.create
|
621
|
-
|
622
|
-
@post.related.add(another_post)
|
623
|
-
|
624
|
-
assert @post.related.include?(another_post)
|
625
|
-
assert !@post.related.include?(Post.create)
|
626
|
-
end
|
627
|
-
|
628
|
-
test "unshift models" do
|
629
|
-
@post.related.unshift(Post.create(:body => "Hello"))
|
630
|
-
@post.related.unshift(Post.create(:body => "Goodbye"))
|
631
|
-
|
632
|
-
assert ["3", "2"] == @post.related.map { |model| model.id }
|
633
|
-
|
634
|
-
assert "3" == @post.related.shift.id
|
635
|
-
|
636
|
-
assert "2" == @post.related.pop.id
|
637
|
-
|
638
|
-
assert @post.related.pop.nil?
|
639
|
-
end
|
640
|
-
|
641
|
-
# Applying arbitrary transformations
|
642
|
-
require "date"
|
643
|
-
|
644
|
-
class MyActiveRecordModel
|
645
|
-
def self.find(id)
|
646
|
-
return new if id.to_i == 1
|
647
|
-
end
|
648
|
-
|
649
|
-
def id
|
650
|
-
1
|
651
|
-
end
|
652
|
-
|
653
|
-
def ==(other)
|
654
|
-
id == other.id
|
655
|
-
end
|
656
|
-
end
|
657
|
-
|
658
|
-
class ::Calendar < Ohm::Model
|
659
|
-
list :holidays, lambda { |v| Date.parse(v) }
|
660
|
-
list :subscribers, lambda { |id| MyActiveRecordModel.find(id) }
|
661
|
-
list :appointments, Appointment
|
662
|
-
|
663
|
-
set :events, lambda { |id| MyActiveRecordModel.find(id) }
|
664
|
-
end
|
665
|
-
|
666
|
-
class ::Appointment < Ohm::Model
|
667
|
-
attribute :text
|
668
|
-
reference :subscriber, lambda { |id| MyActiveRecordModel.find(id) }
|
669
|
-
end
|
670
|
-
|
671
|
-
setup do
|
672
|
-
@calendar = Calendar.create
|
673
|
-
|
674
|
-
@calendar.holidays.key.rpush "2009-05-25"
|
675
|
-
@calendar.holidays.key.rpush "2009-07-09"
|
676
|
-
|
677
|
-
@calendar.subscribers << MyActiveRecordModel.find(1)
|
678
|
-
|
679
|
-
@calendar.events << MyActiveRecordModel.find(1)
|
680
|
-
end
|
681
|
-
|
682
|
-
test "apply a transformation" do
|
683
|
-
assert [Date.new(2009, 5, 25), Date.new(2009, 7, 9)] == @calendar.holidays.all
|
684
|
-
|
685
|
-
assert [1] == @calendar.subscribers.all.map { |model| model.id }
|
686
|
-
assert [MyActiveRecordModel.find(1)] == @calendar.subscribers.all
|
687
|
-
end
|
688
|
-
|
689
|
-
test "doing an each on lists" do
|
690
|
-
arr = []
|
691
|
-
@calendar.subscribers.each do |sub|
|
692
|
-
arr << sub
|
693
|
-
end
|
694
|
-
|
695
|
-
assert [MyActiveRecordModel.find(1)] == arr
|
696
|
-
end
|
697
|
-
|
698
|
-
test "doing an each on sets" do
|
699
|
-
arr = []
|
700
|
-
@calendar.events.each do |sub|
|
701
|
-
arr << sub
|
702
|
-
end
|
703
|
-
|
704
|
-
assert [MyActiveRecordModel.find(1)] == arr
|
705
|
-
end
|
706
|
-
|
707
|
-
test "allow lambdas in references" do
|
708
|
-
appointment = Appointment.create(:subscriber => MyActiveRecordModel.find(1))
|
709
|
-
assert MyActiveRecordModel.find(1) == appointment.subscriber
|
710
|
-
end
|
711
|
-
|
712
|
-
test "work with models too" do
|
713
|
-
@calendar.appointments.add(Appointment.create(:text => "Meet with Bertrand"))
|
714
|
-
|
715
|
-
assert [Appointment[1]] == Calendar[1].appointments.sort
|
716
|
-
end
|
717
|
-
|
718
|
-
# Sorting lists and sets
|
719
|
-
setup do
|
720
|
-
@post = Post.create(:body => "Lorem")
|
721
|
-
@post.related << Post.create
|
722
|
-
@post.related << Post.create
|
723
|
-
@post.related << Post.create
|
724
|
-
end
|
511
|
+
@event.attendees.replace([@person2, @person3])
|
725
512
|
|
726
|
-
|
727
|
-
assert %w{2 3 4} == @post.related.sort.map { |model| model.id }
|
513
|
+
assert [@person2, @person3] == @event.attendees.to_a.sort_by(&:id)
|
728
514
|
end
|
729
515
|
|
730
516
|
# Sorting lists and sets by model attributes
|
731
517
|
setup do
|
732
518
|
@event = Event.create(:name => "Ruby Tuesday")
|
733
|
-
@event.attendees
|
734
|
-
@event.attendees
|
735
|
-
@event.attendees
|
736
|
-
@event.attendees
|
519
|
+
@event.attendees.add(Person.create(:name => "D"))
|
520
|
+
@event.attendees.add(Person.create(:name => "C"))
|
521
|
+
@event.attendees.add(Person.create(:name => "B"))
|
522
|
+
@event.attendees.add(Person.create(:name => "A"))
|
737
523
|
end
|
738
524
|
|
739
525
|
test "sort the model instances by the values provided" do
|
@@ -742,39 +528,49 @@ test "sort the model instances by the values provided" do
|
|
742
528
|
end
|
743
529
|
|
744
530
|
test "accept a number in the limit parameter" do
|
745
|
-
people = @event.attendees.sort_by(:name, :
|
531
|
+
people = @event.attendees.sort_by(:name, limit: [0, 2], order: "ALPHA")
|
746
532
|
assert %w[A B] == people.map { |person| person.name }
|
747
533
|
end
|
748
534
|
|
749
535
|
test "use the start parameter as an offset if the limit is provided" do
|
750
|
-
people = @event.attendees.sort_by(:name, :
|
536
|
+
people = @event.attendees.sort_by(:name, limit: [1, 2], order: "ALPHA")
|
751
537
|
assert %w[B C] == people.map { |person| person.name }
|
752
538
|
end
|
753
539
|
|
754
540
|
# Collections initialized with a Model parameter
|
755
541
|
setup do
|
756
542
|
@user = User.create(:email => "albert@example.com")
|
757
|
-
@user.posts.add
|
758
|
-
@user.posts.add
|
759
|
-
@user.posts.add
|
760
|
-
@user.posts.add
|
543
|
+
@user.posts.add(Post.create(:body => "D"))
|
544
|
+
@user.posts.add(Post.create(:body => "C"))
|
545
|
+
@user.posts.add(Post.create(:body => "B"))
|
546
|
+
@user.posts.add(Post.create(:body => "A"))
|
761
547
|
end
|
762
548
|
|
763
549
|
test "return instances of the passed model" do
|
764
550
|
assert Post == @user.posts.first.class
|
765
551
|
end
|
766
552
|
|
553
|
+
test "remove an object from the set" do
|
554
|
+
post = @user.posts.first
|
555
|
+
assert @user.posts.include?(post)
|
556
|
+
|
557
|
+
@user.posts.key.srem(post.id)
|
558
|
+
assert !@user.posts.include?(post)
|
559
|
+
end
|
560
|
+
|
561
|
+
test "remove an object id from the set" do
|
562
|
+
post = @user.posts.first
|
563
|
+
assert @user.posts.include?(post)
|
564
|
+
|
565
|
+
@user.posts.key.srem(post.id)
|
566
|
+
assert !@user.posts.include?(post)
|
567
|
+
end
|
568
|
+
|
767
569
|
# Counters
|
768
570
|
setup do
|
769
571
|
@event = Event.create(:name => "Ruby Tuesday")
|
770
572
|
end
|
771
573
|
|
772
|
-
test "raise ArgumentError if the attribute is not a counter" do
|
773
|
-
assert_raise ArgumentError do
|
774
|
-
@event.incr(:name)
|
775
|
-
end
|
776
|
-
end
|
777
|
-
|
778
574
|
test "be zero if not initialized" do
|
779
575
|
assert 0 == @event.votes
|
780
576
|
end
|
@@ -824,7 +620,7 @@ class ::Bar < Ohm::Model
|
|
824
620
|
attribute :name
|
825
621
|
counter :visits
|
826
622
|
set :friends, self
|
827
|
-
|
623
|
+
set :comments, self
|
828
624
|
|
829
625
|
def foo
|
830
626
|
bar.foo
|
@@ -839,56 +635,6 @@ class ::Bar < Ohm::Model
|
|
839
635
|
end
|
840
636
|
end
|
841
637
|
|
842
|
-
test "provide a meaningful inspect" do
|
843
|
-
bar = Bar.new
|
844
|
-
|
845
|
-
assert "#<Bar:? name=nil friends=nil comments=nil visits=0>" == bar.inspect
|
846
|
-
|
847
|
-
bar.update(:name => "Albert")
|
848
|
-
bar.friends << Bar.create
|
849
|
-
bar.comments << Bar.create
|
850
|
-
bar.incr(:visits)
|
851
|
-
|
852
|
-
assert %Q{#<Bar:#{bar.id} name="Albert" friends=#<Set (Bar): ["2"]> comments=#<List (Bar): ["3"]> visits=1>} == Bar[bar.id].inspect
|
853
|
-
end
|
854
|
-
|
855
|
-
def assert_wrapper_exception(&block)
|
856
|
-
begin
|
857
|
-
block.call
|
858
|
-
rescue NoMethodError => exception_raised
|
859
|
-
end
|
860
|
-
|
861
|
-
assert exception_raised.message =~
|
862
|
-
/You tried to call SomeMissingConstant#\w+, but SomeMissingConstant is not defined on #{__FILE__}:\d+:in `bar'/
|
863
|
-
end
|
864
|
-
|
865
|
-
test "inform about a miscatch by Wrapper when calling class methods" do
|
866
|
-
assert_wrapper_exception { Bar.new.baz }
|
867
|
-
end
|
868
|
-
|
869
|
-
test "inform about a miscatch by Wrapper when calling instance methods" do
|
870
|
-
assert_wrapper_exception { Bar.new.foo }
|
871
|
-
end
|
872
|
-
|
873
|
-
# Overwriting write
|
874
|
-
class ::Baz < Ohm::Model
|
875
|
-
attribute :name
|
876
|
-
|
877
|
-
def write
|
878
|
-
self.name = "Foobar"
|
879
|
-
super
|
880
|
-
end
|
881
|
-
end
|
882
|
-
|
883
|
-
test "work properly" do
|
884
|
-
baz = Baz.new
|
885
|
-
baz.name = "Foo"
|
886
|
-
baz.save
|
887
|
-
baz.name = "Foo"
|
888
|
-
baz.save
|
889
|
-
assert "Foobar" == Baz[baz.id].name
|
890
|
-
end
|
891
|
-
|
892
638
|
# Models connected to different databases
|
893
639
|
class ::Car < Ohm::Model
|
894
640
|
attribute :name
|
@@ -924,11 +670,10 @@ end
|
|
924
670
|
|
925
671
|
test "allow changing the database" do
|
926
672
|
Car.create(:name => "Twingo")
|
673
|
+
assert_equal ["1"], Car.all.key.smembers
|
927
674
|
|
928
|
-
|
929
|
-
|
930
|
-
Car.connect
|
931
|
-
assert [] == Car.all.key.smembers
|
675
|
+
Car.connect({})
|
676
|
+
assert_equal [], Car.all.key.smembers
|
932
677
|
|
933
678
|
Car.connect :db => 15
|
934
679
|
assert ["1"] == Car.all.key.smembers
|
@@ -941,41 +686,182 @@ test "persist attributes to a hash" do
|
|
941
686
|
|
942
687
|
assert "hash" == Ohm.redis.type("Event:1")
|
943
688
|
|
944
|
-
|
945
|
-
|
946
|
-
].sort == Ohm.redis.keys("Event:*").sort
|
689
|
+
expected= %w[Event:1 Event:1:counters Event:all Event:id]
|
690
|
+
assert_equal expected, Ohm.redis.keys("Event:*").sort
|
947
691
|
|
948
692
|
assert "Redis Meetup" == Event[1].name
|
949
693
|
assert 1 == Event[1].votes
|
950
694
|
end
|
951
695
|
|
952
|
-
#
|
953
|
-
|
954
|
-
|
696
|
+
# namespaced models
|
697
|
+
test "be persisted" do
|
698
|
+
SomeNamespace::Foo.create(:name => "foo")
|
699
|
+
|
700
|
+
assert "hash" == Ohm.redis.type("SomeNamespace::Foo:1")
|
701
|
+
|
702
|
+
assert "foo" == SomeNamespace::Foo[1].name
|
703
|
+
end
|
955
704
|
|
956
|
-
|
957
|
-
|
705
|
+
test "typecast attributes" do
|
706
|
+
class Option < Ohm::Model
|
707
|
+
attribute :votes, lambda { |x| x.to_i }
|
958
708
|
end
|
709
|
+
|
710
|
+
option = Option.create :votes => 20
|
711
|
+
option.update(votes: option.votes + 1)
|
712
|
+
|
713
|
+
assert_equal 21, option.votes
|
959
714
|
end
|
960
715
|
|
961
|
-
test "
|
962
|
-
|
963
|
-
|
964
|
-
|
716
|
+
test "poster-example for overriding writers" do
|
717
|
+
silence_warnings do
|
718
|
+
class Advertiser < Ohm::Model
|
719
|
+
attribute :email
|
720
|
+
|
721
|
+
def email=(e)
|
722
|
+
attributes[:email] = e.to_s.downcase.strip
|
723
|
+
end
|
724
|
+
end
|
725
|
+
end
|
726
|
+
|
727
|
+
a = Advertiser.new(email: " FOO@BAR.COM ")
|
728
|
+
assert_equal "foo@bar.com", a.email
|
965
729
|
end
|
966
730
|
|
967
|
-
|
968
|
-
|
969
|
-
|
970
|
-
|
971
|
-
|
731
|
+
__END__
|
732
|
+
|
733
|
+
These are the vestigial tests for future reference
|
734
|
+
|
735
|
+
def monitor
|
736
|
+
log = []
|
737
|
+
|
738
|
+
monitor = Thread.new do
|
739
|
+
Redis.connect.monitor do |line|
|
740
|
+
break if line =~ /ping/
|
741
|
+
log << line
|
742
|
+
end
|
743
|
+
end
|
744
|
+
|
745
|
+
sleep 0.01
|
746
|
+
|
747
|
+
log.clear.tap do
|
748
|
+
yield
|
749
|
+
Ohm.redis.ping
|
750
|
+
monitor.join
|
751
|
+
end
|
972
752
|
end
|
973
753
|
|
974
|
-
|
975
|
-
|
976
|
-
SomeNamespace::Foo.create(:name => "foo")
|
754
|
+
test "load attributes lazily" do |id|
|
755
|
+
event = Event[id]
|
977
756
|
|
978
|
-
|
757
|
+
log = monitor { event.name }
|
979
758
|
|
980
|
-
assert
|
981
|
-
|
759
|
+
assert !log.empty?
|
760
|
+
|
761
|
+
log = monitor { event.name }
|
762
|
+
|
763
|
+
assert log.empty?
|
764
|
+
end
|
765
|
+
|
766
|
+
test "allow slicing the list" do
|
767
|
+
post1 = Post.create
|
768
|
+
post2 = Post.create
|
769
|
+
post3 = Post.create
|
770
|
+
|
771
|
+
@post.related << post1
|
772
|
+
@post.related << post2
|
773
|
+
@post.related << post3
|
774
|
+
|
775
|
+
assert post1 == @post.related[0]
|
776
|
+
assert post2 == @post.related[1]
|
777
|
+
assert post3 == @post.related[-1]
|
778
|
+
|
779
|
+
assert nil == @post.related[3]
|
780
|
+
|
781
|
+
assert [post2, post3] == @post.related[1, 2]
|
782
|
+
assert [post2, post3] == @post.related[1, -1]
|
783
|
+
|
784
|
+
assert [] == @post.related[4, 5]
|
785
|
+
|
786
|
+
assert [post2, post3] == @post.related[1..2]
|
787
|
+
assert [post2, post3] == @post.related[1..5]
|
788
|
+
|
789
|
+
assert [] == @post.related[4..5]
|
790
|
+
end
|
791
|
+
|
792
|
+
# Applying arbitrary transformations
|
793
|
+
require "date"
|
794
|
+
|
795
|
+
class MyActiveRecordModel
|
796
|
+
def self.find(id)
|
797
|
+
return new if id.to_i == 1
|
798
|
+
end
|
799
|
+
|
800
|
+
def id
|
801
|
+
1
|
802
|
+
end
|
803
|
+
|
804
|
+
def ==(other)
|
805
|
+
id == other.id
|
806
|
+
end
|
807
|
+
end
|
808
|
+
|
809
|
+
class ::Calendar < Ohm::Model
|
810
|
+
list :holidays, lambda { |v| Date.parse(v) }
|
811
|
+
list :subscribers, lambda { |id| MyActiveRecordModel.find(id) }
|
812
|
+
list :appointments, :Appointment
|
813
|
+
|
814
|
+
set :events, lambda { |id| MyActiveRecordModel.find(id) }
|
815
|
+
end
|
816
|
+
|
817
|
+
class ::Appointment < Ohm::Model
|
818
|
+
attribute :text
|
819
|
+
reference :subscriber, lambda { |id| MyActiveRecordModel.find(id) }
|
820
|
+
end
|
821
|
+
|
822
|
+
setup do
|
823
|
+
@calendar = Calendar.create
|
824
|
+
|
825
|
+
@calendar.holidays.key.rpush "2009-05-25"
|
826
|
+
@calendar.holidays.key.rpush "2009-07-09"
|
827
|
+
|
828
|
+
@calendar.subscribers << MyActiveRecordModel.find(1)
|
829
|
+
|
830
|
+
@calendar.events << MyActiveRecordModel.find(1)
|
831
|
+
end
|
832
|
+
|
833
|
+
test "apply a transformation" do
|
834
|
+
assert [Date.new(2009, 5, 25), Date.new(2009, 7, 9)] == @calendar.holidays.all
|
835
|
+
|
836
|
+
assert [1] == @calendar.subscribers.all.map { |model| model.id }
|
837
|
+
assert [MyActiveRecordModel.find(1)] == @calendar.subscribers.all
|
838
|
+
end
|
839
|
+
|
840
|
+
test "doing an each on lists" do
|
841
|
+
arr = []
|
842
|
+
@calendar.subscribers.each do |sub|
|
843
|
+
arr << sub
|
844
|
+
end
|
845
|
+
|
846
|
+
assert [MyActiveRecordModel.find(1)] == arr
|
847
|
+
end
|
848
|
+
|
849
|
+
test "doing an each on sets" do
|
850
|
+
arr = []
|
851
|
+
@calendar.events.each do |sub|
|
852
|
+
arr << sub
|
853
|
+
end
|
854
|
+
|
855
|
+
assert [MyActiveRecordModel.find(1)] == arr
|
856
|
+
end
|
857
|
+
|
858
|
+
test "allow lambdas in references" do
|
859
|
+
appointment = Appointment.create(:subscriber => MyActiveRecordModel.find(1))
|
860
|
+
assert MyActiveRecordModel.find(1) == appointment.subscriber
|
861
|
+
end
|
862
|
+
|
863
|
+
test "work with models too" do
|
864
|
+
@calendar.appointments.add(Appointment.create(:text => "Meet with Bertrand"))
|
865
|
+
|
866
|
+
assert [Appointment[1]] == Calendar[1].appointments.sort
|
867
|
+
end
|