ohm 0.0.2 → 0.0.3
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/lib/ohm.rb +113 -59
- data/lib/ohm/validations.rb +38 -0
- data/test/benchmarks.rb +21 -1
- data/test/db/dump.rdb +0 -0
- data/test/db/redis.pid +1 -1
- data/test/indices_test.rb +36 -0
- data/test/model_test.rb +17 -0
- data/test/validations_test.rb +40 -0
- metadata +4 -5
- data/test/references_test.rb +0 -29
data/lib/ohm.rb
CHANGED
@@ -1,48 +1,14 @@
|
|
1
1
|
require "rubygems"
|
2
2
|
require "redis"
|
3
|
+
require File.join(File.dirname(__FILE__), "ohm", "validations")
|
3
4
|
|
4
5
|
module Ohm
|
5
|
-
|
6
|
-
|
7
|
-
errors.clear
|
8
|
-
validate
|
9
|
-
errors.empty?
|
10
|
-
end
|
11
|
-
|
12
|
-
def validate
|
13
|
-
end
|
14
|
-
|
15
|
-
def errors
|
16
|
-
@errors ||= []
|
17
|
-
end
|
18
|
-
|
19
|
-
private
|
20
|
-
|
21
|
-
def assert_format(att, format)
|
22
|
-
if assert_present(att)
|
23
|
-
assert attribute(att).match(format), [att, :format]
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def assert_present(att)
|
28
|
-
if assert_not_nil(att)
|
29
|
-
assert attribute(att).any?, [att, :empty]
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def assert_not_nil(att)
|
34
|
-
assert attribute(att), [att, :nil]
|
35
|
-
end
|
36
|
-
|
37
|
-
def assert(value, error)
|
38
|
-
value or errors.push(error) && false
|
39
|
-
end
|
40
|
-
|
41
|
-
def attribute(att)
|
42
|
-
instance_variable_get("@#{att}")
|
43
|
-
end
|
6
|
+
def key(*args)
|
7
|
+
args.join(":")
|
44
8
|
end
|
45
9
|
|
10
|
+
module_function :key
|
11
|
+
|
46
12
|
module Attributes
|
47
13
|
class Collection < Array
|
48
14
|
attr_accessor :key, :db
|
@@ -84,18 +50,34 @@ module Ohm
|
|
84
50
|
end
|
85
51
|
|
86
52
|
class Model
|
53
|
+
module Validations
|
54
|
+
include Ohm::Validations
|
55
|
+
|
56
|
+
def assert_unique(attrs)
|
57
|
+
index_key = index_key_for(attrs, read_locals(attrs))
|
58
|
+
assert(db.set_count(index_key).zero? || db.set_member?(index_key, id), [attrs, :not_unique])
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
87
62
|
include Validations
|
88
63
|
|
89
64
|
ModelIsNew = Class.new(StandardError)
|
90
65
|
|
91
66
|
@@attributes = Hash.new { |hash, key| hash[key] = [] }
|
92
67
|
@@collections = Hash.new { |hash, key| hash[key] = [] }
|
68
|
+
@@indices = Hash.new { |hash, key| hash[key] = [] }
|
93
69
|
|
94
70
|
attr_accessor :id
|
95
71
|
|
96
72
|
def self.attribute(name)
|
97
|
-
|
98
|
-
|
73
|
+
define_method(name) do
|
74
|
+
read_local(name)
|
75
|
+
end
|
76
|
+
|
77
|
+
define_method(:"#{name}=") do |value|
|
78
|
+
write_local(name, value)
|
79
|
+
end
|
80
|
+
|
99
81
|
attributes << name
|
100
82
|
end
|
101
83
|
|
@@ -109,13 +91,8 @@ module Ohm
|
|
109
91
|
collections << name
|
110
92
|
end
|
111
93
|
|
112
|
-
|
113
|
-
|
114
|
-
class_eval <<-EOS
|
115
|
-
def #{name}
|
116
|
-
@#{name} ||= db[key("#{name}")]
|
117
|
-
end
|
118
|
-
EOS
|
94
|
+
def self.index(attrs)
|
95
|
+
indices << attrs
|
119
96
|
end
|
120
97
|
|
121
98
|
def self.attr_list_reader(name)
|
@@ -139,9 +116,7 @@ module Ohm
|
|
139
116
|
end
|
140
117
|
|
141
118
|
def self.all
|
142
|
-
filter(:all)
|
143
|
-
new(:id => id)
|
144
|
-
end
|
119
|
+
filter(:all)
|
145
120
|
end
|
146
121
|
|
147
122
|
def self.attributes
|
@@ -152,11 +127,24 @@ module Ohm
|
|
152
127
|
@@collections[self]
|
153
128
|
end
|
154
129
|
|
130
|
+
def self.indices
|
131
|
+
@@indices[self]
|
132
|
+
end
|
133
|
+
|
155
134
|
def self.create(*args)
|
156
135
|
new(*args).create
|
157
136
|
end
|
158
137
|
|
138
|
+
# TODO Add a method that receives several arguments and returns a
|
139
|
+
# string with the values separated by colons.
|
140
|
+
def self.find(attribute, value)
|
141
|
+
# filter("#{attribute}:#{value}")
|
142
|
+
filter(Ohm.key(attribute, value))
|
143
|
+
end
|
144
|
+
|
159
145
|
def initialize(attrs = {})
|
146
|
+
@_attributes = Hash.new {|hash,key| hash[key] = read_remote(key) }
|
147
|
+
|
160
148
|
attrs.each do |key, value|
|
161
149
|
send(:"#{key}=", value)
|
162
150
|
end
|
@@ -166,15 +154,18 @@ module Ohm
|
|
166
154
|
return unless valid?
|
167
155
|
initialize_id
|
168
156
|
create_model_membership
|
157
|
+
add_to_indices
|
169
158
|
save!
|
170
159
|
end
|
171
160
|
|
172
161
|
def save
|
173
162
|
return unless valid?
|
163
|
+
update_indices
|
174
164
|
save!
|
175
165
|
end
|
176
166
|
|
177
167
|
def delete
|
168
|
+
delete_from_indices
|
178
169
|
delete_attributes(collections)
|
179
170
|
delete_attributes(attributes)
|
180
171
|
delete_model_membership
|
@@ -189,6 +180,23 @@ module Ohm
|
|
189
180
|
self.class.collections
|
190
181
|
end
|
191
182
|
|
183
|
+
def indices
|
184
|
+
self.class.indices
|
185
|
+
end
|
186
|
+
|
187
|
+
def ==(other)
|
188
|
+
other.key == key
|
189
|
+
rescue ModelIsNew
|
190
|
+
false
|
191
|
+
end
|
192
|
+
|
193
|
+
protected
|
194
|
+
|
195
|
+
def key(*args)
|
196
|
+
raise ModelIsNew unless id
|
197
|
+
self.class.key(id, *args)
|
198
|
+
end
|
199
|
+
|
192
200
|
private
|
193
201
|
|
194
202
|
def self.db
|
@@ -196,11 +204,13 @@ module Ohm
|
|
196
204
|
end
|
197
205
|
|
198
206
|
def self.key(*args)
|
199
|
-
args.unshift(self)
|
207
|
+
Ohm.key(*args.unshift(self))
|
200
208
|
end
|
201
209
|
|
202
210
|
def self.filter(name)
|
203
|
-
db.set_members(key(name))
|
211
|
+
db.set_members(key(name)).map do |id|
|
212
|
+
new(:id => id)
|
213
|
+
end
|
204
214
|
end
|
205
215
|
|
206
216
|
def self.exists?(id)
|
@@ -215,11 +225,6 @@ module Ohm
|
|
215
225
|
self.class.db
|
216
226
|
end
|
217
227
|
|
218
|
-
def key(*args)
|
219
|
-
raise ModelIsNew unless id
|
220
|
-
self.class.key(id, *args)
|
221
|
-
end
|
222
|
-
|
223
228
|
def delete_attributes(atts)
|
224
229
|
atts.each do |att|
|
225
230
|
db.delete(key(att))
|
@@ -235,8 +240,57 @@ module Ohm
|
|
235
240
|
end
|
236
241
|
|
237
242
|
def save!
|
238
|
-
attributes.each { |att|
|
243
|
+
attributes.each { |att| write_remote(att, send(att)) }
|
239
244
|
self
|
240
245
|
end
|
246
|
+
|
247
|
+
def update_indices
|
248
|
+
delete_from_indices
|
249
|
+
add_to_indices
|
250
|
+
end
|
251
|
+
|
252
|
+
def add_to_indices
|
253
|
+
indices.each do |attrs|
|
254
|
+
db.set_add(index_key_for(attrs, read_locals(attrs)), id)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
def delete_from_indices
|
259
|
+
indices.each do |attrs|
|
260
|
+
db.set_delete(index_key_for(attrs, read_remotes(attrs)), id)
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
def read_local(att)
|
265
|
+
@_attributes[att]
|
266
|
+
end
|
267
|
+
|
268
|
+
def write_local(att, value)
|
269
|
+
@_attributes[att] = value
|
270
|
+
end
|
271
|
+
|
272
|
+
def read_remote(att)
|
273
|
+
id && db[key(att)]
|
274
|
+
end
|
275
|
+
|
276
|
+
def write_remote(att, value)
|
277
|
+
db[key(att)] = value
|
278
|
+
end
|
279
|
+
|
280
|
+
def read_locals(attrs)
|
281
|
+
attrs.map do |att|
|
282
|
+
read_local(att)
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
def read_remotes(attrs)
|
287
|
+
attrs.map do |att|
|
288
|
+
read_remote(att)
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
def index_key_for(attrs, values)
|
293
|
+
self.class.key *(attrs + values)
|
294
|
+
end
|
241
295
|
end
|
242
296
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Ohm
|
2
|
+
module Validations
|
3
|
+
def valid?
|
4
|
+
errors.clear
|
5
|
+
validate
|
6
|
+
errors.empty?
|
7
|
+
end
|
8
|
+
|
9
|
+
def validate
|
10
|
+
end
|
11
|
+
|
12
|
+
def errors
|
13
|
+
@errors ||= []
|
14
|
+
end
|
15
|
+
|
16
|
+
protected
|
17
|
+
|
18
|
+
def assert_format(att, format)
|
19
|
+
if assert_present(att)
|
20
|
+
assert send(att).match(format), [att, :format]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def assert_present(att)
|
25
|
+
if assert_not_nil(att)
|
26
|
+
assert send(att).any?, [att, :empty]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def assert_not_nil(att)
|
31
|
+
assert send(att), [att, :nil]
|
32
|
+
end
|
33
|
+
|
34
|
+
def assert(value, error)
|
35
|
+
value or errors.push(error) && false
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/test/benchmarks.rb
CHANGED
@@ -29,4 +29,24 @@ benchmark "ruby array push" do
|
|
29
29
|
array.push(1)
|
30
30
|
end
|
31
31
|
|
32
|
-
|
32
|
+
$redis.set_add("bar", 1)
|
33
|
+
$redis.set_add("bar", 2)
|
34
|
+
|
35
|
+
benchmark "retrieve a set of two members" do
|
36
|
+
$redis.set_members("bar")
|
37
|
+
end
|
38
|
+
|
39
|
+
benchmark "retrieve membership status and set count" do
|
40
|
+
$redis.set_count("bar")
|
41
|
+
$redis.set_member?("bar", "1")
|
42
|
+
end
|
43
|
+
|
44
|
+
benchmark "retrieve set count" do
|
45
|
+
$redis.set_count("bar").zero?
|
46
|
+
end
|
47
|
+
|
48
|
+
benchmark "retrieve membership status" do
|
49
|
+
$redis.set_member?("bar", "1")
|
50
|
+
end
|
51
|
+
|
52
|
+
run 20_000
|
data/test/db/dump.rdb
CHANGED
Binary file
|
data/test/db/redis.pid
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
26947
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
class IndicesTest < Test::Unit::TestCase
|
4
|
+
class User < Ohm::Model
|
5
|
+
attribute :email
|
6
|
+
|
7
|
+
index [:email]
|
8
|
+
end
|
9
|
+
|
10
|
+
context "A model with an indexed attribute" do
|
11
|
+
setup do
|
12
|
+
$redis.flush_db
|
13
|
+
|
14
|
+
@user1 = User.create(:email => "foo")
|
15
|
+
@user2 = User.create(:email => "bar")
|
16
|
+
end
|
17
|
+
|
18
|
+
should "be able to find by the given attribute" do
|
19
|
+
assert_equal [@user1], User.find(:email, "foo").to_a
|
20
|
+
end
|
21
|
+
|
22
|
+
should "update indices when changing attribute values" do
|
23
|
+
@user1.email = "baz"
|
24
|
+
@user1.save
|
25
|
+
|
26
|
+
assert_equal [], User.find(:email, "foo").to_a
|
27
|
+
assert_equal [@user1], User.find(:email, "baz").to_a
|
28
|
+
end
|
29
|
+
|
30
|
+
should "remove from the index after deleting" do
|
31
|
+
@user2.delete
|
32
|
+
|
33
|
+
assert_equal [], User.find(:email, "bar").to_a
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/test/model_test.rb
CHANGED
@@ -241,4 +241,21 @@ class TestRedis < Test::Unit::TestCase
|
|
241
241
|
assert_equal ["1", "2", "3"], Post[@post.id].comments
|
242
242
|
end
|
243
243
|
end
|
244
|
+
|
245
|
+
context "Comparison" do
|
246
|
+
setup do
|
247
|
+
@user = User.create(:email => "foo")
|
248
|
+
end
|
249
|
+
|
250
|
+
should "be comparable to other instances" do
|
251
|
+
assert_equal @user, User[@user.id]
|
252
|
+
|
253
|
+
assert_not_equal @user, User.create
|
254
|
+
assert_not_equal User.new, User.new
|
255
|
+
end
|
256
|
+
|
257
|
+
should "not be comparable to instances of other models" do
|
258
|
+
assert_not_equal @user, Event.create(:name => "Ruby Tuesday")
|
259
|
+
end
|
260
|
+
end
|
244
261
|
end
|
data/test/validations_test.rb
CHANGED
@@ -3,6 +3,10 @@ require File.dirname(__FILE__) + '/test_helper'
|
|
3
3
|
class ValidationsTest < Test::Unit::TestCase
|
4
4
|
class Event < Ohm::Model
|
5
5
|
attribute :name
|
6
|
+
attribute :place
|
7
|
+
|
8
|
+
index [:name]
|
9
|
+
index [:name, :place]
|
6
10
|
|
7
11
|
def validate
|
8
12
|
assert_format(:name, /^\w+$/)
|
@@ -40,6 +44,42 @@ class ValidationsTest < Test::Unit::TestCase
|
|
40
44
|
end
|
41
45
|
end
|
42
46
|
end
|
47
|
+
|
48
|
+
context "That must have a unique name" do
|
49
|
+
should "fail when the value already exists" do
|
50
|
+
def @event.validate
|
51
|
+
assert_unique [:name]
|
52
|
+
end
|
53
|
+
|
54
|
+
Event.create(:name => "foo")
|
55
|
+
@event.name = "foo"
|
56
|
+
@event.create
|
57
|
+
|
58
|
+
assert_nil @event.id
|
59
|
+
assert_equal [[[:name], :not_unique]], @event.errors
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context "That must have a unique name scoped by place" do
|
64
|
+
should "fail when the value already exists" do
|
65
|
+
def @event.validate
|
66
|
+
assert_unique [:name, :place]
|
67
|
+
end
|
68
|
+
|
69
|
+
Event.create(:name => "foo", :place => "bar")
|
70
|
+
@event.name = "foo"
|
71
|
+
@event.place = "bar"
|
72
|
+
@event.create
|
73
|
+
|
74
|
+
assert_nil @event.id
|
75
|
+
assert_equal [[[:name, :place], :not_unique]], @event.errors
|
76
|
+
|
77
|
+
@event.place = "baz"
|
78
|
+
@event.create
|
79
|
+
|
80
|
+
assert @event.valid?
|
81
|
+
end
|
82
|
+
end
|
43
83
|
end
|
44
84
|
|
45
85
|
context "An existing model with a valid name" do
|
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.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michel Martens, Damian Janowski
|
@@ -31,6 +31,7 @@ extensions: []
|
|
31
31
|
extra_rdoc_files: []
|
32
32
|
|
33
33
|
files:
|
34
|
+
- lib/ohm/validations.rb
|
34
35
|
- lib/ohm.rb
|
35
36
|
- README.markdown
|
36
37
|
- LICENSE
|
@@ -39,15 +40,13 @@ files:
|
|
39
40
|
- test/benchmarks.rb
|
40
41
|
- test/db/dump.rdb
|
41
42
|
- test/db/redis.pid
|
43
|
+
- test/indices_test.rb
|
42
44
|
- test/model_test.rb
|
43
|
-
- test/references_test.rb
|
44
45
|
- test/test.conf
|
45
46
|
- test/test_helper.rb
|
46
47
|
- test/validations_test.rb
|
47
48
|
has_rdoc: false
|
48
49
|
homepage: http://github.com/soveran/ohm
|
49
|
-
licenses: []
|
50
|
-
|
51
50
|
post_install_message:
|
52
51
|
rdoc_options: []
|
53
52
|
|
@@ -68,7 +67,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
68
67
|
requirements: []
|
69
68
|
|
70
69
|
rubyforge_project:
|
71
|
-
rubygems_version: 1.3.
|
70
|
+
rubygems_version: 1.3.1
|
72
71
|
signing_key:
|
73
72
|
specification_version: 2
|
74
73
|
summary: Object-hash mapping library for Redis.
|
data/test/references_test.rb
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
require File.dirname(__FILE__) + '/test_helper'
|
2
|
-
|
3
|
-
module Ohm
|
4
|
-
class Reference
|
5
|
-
attr :model, :id
|
6
|
-
|
7
|
-
def initialize(model, id)
|
8
|
-
end
|
9
|
-
|
10
|
-
def self.marshal_load(*args)
|
11
|
-
1
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
class ReferencesTest < Test::Unit::TestCase
|
17
|
-
class Referee < Ohm::Model
|
18
|
-
end
|
19
|
-
|
20
|
-
test "dump" do
|
21
|
-
model = Referee.new.create
|
22
|
-
|
23
|
-
ref = Ohm::Reference.new(Referee, 1)
|
24
|
-
|
25
|
-
$redis["foo"] = ref
|
26
|
-
|
27
|
-
#assert_equal Referee[1], $redis["foo"]
|
28
|
-
end
|
29
|
-
end
|