ohm_util 0.1
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.
- checksums.yaml +7 -0
- data/.gems +4 -0
- data/.gitignore +3 -0
- data/CHANGELOG.md +408 -0
- data/CONTRIBUTING +19 -0
- data/LICENSE +19 -0
- data/README.md +570 -0
- data/benchmarks/common.rb +28 -0
- data/benchmarks/create.rb +21 -0
- data/benchmarks/delete.rb +13 -0
- data/examples/activity-feed.rb +157 -0
- data/examples/chaining.rb +162 -0
- data/examples/json-hash.rb +75 -0
- data/examples/one-to-many.rb +118 -0
- data/examples/philosophy.rb +137 -0
- data/examples/redis-logging.txt +179 -0
- data/examples/slug.rb +149 -0
- data/examples/tagging.rb +237 -0
- data/lib/lua/delete.lua +72 -0
- data/lib/lua/save.lua +126 -0
- data/lib/ohm_util.rb +116 -0
- data/makefile +9 -0
- data/ohm-util.gemspec +14 -0
- data/test/association.rb +33 -0
- data/test/connection.rb +16 -0
- data/test/core.rb +24 -0
- data/test/counters.rb +67 -0
- data/test/enumerable.rb +79 -0
- data/test/filtering.rb +185 -0
- data/test/hash_key.rb +31 -0
- data/test/helper.rb +23 -0
- data/test/indices.rb +138 -0
- data/test/json.rb +62 -0
- data/test/list.rb +83 -0
- data/test/model.rb +819 -0
- data/test/set.rb +37 -0
- data/test/thread_safety.rb +67 -0
- data/test/to_hash.rb +29 -0
- data/test/uniques.rb +108 -0
- metadata +97 -0
data/makefile
ADDED
data/ohm-util.gemspec
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "ohm_util"
|
3
|
+
s.version = "0.1"
|
4
|
+
s.summary = %{Object-hash mapping library for Redis.}
|
5
|
+
s.description = %Q{Ohm is a library that allows to store an object in Redis, a persistent key-value database. It has very good performance.}
|
6
|
+
s.authors = ["Travis Liu"]
|
7
|
+
s.email = ["travisliu.tw@gmail.com"]
|
8
|
+
s.homepage = "https://github.com/travisliu/ohm-util"
|
9
|
+
s.license = "MIT"
|
10
|
+
|
11
|
+
s.files = `git ls-files`.split("\n")
|
12
|
+
|
13
|
+
s.add_dependency "redic", "~> 1.5.0"
|
14
|
+
end
|
data/test/association.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require_relative "helper"
|
2
|
+
|
3
|
+
class User < Ohm::Model
|
4
|
+
collection :posts, :Post
|
5
|
+
end
|
6
|
+
|
7
|
+
class Post < Ohm::Model
|
8
|
+
reference :user, :User
|
9
|
+
end
|
10
|
+
|
11
|
+
setup do
|
12
|
+
u = User.create
|
13
|
+
p = Post.create(:user => u)
|
14
|
+
|
15
|
+
[u, p]
|
16
|
+
end
|
17
|
+
|
18
|
+
test "basic shake and bake" do |u, p|
|
19
|
+
assert u.posts.include?(p)
|
20
|
+
|
21
|
+
p = Post[p.id]
|
22
|
+
assert_equal u, p.user
|
23
|
+
end
|
24
|
+
|
25
|
+
test "memoization" do |u, p|
|
26
|
+
# This will read the user instance once.
|
27
|
+
p.user
|
28
|
+
assert_equal p.user, p.instance_variable_get(:@_memo)[:user]
|
29
|
+
|
30
|
+
# This will un-memoize the user instance
|
31
|
+
p.user = u
|
32
|
+
assert_equal nil, p.instance_variable_get(:@_memo)[:user]
|
33
|
+
end
|
data/test/connection.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require_relative 'helper'
|
2
|
+
|
3
|
+
test "model inherits Ohm.redis connection by default" do
|
4
|
+
class C < Ohm::Model
|
5
|
+
end
|
6
|
+
|
7
|
+
assert_equal C.redis.url, Ohm.redis.url
|
8
|
+
end
|
9
|
+
|
10
|
+
test "model can define its own connection" do
|
11
|
+
class B < Ohm::Model
|
12
|
+
self.redis = Redic.new("redis://localhost:6379/1")
|
13
|
+
end
|
14
|
+
|
15
|
+
assert B.redis.url != Ohm.redis.url
|
16
|
+
end
|
data/test/core.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require_relative "helper"
|
2
|
+
|
3
|
+
class Event < Ohm::Model
|
4
|
+
attribute :name
|
5
|
+
attribute :location
|
6
|
+
end
|
7
|
+
|
8
|
+
test "assign attributes from the hash" do
|
9
|
+
event = Event.new(name: "Ruby Tuesday")
|
10
|
+
assert_equal event.name, "Ruby Tuesday"
|
11
|
+
end
|
12
|
+
|
13
|
+
test "assign an ID and save the object" do
|
14
|
+
event1 = Event.create(name: "Ruby Tuesday")
|
15
|
+
event2 = Event.create(name: "Ruby Meetup")
|
16
|
+
|
17
|
+
assert_equal "1", event1.id
|
18
|
+
assert_equal "2", event2.id
|
19
|
+
end
|
20
|
+
|
21
|
+
test "save the attributes in UTF8" do
|
22
|
+
event = Event.create(name: "32° Kisei-sen")
|
23
|
+
assert_equal "32° Kisei-sen", Event[event.id].name
|
24
|
+
end
|
data/test/counters.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
require_relative "helper"
|
2
|
+
|
3
|
+
$VERBOSE = false
|
4
|
+
|
5
|
+
class Ad < Ohm::Model
|
6
|
+
end
|
7
|
+
|
8
|
+
test "counters aren't overwritten by competing saves" do
|
9
|
+
Ad.counter :hits
|
10
|
+
|
11
|
+
instance1 = Ad.create
|
12
|
+
instance1.increment :hits
|
13
|
+
|
14
|
+
instance2 = Ad[instance1.id]
|
15
|
+
|
16
|
+
instance1.increment :hits
|
17
|
+
instance1.increment :hits
|
18
|
+
|
19
|
+
instance2.save
|
20
|
+
|
21
|
+
instance1 = Ad[instance1.id]
|
22
|
+
assert_equal 3, instance1.hits
|
23
|
+
end
|
24
|
+
|
25
|
+
test "you can increment counters even when attributes is empty" do
|
26
|
+
Ad.counter :hits
|
27
|
+
|
28
|
+
ad = Ad.create
|
29
|
+
ad = Ad[ad.id]
|
30
|
+
|
31
|
+
ex = nil
|
32
|
+
|
33
|
+
begin
|
34
|
+
ad.increment :hits
|
35
|
+
rescue ArgumentError => e
|
36
|
+
ex = e
|
37
|
+
end
|
38
|
+
|
39
|
+
assert_equal nil, ex
|
40
|
+
end
|
41
|
+
|
42
|
+
test "an attribute gets saved properly" do
|
43
|
+
Ad.attribute :name
|
44
|
+
Ad.counter :hits
|
45
|
+
|
46
|
+
ad = Ad.create(:name => "foo")
|
47
|
+
ad.increment :hits, 10
|
48
|
+
assert_equal 10, ad.hits
|
49
|
+
|
50
|
+
# Now let's just load and save it.
|
51
|
+
ad = Ad[ad.id]
|
52
|
+
ad.save
|
53
|
+
|
54
|
+
# The attributes should remain the same
|
55
|
+
ad = Ad[ad.id]
|
56
|
+
assert_equal "foo", ad.name
|
57
|
+
assert_equal 10, ad.hits
|
58
|
+
|
59
|
+
# If we load and save again while we incr behind the scenes,
|
60
|
+
# the latest counter values should be respected.
|
61
|
+
ad = Ad[ad.id]
|
62
|
+
ad.increment :hits, 5
|
63
|
+
ad.save
|
64
|
+
|
65
|
+
ad = Ad[ad.id]
|
66
|
+
assert_equal 15, ad.hits
|
67
|
+
end
|
data/test/enumerable.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
require_relative "helper"
|
2
|
+
|
3
|
+
scope do
|
4
|
+
class Contact < Ohm::Model
|
5
|
+
attribute :name
|
6
|
+
end
|
7
|
+
|
8
|
+
setup do
|
9
|
+
john = Contact.create(name: "John Doe")
|
10
|
+
jane = Contact.create(name: "Jane Doe")
|
11
|
+
|
12
|
+
[john, jane]
|
13
|
+
end
|
14
|
+
|
15
|
+
test "Set#size doesn't do each" do
|
16
|
+
set = Contact.all
|
17
|
+
|
18
|
+
def set.each
|
19
|
+
raise "Failed"
|
20
|
+
end
|
21
|
+
|
22
|
+
assert_equal 2, set.size
|
23
|
+
end
|
24
|
+
|
25
|
+
test "Set#each as an Enumerator" do |john, jane|
|
26
|
+
enum = Contact.all.each
|
27
|
+
|
28
|
+
enum.each do |c|
|
29
|
+
assert c == john || c == jane
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
test "select" do |john, jane|
|
34
|
+
assert_equal 2, Contact.all.count
|
35
|
+
assert_equal [john], Contact.all.select { |c| c.id == john.id }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
scope do
|
40
|
+
class Comment < Ohm::Model
|
41
|
+
end
|
42
|
+
|
43
|
+
class Post < Ohm::Model
|
44
|
+
list :comments, :Comment
|
45
|
+
end
|
46
|
+
|
47
|
+
setup do
|
48
|
+
c1 = Comment.create
|
49
|
+
c2 = Comment.create
|
50
|
+
|
51
|
+
post = Post.create
|
52
|
+
post.comments.push(c1)
|
53
|
+
post.comments.push(c2)
|
54
|
+
|
55
|
+
[post, c1, c2]
|
56
|
+
end
|
57
|
+
|
58
|
+
test "List#select" do |post, c1, c2|
|
59
|
+
assert_equal [c1], post.comments.select { |comment| comment == c1 }
|
60
|
+
end
|
61
|
+
|
62
|
+
test "List#each as Enumerator" do |post, c1, c2|
|
63
|
+
enum = post.comments.each
|
64
|
+
|
65
|
+
enum.each do |comment|
|
66
|
+
assert comment == c1 || comment == c2
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
test "List#size doesn't do each" do |post, c1, c2|
|
71
|
+
list = post.comments
|
72
|
+
|
73
|
+
def list.each
|
74
|
+
raise "Failed"
|
75
|
+
end
|
76
|
+
|
77
|
+
assert_equal 2, list.size
|
78
|
+
end
|
79
|
+
end
|
data/test/filtering.rb
ADDED
@@ -0,0 +1,185 @@
|
|
1
|
+
require_relative "helper"
|
2
|
+
|
3
|
+
class User < Ohm::Model
|
4
|
+
attribute :fname
|
5
|
+
attribute :lname
|
6
|
+
attribute :status
|
7
|
+
|
8
|
+
index :fname
|
9
|
+
index :lname
|
10
|
+
index :status
|
11
|
+
end
|
12
|
+
|
13
|
+
setup do
|
14
|
+
u1 = User.create(:fname => "John", :lname => "Doe", :status => "active")
|
15
|
+
u2 = User.create(:fname => "Jane", :lname => "Doe", :status => "active")
|
16
|
+
|
17
|
+
[u1, u2]
|
18
|
+
end
|
19
|
+
|
20
|
+
test "findability" do |john, jane|
|
21
|
+
assert_equal 1, User.find(:lname => "Doe", :fname => "John").size
|
22
|
+
assert User.find(:lname => "Doe", :fname => "John").include?(john)
|
23
|
+
|
24
|
+
assert_equal 1, User.find(:lname => "Doe", :fname => "Jane").size
|
25
|
+
assert User.find(:lname => "Doe", :fname => "Jane").include?(jane)
|
26
|
+
end
|
27
|
+
|
28
|
+
test "sets aren't mutable" do |john, jane|
|
29
|
+
assert_raise NoMethodError do
|
30
|
+
User.find(:lname => "Doe").add(john)
|
31
|
+
end
|
32
|
+
|
33
|
+
assert_raise NoMethodError do
|
34
|
+
User.find(:lname => "Doe", :fname => "John").add(john)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
test "#first" do |john, jane|
|
39
|
+
set = User.find(:lname => "Doe", :status => "active")
|
40
|
+
|
41
|
+
assert_equal jane, set.first(:by => "fname", :order => "ALPHA")
|
42
|
+
assert_equal john, set.first(:by => "fname", :order => "ALPHA DESC")
|
43
|
+
|
44
|
+
assert_equal "Jane", set.first(:by => "fname", :order => "ALPHA", :get => "fname")
|
45
|
+
assert_equal "John", set.first(:by => "fname", :order => "ALPHA DESC", :get => "fname")
|
46
|
+
end
|
47
|
+
|
48
|
+
test "#[]" do |john, jane|
|
49
|
+
set = User.find(:lname => "Doe", :status => "active")
|
50
|
+
|
51
|
+
assert_equal john, set[john.id]
|
52
|
+
assert_equal jane, set[jane.id]
|
53
|
+
end
|
54
|
+
|
55
|
+
test "#except" do |john, jane|
|
56
|
+
User.create(:status => "inactive", :lname => "Doe")
|
57
|
+
|
58
|
+
res = User.find(:lname => "Doe").except(:status => "inactive")
|
59
|
+
|
60
|
+
assert_equal 2, res.size
|
61
|
+
assert res.include?(john)
|
62
|
+
assert res.include?(jane)
|
63
|
+
|
64
|
+
res = User.all.except(:status => "inactive")
|
65
|
+
|
66
|
+
assert_equal 2, res.size
|
67
|
+
assert res.include?(john)
|
68
|
+
assert res.include?(jane)
|
69
|
+
end
|
70
|
+
|
71
|
+
test "#except unions keys when passing an array" do |john, jane|
|
72
|
+
expected = User.create(:fname => "Jean", :status => "inactive")
|
73
|
+
|
74
|
+
res = User.find(:status => "inactive").except(:fname => [john.fname, jane.fname])
|
75
|
+
|
76
|
+
assert_equal 1, res.size
|
77
|
+
assert res.include?(expected)
|
78
|
+
|
79
|
+
res = User.all.except(:fname => [john.fname, jane.fname])
|
80
|
+
|
81
|
+
assert_equal 1, res.size
|
82
|
+
assert res.include?(expected)
|
83
|
+
end
|
84
|
+
|
85
|
+
test "indices bug related to a nil attribute" do |john, jane|
|
86
|
+
# First we create a record with a nil attribute
|
87
|
+
out = User.create(:status => nil, :lname => "Doe")
|
88
|
+
|
89
|
+
# Then, we update the old nil attribute to a different
|
90
|
+
# non-nil, value.
|
91
|
+
out.update(status: "inactive")
|
92
|
+
|
93
|
+
# At this point, the index for the nil attribute should
|
94
|
+
# have been cleared.
|
95
|
+
assert_equal 0, User.redis.call("SCARD", "User:indices:status:")
|
96
|
+
end
|
97
|
+
|
98
|
+
test "#union" do |john, jane|
|
99
|
+
User.create(:status => "super", :lname => "Doe")
|
100
|
+
included = User.create(:status => "inactive", :lname => "Doe")
|
101
|
+
|
102
|
+
res = User.find(:status => "active").union(:status => "inactive")
|
103
|
+
|
104
|
+
assert_equal 3, res.size
|
105
|
+
assert res.include?(john)
|
106
|
+
assert res.include?(jane)
|
107
|
+
assert res.include?(included)
|
108
|
+
|
109
|
+
res = User.find(:status => "active").union(:status => "inactive").find(:lname => "Doe")
|
110
|
+
|
111
|
+
assert res.any? { |e| e.status == "inactive" }
|
112
|
+
end
|
113
|
+
|
114
|
+
test "#combine" do |john, jane|
|
115
|
+
res = User.find(:status => "active").combine(fname: ["John", "Jane"])
|
116
|
+
|
117
|
+
assert_equal 2, res.size
|
118
|
+
assert res.include?(john)
|
119
|
+
assert res.include?(jane)
|
120
|
+
end
|
121
|
+
|
122
|
+
# book author thing via @myobie
|
123
|
+
scope do
|
124
|
+
class Book < Ohm::Model
|
125
|
+
collection :authors, :Author
|
126
|
+
end
|
127
|
+
|
128
|
+
class Author < Ohm::Model
|
129
|
+
reference :book, :Book
|
130
|
+
|
131
|
+
attribute :mood
|
132
|
+
index :mood
|
133
|
+
end
|
134
|
+
|
135
|
+
setup do
|
136
|
+
book1 = Book.create
|
137
|
+
book2 = Book.create
|
138
|
+
|
139
|
+
Author.create(:book => book1, :mood => "happy")
|
140
|
+
Author.create(:book => book1, :mood => "sad")
|
141
|
+
Author.create(:book => book2, :mood => "sad")
|
142
|
+
|
143
|
+
[book1, book2]
|
144
|
+
end
|
145
|
+
|
146
|
+
test "straight up intersection + union" do |book1, book2|
|
147
|
+
result = book1.authors.find(:mood => "happy").
|
148
|
+
union(:book_id => book1.id, :mood => "sad")
|
149
|
+
|
150
|
+
assert_equal 2, result.size
|
151
|
+
end
|
152
|
+
|
153
|
+
test "appending an empty set via union" do |book1, book2|
|
154
|
+
res = Author.find(:book_id => book1.id, :mood => "happy").
|
155
|
+
union(:book_id => book2.id, :mood => "sad").
|
156
|
+
union(:book_id => book2.id, :mood => "happy")
|
157
|
+
|
158
|
+
assert_equal 2, res.size
|
159
|
+
end
|
160
|
+
|
161
|
+
test "revert by applying the original intersection" do |book1, book2|
|
162
|
+
res = Author.find(:book_id => book1.id, :mood => "happy").
|
163
|
+
union(:book_id => book2.id, :mood => "sad").
|
164
|
+
find(:book_id => book1.id, :mood => "happy")
|
165
|
+
|
166
|
+
assert_equal 1, res.size
|
167
|
+
end
|
168
|
+
|
169
|
+
test "remove original intersection by doing diff" do |book1, book2|
|
170
|
+
res = Author.find(:book_id => book1.id, :mood => "happy").
|
171
|
+
union(:book_id => book2.id, :mood => "sad").
|
172
|
+
except(:book_id => book1.id, :mood => "happy")
|
173
|
+
|
174
|
+
assert_equal 1, res.size
|
175
|
+
assert res.map(&:mood).include?("sad")
|
176
|
+
assert res.map(&:book_id).include?(book2.id)
|
177
|
+
end
|
178
|
+
|
179
|
+
test "@myobie usecase" do |book1, book2|
|
180
|
+
res = book1.authors.find(:mood => "happy").
|
181
|
+
union(:mood => "sad", :book_id => book1.id)
|
182
|
+
|
183
|
+
assert_equal 2, res.size
|
184
|
+
end
|
185
|
+
end
|
data/test/hash_key.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require_relative "helper"
|
2
|
+
|
3
|
+
class Tag < Ohm::Model
|
4
|
+
attribute :name
|
5
|
+
end
|
6
|
+
|
7
|
+
test "using a new record as a hash key" do
|
8
|
+
tag = Tag.new
|
9
|
+
hash = { tag => "Ruby" }
|
10
|
+
|
11
|
+
assert "Ruby" == hash[tag]
|
12
|
+
assert hash[Tag.new].nil?
|
13
|
+
end
|
14
|
+
|
15
|
+
test "on a persisted model" do
|
16
|
+
tag = Tag.create(:name => "Ruby")
|
17
|
+
|
18
|
+
assert "Ruby" == { tag => "Ruby" }[tag]
|
19
|
+
end
|
20
|
+
|
21
|
+
test "on a reloaded model" do
|
22
|
+
tag = Tag.create(:name => "Ruby")
|
23
|
+
hash = { tag => "Ruby" }
|
24
|
+
|
25
|
+
tag = Tag[tag.id]
|
26
|
+
assert "Ruby" == hash[tag]
|
27
|
+
end
|
28
|
+
|
29
|
+
test "on attributes class method" do
|
30
|
+
assert [:name] == Tag.attributes
|
31
|
+
end
|