transaction-simple 1.2.0 → 1.3.0
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/Changelog +13 -2
- data/Install +5 -1
- data/Rakefile +11 -17
- data/Readme +186 -103
- data/lib/transaction/simple.rb +295 -193
- data/lib/transaction/simple/group.rb +133 -0
- data/lib/transaction/simple/threadsafe.rb +52 -0
- data/lib/transaction/simple/threadsafe/group.rb +23 -0
- data/tests/{tests.rb → tc_transaction_simple.rb} +6 -131
- data/tests/tc_transaction_simple_group.rb +44 -0
- data/tests/tc_transaction_simple_threadsafe.rb +135 -0
- data/tests/testall.rb +20 -0
- metadata +24 -13
- data/ChangeLog +0 -20
- data/README +0 -110
@@ -0,0 +1,133 @@
|
|
1
|
+
require 'transaction/simple'
|
2
|
+
|
3
|
+
# A transaction group is an object wrapper that manages a group of objects
|
4
|
+
# as if they were a single object for the purpose of transaction
|
5
|
+
# management. All transactions for this group of objects should be
|
6
|
+
# performed against the transaction group object, not against individual
|
7
|
+
# objects in the group.
|
8
|
+
#
|
9
|
+
# == Transaction Group Usage
|
10
|
+
# require 'transaction/simple/group'
|
11
|
+
#
|
12
|
+
# x = "Hello, you."
|
13
|
+
# y = "And you, too."
|
14
|
+
#
|
15
|
+
# g = Transaction::Simple::Group.new(x, y)
|
16
|
+
# g.start_transaction(:first) # -> [ x, y ]
|
17
|
+
# g.transaction_open?(:first) # -> true
|
18
|
+
# x.transaction_open?(:first) # -> true
|
19
|
+
# y.transaction_open?(:first) # -> true
|
20
|
+
#
|
21
|
+
# x.gsub!(/you/, "world") # -> "Hello, world."
|
22
|
+
# y.gsub!(/you/, "me") # -> "And me, too."
|
23
|
+
#
|
24
|
+
# g.start_transaction(:second) # -> [ x, y ]
|
25
|
+
# x.gsub!(/world/, "HAL") # -> "Hello, HAL."
|
26
|
+
# y.gsub!(/me/, "Dave") # -> "And Dave, too."
|
27
|
+
# g.rewind_transaction(:second) # -> [ x, y ]
|
28
|
+
# x # -> "Hello, world."
|
29
|
+
# y # -> "And me, too."
|
30
|
+
#
|
31
|
+
# x.gsub!(/world/, "HAL") # -> "Hello, HAL."
|
32
|
+
# y.gsub!(/me/, "Dave") # -> "And Dave, too."
|
33
|
+
#
|
34
|
+
# g.commit_transaction(:second) # -> [ x, y ]
|
35
|
+
# x # -> "Hello, HAL."
|
36
|
+
# y # -> "And Dave, too."
|
37
|
+
#
|
38
|
+
# g.abort_transaction(:first) # -> [ x, y ]
|
39
|
+
# x = -> "Hello, you."
|
40
|
+
# y = -> "And you, too."
|
41
|
+
class Transaction::Simple::Group
|
42
|
+
# Creates a transaction group for the provided objects. If a block is
|
43
|
+
# provided, the transaction group object is yielded to the block; when
|
44
|
+
# the block is finished, the transaction group object will be cleared
|
45
|
+
# with #clear.
|
46
|
+
def initialize(*objects)
|
47
|
+
@objects = objects || []
|
48
|
+
@objects.freeze
|
49
|
+
@objects.each { |obj| obj.extend(Transaction::Simple) }
|
50
|
+
|
51
|
+
if block_given?
|
52
|
+
begin
|
53
|
+
yield self
|
54
|
+
ensure
|
55
|
+
self.clear
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Returns the objects that are covered by this transaction group.
|
61
|
+
attr_reader :objects
|
62
|
+
|
63
|
+
# Clears the object group. Removes references to the objects so that
|
64
|
+
# they can be garbage collected.
|
65
|
+
def clear
|
66
|
+
@objects = @objects.dup.clear
|
67
|
+
end
|
68
|
+
|
69
|
+
# Tests to see if all of the objects in the group have an open
|
70
|
+
# transaction. See Transaction::Simple#transaction_open? for more
|
71
|
+
# information.
|
72
|
+
def transaction_open?(name = nil)
|
73
|
+
@objects.inject(true) do |val, obj|
|
74
|
+
val = val and obj.transaction_open?(name)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Returns the current name of the transaction for the group.
|
79
|
+
# Transactions not explicitly named are named +nil+.
|
80
|
+
def transaction_name
|
81
|
+
@objects[0].transaction_name
|
82
|
+
end
|
83
|
+
|
84
|
+
# Starts a transaction for the group. Stores the current object state.
|
85
|
+
# If a transaction name is specified, the transaction will be named.
|
86
|
+
# Transaction names must be unique. Transaction names of +nil+ will be
|
87
|
+
# treated as unnamed transactions.
|
88
|
+
def start_transaction(name = nil)
|
89
|
+
@objects.each { |obj| obj.start_transaction(name) }
|
90
|
+
end
|
91
|
+
|
92
|
+
# Rewinds the transaction. If +name+ is specified, then the intervening
|
93
|
+
# transactions will be aborted and the named transaction will be
|
94
|
+
# rewound. Otherwise, only the current transaction is rewound.
|
95
|
+
def rewind_transaction(name = nil)
|
96
|
+
@objects.each { |obj| obj.rewind_transaction(name) }
|
97
|
+
end
|
98
|
+
|
99
|
+
# Aborts the transaction. Resets the object state to what it was before
|
100
|
+
# the transaction was started and closes the transaction. If +name+ is
|
101
|
+
# specified, then the intervening transactions and the named transaction
|
102
|
+
# will be aborted. Otherwise, only the current transaction is aborted.
|
103
|
+
#
|
104
|
+
# If the current or named transaction has been started by a block
|
105
|
+
# (Transaction::Simple.start), then the execution of the block will be
|
106
|
+
# halted with +break+ +self+.
|
107
|
+
def abort_transaction(name = nil)
|
108
|
+
@objects.each { |obj| obj.abort_transaction(name) }
|
109
|
+
end
|
110
|
+
|
111
|
+
# If +name+ is +nil+ (default), the current transaction level is closed
|
112
|
+
# out and the changes are committed.
|
113
|
+
#
|
114
|
+
# If +name+ is specified and +name+ is in the list of named
|
115
|
+
# transactions, then all transactions are closed and committed until the
|
116
|
+
# named transaction is reached.
|
117
|
+
def commit_transaction(name = nil)
|
118
|
+
@objects.each { |obj| obj.commit_transaction(name) }
|
119
|
+
end
|
120
|
+
|
121
|
+
# Alternative method for calling the transaction methods. An optional
|
122
|
+
# name can be specified for named transaction support.
|
123
|
+
#
|
124
|
+
# #transaction(:start):: #start_transaction
|
125
|
+
# #transaction(:rewind):: #rewind_transaction
|
126
|
+
# #transaction(:abort):: #abort_transaction
|
127
|
+
# #transaction(:commit):: #commit_transaction
|
128
|
+
# #transaction(:name):: #transaction_name
|
129
|
+
# #transaction:: #transaction_open?
|
130
|
+
def transaction(action = nil, name = nil)
|
131
|
+
@objects.each { |obj| obj.transaction(action, name) }
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'transaction/simple'
|
2
|
+
require 'thread'
|
3
|
+
|
4
|
+
class Transaction::TransactionThreadError < StandardError
|
5
|
+
end
|
6
|
+
|
7
|
+
# = Transaction::Simple::ThreadSafe
|
8
|
+
# Thread-safe simple object transaction support for Ruby.
|
9
|
+
# Transaction::Simple::ThreadSafe is used in the same way as
|
10
|
+
# Transaction::Simple. Transaction::Simple::ThreadSafe uses a Mutex object
|
11
|
+
# to ensure atomicity at the cost of performance in threaded applications.
|
12
|
+
#
|
13
|
+
# Transaction::Simple::ThreadSafe will not wait to obtain a lock; if the
|
14
|
+
# lock cannot be obtained immediately, a
|
15
|
+
# Transaction::TransactionThreadError will be raised.
|
16
|
+
#
|
17
|
+
# Thanks to Mauricio Fern�ndez for help with getting this part working.
|
18
|
+
#
|
19
|
+
# Threadsafe transactions can be used in any place that normal
|
20
|
+
# transactions would. The main difference would be in setup:
|
21
|
+
#
|
22
|
+
# require 'transaction/simple/threadsafe'
|
23
|
+
#
|
24
|
+
# x = "Hello, you."
|
25
|
+
# x.extend(Transaction::Simple::ThreadSafe) # Threadsafe
|
26
|
+
#
|
27
|
+
# y = "Hello, you."
|
28
|
+
# y.extend(Transaction::Simple) # Not threadsafe
|
29
|
+
module Transaction::Simple::ThreadSafe
|
30
|
+
include Transaction::Simple
|
31
|
+
|
32
|
+
SKIP_TRANSACTION_VARS = Transaction::Simple::SKIP_TRANSACTION_VARS.dup #:nodoc:
|
33
|
+
SKIP_TRANSACTION_VARS << "@__transaction_mutex__"
|
34
|
+
|
35
|
+
Transaction::Simple.instance_methods(false) do |meth|
|
36
|
+
next if meth == "transaction"
|
37
|
+
arg = "(name = nil)" unless meth == "transaction_name"
|
38
|
+
module_eval <<-EOS
|
39
|
+
def #{meth}#{arg}
|
40
|
+
if (@__transaction_mutex__ ||= Mutex.new).try_lock
|
41
|
+
result = super
|
42
|
+
@__transaction_mutex__.unlock
|
43
|
+
return result
|
44
|
+
else
|
45
|
+
raise TransactionThreadError, Messages[:cannot_obtain_transaction_lock] % meth
|
46
|
+
end
|
47
|
+
ensure
|
48
|
+
@__transaction_mutex__.unlock
|
49
|
+
end
|
50
|
+
EOS
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'transaction/simple/threadsafe'
|
2
|
+
|
3
|
+
# A transaction group is an object wrapper that manages a group of objects
|
4
|
+
# as if they were a single object for the purpose of transaction
|
5
|
+
# management. All transactions for this group of objects should be
|
6
|
+
# performed against the transaction group object, not against individual
|
7
|
+
# objects in the group. This is the threadsafe version of a transaction
|
8
|
+
# group.
|
9
|
+
class Transaction::Simple::ThreadSafe::Group < Transaction::Simple::Group
|
10
|
+
def initialize(*objects)
|
11
|
+
@objects = objects || []
|
12
|
+
@objects.freeze
|
13
|
+
@objects.each { |obj| obj.extend(Transaction::Simple::ThreadSafe) }
|
14
|
+
|
15
|
+
if block_given?
|
16
|
+
begin
|
17
|
+
yield self
|
18
|
+
ensure
|
19
|
+
self.clear
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -120,7 +120,6 @@ class Test__Transaction_Simple < Test::Unit::TestCase #:nodoc:
|
|
120
120
|
end
|
121
121
|
|
122
122
|
def test_block
|
123
|
-
@value = VALUE.dup
|
124
123
|
Transaction::Simple.start(@value) do |tv|
|
125
124
|
assert_equal(true, tv.transaction_open?)
|
126
125
|
assert_nothing_raised { tv.gsub!(/men/, 'women') }
|
@@ -144,7 +143,6 @@ class Test__Transaction_Simple < Test::Unit::TestCase #:nodoc:
|
|
144
143
|
end
|
145
144
|
|
146
145
|
def test_named_block
|
147
|
-
@value = VALUE.dup
|
148
146
|
Transaction::Simple.start_named(:first, @value) do |tv|
|
149
147
|
assert_equal(true, tv.transaction_open?)
|
150
148
|
assert_equal(true, tv.transaction_open?(:first))
|
@@ -233,7 +231,6 @@ class Test__Transaction_Simple < Test::Unit::TestCase #:nodoc:
|
|
233
231
|
end
|
234
232
|
|
235
233
|
def test_multilevel_block
|
236
|
-
@value = VALUE.dup
|
237
234
|
Transaction::Simple.start_named(:outer, @value) do |tv0|
|
238
235
|
assert_equal(1, tv0.instance_variable_get(:@__transaction_level__))
|
239
236
|
assert_equal(true, tv0.transaction_open?(:outer))
|
@@ -267,136 +264,14 @@ class Test__Transaction_Simple < Test::Unit::TestCase #:nodoc:
|
|
267
264
|
assert_nothing_raised { @value.abort_transaction }
|
268
265
|
assert_equal(@orig, @value)
|
269
266
|
end
|
270
|
-
end
|
271
267
|
|
272
|
-
|
273
|
-
VALUE = "Now is the time for all good men to come to the aid of their country."
|
274
|
-
|
275
|
-
def setup
|
268
|
+
def test_instance_var
|
276
269
|
@value = VALUE.dup
|
277
|
-
@value.extend(Transaction::Simple
|
278
|
-
|
279
|
-
|
280
|
-
def test_extended
|
281
|
-
assert_respond_to(@value, :start_transaction)
|
282
|
-
end
|
283
|
-
|
284
|
-
def test_started
|
285
|
-
assert_equal(false, @value.transaction_open?)
|
286
|
-
assert_nothing_raised { @value.start_transaction }
|
287
|
-
assert_equal(true, @value.transaction_open?)
|
288
|
-
end
|
289
|
-
|
290
|
-
def test_rewind
|
291
|
-
assert_equal(false, @value.transaction_open?)
|
292
|
-
assert_raises(Transaction::TransactionError) { @value.rewind_transaction }
|
293
|
-
assert_nothing_raised { @value.start_transaction }
|
294
|
-
assert_equal(true, @value.transaction_open?)
|
295
|
-
assert_nothing_raised { @value.gsub!(/men/, 'women') }
|
296
|
-
assert_not_equal(VALUE, @value)
|
297
|
-
assert_nothing_raised { @value.rewind_transaction }
|
298
|
-
assert_equal(true, @value.transaction_open?)
|
299
|
-
assert_equal(VALUE, @value)
|
300
|
-
end
|
301
|
-
|
302
|
-
def test_abort
|
303
|
-
assert_equal(false, @value.transaction_open?)
|
304
|
-
assert_raises(Transaction::TransactionError) { @value.abort_transaction }
|
305
|
-
assert_nothing_raised { @value.start_transaction }
|
306
|
-
assert_equal(true, @value.transaction_open?)
|
307
|
-
assert_nothing_raised { @value.gsub!(/men/, 'women') }
|
308
|
-
assert_not_equal(VALUE, @value)
|
309
|
-
assert_nothing_raised { @value.abort_transaction }
|
310
|
-
assert_equal(false, @value.transaction_open?)
|
311
|
-
assert_equal(VALUE, @value)
|
312
|
-
end
|
313
|
-
|
314
|
-
def test_commit
|
315
|
-
assert_equal(false, @value.transaction_open?)
|
316
|
-
assert_raises(Transaction::TransactionError) { @value.commit_transaction }
|
317
|
-
assert_nothing_raised { @value.start_transaction }
|
318
|
-
assert_equal(true, @value.transaction_open?)
|
319
|
-
assert_nothing_raised { @value.gsub!(/men/, 'women') }
|
320
|
-
assert_not_equal(VALUE, @value)
|
321
|
-
assert_equal(true, @value.transaction_open?)
|
322
|
-
assert_nothing_raised { @value.commit_transaction }
|
323
|
-
assert_equal(false, @value.transaction_open?)
|
324
|
-
assert_not_equal(VALUE, @value)
|
325
|
-
end
|
326
|
-
|
327
|
-
def test_multilevel
|
328
|
-
assert_equal(false, @value.transaction_open?)
|
329
|
-
assert_nothing_raised { @value.start_transaction }
|
330
|
-
assert_equal(true, @value.transaction_open?)
|
331
|
-
assert_nothing_raised { @value.gsub!(/men/, 'women') }
|
332
|
-
assert_equal(VALUE.gsub(/men/, 'women'), @value)
|
333
|
-
assert_equal(true, @value.transaction_open?)
|
334
|
-
assert_nothing_raised { @value.start_transaction }
|
335
|
-
assert_nothing_raised { @value.gsub!(/country/, 'nation-state') }
|
336
|
-
assert_nothing_raised { @value.commit_transaction }
|
337
|
-
assert_equal(VALUE.gsub(/men/, 'women').gsub(/country/, 'nation-state'), @value)
|
338
|
-
assert_equal(true, @value.transaction_open?)
|
339
|
-
assert_nothing_raised { @value.abort_transaction }
|
340
|
-
assert_equal(VALUE, @value)
|
341
|
-
end
|
342
|
-
|
343
|
-
def test_multilevel_named
|
344
|
-
assert_equal(false, @value.transaction_open?)
|
345
|
-
assert_raises(Transaction::TransactionError) { @value.transaction_name }
|
346
|
-
assert_nothing_raised { @value.start_transaction(:first) } # 1
|
347
|
-
assert_raises(Transaction::TransactionError) { @value.start_transaction(:first) }
|
348
|
-
assert_equal(true, @value.transaction_open?)
|
349
|
-
assert_equal(true, @value.transaction_open?(:first))
|
350
|
-
assert_equal(:first, @value.transaction_name)
|
351
|
-
assert_nothing_raised { @value.start_transaction } # 2
|
352
|
-
assert_not_equal(:first, @value.transaction_name)
|
353
|
-
assert_equal(nil, @value.transaction_name)
|
354
|
-
assert_raises(Transaction::TransactionError) { @value.abort_transaction(:second) }
|
355
|
-
assert_nothing_raised { @value.abort_transaction(:first) }
|
356
|
-
assert_equal(false, @value.transaction_open?)
|
357
|
-
assert_nothing_raised do
|
358
|
-
@value.start_transaction(:first)
|
359
|
-
@value.gsub!(/men/, 'women')
|
360
|
-
@value.start_transaction(:second)
|
361
|
-
@value.gsub!(/women/, 'people')
|
362
|
-
@value.start_transaction
|
363
|
-
@value.gsub!(/people/, 'sentients')
|
364
|
-
end
|
365
|
-
assert_nothing_raised { @value.abort_transaction(:second) }
|
366
|
-
assert_equal(true, @value.transaction_open?(:first))
|
367
|
-
assert_equal(VALUE.gsub(/men/, 'women'), @value)
|
368
|
-
assert_nothing_raised do
|
369
|
-
@value.start_transaction(:second)
|
370
|
-
@value.gsub!(/women/, 'people')
|
371
|
-
@value.start_transaction
|
372
|
-
@value.gsub!(/people/, 'sentients')
|
373
|
-
end
|
374
|
-
assert_raises(Transaction::TransactionError) { @value.rewind_transaction(:foo) }
|
375
|
-
assert_nothing_raised { @value.rewind_transaction(:second) }
|
376
|
-
assert_equal(VALUE.gsub(/men/, 'women'), @value)
|
377
|
-
assert_nothing_raised do
|
378
|
-
@value.gsub!(/women/, 'people')
|
379
|
-
@value.start_transaction
|
380
|
-
@value.gsub!(/people/, 'sentients')
|
381
|
-
end
|
382
|
-
assert_raises(Transaction::TransactionError) { @value.commit_transaction(:foo) }
|
383
|
-
assert_nothing_raised { @value.commit_transaction(:first) }
|
384
|
-
assert_equal(VALUE.gsub(/men/, 'sentients'), @value)
|
385
|
-
assert_equal(false, @value.transaction_open?)
|
386
|
-
end
|
387
|
-
|
388
|
-
def test_array
|
389
|
-
assert_nothing_raised do
|
390
|
-
@orig = ["first", "second", "third"]
|
391
|
-
@value = ["first", "second", "third"]
|
392
|
-
@value.extend(Transaction::Simple::ThreadSafe)
|
393
|
-
end
|
394
|
-
assert_equal(@orig, @value)
|
395
|
-
assert_nothing_raised { @value.start_transaction }
|
270
|
+
@value.extend(Transaction::Simple)
|
271
|
+
@value.start_transaction
|
396
272
|
assert_equal(true, @value.transaction_open?)
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
assert_equal(@orig, @value)
|
273
|
+
@value.instance_variable_set("@foo", "bar")
|
274
|
+
@value.rewind_transaction
|
275
|
+
assert_nil(@value.instance_variable_get("@foo"))
|
401
276
|
end
|
402
277
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
$LOAD_PATH.unshift("#{File.dirname(__FILE__)}/../lib") if __FILE__ == $0
|
2
|
+
|
3
|
+
require 'transaction/simple/group'
|
4
|
+
require 'test/unit'
|
5
|
+
class Test__Transaction_Simple_Group < Test::Unit::TestCase #:nodoc:
|
6
|
+
VALUE1 = "Hello, you."
|
7
|
+
VALUE2 = "And you, too."
|
8
|
+
|
9
|
+
def setup
|
10
|
+
@x = VALUE1.dup
|
11
|
+
@y = VALUE2.dup
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_group
|
15
|
+
group = Transaction::Simple::Group.new(@x, @y)
|
16
|
+
|
17
|
+
assert_nothing_raised { group.start_transaction(:first) }
|
18
|
+
assert_equal(true, group.transaction_open?(:first))
|
19
|
+
assert_equal(true, @x.transaction_open?(:first))
|
20
|
+
assert_equal(true, @y.transaction_open?(:first))
|
21
|
+
|
22
|
+
assert_equal("Hello, world.", @x.gsub!(/you/, "world"))
|
23
|
+
assert_equal("And me, too.", @y.gsub!(/you/, "me"))
|
24
|
+
|
25
|
+
assert_nothing_raised { group.start_transaction(:second) }
|
26
|
+
assert_equal("Hello, HAL.", @x.gsub!(/world/, "HAL"))
|
27
|
+
assert_equal("And Dave, too.", @y.gsub!(/me/, "Dave"))
|
28
|
+
|
29
|
+
assert_nothing_raised { group.rewind_transaction(:second) }
|
30
|
+
assert_equal("Hello, world.", @x)
|
31
|
+
assert_equal("And me, too.", @y)
|
32
|
+
|
33
|
+
assert_equal("Hello, HAL.", @x.gsub!(/world/, "HAL"))
|
34
|
+
assert_equal("And Dave, too.", @y.gsub!(/me/, "Dave"))
|
35
|
+
|
36
|
+
assert_nothing_raised { group.commit_transaction(:second) }
|
37
|
+
assert_equal("Hello, HAL.", @x)
|
38
|
+
assert_equal("And Dave, too.", @y)
|
39
|
+
|
40
|
+
assert_nothing_raised { group.abort_transaction(:first) }
|
41
|
+
assert_equal("Hello, you.", @x)
|
42
|
+
assert_equal("And you, too.", @y)
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
$LOAD_PATH.unshift("#{File.dirname(__FILE__)}/../lib") if __FILE__ == $0
|
2
|
+
|
3
|
+
require 'transaction/simple/threadsafe'
|
4
|
+
require 'test/unit'
|
5
|
+
class Test__Transaction_Simple_ThreadSafe < Test::Unit::TestCase #:nodoc:
|
6
|
+
VALUE = "Now is the time for all good men to come to the aid of their country."
|
7
|
+
|
8
|
+
def setup
|
9
|
+
@value = VALUE.dup
|
10
|
+
@value.extend(Transaction::Simple::ThreadSafe)
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_extended
|
14
|
+
assert_respond_to(@value, :start_transaction)
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_started
|
18
|
+
assert_equal(false, @value.transaction_open?)
|
19
|
+
assert_nothing_raised { @value.start_transaction }
|
20
|
+
assert_equal(true, @value.transaction_open?)
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_rewind
|
24
|
+
assert_equal(false, @value.transaction_open?)
|
25
|
+
assert_raises(Transaction::TransactionError) { @value.rewind_transaction }
|
26
|
+
assert_nothing_raised { @value.start_transaction }
|
27
|
+
assert_equal(true, @value.transaction_open?)
|
28
|
+
assert_nothing_raised { @value.gsub!(/men/, 'women') }
|
29
|
+
assert_not_equal(VALUE, @value)
|
30
|
+
assert_nothing_raised { @value.rewind_transaction }
|
31
|
+
assert_equal(true, @value.transaction_open?)
|
32
|
+
assert_equal(VALUE, @value)
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_abort
|
36
|
+
assert_equal(false, @value.transaction_open?)
|
37
|
+
assert_raises(Transaction::TransactionError) { @value.abort_transaction }
|
38
|
+
assert_nothing_raised { @value.start_transaction }
|
39
|
+
assert_equal(true, @value.transaction_open?)
|
40
|
+
assert_nothing_raised { @value.gsub!(/men/, 'women') }
|
41
|
+
assert_not_equal(VALUE, @value)
|
42
|
+
assert_nothing_raised { @value.abort_transaction }
|
43
|
+
assert_equal(false, @value.transaction_open?)
|
44
|
+
assert_equal(VALUE, @value)
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_commit
|
48
|
+
assert_equal(false, @value.transaction_open?)
|
49
|
+
assert_raises(Transaction::TransactionError) { @value.commit_transaction }
|
50
|
+
assert_nothing_raised { @value.start_transaction }
|
51
|
+
assert_equal(true, @value.transaction_open?)
|
52
|
+
assert_nothing_raised { @value.gsub!(/men/, 'women') }
|
53
|
+
assert_not_equal(VALUE, @value)
|
54
|
+
assert_equal(true, @value.transaction_open?)
|
55
|
+
assert_nothing_raised { @value.commit_transaction }
|
56
|
+
assert_equal(false, @value.transaction_open?)
|
57
|
+
assert_not_equal(VALUE, @value)
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_multilevel
|
61
|
+
assert_equal(false, @value.transaction_open?)
|
62
|
+
assert_nothing_raised { @value.start_transaction }
|
63
|
+
assert_equal(true, @value.transaction_open?)
|
64
|
+
assert_nothing_raised { @value.gsub!(/men/, 'women') }
|
65
|
+
assert_equal(VALUE.gsub(/men/, 'women'), @value)
|
66
|
+
assert_equal(true, @value.transaction_open?)
|
67
|
+
assert_nothing_raised { @value.start_transaction }
|
68
|
+
assert_nothing_raised { @value.gsub!(/country/, 'nation-state') }
|
69
|
+
assert_nothing_raised { @value.commit_transaction }
|
70
|
+
assert_equal(VALUE.gsub(/men/, 'women').gsub(/country/, 'nation-state'), @value)
|
71
|
+
assert_equal(true, @value.transaction_open?)
|
72
|
+
assert_nothing_raised { @value.abort_transaction }
|
73
|
+
assert_equal(VALUE, @value)
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_multilevel_named
|
77
|
+
assert_equal(false, @value.transaction_open?)
|
78
|
+
assert_raises(Transaction::TransactionError) { @value.transaction_name }
|
79
|
+
assert_nothing_raised { @value.start_transaction(:first) } # 1
|
80
|
+
assert_raises(Transaction::TransactionError) { @value.start_transaction(:first) }
|
81
|
+
assert_equal(true, @value.transaction_open?)
|
82
|
+
assert_equal(true, @value.transaction_open?(:first))
|
83
|
+
assert_equal(:first, @value.transaction_name)
|
84
|
+
assert_nothing_raised { @value.start_transaction } # 2
|
85
|
+
assert_not_equal(:first, @value.transaction_name)
|
86
|
+
assert_equal(nil, @value.transaction_name)
|
87
|
+
assert_raises(Transaction::TransactionError) { @value.abort_transaction(:second) }
|
88
|
+
assert_nothing_raised { @value.abort_transaction(:first) }
|
89
|
+
assert_equal(false, @value.transaction_open?)
|
90
|
+
assert_nothing_raised do
|
91
|
+
@value.start_transaction(:first)
|
92
|
+
@value.gsub!(/men/, 'women')
|
93
|
+
@value.start_transaction(:second)
|
94
|
+
@value.gsub!(/women/, 'people')
|
95
|
+
@value.start_transaction
|
96
|
+
@value.gsub!(/people/, 'sentients')
|
97
|
+
end
|
98
|
+
assert_nothing_raised { @value.abort_transaction(:second) }
|
99
|
+
assert_equal(true, @value.transaction_open?(:first))
|
100
|
+
assert_equal(VALUE.gsub(/men/, 'women'), @value)
|
101
|
+
assert_nothing_raised do
|
102
|
+
@value.start_transaction(:second)
|
103
|
+
@value.gsub!(/women/, 'people')
|
104
|
+
@value.start_transaction
|
105
|
+
@value.gsub!(/people/, 'sentients')
|
106
|
+
end
|
107
|
+
assert_raises(Transaction::TransactionError) { @value.rewind_transaction(:foo) }
|
108
|
+
assert_nothing_raised { @value.rewind_transaction(:second) }
|
109
|
+
assert_equal(VALUE.gsub(/men/, 'women'), @value)
|
110
|
+
assert_nothing_raised do
|
111
|
+
@value.gsub!(/women/, 'people')
|
112
|
+
@value.start_transaction
|
113
|
+
@value.gsub!(/people/, 'sentients')
|
114
|
+
end
|
115
|
+
assert_raises(Transaction::TransactionError) { @value.commit_transaction(:foo) }
|
116
|
+
assert_nothing_raised { @value.commit_transaction(:first) }
|
117
|
+
assert_equal(VALUE.gsub(/men/, 'sentients'), @value)
|
118
|
+
assert_equal(false, @value.transaction_open?)
|
119
|
+
end
|
120
|
+
|
121
|
+
def test_array
|
122
|
+
assert_nothing_raised do
|
123
|
+
@orig = ["first", "second", "third"]
|
124
|
+
@value = ["first", "second", "third"]
|
125
|
+
@value.extend(Transaction::Simple::ThreadSafe)
|
126
|
+
end
|
127
|
+
assert_equal(@orig, @value)
|
128
|
+
assert_nothing_raised { @value.start_transaction }
|
129
|
+
assert_equal(true, @value.transaction_open?)
|
130
|
+
assert_nothing_raised { @value[1].gsub!(/second/, "fourth") }
|
131
|
+
assert_not_equal(@orig, @value)
|
132
|
+
assert_nothing_raised { @value.abort_transaction }
|
133
|
+
assert_equal(@orig, @value)
|
134
|
+
end
|
135
|
+
end
|