ohm 0.1.5 → 1.0.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,65 @@
1
+ require_relative "helper"
2
+
3
+ Ohm.flush
4
+
5
+ class User < Ohm::Model
6
+ attribute :fname
7
+ attribute :lname
8
+ attribute :bday
9
+ attribute :gender
10
+ attribute :city
11
+ attribute :state
12
+ attribute :country
13
+ attribute :zip
14
+ end
15
+
16
+ create = lambda do |i|
17
+ User.new(fname: "John#{i}",
18
+ lname: "Doe#{i}",
19
+ bday: Time.now.to_s,
20
+ gender: "Male",
21
+ city: "Los Angeles",
22
+ state: "CA",
23
+ country: "US",
24
+ zip: "90210").save
25
+ end
26
+
27
+ 10.times(&create)
28
+
29
+ require "benchmark"
30
+
31
+ t1 = Benchmark.realtime do
32
+ User.all.sort_by(:fname, order: "DESC ALPHA").each do |user|
33
+ end
34
+ end
35
+
36
+ t2 = Benchmark.realtime do
37
+ ids = User.key[:all].smembers
38
+
39
+ ids.each do |id|
40
+ User[id]
41
+ end
42
+ end
43
+
44
+ test "pipelined approach should be 1.5 at least times faster for 10 records" do
45
+ assert(t2 / t1 >= 1.5)
46
+ end
47
+
48
+ 90.times(&create)
49
+
50
+ t1 = Benchmark.realtime do
51
+ User.all.sort_by(:fname, order: "DESC ALPHA").each do |user|
52
+ end
53
+ end
54
+
55
+ t2 = Benchmark.realtime do
56
+ ids = User.key[:all].smembers
57
+
58
+ ids.each do |id|
59
+ User[id]
60
+ end
61
+ end
62
+
63
+ test "the pipelined approach should be 2 times faster for 100 records" do
64
+ assert(t2 / t1 >= 2)
65
+ end
@@ -0,0 +1,241 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path("./helper", File.dirname(__FILE__))
4
+ require "ohm/transaction"
5
+
6
+ prepare do
7
+ Ohm.redis.del("foo")
8
+ end
9
+
10
+ setup do
11
+ Ohm.redis
12
+ end
13
+
14
+ test "basic functionality" do |db|
15
+ t = Ohm::Transaction.new
16
+ x = nil
17
+
18
+ t.watch("foo")
19
+
20
+ t.read do
21
+ x = db.get("foo")
22
+ end
23
+
24
+ t.write do
25
+ db.set("foo", x.to_i + 2)
26
+ end
27
+
28
+ t.commit(db)
29
+
30
+ assert_equal "2", db.get("foo")
31
+ end
32
+
33
+ test "new returns a transaction" do |db|
34
+ t1 = Ohm::Transaction.new do |t|
35
+ t.write do
36
+ db.set("foo", "bar")
37
+ end
38
+ end
39
+
40
+ t1.commit(db)
41
+
42
+ assert_equal "bar", db.get("foo")
43
+ end
44
+
45
+ test "transaction local storage" do |db|
46
+ t1 = Ohm::Transaction.new do |t|
47
+ t.read do |s|
48
+ s.foo = db.type("foo")
49
+ end
50
+
51
+ t.write do |s|
52
+ db.set("foo", s.foo.reverse)
53
+ end
54
+ end
55
+
56
+ t1.commit(db)
57
+
58
+ assert_equal "enon", db.get("foo")
59
+ end
60
+
61
+ test "composed transaction" do |db|
62
+ t1 = Ohm::Transaction.new do |t|
63
+ t.watch("foo")
64
+
65
+ t.write do |s|
66
+ db.set("foo", "bar")
67
+ end
68
+ end
69
+
70
+ t2 = Ohm::Transaction.new do |t|
71
+ t.watch("foo")
72
+
73
+ t.write do |s|
74
+ db.set("foo", "baz")
75
+ end
76
+ end
77
+
78
+ t3 = Ohm::Transaction.new
79
+ t3.append(t1)
80
+ t3.append(t2)
81
+ t3.commit(db)
82
+
83
+ assert_equal "baz", db.get("foo")
84
+
85
+ t4 = Ohm::Transaction.new
86
+ t4.append(t2)
87
+ t4.append(t1)
88
+ t4.commit(db)
89
+
90
+ assert_equal "bar", db.get("foo")
91
+
92
+ t5 = Ohm::Transaction.new
93
+ t5.append(t4)
94
+ t5.commit(db)
95
+
96
+ assert_equal "bar", db.get("foo")
97
+
98
+ assert_equal Set.new(["foo"]), t5.phase[:watch]
99
+ assert_equal 2, t5.phase[:write].size
100
+ end
101
+
102
+ test "composing transactions with append" do |db|
103
+ t1 = Ohm::Transaction.new do |t|
104
+ t.write do
105
+ db.set("foo", "bar")
106
+ end
107
+ end
108
+
109
+ t2 = Ohm::Transaction.new do |t|
110
+ t.write do
111
+ db.set("foo", "baz")
112
+ end
113
+ end
114
+
115
+ t1.append(t2)
116
+ t1.commit(db)
117
+
118
+ assert_equal "baz", db.get("foo")
119
+
120
+ t2.append(t1)
121
+ t2.commit(db)
122
+
123
+ assert_equal "bar", db.get("foo")
124
+ end
125
+
126
+ test "appending or prepending is determined by when append is called" do |db|
127
+ t1 = Ohm::Transaction.new do |t|
128
+ t.write do
129
+ db.set("foo", "bar")
130
+ end
131
+ end
132
+
133
+ t2 = Ohm::Transaction.new do |t|
134
+ t.append(t1)
135
+
136
+ t.write do
137
+ db.set("foo", "baz")
138
+ end
139
+ end
140
+
141
+ t3 = Ohm::Transaction.new do |t|
142
+ t.write do
143
+ db.set("foo", "baz")
144
+ end
145
+
146
+ t.append(t1)
147
+ end
148
+
149
+ t2.commit(db)
150
+
151
+ assert_equal "baz", db.get("foo")
152
+
153
+ t3.commit(db)
154
+
155
+ assert_equal "bar", db.get("foo")
156
+ end
157
+
158
+ test "storage in composed transactions" do |db|
159
+ t1 = Ohm::Transaction.new do |t|
160
+ t.read do |s|
161
+ s.foo = db.type("foo")
162
+ end
163
+ end
164
+
165
+ t2 = Ohm::Transaction.new do |t|
166
+ t.write do |s|
167
+ db.set("foo", s.foo.reverse)
168
+ end
169
+ end
170
+
171
+ t1.append(t2).commit(db)
172
+
173
+ assert_equal "enon", db.get("foo")
174
+ end
175
+
176
+ test "storage entries can't be overriden" do |db|
177
+ t1 = Ohm::Transaction.new do |t|
178
+ t.read do |s|
179
+ s.foo = db.type("foo")
180
+ end
181
+ end
182
+
183
+ t2 = Ohm::Transaction.new do |t|
184
+ t.read do |s|
185
+ s.foo = db.exists("foo")
186
+ end
187
+ end
188
+
189
+ assert_raise Ohm::Transaction::Store::EntryAlreadyExistsError do
190
+ t1.append(t2).commit(db)
191
+ end
192
+ end
193
+
194
+ __END__
195
+ # We leave this here to indicate what the past behavior was with
196
+ # model transactions.
197
+
198
+ class Post < Ohm::Model
199
+ attribute :body
200
+ attribute :state
201
+ index :state
202
+
203
+ def before_save
204
+ self.body = body.to_s.strip
205
+ end
206
+
207
+ def before_create
208
+ self.state = "draft"
209
+ end
210
+ end
211
+
212
+ test "transactions in models" do |db|
213
+ p = Post.new(body: " foo ")
214
+
215
+ db.set "csv:foo", "A,B"
216
+
217
+ t1 = Ohm::Transaction.define do |t|
218
+ t.watch("csv:foo")
219
+
220
+ t.read do |s|
221
+ s.csv = db.get("csv:foo")
222
+ end
223
+
224
+ t.write do |s|
225
+ db.set("csv:foo", s.csv + "," + "C")
226
+ end
227
+ end
228
+
229
+ main = Ohm::Transaction.new(p.transaction_for_create, t1)
230
+ main.commit(db)
231
+
232
+ # Verify the Post transaction proceeded without a hitch
233
+ p = Post[p.id]
234
+
235
+ assert_equal "draft", p.state
236
+ assert_equal "foo", p.body
237
+ assert Post.find(state: "draft").include?(p)
238
+
239
+ # Verify that the second transaction happened
240
+ assert_equal "A,B,C", db.get("csv:foo")
241
+ end
data/test/uniques.rb ADDED
@@ -0,0 +1,87 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path("./helper", File.dirname(__FILE__))
4
+
5
+ class User < Ohm::Model
6
+ attribute :email
7
+ unique :email
8
+ unique :provider
9
+
10
+ def self.[](id)
11
+ super(id.to_i)
12
+ end
13
+
14
+ def provider
15
+ email[/@(.*?).com/, 1]
16
+ end
17
+ end
18
+
19
+ setup do
20
+ User.create(email: "a@a.com")
21
+ end
22
+
23
+ test "findability" do |u|
24
+ assert_equal u, User.with(:email, "a@a.com")
25
+ end
26
+
27
+ test "raises when it already exists during create" do
28
+ assert_raise Ohm::UniqueIndexViolation do
29
+ User.create(email: "a@a.com")
30
+ end
31
+ end
32
+
33
+ test "raises when it already exists during save" do
34
+ u = User.create(email: "b@b.com")
35
+ u.email = "a@a.com"
36
+
37
+ assert_raise Ohm::UniqueIndexViolation do
38
+ u.save
39
+ end
40
+ end
41
+
42
+ test "doesn't raise when saving again and again" do |u|
43
+ ex = nil
44
+
45
+ begin
46
+ User[u.id].save
47
+ rescue Exception => e
48
+ ex = e
49
+ end
50
+
51
+ assert_equal nil, ex
52
+ end
53
+
54
+ test "removes the previous index when changing" do
55
+ u = User.create(email: "c@c.com")
56
+ u.update(email: "d@d.com")
57
+
58
+ assert_equal nil, User.with(:email, "c@c.com")
59
+ assert_equal nil, User.key[:unique][:email].hget("c@c.com")
60
+ assert_equal u, User.with(:email, "d@d.com")
61
+ end
62
+
63
+ test "removes the previous index when deleting" do |u|
64
+ u.delete
65
+
66
+ assert_equal nil, User.with(:email, "a@a.com")
67
+ assert_equal nil, User.key[:unique][:email].hget("a@a.com")
68
+ end
69
+
70
+ test "unique virtual attribute" do
71
+ u = User.create(email: "foo@yahoo.com")
72
+
73
+ assert_equal u, User.with(:provider, "yahoo")
74
+
75
+ # Yahoo should be allowed because this user is the one reserved for it.
76
+ u.update(email: "bar@yahoo.com")
77
+
78
+ # `a` is not allowed though.
79
+ assert_raise Ohm::UniqueIndexViolation do
80
+ u.update(email: "bar@a.com")
81
+ end
82
+
83
+ # And so is yahoo if we try creating a different user.
84
+ assert_raise Ohm::UniqueIndexViolation do
85
+ User.create(email: "baz@yahoo.com")
86
+ end
87
+ end
@@ -63,12 +63,12 @@ test "be cached in an instance variable" do
63
63
 
64
64
  @post.update(:author => Person.create(:name => "Bertrand"))
65
65
 
66
- assert "Bertrand" == @post.author.name
67
- assert @post.author.object_id == @post.author.object_id
66
+ assert_equal "Bertrand", @post.author.name
67
+ assert_equal @post.author.object_id, @post.author.object_id
68
68
 
69
69
  @post.update(:author_id => Person.create(:name => "Charles").id)
70
70
 
71
- assert "Charles" == @post.author.name
71
+ assert_equal "Charles", @post.author.name
72
72
  end
73
73
 
74
74
  setup do
File without changes
File without changes
@@ -18,7 +18,7 @@ end
18
18
  class Validatable
19
19
  attr_accessor :name
20
20
 
21
- include Ohm::Validations
21
+ include Scrivener::Validations
22
22
  end
23
23
 
24
24
  # A new model with validations
@@ -30,19 +30,19 @@ scope do
30
30
  # That must have a present name
31
31
  scope do
32
32
  test "not be created if the name is never assigned" do |event|
33
- event.create
33
+ event.save
34
34
  assert event.new?
35
35
  end
36
36
 
37
37
  test "not be created if the name assigned is empty" do |event|
38
38
  event.name = ""
39
- event.create
39
+ event.save
40
40
  assert event.new?
41
41
  end
42
42
 
43
43
  test "be created if the name assigned is not empty" do |event|
44
44
  event.name = "hello"
45
- event.create
45
+ event.save
46
46
  assert event.id
47
47
  end
48
48
 
@@ -50,7 +50,7 @@ scope do
50
50
  scope do
51
51
  test "not be created if the name doesn't match /^\w+$/" do |event|
52
52
  event.name = "hello-world"
53
- event.create
53
+ event.save
54
54
  assert event.new?
55
55
  end
56
56
  end
@@ -65,10 +65,10 @@ scope do
65
65
 
66
66
  event.name = "foo"
67
67
  event.place = "bar"
68
- event.create
68
+ event.save
69
69
 
70
70
  assert event.new?
71
- assert [[:capacity, :not_numeric]] == event.errors
71
+ assert_equal({capacity: [:not_numeric]}, event.errors)
72
72
  end
73
73
 
74
74
  test "fail when the value is not numeric" do |event|
@@ -79,10 +79,10 @@ scope do
79
79
  event.name = "foo"
80
80
  event.place = "bar"
81
81
  event.capacity = "baz"
82
- event.create
82
+ event.save
83
83
 
84
84
  assert event.new?
85
- assert [[:capacity, :not_numeric]] == event.errors
85
+ assert_equal({capacity: [:not_numeric]}, event.errors)
86
86
  end
87
87
 
88
88
  test "succeed when the value is numeric" do |event|
@@ -93,62 +93,11 @@ scope do
93
93
  event.name = "foo"
94
94
  event.place = "bar"
95
95
  event.capacity = 42
96
- event.create
96
+ event.save
97
97
 
98
98
  assert event.id
99
99
  end
100
100
  end
101
-
102
- # That must have a unique name
103
- scope do
104
- test "fail when the value already exists" do |event|
105
- def event.validate
106
- assert_unique :name
107
- end
108
-
109
- Event.create(:name => "foo")
110
- event.name = "foo"
111
- event.create
112
-
113
- assert event.new?
114
- assert [[:name, :not_unique]] == event.errors
115
- end
116
- end
117
-
118
- # That must have a unique name scoped by place
119
- scope do
120
- test "fail when the value already exists for a scoped attribute" do |event|
121
- def event.validate
122
- assert_unique [:name, :place]
123
- end
124
-
125
- Event.create(:name => "foo", :place => "bar")
126
- event.name = "foo"
127
- event.place = "bar"
128
- event.create
129
-
130
- assert event.new?
131
- assert [[[:name, :place], :not_unique]] == event.errors
132
-
133
- event.place = "baz"
134
- event.create
135
-
136
- assert event.valid?
137
- end
138
- end
139
-
140
- # That defines a unique validation on a non indexed attribute
141
- scope do
142
- test "raise ArgumentError" do |event|
143
- def event.validate
144
- assert_unique :capacity
145
- end
146
-
147
- assert_raise(Ohm::Model::IndexNotFound) do
148
- event.valid?
149
- end
150
- end
151
- end
152
101
  end
153
102
 
154
103
  # An existing model with a valid name
@@ -192,28 +141,29 @@ scope do
192
141
  scope do
193
142
  test "add errors to a collection" do |target|
194
143
  def target.validate
195
- assert(false, "Something bad")
144
+ assert(false, ["Attribute", "Something bad"])
196
145
  end
197
146
 
198
147
  target.validate
199
148
 
200
- assert ["Something bad"] == target.errors
149
+ assert_equal({ "Attribute" => ["Something bad"] }, target.errors)
201
150
  end
202
151
 
203
152
  test "allow for nested validations" do |target|
204
153
  def target.validate
205
- if assert(true, "No error")
206
- assert(false, "Chained error")
154
+ if assert(true, ["Attribute", "No error"])
155
+ assert(false, ["Attribute", "Chained error"])
207
156
  end
208
157
 
209
- if assert(false, "Parent error")
210
- assert(false, "No chained error")
158
+ if assert(false, ["Attribute", "Parent error"])
159
+ assert(false, ["Attribute", "No chained error"])
211
160
  end
212
161
  end
213
162
 
214
163
  target.validate
215
164
 
216
- assert ["Chained error", "Parent error"] == target.errors
165
+ expected = {"Attribute"=>["Chained error", "Parent error"]}
166
+ assert_equal expected, target.errors
217
167
  end
218
168
  end
219
169
 
@@ -232,14 +182,14 @@ scope do
232
182
  test "fail when the attribute is nil" do |target|
233
183
  target.validate
234
184
 
235
- assert [[:name, :not_present]] == target.errors
185
+ assert_equal({ name: [:not_present] }, target.errors)
236
186
  end
237
187
 
238
188
  test "fail when the attribute is empty" do |target|
239
189
  target.name = ""
240
190
  target.validate
241
191
 
242
- assert [[:name, :not_present]] == target.errors
192
+ assert_equal({ name: [:not_present] }, target.errors)
243
193
  end
244
194
  end
245
195
  end