ohm 1.4.0 → 2.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.
- checksums.yaml +4 -4
- data/.gems +4 -4
- data/CHANGELOG +0 -8
- data/README.md +4 -6
- data/examples/chaining.rb +52 -56
- data/lib/ohm.rb +145 -358
- data/lib/ohm/command.rb +14 -10
- data/lib/ohm/lua/delete.lua +51 -0
- data/lib/ohm/lua/save.lua +104 -0
- data/ohm.gemspec +8 -6
- data/test/command.rb +22 -24
- data/test/connection.rb +5 -89
- data/test/filtering.rb +140 -8
- data/test/helper.rb +3 -1
- data/test/indices.rb +1 -1
- data/test/issue-52.rb +2 -2
- data/test/json.rb +0 -16
- data/test/list.rb +1 -1
- data/test/model.rb +70 -194
- data/test/uniques.rb +2 -32
- metadata +23 -26
- data/lib/ohm/transaction.rb +0 -133
- data/test/1.8.6_test.rb +0 -25
- data/test/pipeline-performance.rb +0 -67
- data/test/transactions.rb +0 -240
- data/test/validations.rb +0 -195
data/test/uniques.rb
CHANGED
@@ -64,7 +64,7 @@ test "removes the previous index when changing" do
|
|
64
64
|
u.update(:email => "d@d.com")
|
65
65
|
|
66
66
|
assert_equal nil, User.with(:email, "c@c.com")
|
67
|
-
assert_equal nil, User.key[:uniques][:email]
|
67
|
+
assert_equal nil, User.redis.call("HGET", User.key[:uniques][:email], "c@c.com")
|
68
68
|
assert_equal u, User.with(:email, "d@d.com")
|
69
69
|
end
|
70
70
|
|
@@ -72,7 +72,7 @@ test "removes the previous index when deleting" do |u|
|
|
72
72
|
u.delete
|
73
73
|
|
74
74
|
assert_equal nil, User.with(:email, "a@a.com")
|
75
|
-
assert_equal nil, User.key[:uniques][:email]
|
75
|
+
assert_equal nil, User.redis.call("HGET", User.key[:uniques][:email], "a@a.com")
|
76
76
|
end
|
77
77
|
|
78
78
|
test "unique virtual attribute" do
|
@@ -93,33 +93,3 @@ test "unique virtual attribute" do
|
|
93
93
|
User.create(:email => "baz@yahoo.com")
|
94
94
|
end
|
95
95
|
end
|
96
|
-
|
97
|
-
test "assert_unique" do |u|
|
98
|
-
class User
|
99
|
-
def assert_unique(att)
|
100
|
-
result = self.class.with(att, send(att))
|
101
|
-
assert((result.nil? || result.eql?(self)), [att, :not_unique])
|
102
|
-
end
|
103
|
-
|
104
|
-
def validate
|
105
|
-
assert_unique :email
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
# There's one user with email "a@a.com".
|
110
|
-
user = User.new(:email => "a@a.com")
|
111
|
-
|
112
|
-
# A new user with a conflicting attribute.
|
113
|
-
assert_equal true, user.new?
|
114
|
-
assert_equal false, user.valid?
|
115
|
-
assert_equal [:not_unique], user.errors[:email]
|
116
|
-
|
117
|
-
user.email = "b@b.com"
|
118
|
-
user.save
|
119
|
-
user.email = "a@a.com"
|
120
|
-
|
121
|
-
# An existing user with a conflicting attribute.
|
122
|
-
assert_equal false, user.new?
|
123
|
-
assert_equal false, user.valid?
|
124
|
-
assert_equal [:not_unique], user.errors[:email]
|
125
|
-
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:
|
4
|
+
version: 2.0.0.alpha1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michel Martens
|
@@ -10,10 +10,10 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2013-04-29 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
|
-
name:
|
16
|
+
name: redic
|
17
17
|
requirement: !ruby/object:Gem::Requirement
|
18
18
|
requirements:
|
19
19
|
- - '>='
|
@@ -27,47 +27,47 @@ dependencies:
|
|
27
27
|
- !ruby/object:Gem::Version
|
28
28
|
version: '0'
|
29
29
|
- !ruby/object:Gem::Dependency
|
30
|
-
name:
|
30
|
+
name: nido
|
31
31
|
requirement: !ruby/object:Gem::Requirement
|
32
32
|
requirements:
|
33
|
-
- -
|
33
|
+
- - '>='
|
34
34
|
- !ruby/object:Gem::Version
|
35
|
-
version: '
|
35
|
+
version: '0'
|
36
36
|
type: :runtime
|
37
37
|
prerelease: false
|
38
38
|
version_requirements: !ruby/object:Gem::Requirement
|
39
39
|
requirements:
|
40
|
-
- -
|
40
|
+
- - '>='
|
41
41
|
- !ruby/object:Gem::Version
|
42
|
-
version: '
|
42
|
+
version: '0'
|
43
43
|
- !ruby/object:Gem::Dependency
|
44
|
-
name:
|
44
|
+
name: msgpack
|
45
45
|
requirement: !ruby/object:Gem::Requirement
|
46
46
|
requirements:
|
47
|
-
- -
|
47
|
+
- - '>='
|
48
48
|
- !ruby/object:Gem::Version
|
49
|
-
version: 0
|
49
|
+
version: '0'
|
50
50
|
type: :runtime
|
51
51
|
prerelease: false
|
52
52
|
version_requirements: !ruby/object:Gem::Requirement
|
53
53
|
requirements:
|
54
|
-
- -
|
54
|
+
- - '>='
|
55
55
|
- !ruby/object:Gem::Version
|
56
|
-
version: 0
|
56
|
+
version: '0'
|
57
57
|
- !ruby/object:Gem::Dependency
|
58
58
|
name: cutest
|
59
59
|
requirement: !ruby/object:Gem::Requirement
|
60
60
|
requirements:
|
61
|
-
- -
|
61
|
+
- - '>='
|
62
62
|
- !ruby/object:Gem::Version
|
63
|
-
version: '
|
63
|
+
version: '0'
|
64
64
|
type: :development
|
65
65
|
prerelease: false
|
66
66
|
version_requirements: !ruby/object:Gem::Requirement
|
67
67
|
requirements:
|
68
|
-
- -
|
68
|
+
- - '>='
|
69
69
|
- !ruby/object:Gem::Version
|
70
|
-
version: '
|
70
|
+
version: '0'
|
71
71
|
description: Ohm is a library that allows to store an object in Redis, a persistent
|
72
72
|
key-value database. It includes an extensible list of validations and has very good
|
73
73
|
performance.
|
@@ -99,9 +99,9 @@ files:
|
|
99
99
|
- lib/ohm.rb
|
100
100
|
- lib/ohm/command.rb
|
101
101
|
- lib/ohm/json.rb
|
102
|
-
- lib/ohm/
|
102
|
+
- lib/ohm/lua/delete.lua
|
103
|
+
- lib/ohm/lua/save.lua
|
103
104
|
- ohm.gemspec
|
104
|
-
- test/1.8.6_test.rb
|
105
105
|
- test/association.rb
|
106
106
|
- test/command.rb
|
107
107
|
- test/connection.rb
|
@@ -118,12 +118,9 @@ files:
|
|
118
118
|
- test/json.rb
|
119
119
|
- test/list.rb
|
120
120
|
- test/model.rb
|
121
|
-
- test/pipeline-performance.rb
|
122
121
|
- test/test.conf
|
123
|
-
- test/transactions.rb
|
124
122
|
- test/uniques.rb
|
125
|
-
|
126
|
-
homepage: http://soveran.github.com/ohm/
|
123
|
+
homepage: http://soveran.github.io/ohm/
|
127
124
|
licenses:
|
128
125
|
- MIT
|
129
126
|
metadata: {}
|
@@ -138,12 +135,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
138
135
|
version: '0'
|
139
136
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
140
137
|
requirements:
|
141
|
-
- - '
|
138
|
+
- - '>'
|
142
139
|
- !ruby/object:Gem::Version
|
143
|
-
version:
|
140
|
+
version: 1.3.1
|
144
141
|
requirements: []
|
145
142
|
rubyforge_project: ohm
|
146
|
-
rubygems_version: 2.0.
|
143
|
+
rubygems_version: 2.0.3
|
147
144
|
signing_key:
|
148
145
|
specification_version: 4
|
149
146
|
summary: Object-hash mapping library for Redis.
|
data/lib/ohm/transaction.rb
DELETED
@@ -1,133 +0,0 @@
|
|
1
|
-
module Ohm
|
2
|
-
|
3
|
-
# Transactions in Ohm are designed to be composable and atomic. They use
|
4
|
-
# Redis WATCH/MULTI/EXEC to perform the comands sequentially but in a single
|
5
|
-
# step.
|
6
|
-
#
|
7
|
-
# @example
|
8
|
-
#
|
9
|
-
# redis = Ohm.redis
|
10
|
-
#
|
11
|
-
# t1 = Ohm::Transaction.new do |t|
|
12
|
-
# s = nil
|
13
|
-
#
|
14
|
-
# t.watch("foo")
|
15
|
-
#
|
16
|
-
# t.read do
|
17
|
-
# s = redis.type("foo")
|
18
|
-
# end
|
19
|
-
#
|
20
|
-
# t.write do
|
21
|
-
# redis.set("foo", s)
|
22
|
-
# end
|
23
|
-
# end
|
24
|
-
#
|
25
|
-
# t2 = Ohm::Transaction.new do |t|
|
26
|
-
# t.watch("foo")
|
27
|
-
#
|
28
|
-
# t.write do
|
29
|
-
# redis.set("foo", "bar")
|
30
|
-
# end
|
31
|
-
# end
|
32
|
-
#
|
33
|
-
# # Compose transactions by passing them to Ohm::Transaction.new.
|
34
|
-
# t3 = Ohm::Transaction.new(t1, t2)
|
35
|
-
# t3.commit(redis)
|
36
|
-
#
|
37
|
-
# # Compose transactions by appending them.
|
38
|
-
# t1.append(t2)
|
39
|
-
# t1.commit(redis)
|
40
|
-
#
|
41
|
-
# @see http://redis.io/topics/transactions Transactions in Redis.
|
42
|
-
class Transaction
|
43
|
-
class Store
|
44
|
-
class EntryAlreadyExistsError < RuntimeError
|
45
|
-
end
|
46
|
-
|
47
|
-
class NoEntryError < RuntimeError
|
48
|
-
end
|
49
|
-
|
50
|
-
def initialize
|
51
|
-
@dict = Hash.new
|
52
|
-
end
|
53
|
-
|
54
|
-
def [](key)
|
55
|
-
raise NoEntryError unless @dict.member?(key)
|
56
|
-
|
57
|
-
@dict[key]
|
58
|
-
end
|
59
|
-
|
60
|
-
def []=(key, value)
|
61
|
-
raise EntryAlreadyExistsError if @dict.member?(key)
|
62
|
-
|
63
|
-
@dict[key] = value
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
attr :phase
|
68
|
-
|
69
|
-
def initialize
|
70
|
-
@phase = Hash.new { |h, k| h[k] = Array.new }
|
71
|
-
|
72
|
-
yield self if block_given?
|
73
|
-
end
|
74
|
-
|
75
|
-
def append(t)
|
76
|
-
t.phase.each do |key, values|
|
77
|
-
phase[key].concat(values - phase[key])
|
78
|
-
end
|
79
|
-
|
80
|
-
self
|
81
|
-
end
|
82
|
-
|
83
|
-
def watch(*keys)
|
84
|
-
phase[:watch].concat(keys - phase[:watch])
|
85
|
-
end
|
86
|
-
|
87
|
-
def read(&block)
|
88
|
-
phase[:read] << block
|
89
|
-
end
|
90
|
-
|
91
|
-
def write(&block)
|
92
|
-
phase[:write] << block
|
93
|
-
end
|
94
|
-
|
95
|
-
def before(&block)
|
96
|
-
phase[:before] << block
|
97
|
-
end
|
98
|
-
|
99
|
-
def after(&block)
|
100
|
-
phase[:after] << block
|
101
|
-
end
|
102
|
-
|
103
|
-
def commit(db)
|
104
|
-
phase[:before].each(&:call)
|
105
|
-
|
106
|
-
loop do
|
107
|
-
store = Store.new
|
108
|
-
|
109
|
-
if phase[:watch].any?
|
110
|
-
db.watch(*phase[:watch])
|
111
|
-
end
|
112
|
-
|
113
|
-
run(phase[:read], store)
|
114
|
-
|
115
|
-
break if db.multi do
|
116
|
-
run(phase[:write], store)
|
117
|
-
end
|
118
|
-
|
119
|
-
store = nil
|
120
|
-
end
|
121
|
-
|
122
|
-
phase[:after].each(&:call)
|
123
|
-
end
|
124
|
-
|
125
|
-
def run(procs, store)
|
126
|
-
procs.each { |p| p.call(store) }
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
def self.transaction(&block)
|
131
|
-
Transaction.new(&block)
|
132
|
-
end
|
133
|
-
end
|
data/test/1.8.6_test.rb
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
|
3
|
-
require File.expand_path("./helper", File.dirname(__FILE__))
|
4
|
-
|
5
|
-
prepare.clear
|
6
|
-
|
7
|
-
test "String#lines should return the parts when separated with \\n" do
|
8
|
-
assert ["a\n", "b\n", "c\n"] == "a\nb\nc\n".lines.to_a
|
9
|
-
end
|
10
|
-
|
11
|
-
test "String#lines return the parts when separated with \\r\\n" do
|
12
|
-
assert ["a\r\n", "b\r\n", "c\r\n"] == "a\r\nb\r\nc\r\n".lines.to_a
|
13
|
-
end
|
14
|
-
|
15
|
-
test "String#lines accept a record separator" do
|
16
|
-
assert ["ax", "bx", "cx"] == "axbxcx".lines("x").to_a
|
17
|
-
end
|
18
|
-
|
19
|
-
test "String#lines execute the passed block" do
|
20
|
-
lines = ["a\r\n", "b\r\n", "c\r\n"]
|
21
|
-
|
22
|
-
"a\r\nb\r\nc\r\n".lines do |line|
|
23
|
-
assert lines.shift == line
|
24
|
-
end
|
25
|
-
end
|
@@ -1,67 +0,0 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
|
3
|
-
require File.expand_path("./helper", File.dirname(__FILE__))
|
4
|
-
|
5
|
-
Ohm.flush
|
6
|
-
|
7
|
-
class User < Ohm::Model
|
8
|
-
attribute :fname
|
9
|
-
attribute :lname
|
10
|
-
attribute :bday
|
11
|
-
attribute :gender
|
12
|
-
attribute :city
|
13
|
-
attribute :state
|
14
|
-
attribute :country
|
15
|
-
attribute :zip
|
16
|
-
end
|
17
|
-
|
18
|
-
create = lambda do |i|
|
19
|
-
User.new(:fname => "John#{i}",
|
20
|
-
:lname => "Doe#{i}",
|
21
|
-
:bday => Time.now.to_s,
|
22
|
-
:gender => "Male",
|
23
|
-
:city => "Los Angeles",
|
24
|
-
:state => "CA",
|
25
|
-
:country => "US",
|
26
|
-
:zip => "90210").save
|
27
|
-
end
|
28
|
-
|
29
|
-
10.times(&create)
|
30
|
-
|
31
|
-
require "benchmark"
|
32
|
-
|
33
|
-
t1 = Benchmark.realtime do
|
34
|
-
User.all.sort_by(:fname, :order => "DESC ALPHA").each do |user|
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
t2 = Benchmark.realtime do
|
39
|
-
ids = User.key[:all].smembers
|
40
|
-
|
41
|
-
ids.each do |id|
|
42
|
-
User[id]
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
test "pipelined approach should be 1.5 at least times faster for 10 records" do
|
47
|
-
assert(t2 / t1 >= 1.5)
|
48
|
-
end
|
49
|
-
|
50
|
-
90.times(&create)
|
51
|
-
|
52
|
-
t1 = Benchmark.realtime do
|
53
|
-
User.all.sort_by(:fname, :order => "DESC ALPHA").each do |user|
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
t2 = Benchmark.realtime do
|
58
|
-
ids = User.key[:all].smembers
|
59
|
-
|
60
|
-
ids.each do |id|
|
61
|
-
User[id]
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
test "the pipelined approach should be faster for 100 records" do
|
66
|
-
assert(t2 > t1)
|
67
|
-
end
|
data/test/transactions.rb
DELETED
@@ -1,240 +0,0 @@
|
|
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
|
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
|
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 ["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 "reading an storage entries that doesn't exist raises" do |db|
|
177
|
-
t1 = Ohm::Transaction.new do |t|
|
178
|
-
t.read do |s|
|
179
|
-
s[:foo]
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
|
-
assert_raise Ohm::Transaction::Store::NoEntryError do
|
184
|
-
t1.commit(db)
|
185
|
-
end
|
186
|
-
end
|
187
|
-
|
188
|
-
test "storage entries can't be overriden" do |db|
|
189
|
-
t1 = Ohm::Transaction.new do |t|
|
190
|
-
t.read do |s|
|
191
|
-
s[:foo] = db.type("foo")
|
192
|
-
end
|
193
|
-
end
|
194
|
-
|
195
|
-
t2 = Ohm::Transaction.new do |t|
|
196
|
-
t.read do |s|
|
197
|
-
s[:foo] = db.exists("foo")
|
198
|
-
end
|
199
|
-
end
|
200
|
-
|
201
|
-
assert_raise Ohm::Transaction::Store::EntryAlreadyExistsError do
|
202
|
-
t1.append(t2).commit(db)
|
203
|
-
end
|
204
|
-
end
|
205
|
-
|
206
|
-
test "banking transaction" do |db|
|
207
|
-
class A < Ohm::Model
|
208
|
-
attribute :amount
|
209
|
-
end
|
210
|
-
|
211
|
-
class B < Ohm::Model
|
212
|
-
attribute :amount
|
213
|
-
end
|
214
|
-
|
215
|
-
def transfer(amount, account1, account2)
|
216
|
-
Ohm.transaction do |t|
|
217
|
-
|
218
|
-
t.watch(account1.key, account2.key)
|
219
|
-
|
220
|
-
t.read do |s|
|
221
|
-
s[:available] = account1.get(:amount).to_i
|
222
|
-
end
|
223
|
-
|
224
|
-
t.write do |s|
|
225
|
-
if s[:available] >= amount
|
226
|
-
account1.key.hincrby(:amount, - amount)
|
227
|
-
account2.key.hincrby(:amount, amount)
|
228
|
-
end
|
229
|
-
end
|
230
|
-
end
|
231
|
-
end
|
232
|
-
|
233
|
-
a = A.create :amount => 100
|
234
|
-
b = B.create :amount => 0
|
235
|
-
|
236
|
-
transfer(100, a, b).commit(db)
|
237
|
-
|
238
|
-
assert_equal a.get(:amount), "0"
|
239
|
-
assert_equal b.get(:amount), "100"
|
240
|
-
end
|