transaction-simple 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/Changelog CHANGED
@@ -1,4 +1,15 @@
1
- $Id: Changelog,v 1.2 2004/09/14 18:46:15 austin Exp $
1
+ $Id: Changelog,v 1.5 2005/05/05 16:16:49 austin Exp $
2
+
3
+ == Transaction::simple 1.3.0
4
+ * Updated to fix a lot of warnings.
5
+ * Added a per-transaction-object list of excluded instance variables.
6
+ * Moved Transaction::simple::ThreadSafe to transaction/simple/threadsafe.
7
+ * Added transaction groups. Transaction groups are wrapper objects to allow
8
+ the coordination of transactions with a group of objects. There are both
9
+ normal and threadsafe versions of transaction groups.
10
+ * Fixed a long-standing problem where instance variables that were added to an
11
+ object after a transaction was started would remain.
12
+ * Reorganised unit tests.
2
13
 
3
14
  == Transaction::Simple 1.2.0
4
15
  * Added a RubyGem.
@@ -11,7 +22,7 @@ $Id: Changelog,v 1.2 2004/09/14 18:46:15 austin Exp $
11
22
  * Added Transaction::Simple::ThreadSafe for truly atomic and thread-safe
12
23
  transactions.
13
24
  * Fixed the description of Transaction::Simple to note that it is *not* atomic
14
- because it is not necessarily thread-safe.
25
+ because it is not implicitly thread-safe.
15
26
  * Added support for named transactions. Named transactions can be used to make
16
27
  checkpoints that can be committed, aborted, or rewound without explicitly
17
28
  committing, aborting, or rewinding the intervening transactions.
data/Install CHANGED
@@ -1,2 +1,6 @@
1
- Simply run:
1
+ Installing this package is as simple as:
2
+
2
3
  % ruby install.rb
4
+
5
+ Alternatively, you can juse the RubyGem version of Transaction::Simple
6
+ available as transaction-simple-1.3.0.gem from the usual sources.
data/Rakefile CHANGED
@@ -8,7 +8,7 @@ require 'transaction/simple'
8
8
  require 'archive/tar/minitar'
9
9
  require 'zlib'
10
10
 
11
- DISTDIR = "transaction-simple-#{Transaction::Simple::VERSION}"
11
+ DISTDIR = "transaction-simple-#{Transaction::Simple::TRANSACTION_SIMPLE_VERSION}"
12
12
  TARDIST = "../#{DISTDIR}.tar.gz"
13
13
 
14
14
  DATE_RE = %r<(\d{4})[./-]?(\d{2})[./-]?(\d{2})(?:[\sT]?(\d{2})[:.]?(\d{2})[:.]?(\d{2})?)?>
@@ -34,7 +34,7 @@ task :test do |t|
34
34
 
35
35
  $LOAD_PATH.unshift('tests')
36
36
  $stderr.puts "Checking for test cases:" if t.verbose
37
- Dir['tests/*test*.rb'].each do |testcase|
37
+ Dir['tests/**/tc_*.rb'].each do |testcase|
38
38
  $stderr.puts "\t#{testcase}" if t.verbose
39
39
  load testcase
40
40
  end
@@ -49,7 +49,7 @@ task :test do |t|
49
49
  end
50
50
 
51
51
  spec = eval(File.read("transaction-simple.gemspec"))
52
- spec.version = Transaction::Simple::VERSION
52
+ spec.version = Transaction::Simple::TRANSACTION_SIMPLE_VERSION
53
53
  desc "Build the RubyGem for Transaction::Simple"
54
54
  task :gem => [ :test ]
55
55
  Rake::GemPackageTask.new(spec) do |g|
@@ -101,20 +101,14 @@ file TARDIST => [ :test ] do |t|
101
101
  end
102
102
  task TARDIST => [ :test ]
103
103
 
104
- def sign(file)
105
- begin
106
- sh %("C:\\Program Files\\Windows Privacy Tools\\GnuPG\\Gpg.exe" -ba #{file.gsub(%r</>) do '\\'; end})
107
- rescue RuntimeError
108
- raise unless File.exist?("#{file}.asc")
109
- end
110
- end
111
-
112
- task :signtar => [ :tar ] do
113
- sign TARDIST
114
- end
115
- task :signgem => [ :gem ] do
116
- sign "../#{DISTDIR}.gem"
104
+ desc "Build the rdoc documentation for Transaction::Simple"
105
+ task :docs do
106
+ require 'rdoc/rdoc'
107
+ rdoc_options = %w(--title Transaction::Simple --main Readme --line-numbers)
108
+ files = FileList[*%w(Readme Changelog bin/**/*.rb lib/**/*.rb)]
109
+ rdoc_options += files.to_a
110
+ RDoc::RDoc.new.document(rdoc_options)
117
111
  end
118
112
 
119
113
  desc "Build everything."
120
- task :default => [ :signtar, :signgem ]
114
+ task :default => [ :tar, :gem ]
data/Readme CHANGED
@@ -1,110 +1,193 @@
1
- Transaction::Simple for Ruby
2
- Simple object transaction support for Ruby
3
-
4
- Introduction
5
- ------------
6
- Transaction::Simple provides a generic way to add active transactional support
7
- to objects. The transaction methods added by this module will work with most
8
- objects, excluding those that cannot be Marshal-ed (bindings, procedure
9
- objects, IO instances, or singleton objects).
10
-
11
- The transactions supported by Transaction::Simple are not backend transaction;
12
- that is, they have nothing to do with any sort of data store. They are "live"
13
- transactions occurring in memory and in the object itself. This is to allow
14
- "test" changes to be made to an object before making the changes permanent.
15
-
16
- Transaction::Simple can handle an "infinite" number of transactional levels
17
- (limited only by memory). If I open two transactions, commit the first, but
18
- abort the second, the object will revert to the original version.
19
-
20
- Transaction::Simple supports "named" transactions, so that multiple levels of
21
- transactions can be committed, aborted, or rewound by referring to the
22
- appropriate name of the transaction. Names may be any object except nil.
23
-
24
- Copyright: Copyright 2003 by Austin Ziegler
25
- Version: 1.11
1
+ = Transaction::Simple for Ruby
2
+ Transaction::Simple provides a generic way to add active transaction
3
+ support to objects. The transaction methods added by this module will
4
+ work with most objects, excluding those that cannot be Marshal-ed
5
+ (bindings, procedure objects, IO instances, or singleton objects).
6
+
7
+ The transactions supported by Transaction::Simple are not backend
8
+ transaction; that is, they are not associated with any sort of data
9
+ store. They are "live" transactions occurring in memory and in the
10
+ object itself. This is to allow "test" changes to be made to an object
11
+ before making the changes permanent.
12
+
13
+ Transaction::Simple can handle an "infinite" number of transaction
14
+ levels (limited only by memory). If I open two transactions, commit the
15
+ second, but abort the first, the object will revert to the original
16
+ version.
17
+
18
+ Transaction::Simple supports "named" transactions, so that multiple
19
+ levels of transactions can be committed, aborted, or rewound by
20
+ referring to the appropriate name of the transaction. Names may be any
21
+ object except nil.
22
+
23
+ Version 1.3.0 of Transaction::Simple adds transaction groups. A
24
+ transaction group is an object wrapper that manages a group of objects
25
+ as if they were a single object for the purpose of transaction
26
+ management. All transactions for this group of objects should be
27
+ performed against the transaction group object, not against individual
28
+ objects in the group.
29
+
30
+ Copyright: Copyright � 2003 - 2005 by Austin Ziegler
31
+ Version: 1.3.0
26
32
  Licence: MIT-Style
27
33
 
28
- Thanks to David Black and Mauricio Fern�ndez for their help with this library.
34
+ Thanks to David Black and Mauricio Fern�ndez for their help with this
35
+ library.
29
36
 
30
- Usage
31
- -----
37
+ == Usage
32
38
  include 'transaction/simple'
33
39
 
34
- v = "Hello, you." # => "Hello, you."
35
- v.extend(Transaction::Simple) # => "Hello, you."
36
-
37
- v.start_transaction # => ... (a Marshal string)
38
- v.transaction_open? # => true
39
- v.gsub!(/you/, "world") # => "Hello, world."
40
-
41
- v.rewind_transaction # => "Hello, you."
42
- v.transaction_open? # => true
43
-
44
- v.gsub!(/you/, "HAL") # => "Hello, HAL."
45
- v.abort_transaction # => "Hello, you."
46
- v.transaction_open? # => false
47
-
48
- v.start_transaction # => ... (a Marshal string)
49
- v.start_transaction # => ... (a Marshal string)
50
-
51
- v.transaction_open? # => true
52
- v.gsub!(/you/, "HAL") # => "Hello, HAL."
53
-
54
- v.commit_transaction # => "Hello, HAL."
55
- v.transaction_open? # => true
56
- v.abort_transaction # => "Hello, you."
57
- v.transaction_open? # => false
58
-
59
- Named Transaction Usage
60
- -----------------------
61
- v = "Hello, you." # => "Hello, you."
62
- v.extend(Transaction::Simple) # => "Hello, you."
63
-
64
- v.start_transaction(:first) # => ... (a Marshal string)
65
- v.transaction_open? # => true
66
- v.transaction_open?(:first) # => true
67
- v.transaction_open?(:second) # => false
68
- v.gsub!(/you/, "world") # => "Hello, world."
69
-
70
- v.start_transaction(:second) # => ... (a Marshal string)
71
- v.gsub!(/world/, "HAL") # => "Hello, HAL."
72
- v.rewind_transaction(:first) # => "Hello, you."
73
- v.transaction_open? # => true
74
- v.transaction_open?(:first) # => true
75
- v.transaction_open?(:second) # => false
76
-
77
- v.gsub!(/you/, "world") # => "Hello, world."
78
- v.start_transaction(:second) # => ... (a Marshal string)
79
- v.gsub!(/world/, "HAL") # => "Hello, HAL."
80
- v.transaction_name # => :second
81
- v.abort_transaction(:first) # => "Hello, you."
82
- v.transaction_open? # => false
83
-
84
- v.start_transaction(:first) # => ... (a Marshal string)
85
- v.gsub!(/you/, "world") # => "Hello, world."
86
- v.start_transaction(:second) # => ... (a Marshal string)
87
- v.gsub!(/world/, "HAL") # => "Hello, HAL."
88
-
89
- v.commit_transaction(:first) # => "Hello, HAL."
90
- v.transaction_open? # => false
91
-
92
- Contraindications
93
- -----------------
94
- While Transaction::Simple is very useful, it has some severe limitations that
95
- must be understood. Transaction::Simple:
40
+ v = "Hello, you." # -> "Hello, you."
41
+ v.extend(Transaction::Simple) # -> "Hello, you."
42
+
43
+ v.start_transaction # -> ... (a Marshal string)
44
+ v.transaction_open? # -> true
45
+ v.gsub!(/you/, "world") # -> "Hello, world."
46
+
47
+ v.rewind_transaction # -> "Hello, you."
48
+ v.transaction_open? # -> true
49
+
50
+ v.gsub!(/you/, "HAL") # -> "Hello, HAL."
51
+ v.abort_transaction # -> "Hello, you."
52
+ v.transaction_open? # -> false
53
+
54
+ v.start_transaction # -> ... (a Marshal string)
55
+ v.start_transaction # -> ... (a Marshal string)
56
+
57
+ v.transaction_open? # -> true
58
+ v.gsub!(/you/, "HAL") # -> "Hello, HAL."
59
+
60
+ v.commit_transaction # -> "Hello, HAL."
61
+ v.transaction_open? # -> true
62
+ v.abort_transaction # -> "Hello, you."
63
+ v.transaction_open? # -> false
64
+
65
+ == Named Transaction Usage
66
+ v = "Hello, you." # -> "Hello, you."
67
+ v.extend(Transaction::Simple) # -> "Hello, you."
68
+
69
+ v.start_transaction(:first) # -> ... (a Marshal string)
70
+ v.transaction_open? # -> true
71
+ v.transaction_open?(:first) # -> true
72
+ v.transaction_open?(:second) # -> false
73
+ v.gsub!(/you/, "world") # -> "Hello, world."
74
+
75
+ v.start_transaction(:second) # -> ... (a Marshal string)
76
+ v.gsub!(/world/, "HAL") # -> "Hello, HAL."
77
+ v.rewind_transaction(:first) # -> "Hello, you."
78
+ v.transaction_open? # -> true
79
+ v.transaction_open?(:first) # -> true
80
+ v.transaction_open?(:second) # -> false
81
+
82
+ v.gsub!(/you/, "world") # -> "Hello, world."
83
+ v.start_transaction(:second) # -> ... (a Marshal string)
84
+ v.gsub!(/world/, "HAL") # -> "Hello, HAL."
85
+ v.transaction_name # -> :second
86
+ v.abort_transaction(:first) # -> "Hello, you."
87
+ v.transaction_open? # -> false
88
+
89
+ v.start_transaction(:first) # -> ... (a Marshal string)
90
+ v.gsub!(/you/, "world") # -> "Hello, world."
91
+ v.start_transaction(:second) # -> ... (a Marshal string)
92
+ v.gsub!(/world/, "HAL") # -> "Hello, HAL."
93
+
94
+ v.commit_transaction(:first) # -> "Hello, HAL."
95
+ v.transaction_open? # -> false
96
+
97
+ == Block Transaction Usage
98
+ v = "Hello, you." # -> "Hello, you."
99
+ Transaction::Simple.start(v) do |tv|
100
+ # v has been extended with Transaction::Simple and an unnamed
101
+ # transaction has been started.
102
+ tv.transaction_open? # -> true
103
+ tv.gsub!(/you/, "world") # -> "Hello, world."
104
+
105
+ tv.rewind_transaction # -> "Hello, you."
106
+ tv.transaction_open? # -> true
107
+
108
+ tv.gsub!(/you/, "HAL") # -> "Hello, HAL."
109
+ # The following breaks out of the transaction block after
110
+ # aborting the transaction.
111
+ tv.abort_transaction # -> "Hello, you."
112
+ end
113
+ # v still has Transaction::Simple applied from here on out.
114
+ v.transaction_open? # -> false
115
+
116
+ Transaction::Simple.start(v) do |tv|
117
+ tv.start_transaction # -> ... (a Marshal string)
118
+
119
+ tv.transaction_open? # -> true
120
+ tv.gsub!(/you/, "HAL") # -> "Hello, HAL."
121
+
122
+ # If #commit_transaction were called without having started a
123
+ # second transaction, then it would break out of the transaction
124
+ # block after committing the transaction.
125
+ tv.commit_transaction # -> "Hello, HAL."
126
+ tv.transaction_open? # -> true
127
+ tv.abort_transaction # -> "Hello, you."
128
+ end
129
+ v.transaction_open? # -> false
130
+
131
+ == Transaction Groups
132
+ require 'transaction/simple/group'
133
+
134
+ x = "Hello, you."
135
+ y = "And you, too."
136
+
137
+ g = Transaction::Simple::Group.new(x, y)
138
+ g.start_transaction(:first) # -> [ x, y ]
139
+ g.transaction_open?(:first) # -> true
140
+ x.transaction_open?(:first) # -> true
141
+ y.transaction_open?(:first) # -> true
142
+
143
+ x.gsub!(/you/, "world") # -> "Hello, world."
144
+ y.gsub!(/you/, "me") # -> "And me, too."
145
+
146
+ g.start_transaction(:second) # -> [ x, y ]
147
+ x.gsub!(/world/, "HAL") # -> "Hello, HAL."
148
+ y.gsub!(/me/, "Dave") # -> "And Dave, too."
149
+
150
+ g.rewind_transaction(:second) # -> [ x, y ]
151
+ x # -> "Hello, world."
152
+ y # -> "And me, too."
153
+
154
+ x.gsub!(/world/, "HAL") # -> "Hello, HAL."
155
+ y.gsub!(/me/, "Dave") # -> "And Dave, too."
156
+
157
+ g.commit_transaction(:second) # -> [ x, y ]
158
+ x # -> "Hello, HAL."
159
+ y # -> "And Dave, too."
160
+
161
+ g.abort_transaction(:first) # -> [ x, y ]
162
+ x = -> "Hello, you."
163
+ y = -> "And you, too."
164
+
165
+ == Thread Safety
166
+ Threadsafe version of Transaction::Simple and Transaction::Simple::Group
167
+ exist; these are loaded from 'transaction/simple/threadsafe' and
168
+ 'transaction/simple/threadsafe/group', respectively, and are represented
169
+ in Ruby code as Transaction::Simple::ThreadSafe and
170
+ Transaction::Simple::ThreadSafe::Group, respectively.
171
+
172
+ == Contraindications
173
+ While Transaction::Simple is very useful, it has some severe limitations
174
+ that must be understood. Transaction::Simple:
96
175
 
97
176
  * uses Marshal. Thus, any object which cannot be Marshal-ed cannot use
98
- Transaction::Simple.
99
- * does not manage resources. Resources external to the object and its instance
100
- variables are not managed at all. However, all instance variables and
101
- objects "belonging" to those instance variables are managed. If there are
102
- object reference counts to be handled, Transaction::Simple will probably
103
- cause problems.
104
- * is not thread-safe. In the ACID ("atomic, consistent, isolated, durable")
105
- test, Transaction::Simple provides C and D, but it is up to the user of
106
- Transaction::Simple to provide isolation. Transactions should be considered
107
- "critical sections" in multi-threaded applications. Thread safety can be
108
- ensured with Transaction::Simple::ThreadSafe.
109
- * does not maintain Object#__id__ values on rewind or abort. This may change
110
- for future versions that will be Ruby 1.8 or better only.
177
+ Transaction::Simple. In my experience, this affects singleton objects
178
+ more often than any other object. It may be that Ruby 2.0 will solve
179
+ this problem.
180
+ * does not manage resources. Resources external to the object and its
181
+ instance variables are not managed at all. However, all instance
182
+ variables and objects "belonging" to those instance variables are
183
+ managed. If there are object reference counts to be handled,
184
+ Transaction::Simple will probably cause problems.
185
+ * is not thread-safe. In the ACID ("atomic, consistent, isolated,
186
+ durable") test, Transaction::Simple provides C and D, but it is up to
187
+ the user of Transaction::Simple to provide isolation. Transactions
188
+ should be considered "critical sections" in multi-threaded
189
+ applications. Thread safety can be ensured with
190
+ Transaction::Simple::ThreadSafe. With transaction groups, some level
191
+ of atomicity is assured.
192
+ * does not maintain Object#__id__ values on rewind or abort. This may
193
+ change for future versions.
@@ -1,4 +1,4 @@
1
- # :title: Transaction::Simple
1
+ # :title: Transaction::Simple -- Active Object Transaction Support for Ruby
2
2
  # :main: Transaction::Simple
3
3
  #
4
4
  # == Licence
@@ -23,55 +23,86 @@
23
23
  #--
24
24
  # Transaction::Simple
25
25
  # Simple object transaction support for Ruby
26
- # Version 1.2.0
26
+ # Version 1.3.0
27
27
  #
28
- # Copyright (c) 2003 - 2004 Austin Ziegler
28
+ # Copyright (c) 2003 - 2005 Austin Ziegler
29
29
  #
30
- # $Id: simple.rb,v 1.2 2004/09/14 18:46:15 austin Exp $
30
+ # $Id: simple.rb,v 1.5 2005/05/05 16:16:49 austin Exp $
31
31
  #++
32
- # Required for Transaction::Simple::ThreadSafe
33
- require 'thread'
34
-
35
- # The "Transaction" namespace can be used for additional transactional
32
+ # The "Transaction" namespace can be used for additional transaction
36
33
  # support objects and modules.
37
34
  module Transaction
38
- # A standard exception for transactional errors.
35
+ # A standard exception for transaction errors.
39
36
  class TransactionError < StandardError; end
37
+ # The TransactionAborted exception is used to indicate when a
38
+ # transaction has been aborted in the block form.
40
39
  class TransactionAborted < Exception; end
40
+ # The TransactionCommitted exception is used to indicate when a
41
+ # transaction has been committed in the block form.
41
42
  class TransactionCommitted < Exception; end
42
- # A standard exception for transactional errors involving the acquisition
43
- # of locks for Transaction::Simple::ThreadSafe.
44
- class TransactionThreadError < StandardError; end
43
+
44
+ te = "Transaction Error: %s"
45
+
46
+ Messages = {
47
+ :bad_debug_object =>
48
+ te % "the transaction debug object must respond to #<<.",
49
+ :unique_names =>
50
+ te % "named transactions must be unique.",
51
+ :no_transaction_open =>
52
+ te % "no transaction open.",
53
+ :cannot_rewind_no_transaction =>
54
+ te % "cannot rewind; there is no current transaction.",
55
+ :cannot_rewind_named_transaction =>
56
+ te % "cannot rewind to transaction %s because it does not exist.",
57
+ :cannot_rewind_transaction_before_block =>
58
+ te % "cannot rewind a transaction started before the execution block.",
59
+ :cannot_abort_no_transaction =>
60
+ te % "cannot abort; there is no current transaction.",
61
+ :cannot_abort_transaction_before_block =>
62
+ te % "cannot abort a transaction started before the execution block.",
63
+ :cannot_abort_named_transaction =>
64
+ te % "cannot abort nonexistant transaction %s.",
65
+ :cannot_commit_no_transaction =>
66
+ te % "cannot commit; there is no current transaction.",
67
+ :cannot_commit_transaction_before_block =>
68
+ te % "cannot commit a transaction started before the execution block.",
69
+ :cannot_commit_named_transaction =>
70
+ te % "cannot commit nonexistant transaction %s.",
71
+ :cannot_start_empty_block_transaction =>
72
+ te % "cannot start a block transaction with no objects.",
73
+ :cannot_obtain_transaction_lock =>
74
+ te % "cannot obtain transaction lock for #%s.",
75
+ }
45
76
 
46
77
  # = Transaction::Simple for Ruby
47
78
  # Simple object transaction support for Ruby
48
79
  #
49
80
  # == Introduction
50
- #
51
- # Transaction::Simple provides a generic way to add active transactional
81
+ # Transaction::Simple provides a generic way to add active transaction
52
82
  # support to objects. The transaction methods added by this module will
53
83
  # work with most objects, excluding those that cannot be
54
84
  # <i>Marshal</i>ed (bindings, procedure objects, IO instances, or
55
85
  # singleton objects).
56
86
  #
57
87
  # The transactions supported by Transaction::Simple are not backed
58
- # transactions; that is, they have nothing to do with any sort of data
59
- # store. They are "live" transactions occurring in memory and in the
60
- # object itself. This is to allow "test" changes to be made to an object
88
+ # transactions; they are not associated with any sort of data store.
89
+ # They are "live" transactions occurring in memory and in the object
90
+ # itself. This is to allow "test" changes to be made to an object
61
91
  # before making the changes permanent.
62
92
  #
63
- # Transaction::Simple can handle an "infinite" number of transactional
93
+ # Transaction::Simple can handle an "infinite" number of transaction
64
94
  # levels (limited only by memory). If I open two transactions, commit
65
- # the first, but abort the second, the object will revert to the
95
+ # the second, but abort the first, the object will revert to the
66
96
  # original version.
67
97
  #
68
98
  # Transaction::Simple supports "named" transactions, so that multiple
69
99
  # levels of transactions can be committed, aborted, or rewound by
70
100
  # referring to the appropriate name of the transaction. Names may be any
71
- # object *except* +nil+.
101
+ # object *except* +nil+. As with Hash keys, String names will be
102
+ # duplicated and frozen before using.
72
103
  #
73
- # Copyright:: Copyright � 2003 - 2004 by Austin Ziegler
74
- # Version:: 1.2
104
+ # Copyright:: Copyright � 2003 - 2005 by Austin Ziegler
105
+ # Version:: 1.3.0
75
106
  # Licence:: MIT-Style
76
107
  #
77
108
  # Thanks to David Black for help with the initial concept that led to
@@ -80,138 +111,145 @@ module Transaction
80
111
  # == Usage
81
112
  # include 'transaction/simple'
82
113
  #
83
- # v = "Hello, you." # => "Hello, you."
84
- # v.extend(Transaction::Simple) # => "Hello, you."
114
+ # v = "Hello, you." # -> "Hello, you."
115
+ # v.extend(Transaction::Simple) # -> "Hello, you."
85
116
  #
86
- # v.start_transaction # => ... (a Marshal string)
87
- # v.transaction_open? # => true
88
- # v.gsub!(/you/, "world") # => "Hello, world."
117
+ # v.start_transaction # -> ... (a Marshal string)
118
+ # v.transaction_open? # -> true
119
+ # v.gsub!(/you/, "world") # -> "Hello, world."
89
120
  #
90
- # v.rewind_transaction # => "Hello, you."
91
- # v.transaction_open? # => true
121
+ # v.rewind_transaction # -> "Hello, you."
122
+ # v.transaction_open? # -> true
92
123
  #
93
- # v.gsub!(/you/, "HAL") # => "Hello, HAL."
94
- # v.abort_transaction # => "Hello, you."
95
- # v.transaction_open? # => false
124
+ # v.gsub!(/you/, "HAL") # -> "Hello, HAL."
125
+ # v.abort_transaction # -> "Hello, you."
126
+ # v.transaction_open? # -> false
96
127
  #
97
- # v.start_transaction # => ... (a Marshal string)
98
- # v.start_transaction # => ... (a Marshal string)
128
+ # v.start_transaction # -> ... (a Marshal string)
129
+ # v.start_transaction # -> ... (a Marshal string)
99
130
  #
100
- # v.transaction_open? # => true
101
- # v.gsub!(/you/, "HAL") # => "Hello, HAL."
131
+ # v.transaction_open? # -> true
132
+ # v.gsub!(/you/, "HAL") # -> "Hello, HAL."
102
133
  #
103
- # v.commit_transaction # => "Hello, HAL."
104
- # v.transaction_open? # => true
105
- # v.abort_transaction # => "Hello, you."
106
- # v.transaction_open? # => false
134
+ # v.commit_transaction # -> "Hello, HAL."
135
+ # v.transaction_open? # -> true
136
+ # v.abort_transaction # -> "Hello, you."
137
+ # v.transaction_open? # -> false
107
138
  #
108
139
  # == Named Transaction Usage
109
- # v = "Hello, you." # => "Hello, you."
110
- # v.extend(Transaction::Simple) # => "Hello, you."
140
+ # v = "Hello, you." # -> "Hello, you."
141
+ # v.extend(Transaction::Simple) # -> "Hello, you."
111
142
  #
112
- # v.start_transaction(:first) # => ... (a Marshal string)
113
- # v.transaction_open? # => true
114
- # v.transaction_open?(:first) # => true
115
- # v.transaction_open?(:second) # => false
116
- # v.gsub!(/you/, "world") # => "Hello, world."
143
+ # v.start_transaction(:first) # -> ... (a Marshal string)
144
+ # v.transaction_open? # -> true
145
+ # v.transaction_open?(:first) # -> true
146
+ # v.transaction_open?(:second) # -> false
147
+ # v.gsub!(/you/, "world") # -> "Hello, world."
117
148
  #
118
- # v.start_transaction(:second) # => ... (a Marshal string)
119
- # v.gsub!(/world/, "HAL") # => "Hello, HAL."
120
- # v.rewind_transaction(:first) # => "Hello, you."
121
- # v.transaction_open? # => true
122
- # v.transaction_open?(:first) # => true
123
- # v.transaction_open?(:second) # => false
149
+ # v.start_transaction(:second) # -> ... (a Marshal string)
150
+ # v.gsub!(/world/, "HAL") # -> "Hello, HAL."
151
+ # v.rewind_transaction(:first) # -> "Hello, you."
152
+ # v.transaction_open? # -> true
153
+ # v.transaction_open?(:first) # -> true
154
+ # v.transaction_open?(:second) # -> false
124
155
  #
125
- # v.gsub!(/you/, "world") # => "Hello, world."
126
- # v.start_transaction(:second) # => ... (a Marshal string)
127
- # v.gsub!(/world/, "HAL") # => "Hello, HAL."
128
- # v.transaction_name # => :second
129
- # v.abort_transaction(:first) # => "Hello, you."
130
- # v.transaction_open? # => false
156
+ # v.gsub!(/you/, "world") # -> "Hello, world."
157
+ # v.start_transaction(:second) # -> ... (a Marshal string)
158
+ # v.gsub!(/world/, "HAL") # -> "Hello, HAL."
159
+ # v.transaction_name # -> :second
160
+ # v.abort_transaction(:first) # -> "Hello, you."
161
+ # v.transaction_open? # -> false
131
162
  #
132
- # v.start_transaction(:first) # => ... (a Marshal string)
133
- # v.gsub!(/you/, "world") # => "Hello, world."
134
- # v.start_transaction(:second) # => ... (a Marshal string)
135
- # v.gsub!(/world/, "HAL") # => "Hello, HAL."
163
+ # v.start_transaction(:first) # -> ... (a Marshal string)
164
+ # v.gsub!(/you/, "world") # -> "Hello, world."
165
+ # v.start_transaction(:second) # -> ... (a Marshal string)
166
+ # v.gsub!(/world/, "HAL") # -> "Hello, HAL."
136
167
  #
137
- # v.commit_transaction(:first) # => "Hello, HAL."
138
- # v.transaction_open? # => false
168
+ # v.commit_transaction(:first) # -> "Hello, HAL."
169
+ # v.transaction_open? # -> false
139
170
  #
140
171
  # == Block Usage
141
- # include 'transaction/simple'
142
- #
143
- # v = "Hello, you." # => "Hello, you."
172
+ # v = "Hello, you." # -> "Hello, you."
144
173
  # Transaction::Simple.start(v) do |tv|
145
174
  # # v has been extended with Transaction::Simple and an unnamed
146
175
  # # transaction has been started.
147
- # tv.transaction_open? # => true
148
- # tv.gsub!(/you/, "world") # => "Hello, world."
176
+ # tv.transaction_open? # -> true
177
+ # tv.gsub!(/you/, "world") # -> "Hello, world."
149
178
  #
150
- # tv.rewind_transaction # => "Hello, you."
151
- # tv.transaction_open? # => true
179
+ # tv.rewind_transaction # -> "Hello, you."
180
+ # tv.transaction_open? # -> true
152
181
  #
153
- # tv.gsub!(/you/, "HAL") # => "Hello, HAL."
182
+ # tv.gsub!(/you/, "HAL") # -> "Hello, HAL."
154
183
  # # The following breaks out of the transaction block after
155
184
  # # aborting the transaction.
156
- # tv.abort_transaction # => "Hello, you."
185
+ # tv.abort_transaction # -> "Hello, you."
157
186
  # end
158
187
  # # v still has Transaction::Simple applied from here on out.
159
- # v.transaction_open? # => false
188
+ # v.transaction_open? # -> false
160
189
  #
161
190
  # Transaction::Simple.start(v) do |tv|
162
- # tv.start_transaction # => ... (a Marshal string)
191
+ # tv.start_transaction # -> ... (a Marshal string)
163
192
  #
164
- # tv.transaction_open? # => true
165
- # tv.gsub!(/you/, "HAL") # => "Hello, HAL."
193
+ # tv.transaction_open? # -> true
194
+ # tv.gsub!(/you/, "HAL") # -> "Hello, HAL."
166
195
  #
167
196
  # # If #commit_transaction were called without having started a
168
197
  # # second transaction, then it would break out of the transaction
169
198
  # # block after committing the transaction.
170
- # tv.commit_transaction # => "Hello, HAL."
171
- # tv.transaction_open? # => true
172
- # tv.abort_transaction # => "Hello, you."
199
+ # tv.commit_transaction # -> "Hello, HAL."
200
+ # tv.transaction_open? # -> true
201
+ # tv.abort_transaction # -> "Hello, you."
173
202
  # end
174
- # v.transaction_open? # => false
203
+ # v.transaction_open? # -> false
175
204
  #
176
205
  # == Named Transaction Usage
177
- # v = "Hello, you." # => "Hello, you."
178
- # v.extend(Transaction::Simple) # => "Hello, you."
206
+ # v = "Hello, you." # -> "Hello, you."
207
+ # v.extend(Transaction::Simple) # -> "Hello, you."
179
208
  #
180
- # v.start_transaction(:first) # => ... (a Marshal string)
181
- # v.transaction_open? # => true
182
- # v.transaction_open?(:first) # => true
183
- # v.transaction_open?(:second) # => false
184
- # v.gsub!(/you/, "world") # => "Hello, world."
209
+ # v.start_transaction(:first) # -> ... (a Marshal string)
210
+ # v.transaction_open? # -> true
211
+ # v.transaction_open?(:first) # -> true
212
+ # v.transaction_open?(:second) # -> false
213
+ # v.gsub!(/you/, "world") # -> "Hello, world."
185
214
  #
186
- # v.start_transaction(:second) # => ... (a Marshal string)
187
- # v.gsub!(/world/, "HAL") # => "Hello, HAL."
188
- # v.rewind_transaction(:first) # => "Hello, you."
189
- # v.transaction_open? # => true
190
- # v.transaction_open?(:first) # => true
191
- # v.transaction_open?(:second) # => false
215
+ # v.start_transaction(:second) # -> ... (a Marshal string)
216
+ # v.gsub!(/world/, "HAL") # -> "Hello, HAL."
217
+ # v.rewind_transaction(:first) # -> "Hello, you."
218
+ # v.transaction_open? # -> true
219
+ # v.transaction_open?(:first) # -> true
220
+ # v.transaction_open?(:second) # -> false
192
221
  #
193
- # v.gsub!(/you/, "world") # => "Hello, world."
194
- # v.start_transaction(:second) # => ... (a Marshal string)
195
- # v.gsub!(/world/, "HAL") # => "Hello, HAL."
196
- # v.transaction_name # => :second
197
- # v.abort_transaction(:first) # => "Hello, you."
198
- # v.transaction_open? # => false
222
+ # v.gsub!(/you/, "world") # -> "Hello, world."
223
+ # v.start_transaction(:second) # -> ... (a Marshal string)
224
+ # v.gsub!(/world/, "HAL") # -> "Hello, HAL."
225
+ # v.transaction_name # -> :second
226
+ # v.abort_transaction(:first) # -> "Hello, you."
227
+ # v.transaction_open? # -> false
199
228
  #
200
- # v.start_transaction(:first) # => ... (a Marshal string)
201
- # v.gsub!(/you/, "world") # => "Hello, world."
202
- # v.start_transaction(:second) # => ... (a Marshal string)
203
- # v.gsub!(/world/, "HAL") # => "Hello, HAL."
229
+ # v.start_transaction(:first) # -> ... (a Marshal string)
230
+ # v.gsub!(/you/, "world") # -> "Hello, world."
231
+ # v.start_transaction(:second) # -> ... (a Marshal string)
232
+ # v.gsub!(/world/, "HAL") # -> "Hello, HAL."
204
233
  #
205
- # v.commit_transaction(:first) # => "Hello, HAL."
206
- # v.transaction_open? # => false
234
+ # v.commit_transaction(:first) # -> "Hello, HAL."
235
+ # v.transaction_open? # -> false
207
236
  #
208
- # == Contraindications
237
+ # == Thread Safety
238
+ # Threadsafe version of Transaction::Simple and
239
+ # Transaction::Simple::Group exist; these are loaded from
240
+ # 'transaction/simple/threadsafe' and
241
+ # 'transaction/simple/threadsafe/group', respectively, and are
242
+ # represented in Ruby code as Transaction::Simple::ThreadSafe and
243
+ # Transaction::Simple::ThreadSafe::Group, respectively.
209
244
  #
245
+ # == Contraindications
210
246
  # While Transaction::Simple is very useful, it has some severe
211
247
  # limitations that must be understood. Transaction::Simple:
212
248
  #
213
249
  # * uses Marshal. Thus, any object which cannot be <i>Marshal</i>ed
214
- # cannot use Transaction::Simple.
250
+ # cannot use Transaction::Simple. In my experience, this affects
251
+ # singleton objects more often than any other object. It may be that
252
+ # Ruby 2.0 will solve this problem.
215
253
  # * does not manage resources. Resources external to the object and its
216
254
  # instance variables are not managed at all. However, all instance
217
255
  # variables and objects "belonging" to those instance variables are
@@ -224,7 +262,7 @@ module Transaction
224
262
  # multi-threaded applications. If thread safety and atomicity is
225
263
  # absolutely required, use Transaction::Simple::ThreadSafe, which uses
226
264
  # a Mutex object to synchronize the accesses on the object during the
227
- # transactional operations.
265
+ # transaction operations.
228
266
  # * does not necessarily maintain Object#__id__ values on rewind or
229
267
  # abort. This may change for future versions that will be Ruby 1.8 or
230
268
  # better *only*. Certain objects that support #replace will maintain
@@ -233,20 +271,34 @@ module Transaction
233
271
  # objects.
234
272
  #
235
273
  module Simple
236
- VERSION = '1.2.0'
274
+ TRANSACTION_SIMPLE_VERSION = '1.3.0'
237
275
 
238
276
  # Sets the Transaction::Simple debug object. It must respond to #<<.
239
277
  # Sets the transaction debug object. Debugging will be performed
240
278
  # automatically if there's a debug object. The generic transaction
241
279
  # error class.
242
280
  def self.debug_io=(io)
243
- raise TransactionError, "Transaction Error: the transaction debug object must respond to #<<" unless io.respond_to?(:<<)
244
- @tdi = io
281
+ if io.nil?
282
+ @tdi = nil
283
+ @debugging = false
284
+ else
285
+ unless io.respond_to?(:<<)
286
+ raise TransactionError, Messages[:bad_debug_object]
287
+ end
288
+ @tdi = io
289
+ @debugging = true
290
+ end
291
+ end
292
+
293
+ # Returns +true+ if we are debugging.
294
+ def self.debugging?
295
+ @debugging
245
296
  end
246
297
 
247
298
  # Returns the Transaction::Simple debug object. It must respond to
248
299
  # #<<.
249
300
  def self.debug_io
301
+ @tdi ||= ""
250
302
  @tdi
251
303
  end
252
304
 
@@ -257,10 +309,16 @@ module Transaction
257
309
  # transaction that responds to +name+ open.
258
310
  def transaction_open?(name = nil)
259
311
  if name.nil?
260
- Transaction::Simple.debug_io << "Transaction [#{(@__transaction_checkpoint__.nil?) ? 'closed' : 'open'}]\n" unless Transaction::Simple.debug_io.nil?
312
+ if Transaction::Simple.debugging?
313
+ Transaction::Simple.debug_io << "Transaction " <<
314
+ "[#{(@__transaction_checkpoint__.nil?) ? 'closed' : 'open'}]\n"
315
+ end
261
316
  return (not @__transaction_checkpoint__.nil?)
262
317
  else
263
- Transaction::Simple.debug_io << "Transaction(#{name.inspect}) [#{(@__transaction_checkpoint__.nil?) ? 'closed' : 'open'}]\n" unless Transaction::Simple.debug_io.nil?
318
+ if Transaction::Simple.debugging?
319
+ Transaction::Simple.debug_io << "Transaction(#{name.inspect}) " <<
320
+ "[#{(@__transaction_checkpoint__.nil?) ? 'closed' : 'open'}]\n"
321
+ end
264
322
  return ((not @__transaction_checkpoint__.nil?) and @__transaction_names__.include?(name))
265
323
  end
266
324
  end
@@ -268,9 +326,18 @@ module Transaction
268
326
  # Returns the current name of the transaction. Transactions not
269
327
  # explicitly named are named +nil+.
270
328
  def transaction_name
271
- raise TransactionError, "Transaction Error: No transaction open." if @__transaction_checkpoint__.nil?
272
- Transaction::Simple.debug_io << "#{'|' * @__transaction_level__} Transaction Name: #{@__transaction_names__[-1].inspect}\n" unless Transaction::Simple.debug_io.nil?
273
- @__transaction_names__[-1]
329
+ if @__transaction_checkpoint__.nil?
330
+ raise TransactionError, Messages[:no_transaction_open]
331
+ end
332
+ if Transaction::Simple.debugging?
333
+ Transaction::Simple.debug_io << "#{'|' * @__transaction_level__} " <<
334
+ "Transaction Name: #{@__transaction_names__[-1].inspect}\n"
335
+ end
336
+ if @__transaction_names__[-1].kind_of?(String)
337
+ @__transaction_names__[-1].dup
338
+ else
339
+ @__transaction_names__[-1]
340
+ end
274
341
  end
275
342
 
276
343
  # Starts a transaction. Stores the current object state. If a
@@ -283,16 +350,22 @@ module Transaction
283
350
 
284
351
  if name.nil?
285
352
  @__transaction_names__ << nil
286
- ss = ""
353
+ ss = "" if Transaction::Simple.debugging?
287
354
  else
288
- raise TransactionError, "Transaction Error: Named transactions must be unique." if @__transaction_names__.include?(name)
355
+ if @__transaction_names__.include?(name)
356
+ raise TransactionError, Messages[:unique_names]
357
+ end
358
+ name = name.dup.freeze if name.kind_of?(String)
289
359
  @__transaction_names__ << name
290
- ss = "(#{name.inspect})"
360
+ ss = "(#{name.inspect})" if Transaction::Simple.debugging?
291
361
  end
292
362
 
293
363
  @__transaction_level__ += 1
294
364
 
295
- Transaction::Simple.debug_io << "#{'>' * @__transaction_level__} Start Transaction#{ss}\n" unless Transaction::Simple.debug_io.nil?
365
+ if Transaction::Simple.debugging?
366
+ Transaction::Simple.debug_io << "#{'>' * @__transaction_level__} " <<
367
+ "Start Transaction#{ss}\n"
368
+ end
296
369
 
297
370
  @__transaction_checkpoint__ = Marshal.dump(self)
298
371
  end
@@ -301,23 +374,43 @@ module Transaction
301
374
  # intervening transactions will be aborted and the named transaction
302
375
  # will be rewound. Otherwise, only the current transaction is rewound.
303
376
  def rewind_transaction(name = nil)
304
- raise TransactionError, "Transaction Error: Cannot rewind. There is no current transaction." if @__transaction_checkpoint__.nil?
377
+ if @__transaction_checkpoint__.nil?
378
+ raise TransactionError, Messages[:cannot_rewind_no_transaction]
379
+ end
380
+
381
+ # Check to see if we are trying to rewind a transaction that is
382
+ # outside of the current transaction block.
383
+ if @__transaction_block__ and name
384
+ nix = @__transaction_names__.index(name) + 1
385
+ if nix < @__transaction_block__
386
+ raise TransactionError, Messages[:cannot_rewind_transaction_before_block]
387
+ end
388
+ end
389
+
305
390
  if name.nil?
306
391
  __rewind_this_transaction
307
- ss = ""
392
+ ss = "" if Transaction::Simple.debugging?
308
393
  else
309
- raise TransactionError, "Transaction Error: Cannot rewind to transaction #{name.inspect} because it does not exist." unless @__transaction_names__.include?(name)
310
- ss = "(#{name})"
394
+ unless @__transaction_names__.include?(name)
395
+ raise TransactionError, Messages[:cannot_rewind_named_transaction] % name.inspect
396
+ end
397
+ ss = "(#{name})" if Transaction::Simple.debugging?
311
398
 
312
399
  while @__transaction_names__[-1] != name
313
400
  @__transaction_checkpoint__ = __rewind_this_transaction
314
- Transaction::Simple.debug_io << "#{'|' * @__transaction_level__} Rewind Transaction#{ss}\n" unless Transaction::Simple.debug_io.nil?
401
+ if Transaction::Simple.debugging?
402
+ Transaction::Simple.debug_io << "#{'|' * @__transaction_level__} " <<
403
+ "Rewind Transaction#{ss}\n"
404
+ end
315
405
  @__transaction_level__ -= 1
316
406
  @__transaction_names__.pop
317
407
  end
318
408
  __rewind_this_transaction
319
409
  end
320
- Transaction::Simple.debug_io << "#{'|' * @__transaction_level__} Rewind Transaction#{ss}\n" unless Transaction::Simple.debug_io.nil?
410
+ if Transaction::Simple.debugging?
411
+ Transaction::Simple.debug_io << "#{'|' * @__transaction_level__} " <<
412
+ "Rewind Transaction#{ss}\n"
413
+ end
321
414
  self
322
415
  end
323
416
 
@@ -331,14 +424,18 @@ module Transaction
331
424
  # (Transaction::Simple.start), then the execution of the block will be
332
425
  # halted with +break+ +self+.
333
426
  def abort_transaction(name = nil)
334
- raise TransactionError, "Transaction Error: Cannot abort. There is no current transaction." if @__transaction_checkpoint__.nil?
427
+ if @__transaction_checkpoint__.nil?
428
+ raise TransactionError, Messages[:cannot_abort_no_transaction]
429
+ end
335
430
 
336
431
  # Check to see if we are trying to abort a transaction that is
337
432
  # outside of the current transaction block. Otherwise, raise
338
433
  # TransactionAborted if they are the same.
339
434
  if @__transaction_block__ and name
340
435
  nix = @__transaction_names__.index(name) + 1
341
- raise TransactionError, "Transaction Error: Cannot abort a transaction outside of this execution block." if nix < @__transaction_block__
436
+ if nix < @__transaction_block__
437
+ raise TransactionError, Messages[:cannot_abort_transaction_before_block]
438
+ end
342
439
 
343
440
  raise TransactionAborted if @__transaction_block__ == nix
344
441
  end
@@ -348,8 +445,9 @@ module Transaction
348
445
  if name.nil?
349
446
  __abort_transaction(name)
350
447
  else
351
- raise TransactionError, "Transaction Error: Cannot abort nonexistant transaction #{name.inspect}." unless @__transaction_names__.include?(name)
352
-
448
+ unless @__transaction_names__.include?(name)
449
+ raise TransactionError, Messages[:cannot_abort_named_transaction] % name.inspect
450
+ end
353
451
  __abort_transaction(name) while @__transaction_names__.include?(name)
354
452
  end
355
453
  self
@@ -362,14 +460,19 @@ module Transaction
362
460
  # transactions, then all transactions are closed and committed until
363
461
  # the named transaction is reached.
364
462
  def commit_transaction(name = nil)
365
- raise TransactionError, "Transaction Error: Cannot commit. There is no current transaction." if @__transaction_checkpoint__.nil?
463
+ if @__transaction_checkpoint__.nil?
464
+ raise TransactionError, Messages[:cannot_commit_no_transaction]
465
+ end
466
+ @__transaction_block__ ||= nil
366
467
 
367
468
  # Check to see if we are trying to commit a transaction that is
368
469
  # outside of the current transaction block. Otherwise, raise
369
470
  # TransactionCommitted if they are the same.
370
471
  if @__transaction_block__ and name
371
472
  nix = @__transaction_names__.index(name) + 1
372
- raise TransactionError, "Transaction Error: Cannot commit a transaction outside of this execution block." if nix < @__transaction_block__
473
+ if nix < @__transaction_block__
474
+ raise TransactionError, Messages[:cannot_commit_transaction_before_block]
475
+ end
373
476
 
374
477
  raise TransactionCommitted if @__transaction_block__ == nix
375
478
  end
@@ -377,18 +480,29 @@ module Transaction
377
480
  raise TransactionCommitted if @__transaction_block__ == @__transaction_level__
378
481
 
379
482
  if name.nil?
380
- ss = ""
483
+ ss = "" if Transaction::Simple.debugging?
381
484
  __commit_transaction
382
- Transaction::Simple.debug_io << "#{'<' * @__transaction_level__} Commit Transaction#{ss}\n" unless Transaction::Simple.debug_io.nil?
485
+ if Transaction::Simple.debugging?
486
+ Transaction::Simple.debug_io << "#{'<' * @__transaction_level__} " <<
487
+ "Commit Transaction#{ss}\n"
488
+ end
383
489
  else
384
- raise TransactionError, "Transaction Error: Cannot commit nonexistant transaction #{name.inspect}." unless @__transaction_names__.include?(name)
385
- ss = "(#{name})"
490
+ unless @__transaction_names__.include?(name)
491
+ raise TransactionError, Messages[:cannot_commit_named_transaction] % name.inspect
492
+ end
493
+ ss = "(#{name})" if Transaction::Simple.debugging?
386
494
 
387
495
  while @__transaction_names__[-1] != name
388
- Transaction::Simple.debug_io << "#{'<' * @__transaction_level__} Commit Transaction#{ss}\n" unless Transaction::Simple.debug_io.nil?
496
+ if Transaction::Simple.debugging?
497
+ Transaction::Simple.debug_io << "#{'<' * @__transaction_level__} " <<
498
+ "Commit Transaction#{ss}\n"
499
+ end
389
500
  __commit_transaction
390
501
  end
391
- Transaction::Simple.debug_io << "#{'<' * @__transaction_level__} Commit Transaction#{ss}\n" unless Transaction::Simple.debug_io.nil?
502
+ if Transaction::Simple.debugging?
503
+ Transaction::Simple.debug_io << "#{'<' * @__transaction_level__} " <<
504
+ "Commit Transaction#{ss}\n"
505
+ end
392
506
  __commit_transaction
393
507
  end
394
508
 
@@ -421,9 +535,20 @@ module Transaction
421
535
  end
422
536
  end
423
537
 
538
+ # Allows specific variables to be excluded from transaction support.
539
+ # Must be done after extending the object but before starting the
540
+ # first transaction on the object.
541
+ #
542
+ # vv.transaction_exclusions << "@io"
543
+ def transaction_exclusions
544
+ @transaction_exclusions ||= []
545
+ end
546
+
424
547
  class << self
425
548
  def __common_start(name, vars, &block)
426
- raise TransactionError, "Transaction Error: Cannot start a transaction with no objects." if vars.empty?
549
+ if vars.empty?
550
+ raise TransactionError, Messages[:cannot_start_empty_block_transaction]
551
+ end
427
552
 
428
553
  if block
429
554
  begin
@@ -436,7 +561,7 @@ module Transaction
436
561
  vv.instance_variable_set(:@__transaction_block__, vlevel[vv.__id__])
437
562
  end
438
563
 
439
- yield *vars
564
+ yield(*vars)
440
565
  rescue TransactionAborted
441
566
  vars.each do |vv|
442
567
  if name.nil? and vv.transaction_open?
@@ -490,12 +615,15 @@ module Transaction
490
615
  @__transaction_checkpoint__ = __rewind_this_transaction
491
616
 
492
617
  if name.nil?
493
- ss = ""
618
+ ss = "" if Transaction::Simple.debugging?
494
619
  else
495
- ss = "(#{name.inspect})"
620
+ ss = "(#{name.inspect})" if Transaction::Simple.debugging?
496
621
  end
497
622
 
498
- Transaction::Simple.debug_io << "#{'<' * @__transaction_level__} Abort Transaction#{ss}\n" unless Transaction::Simple.debug_io.nil?
623
+ if Transaction::Simple.debugging?
624
+ Transaction::Simple.debug_io << "#{'<' * @__transaction_level__} " <<
625
+ "Abort Transaction#{ss}\n"
626
+ end
499
627
  @__transaction_level__ -= 1
500
628
  @__transaction_names__.pop
501
629
  if @__transaction_level__ < 1
@@ -508,27 +636,37 @@ module Transaction
508
636
  SKIP_TRANSACTION_VARS = [TRANSACTION_CHECKPOINT, "@__transaction_level__"] #:nodoc:
509
637
 
510
638
  def __rewind_this_transaction #:nodoc:
511
- r = Marshal.restore(@__transaction_checkpoint__)
639
+ rr = Marshal.restore(@__transaction_checkpoint__)
512
640
 
513
641
  begin
514
- self.replace(r) if respond_to?(:replace)
642
+ self.replace(rr) if respond_to?(:replace)
515
643
  rescue
516
644
  nil
517
645
  end
518
646
 
519
- r.instance_variables.each do |i|
520
- next if SKIP_TRANSACTION_VARS.include?(i)
647
+ rr.instance_variables.each do |vv|
648
+ next if SKIP_TRANSACTION_VARS.include?(vv)
649
+ next if self.transaction_exclusions.include?(vv)
521
650
  if respond_to?(:instance_variable_get)
522
- instance_variable_set(i, r.instance_variable_get(i))
651
+ instance_variable_set(vv, rr.instance_variable_get(vv))
523
652
  else
524
- instance_eval(%q|#{i} = r.instance_eval("#{i}")|)
653
+ instance_eval(%q|#{vv} = rr.instance_eval("#{vv}")|)
654
+ end
655
+ end
656
+
657
+ new_ivar = instance_variables - rr.instance_variables - SKIP_TRANSACTION_VARS
658
+ new_ivar.each do |vv|
659
+ if respond_to?(:instance_variable_set)
660
+ instance_variable_set(vv, nil)
661
+ else
662
+ instance_eval(%q|#{vv} = nil|)
525
663
  end
526
664
  end
527
665
 
528
666
  if respond_to?(:instance_variable_get)
529
- return r.instance_variable_get(TRANSACTION_CHECKPOINT)
667
+ rr.instance_variable_get(TRANSACTION_CHECKPOINT)
530
668
  else
531
- return r.instance_eval(TRANSACTION_CHECKPOINT)
669
+ rr.instance_eval(TRANSACTION_CHECKPOINT)
532
670
  end
533
671
  end
534
672
 
@@ -541,51 +679,15 @@ module Transaction
541
679
 
542
680
  @__transaction_level__ -= 1
543
681
  @__transaction_names__.pop
682
+
544
683
  if @__transaction_level__ < 1
545
684
  @__transaction_level__ = 0
546
685
  @__transaction_names__ = []
547
686
  end
548
687
  end
549
688
 
550
- private :__abort_transaction, :__rewind_this_transaction, :__commit_transaction
551
-
552
- # = Transaction::Simple::ThreadSafe
553
- # Thread-safe simple object transaction support for Ruby.
554
- # Transaction::Simple::ThreadSafe is used in the same way as
555
- # Transaction::Simple. Transaction::Simple::ThreadSafe uses a Mutex
556
- # object to ensure atomicity at the cost of performance in threaded
557
- # applications.
558
- #
559
- # Transaction::Simple::ThreadSafe will not wait to obtain a lock; if the
560
- # lock cannot be obtained immediately, a
561
- # Transaction::TransactionThreadError will be raised.
562
- #
563
- # Thanks to Mauricio Fern�ndez for help with getting this part working.
564
- module ThreadSafe
565
- VERSION = '1.2.0'
566
-
567
- include Transaction::Simple
568
-
569
- SKIP_TRANSACTION_VARS = Transaction::Simple::SKIP_TRANSACTION_VARS.dup #:nodoc:
570
- SKIP_TRANSACTION_VARS << "@__transaction_mutex__"
571
-
572
- Transaction::Simple.instance_methods(false) do |meth|
573
- next if meth == "transaction"
574
- arg = "(name = nil)" unless meth == "transaction_name"
575
- module_eval <<-EOS
576
- def #{meth}#{arg}
577
- if (@__transaction_mutex__ ||= Mutex.new).try_lock
578
- result = super
579
- @__transaction_mutex__.unlock
580
- return result
581
- else
582
- raise TransactionThreadError, "Transaction Error: Cannot obtain lock for ##{meth}"
583
- end
584
- ensure
585
- @__transaction_mutex__.unlock
586
- end
587
- EOS
588
- end
589
- end
689
+ private :__abort_transaction
690
+ private :__rewind_this_transaction
691
+ private :__commit_transaction
590
692
  end
591
693
  end