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.
@@ -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