ohm 0.0.7 → 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +6 -4
- data/lib/ohm.rb +173 -17
- data/lib/ohm/redis.rb +6 -3
- data/lib/ohm/validations.rb +9 -9
- data/test/db/redis.pid +1 -1
- data/test/errors_test.rb +6 -6
- data/test/indices_test.rb +6 -1
- data/test/model_test.rb +72 -10
- data/test/validations_test.rb +45 -20
- metadata +2 -2
data/README.markdown
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
Ohm
|
2
|
-
|
1
|
+
Ohm ॐ
|
2
|
+
=====
|
3
3
|
|
4
4
|
Object-hash mapping library for Redis.
|
5
5
|
|
@@ -23,6 +23,8 @@ Usage
|
|
23
23
|
set :participants
|
24
24
|
list :comments
|
25
25
|
|
26
|
+
index :name
|
27
|
+
|
26
28
|
def validate
|
27
29
|
assert_present :name
|
28
30
|
end
|
@@ -31,11 +33,11 @@ Usage
|
|
31
33
|
event = Event.create(:name => "Ruby Tuesday")
|
32
34
|
event.participants << "Michel Martens"
|
33
35
|
event.participants << "Damian Janowski"
|
34
|
-
event.participants
|
36
|
+
event.participants.all #=> ["Damian Janowski", "Michel Martens"]
|
35
37
|
|
36
38
|
event.comments << "Very interesting event!"
|
37
39
|
event.comments << "Agree"
|
38
|
-
event.comments
|
40
|
+
event.comments.all #=> ["Very interesting event!", "Agree"]
|
39
41
|
|
40
42
|
another_event = Event.new
|
41
43
|
another_event.valid? #=> false
|
data/lib/ohm.rb
CHANGED
@@ -1,19 +1,33 @@
|
|
1
|
+
require "base64"
|
1
2
|
require File.join(File.dirname(__FILE__), "ohm", "redis")
|
2
3
|
require File.join(File.dirname(__FILE__), "ohm", "validations")
|
3
4
|
|
4
5
|
module Ohm
|
6
|
+
|
7
|
+
# Provides access to the redis database. This is shared accross all models and instances.
|
5
8
|
def redis
|
6
9
|
@redis
|
7
10
|
end
|
8
11
|
|
9
|
-
|
10
|
-
|
12
|
+
# Connect to a redis database.
|
13
|
+
#
|
14
|
+
# @param options [Hash] options to create a message with.
|
15
|
+
# @option options [#to_s] :host ('127.0.0.1') Host of the redis database.
|
16
|
+
# @option options [#to_s] :port (6379) Port number.
|
17
|
+
# @option options [#to_s] :db (0) Database number.
|
18
|
+
# @option options [#to_s] :timeout (0) Database timeout in seconds.
|
19
|
+
# @example Connect to a database in port 6380.
|
20
|
+
# Ohm.connect(:port => 6380)
|
21
|
+
def connect(*options)
|
22
|
+
@redis = Ohm::Redis.new(*options)
|
11
23
|
end
|
12
24
|
|
25
|
+
# Clear the database.
|
13
26
|
def flush
|
14
27
|
@redis.flushdb
|
15
28
|
end
|
16
29
|
|
30
|
+
# Join the parameters with ":" to create a key.
|
17
31
|
def key(*args)
|
18
32
|
args.join(":")
|
19
33
|
end
|
@@ -21,42 +35,93 @@ module Ohm
|
|
21
35
|
module_function :key, :connect, :flush, :redis
|
22
36
|
|
23
37
|
module Attributes
|
24
|
-
class Collection
|
38
|
+
class Collection
|
39
|
+
include Enumerable
|
40
|
+
|
25
41
|
attr_accessor :key, :db
|
26
42
|
|
27
43
|
def initialize(db, key)
|
28
44
|
self.db = db
|
29
45
|
self.key = key
|
30
|
-
|
46
|
+
end
|
47
|
+
|
48
|
+
def each(&block)
|
49
|
+
all.each(&block)
|
50
|
+
end
|
51
|
+
|
52
|
+
def all(model = nil)
|
53
|
+
model ? raw.collect { |id| model[id] } : raw
|
31
54
|
end
|
32
55
|
end
|
33
56
|
|
57
|
+
# Represents a Redis list.
|
58
|
+
#
|
59
|
+
# @example Use a list attribute.
|
60
|
+
#
|
61
|
+
# class Event < Ohm::Model
|
62
|
+
# attribute :name
|
63
|
+
# list :participants
|
64
|
+
# end
|
65
|
+
#
|
66
|
+
# event = Event.create :name => "Redis Meeting"
|
67
|
+
# event.participants << "Albert"
|
68
|
+
# event.participants << "Benoit"
|
69
|
+
# event.participants.all #=> ["Albert", "Benoit"]
|
34
70
|
class List < Collection
|
35
|
-
def retrieve
|
36
|
-
db.list(key)
|
37
|
-
end
|
38
71
|
|
72
|
+
# @param value [#to_s] Pushes value to the list.
|
39
73
|
def << value
|
40
|
-
|
74
|
+
db.rpush(key, value)
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def raw
|
80
|
+
db.list(key)
|
41
81
|
end
|
42
82
|
end
|
43
83
|
|
84
|
+
# Represents a Redis set.
|
85
|
+
#
|
86
|
+
# @example Use a set attribute.
|
87
|
+
#
|
88
|
+
# class Company < Ohm::Model
|
89
|
+
# attribute :name
|
90
|
+
# set :employees
|
91
|
+
# end
|
92
|
+
#
|
93
|
+
# company = Company.create :name => "Redis Co."
|
94
|
+
# company.employees << "Albert"
|
95
|
+
# company.employees << "Benoit"
|
96
|
+
# company.employees.all #=> ["Albert", "Benoit"]
|
97
|
+
# company.include?("Albert") #=> true
|
44
98
|
class Set < Collection
|
45
|
-
def retrieve
|
46
|
-
db.smembers(key).sort
|
47
|
-
end
|
48
99
|
|
100
|
+
# @param value [#to_s] Adds value to the list.
|
49
101
|
def << value
|
50
|
-
|
102
|
+
db.sadd(key, value)
|
103
|
+
end
|
104
|
+
|
105
|
+
# @param value [Ohm::Model#id] Adds the id of the object if it's an Ohm::Model.
|
106
|
+
def add model
|
107
|
+
raise ArgumentError unless model.kind_of?(Ohm::Model)
|
108
|
+
raise ArgumentError unless model.id
|
109
|
+
self << model.id
|
51
110
|
end
|
52
111
|
|
53
112
|
def delete(value)
|
54
|
-
|
113
|
+
db.srem(key, value)
|
55
114
|
end
|
56
115
|
|
57
116
|
def include?(value)
|
58
117
|
db.sismember(key, value)
|
59
118
|
end
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
def raw
|
123
|
+
db.smembers(key).sort
|
124
|
+
end
|
60
125
|
end
|
61
126
|
end
|
62
127
|
|
@@ -64,6 +129,13 @@ module Ohm
|
|
64
129
|
module Validations
|
65
130
|
include Ohm::Validations
|
66
131
|
|
132
|
+
# Validates that the attribute or array of attributes are unique. For this,
|
133
|
+
# an index of the same kind must exist.
|
134
|
+
#
|
135
|
+
# @overload assert_unique :name
|
136
|
+
# Validates that the name attribute is unique.
|
137
|
+
# @overload assert_unique [:street, :city]
|
138
|
+
# Validates that the :street and :city pair is unique.
|
67
139
|
def assert_unique(attrs)
|
68
140
|
index_key = index_key_for(Array(attrs), read_locals(Array(attrs)))
|
69
141
|
assert(db.scard(index_key).zero? || db.sismember(index_key, id), [Array(attrs), :not_unique])
|
@@ -76,10 +148,15 @@ module Ohm
|
|
76
148
|
|
77
149
|
@@attributes = Hash.new { |hash, key| hash[key] = [] }
|
78
150
|
@@collections = Hash.new { |hash, key| hash[key] = [] }
|
151
|
+
@@counters = Hash.new { |hash, key| hash[key] = [] }
|
79
152
|
@@indices = Hash.new { |hash, key| hash[key] = [] }
|
80
153
|
|
81
154
|
attr_accessor :id
|
82
155
|
|
156
|
+
# Defines a string attribute for the model. This attribute will be persisted by Redis
|
157
|
+
# as a string. Any value stored here will be retrieved in its string representation.
|
158
|
+
#
|
159
|
+
# @param name [Symbol] Name of the attribute.
|
83
160
|
def self.attribute(name)
|
84
161
|
define_method(name) do
|
85
162
|
read_local(name)
|
@@ -92,16 +169,58 @@ module Ohm
|
|
92
169
|
attributes << name
|
93
170
|
end
|
94
171
|
|
172
|
+
# Defines a counter attribute for the model. This attribute can't be assigned, only incremented
|
173
|
+
# or decremented. It will be zero by default.
|
174
|
+
#
|
175
|
+
# @param name [Symbol] Name of the counter.
|
176
|
+
def self.counter(name)
|
177
|
+
define_method(name) do
|
178
|
+
read_local(name).to_i
|
179
|
+
end
|
180
|
+
|
181
|
+
counters << name
|
182
|
+
end
|
183
|
+
|
184
|
+
# Defines a list attribute for the model. It can be accessed only after the model instance
|
185
|
+
# is created.
|
186
|
+
#
|
187
|
+
# @param name [Symbol] Name of the list.
|
95
188
|
def self.list(name)
|
96
189
|
attr_list_reader(name)
|
97
190
|
collections << name
|
98
191
|
end
|
99
192
|
|
193
|
+
# Defines a set attribute for the model. It can be accessed only after the model instance
|
194
|
+
# is created. Sets are recommended when insertion and retrival order is irrelevant, and
|
195
|
+
# operations like union, join, and membership checks are important.
|
196
|
+
#
|
197
|
+
# @param name [Symbol] Name of the set.
|
100
198
|
def self.set(name)
|
101
199
|
attr_set_reader(name)
|
102
200
|
collections << name
|
103
201
|
end
|
104
202
|
|
203
|
+
# Creates an index (a set) that will be used for finding instances.
|
204
|
+
#
|
205
|
+
# If you want to find a model instance by some attribute value, then an index for that
|
206
|
+
# attribute must exist.
|
207
|
+
#
|
208
|
+
# Each index declaration creates an index. It can be either an index on one particular attribute,
|
209
|
+
# or an index accross many attributes.
|
210
|
+
#
|
211
|
+
# @example
|
212
|
+
# class User < Ohm::Model
|
213
|
+
# attribute :email
|
214
|
+
# index :email
|
215
|
+
# end
|
216
|
+
#
|
217
|
+
# # Now this is possible:
|
218
|
+
# User.find :email, "ohm@example.com"
|
219
|
+
#
|
220
|
+
# @overload index :name
|
221
|
+
# Creates an index for the name attribute.
|
222
|
+
# @overload index [:street, :city]
|
223
|
+
# Creates a composite index for street and city.
|
105
224
|
def self.index(attrs)
|
106
225
|
indices << Array(attrs)
|
107
226
|
end
|
@@ -134,6 +253,10 @@ module Ohm
|
|
134
253
|
@@attributes[self]
|
135
254
|
end
|
136
255
|
|
256
|
+
def self.counters
|
257
|
+
@@counters[self]
|
258
|
+
end
|
259
|
+
|
137
260
|
def self.collections
|
138
261
|
@@collections[self]
|
139
262
|
end
|
@@ -143,11 +266,23 @@ module Ohm
|
|
143
266
|
end
|
144
267
|
|
145
268
|
def self.create(*args)
|
146
|
-
new(*args)
|
269
|
+
model = new(*args)
|
270
|
+
model.create
|
271
|
+
model
|
147
272
|
end
|
148
273
|
|
149
274
|
def self.find(attribute, value)
|
150
|
-
filter(Ohm.key(attribute, value))
|
275
|
+
filter(Ohm.key(attribute, encode(value)))
|
276
|
+
end
|
277
|
+
|
278
|
+
def self.encode(value)
|
279
|
+
Base64.encode64(value.to_s).chomp
|
280
|
+
end
|
281
|
+
|
282
|
+
def self.encode_each(values)
|
283
|
+
values.collect do |value|
|
284
|
+
encode(value)
|
285
|
+
end
|
151
286
|
end
|
152
287
|
|
153
288
|
def initialize(attrs = {})
|
@@ -179,16 +314,37 @@ module Ohm
|
|
179
314
|
|
180
315
|
def delete
|
181
316
|
delete_from_indices
|
182
|
-
delete_attributes(collections)
|
183
317
|
delete_attributes(attributes)
|
318
|
+
delete_attributes(counters)
|
319
|
+
delete_attributes(collections)
|
184
320
|
delete_model_membership
|
185
321
|
self
|
186
322
|
end
|
187
323
|
|
324
|
+
# Increment the attribute denoted by :att.
|
325
|
+
#
|
326
|
+
# @param att [Symbol] Attribute to increment.
|
327
|
+
def incr(att)
|
328
|
+
raise ArgumentError unless counters.include?(att)
|
329
|
+
write_local(att, db.incr(key(att)))
|
330
|
+
end
|
331
|
+
|
332
|
+
# Decrement the attribute denoted by :att.
|
333
|
+
#
|
334
|
+
# @param att [Symbol] Attribute to decrement.
|
335
|
+
def decr(att)
|
336
|
+
raise ArgumentError unless counters.include?(att)
|
337
|
+
write_local(att, db.decr(key(att)))
|
338
|
+
end
|
339
|
+
|
188
340
|
def attributes
|
189
341
|
self.class.attributes
|
190
342
|
end
|
191
343
|
|
344
|
+
def counters
|
345
|
+
self.class.counters
|
346
|
+
end
|
347
|
+
|
192
348
|
def collections
|
193
349
|
self.class.collections
|
194
350
|
end
|
@@ -303,7 +459,7 @@ module Ohm
|
|
303
459
|
end
|
304
460
|
|
305
461
|
def index_key_for(attrs, values)
|
306
|
-
self.class.key *(attrs + values)
|
462
|
+
self.class.key *(attrs + self.class.encode_each(values))
|
307
463
|
end
|
308
464
|
end
|
309
465
|
end
|
data/lib/ohm/redis.rb
CHANGED
@@ -157,9 +157,12 @@ module Ohm
|
|
157
157
|
def call_command(argv)
|
158
158
|
connect unless connected?
|
159
159
|
raw_call_command(argv.dup)
|
160
|
-
rescue Errno::ECONNRESET
|
161
|
-
reconnect
|
162
|
-
|
160
|
+
rescue Errno::ECONNRESET, Errno::EPIPE
|
161
|
+
if reconnect
|
162
|
+
raw_call_command(argv.dup)
|
163
|
+
else
|
164
|
+
raise Errno::ECONNRESET
|
165
|
+
end
|
163
166
|
end
|
164
167
|
|
165
168
|
def raw_call_command(argv)
|
data/lib/ohm/validations.rb
CHANGED
@@ -72,20 +72,20 @@ module Ohm
|
|
72
72
|
|
73
73
|
protected
|
74
74
|
|
75
|
-
def assert_format(att, format)
|
76
|
-
if assert_present(att)
|
77
|
-
assert
|
75
|
+
def assert_format(att, format, error = [att, :format])
|
76
|
+
if assert_present(att, error)
|
77
|
+
assert(send(att).to_s.match(format), error)
|
78
78
|
end
|
79
79
|
end
|
80
80
|
|
81
|
-
def assert_present(att)
|
82
|
-
|
83
|
-
assert !send(att).empty?, [att, :empty]
|
84
|
-
end
|
81
|
+
def assert_present(att, error = [att, :not_present])
|
82
|
+
assert(!send(att).to_s.empty?, error)
|
85
83
|
end
|
86
84
|
|
87
|
-
def
|
88
|
-
|
85
|
+
def assert_numeric(att, error = [att, :not_numeric])
|
86
|
+
if assert_present(att, error)
|
87
|
+
assert_format(att, /^\d+$/, error)
|
88
|
+
end
|
89
89
|
end
|
90
90
|
|
91
91
|
def assert(value, error)
|
data/test/db/redis.pid
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
65913
|
data/test/errors_test.rb
CHANGED
@@ -31,11 +31,11 @@ class ErrorsTest < Test::Unit::TestCase
|
|
31
31
|
values = []
|
32
32
|
|
33
33
|
@model.errors.present do |e|
|
34
|
-
e.on [:name, :
|
34
|
+
e.on [:name, :not_present] do
|
35
35
|
values << 1
|
36
36
|
end
|
37
37
|
|
38
|
-
e.on [:account, :
|
38
|
+
e.on [:account, :not_present] do
|
39
39
|
values << 2
|
40
40
|
end
|
41
41
|
|
@@ -65,8 +65,8 @@ class ErrorsTest < Test::Unit::TestCase
|
|
65
65
|
|
66
66
|
should "accept multiple matches for an error" do
|
67
67
|
values = @model.errors.present do |e|
|
68
|
-
e.on [:name, :
|
69
|
-
e.on [:account, :
|
68
|
+
e.on [:name, :not_present], "A"
|
69
|
+
e.on [:account, :not_present] do
|
70
70
|
"B"
|
71
71
|
end
|
72
72
|
e.on :terrible_error, "C"
|
@@ -85,8 +85,8 @@ class ErrorsTest < Test::Unit::TestCase
|
|
85
85
|
|
86
86
|
should "take a custom presenter" do
|
87
87
|
values = @model.errors.present(MyPresenter) do |e|
|
88
|
-
e.on([:name, :
|
89
|
-
e.on([:account, :
|
88
|
+
e.on([:name, :not_present]) { "A" }
|
89
|
+
e.on([:account, :not_present]) { "B" }
|
90
90
|
e.on(:terrible_error) { "C" }
|
91
91
|
end
|
92
92
|
|
data/test/indices_test.rb
CHANGED
@@ -9,10 +9,11 @@ class IndicesTest < Test::Unit::TestCase
|
|
9
9
|
|
10
10
|
context "A model with an indexed attribute" do
|
11
11
|
setup do
|
12
|
-
Ohm.
|
12
|
+
Ohm.flush
|
13
13
|
|
14
14
|
@user1 = User.create(:email => "foo")
|
15
15
|
@user2 = User.create(:email => "bar")
|
16
|
+
@user3 = User.create(:email => "baz qux")
|
16
17
|
end
|
17
18
|
|
18
19
|
should "be able to find by the given attribute" do
|
@@ -32,5 +33,9 @@ class IndicesTest < Test::Unit::TestCase
|
|
32
33
|
|
33
34
|
assert_equal [], User.find(:email, "bar")
|
34
35
|
end
|
36
|
+
|
37
|
+
should "work with attributes that contain spaces" do
|
38
|
+
assert_equal [@user3], User.find(:email, "baz qux")
|
39
|
+
end
|
35
40
|
end
|
36
41
|
end
|
data/test/model_test.rb
CHANGED
@@ -2,6 +2,7 @@ require File.join(File.dirname(__FILE__), "test_helper")
|
|
2
2
|
|
3
3
|
class Event < Ohm::Model
|
4
4
|
attribute :name
|
5
|
+
counter :votes
|
5
6
|
set :attendees
|
6
7
|
end
|
7
8
|
|
@@ -14,6 +15,14 @@ class Post < Ohm::Model
|
|
14
15
|
list :comments
|
15
16
|
end
|
16
17
|
|
18
|
+
class Person < Ohm::Model
|
19
|
+
attribute :name
|
20
|
+
|
21
|
+
def validate
|
22
|
+
assert_present :name
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
17
26
|
class TestRedis < Test::Unit::TestCase
|
18
27
|
context "An event initialized with a hash of attributes" do
|
19
28
|
should "assign the passed attributes" do
|
@@ -28,6 +37,10 @@ class TestRedis < Test::Unit::TestCase
|
|
28
37
|
event2 = Event.create(:name => "Ruby Meetup")
|
29
38
|
assert_equal event1.id + 1, event2.id
|
30
39
|
end
|
40
|
+
|
41
|
+
should "return the unsaved object if validation fails" do
|
42
|
+
assert Person.create(:name => nil).kind_of?(Person)
|
43
|
+
end
|
31
44
|
end
|
32
45
|
|
33
46
|
context "Finding an event" do
|
@@ -189,19 +202,14 @@ class TestRedis < Test::Unit::TestCase
|
|
189
202
|
end
|
190
203
|
end
|
191
204
|
|
192
|
-
should "return an array if the model exists" do
|
193
|
-
@event.create
|
194
|
-
assert @event.attendees.kind_of?(Array)
|
195
|
-
end
|
196
|
-
|
197
205
|
should "remove an element if sent :delete" do
|
198
206
|
@event.create
|
199
207
|
@event.attendees << "1"
|
200
208
|
@event.attendees << "2"
|
201
209
|
@event.attendees << "3"
|
202
|
-
assert_equal ["1", "2", "3"], @event.attendees
|
210
|
+
assert_equal ["1", "2", "3"], @event.attendees.all
|
203
211
|
@event.attendees.delete("2")
|
204
|
-
assert_equal ["1", "3"], Event[@event.id].attendees
|
212
|
+
assert_equal ["1", "3"], Event[@event.id].attendees.all
|
205
213
|
end
|
206
214
|
|
207
215
|
should "return true if the set includes some member" do
|
@@ -212,6 +220,22 @@ class TestRedis < Test::Unit::TestCase
|
|
212
220
|
assert @event.attendees.include?("2")
|
213
221
|
assert_equal false, @event.attendees.include?("4")
|
214
222
|
end
|
223
|
+
|
224
|
+
should "return instances of the passed model if the call to all includes a class" do
|
225
|
+
@event.create
|
226
|
+
@person = Person.create :name => "albert"
|
227
|
+
@event.attendees << @person.id
|
228
|
+
|
229
|
+
assert_equal [@person], @event.attendees.all(Person)
|
230
|
+
end
|
231
|
+
|
232
|
+
should "insert the model instance id instead of the object if using add" do
|
233
|
+
@event.create
|
234
|
+
@person = Person.create :name => "albert"
|
235
|
+
@event.attendees.add(@person)
|
236
|
+
|
237
|
+
assert_equal [@person.id.to_s], @event.attendees.all
|
238
|
+
end
|
215
239
|
end
|
216
240
|
|
217
241
|
context "Attributes of type List" do
|
@@ -222,14 +246,14 @@ class TestRedis < Test::Unit::TestCase
|
|
222
246
|
end
|
223
247
|
|
224
248
|
should "return an array" do
|
225
|
-
assert @post.comments.kind_of?(Array)
|
249
|
+
assert @post.comments.all.kind_of?(Array)
|
226
250
|
end
|
227
251
|
|
228
252
|
should "keep the inserting order" do
|
229
253
|
@post.comments << "1"
|
230
254
|
@post.comments << "2"
|
231
255
|
@post.comments << "3"
|
232
|
-
assert_equal ["1", "2", "3"], @post.comments
|
256
|
+
assert_equal ["1", "2", "3"], @post.comments.all
|
233
257
|
end
|
234
258
|
|
235
259
|
should "keep the inserting order after saving" do
|
@@ -237,7 +261,45 @@ class TestRedis < Test::Unit::TestCase
|
|
237
261
|
@post.comments << "2"
|
238
262
|
@post.comments << "3"
|
239
263
|
@post.save
|
240
|
-
assert_equal ["1", "2", "3"], Post[@post.id].comments
|
264
|
+
assert_equal ["1", "2", "3"], Post[@post.id].comments.all
|
265
|
+
end
|
266
|
+
|
267
|
+
should "respond to each" do
|
268
|
+
@post.comments << "1"
|
269
|
+
@post.comments << "2"
|
270
|
+
@post.comments << "3"
|
271
|
+
|
272
|
+
i = 1
|
273
|
+
@post.comments.each do |c|
|
274
|
+
assert_equal i, c.to_i
|
275
|
+
i += 1
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
context "Counters" do
|
281
|
+
setup do
|
282
|
+
@event = Event.create(:name => "Ruby Tuesday")
|
283
|
+
end
|
284
|
+
|
285
|
+
should "raise ArgumentError if the attribute is not a counter" do
|
286
|
+
assert_raise ArgumentError do
|
287
|
+
@event.incr(:name)
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
should "be zero if not initialized" do
|
292
|
+
assert_equal 0, @event.votes
|
293
|
+
end
|
294
|
+
|
295
|
+
should "be able to increment a counter" do
|
296
|
+
@event.incr(:votes)
|
297
|
+
assert_equal 1, @event.votes
|
298
|
+
end
|
299
|
+
|
300
|
+
should "be able to decrement a counter" do
|
301
|
+
@event.decr(:votes)
|
302
|
+
assert_equal -1, @event.votes
|
241
303
|
end
|
242
304
|
end
|
243
305
|
|
data/test/validations_test.rb
CHANGED
@@ -4,6 +4,7 @@ class ValidationsTest < Test::Unit::TestCase
|
|
4
4
|
class Event < Ohm::Model
|
5
5
|
attribute :name
|
6
6
|
attribute :place
|
7
|
+
attribute :capacity
|
7
8
|
|
8
9
|
index :name
|
9
10
|
index [:name, :place]
|
@@ -45,6 +46,48 @@ class ValidationsTest < Test::Unit::TestCase
|
|
45
46
|
end
|
46
47
|
end
|
47
48
|
|
49
|
+
context "That must have a numeric attribute :capacity" do
|
50
|
+
should "fail when the value is nil" do
|
51
|
+
def @event.validate
|
52
|
+
assert_numeric :capacity
|
53
|
+
end
|
54
|
+
|
55
|
+
@event.name = "foo"
|
56
|
+
@event.place = "bar"
|
57
|
+
@event.create
|
58
|
+
|
59
|
+
assert_nil @event.id
|
60
|
+
assert_equal [[:capacity, :not_numeric]], @event.errors
|
61
|
+
end
|
62
|
+
|
63
|
+
should "fail when the value is not numeric" do
|
64
|
+
def @event.validate
|
65
|
+
assert_numeric :capacity
|
66
|
+
end
|
67
|
+
|
68
|
+
@event.name = "foo"
|
69
|
+
@event.place = "bar"
|
70
|
+
@event.capacity = "baz"
|
71
|
+
@event.create
|
72
|
+
|
73
|
+
assert_nil @event.id
|
74
|
+
assert_equal [[:capacity, :not_numeric]], @event.errors
|
75
|
+
end
|
76
|
+
|
77
|
+
should "succeed when the value is numeric" do
|
78
|
+
def @event.validate
|
79
|
+
assert_numeric :capacity
|
80
|
+
end
|
81
|
+
|
82
|
+
@event.name = "foo"
|
83
|
+
@event.place = "bar"
|
84
|
+
@event.capacity = 42
|
85
|
+
@event.create
|
86
|
+
|
87
|
+
assert_not_nil @event.id
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
48
91
|
context "That must have a unique name" do
|
49
92
|
should "fail when the value already exists" do
|
50
93
|
def @event.validate
|
@@ -160,32 +203,14 @@ class ValidationsTest < Test::Unit::TestCase
|
|
160
203
|
should "fail when the attribute is nil" do
|
161
204
|
@target.validate
|
162
205
|
|
163
|
-
assert_equal [[:name, :
|
206
|
+
assert_equal [[:name, :not_present]], @target.errors
|
164
207
|
end
|
165
208
|
|
166
209
|
should "fail when the attribute is empty" do
|
167
210
|
@target.name = ""
|
168
211
|
@target.validate
|
169
212
|
|
170
|
-
assert_equal [[:name, :
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
|
-
context "assert_not_nil" do
|
175
|
-
should "fail when the attribute is nil" do
|
176
|
-
def @target.validate
|
177
|
-
assert_not_nil(:name)
|
178
|
-
end
|
179
|
-
|
180
|
-
@target.validate
|
181
|
-
|
182
|
-
assert_equal [[:name, :nil]], @target.errors
|
183
|
-
|
184
|
-
@target.errors.clear
|
185
|
-
@target.name = ""
|
186
|
-
@target.validate
|
187
|
-
|
188
|
-
assert_equal [], @target.errors
|
213
|
+
assert_equal [[:name, :not_present]], @target.errors
|
189
214
|
end
|
190
215
|
end
|
191
216
|
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: 0.0.
|
4
|
+
version: 0.0.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michel Martens
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2009-
|
13
|
+
date: 2009-07-22 00:00:00 -03:00
|
14
14
|
default_executable:
|
15
15
|
dependencies: []
|
16
16
|
|