transaction-simple 1.4.0 → 1.4.0.2

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.
@@ -1,61 +1,49 @@
1
- #--
2
- # Transaction::Simple
3
- # Simple object transaction support for Ruby
4
- # http://rubyforge.org/projects/trans-simple/
5
- # Version 1.4.0
1
+ # -*- ruby encoding: utf-8 -*-
2
+
3
+ require 'transaction/simple'
4
+
5
+ # A transaction group is an object wrapper that manages a group of objects
6
+ # as if they were a single object for the purpose of transaction management.
7
+ # All transactions for this group of objects should be performed against the
8
+ # transaction group object, not against individual objects in the group.
6
9
  #
7
- # Licensed under a MIT-style licence. See Licence.txt in the main
8
- # distribution for full licensing information.
10
+ # == Transaction Group Usage
11
+ # require 'transaction/simple/group'
9
12
  #
10
- # Copyright (c) 2003 - 2007 Austin Ziegler
13
+ # x = "Hello, you."
14
+ # y = "And you, too."
11
15
  #
12
- # $Id: group.rb 47 2007-02-03 15:02:51Z austin $
13
- #++
14
- require 'transaction/simple'
15
-
16
- # A transaction group is an object wrapper that manages a group of objects
17
- # as if they were a single object for the purpose of transaction
18
- # management. All transactions for this group of objects should be
19
- # performed against the transaction group object, not against individual
20
- # objects in the group.
21
- #
22
- # == Transaction Group Usage
23
- # require 'transaction/simple/group'
24
- #
25
- # x = "Hello, you."
26
- # y = "And you, too."
27
- #
28
- # g = Transaction::Simple::Group.new(x, y)
29
- # g.start_transaction(:first) # -> [ x, y ]
30
- # g.transaction_open?(:first) # -> true
31
- # x.transaction_open?(:first) # -> true
32
- # y.transaction_open?(:first) # -> true
33
- #
34
- # x.gsub!(/you/, "world") # -> "Hello, world."
35
- # y.gsub!(/you/, "me") # -> "And me, too."
36
- #
37
- # g.start_transaction(:second) # -> [ x, y ]
38
- # x.gsub!(/world/, "HAL") # -> "Hello, HAL."
39
- # y.gsub!(/me/, "Dave") # -> "And Dave, too."
40
- # g.rewind_transaction(:second) # -> [ x, y ]
41
- # x # -> "Hello, world."
42
- # y # -> "And me, too."
43
- #
44
- # x.gsub!(/world/, "HAL") # -> "Hello, HAL."
45
- # y.gsub!(/me/, "Dave") # -> "And Dave, too."
46
- #
47
- # g.commit_transaction(:second) # -> [ x, y ]
48
- # x # -> "Hello, HAL."
49
- # y # -> "And Dave, too."
50
- #
51
- # g.abort_transaction(:first) # -> [ x, y ]
52
- # x = -> "Hello, you."
53
- # y = -> "And you, too."
16
+ # g = Transaction::Simple::Group.new(x, y)
17
+ # g.start_transaction(:first) # -> [ x, y ]
18
+ # g.transaction_open?(:first) # -> true
19
+ # x.transaction_open?(:first) # -> true
20
+ # y.transaction_open?(:first) # -> true
21
+ #
22
+ # x.gsub!(/you/, "world") # -> "Hello, world."
23
+ # y.gsub!(/you/, "me") # -> "And me, too."
24
+ #
25
+ # g.start_transaction(:second) # -> [ x, y ]
26
+ # x.gsub!(/world/, "HAL") # -> "Hello, HAL."
27
+ # y.gsub!(/me/, "Dave") # -> "And Dave, too."
28
+ # g.rewind_transaction(:second) # -> [ x, y ]
29
+ # x # -> "Hello, world."
30
+ # y # -> "And me, too."
31
+ #
32
+ # x.gsub!(/world/, "HAL") # -> "Hello, HAL."
33
+ # y.gsub!(/me/, "Dave") # -> "And Dave, too."
34
+ #
35
+ # g.commit_transaction(:second) # -> [ x, y ]
36
+ # x # -> "Hello, HAL."
37
+ # y # -> "And Dave, too."
38
+ #
39
+ # g.abort_transaction(:first) # -> [ x, y ]
40
+ # x = -> "Hello, you."
41
+ # y = -> "And you, too."
54
42
  class Transaction::Simple::Group
55
- # Creates a transaction group for the provided objects. If a block is
56
- # provided, the transaction group object is yielded to the block; when
57
- # the block is finished, the transaction group object will be cleared
58
- # with #clear.
43
+ # Creates a transaction group for the provided objects. If a block is
44
+ # provided, the transaction group object is yielded to the block; when the
45
+ # block is finished, the transaction group object will be cleared with
46
+ # #clear.
59
47
  def initialize(*objects)
60
48
  @objects = objects || []
61
49
  @objects.freeze
@@ -70,77 +58,79 @@ class Transaction::Simple::Group
70
58
  end
71
59
  end
72
60
 
73
- # Returns the objects that are covered by this transaction group.
61
+ # Returns the objects that are covered by this transaction group.
74
62
  attr_reader :objects
75
63
 
76
- # Clears the object group. Removes references to the objects so that
77
- # they can be garbage collected.
64
+ # Clears the object group. Removes references to the objects so that they
65
+ # can be garbage collected.
78
66
  def clear
79
67
  @objects = @objects.dup.clear
80
68
  end
81
69
 
82
- # Tests to see if all of the objects in the group have an open
83
- # transaction. See Transaction::Simple#transaction_open? for more
84
- # information.
70
+ # Tests to see if all of the objects in the group have an open
71
+ # transaction. See Transaction::Simple#transaction_open? for more
72
+ # information.
85
73
  def transaction_open?(name = nil)
86
74
  @objects.inject(true) do |val, obj|
87
75
  val = val and obj.transaction_open?(name)
88
76
  end
89
77
  end
90
78
 
91
- # Returns the current name of the transaction for the group.
92
- # Transactions not explicitly named are named +nil+.
79
+ # Returns the current name of the transaction for the group. Transactions
80
+ # not explicitly named are named +nil+.
93
81
  def transaction_name
94
82
  @objects[0].transaction_name
95
83
  end
96
84
 
97
- # Starts a transaction for the group. Stores the current object state.
98
- # If a transaction name is specified, the transaction will be named.
99
- # Transaction names must be unique. Transaction names of +nil+ will be
100
- # treated as unnamed transactions.
85
+ # Starts a transaction for the group. Stores the current object state. If
86
+ # a transaction name is specified, the transaction will be named.
87
+ # Transaction names must be unique. Transaction names of +nil+ will be
88
+ # treated as unnamed transactions.
101
89
  def start_transaction(name = nil)
102
90
  @objects.each { |obj| obj.start_transaction(name) }
103
91
  end
104
92
 
105
- # Rewinds the transaction. If +name+ is specified, then the intervening
106
- # transactions will be aborted and the named transaction will be
107
- # rewound. Otherwise, only the current transaction is rewound.
93
+ # Rewinds the transaction. If +name+ is specified, then the intervening
94
+ # transactions will be aborted and the named transaction will be rewound.
95
+ # Otherwise, only the current transaction is rewound.
108
96
  def rewind_transaction(name = nil)
109
97
  @objects.each { |obj| obj.rewind_transaction(name) }
110
98
  end
111
99
 
112
- # Aborts the transaction. Resets the object state to what it was before
113
- # the transaction was started and closes the transaction. If +name+ is
114
- # specified, then the intervening transactions and the named transaction
115
- # will be aborted. Otherwise, only the current transaction is aborted.
116
- #
117
- # If the current or named transaction has been started by a block
118
- # (Transaction::Simple.start), then the execution of the block will be
119
- # halted with +break+ +self+.
100
+ # Aborts the transaction. Resets the object state to what it was before
101
+ # the transaction was started and closes the transaction. If +name+ is
102
+ # specified, then the intervening transactions and the named transaction
103
+ # will be aborted. Otherwise, only the current transaction is aborted.
104
+ #
105
+ # If the current or named transaction has been started by a block
106
+ # (Transaction::Simple.start), then the execution of the block will be
107
+ # halted with +break+ +self+.
120
108
  def abort_transaction(name = nil)
121
109
  @objects.each { |obj| obj.abort_transaction(name) }
122
110
  end
123
111
 
124
- # If +name+ is +nil+ (default), the current transaction level is closed
125
- # out and the changes are committed.
126
- #
127
- # If +name+ is specified and +name+ is in the list of named
128
- # transactions, then all transactions are closed and committed until the
129
- # named transaction is reached.
112
+ # If +name+ is +nil+ (default), the current transaction level is closed
113
+ # out and the changes are committed.
114
+ #
115
+ # If +name+ is specified and +name+ is in the list of named transactions,
116
+ # then all transactions are closed and committed until the named
117
+ # transaction is reached.
130
118
  def commit_transaction(name = nil)
131
119
  @objects.each { |obj| obj.commit_transaction(name) }
132
120
  end
133
121
 
134
- # Alternative method for calling the transaction methods. An optional
135
- # name can be specified for named transaction support.
136
- #
137
- # #transaction(:start):: #start_transaction
138
- # #transaction(:rewind):: #rewind_transaction
139
- # #transaction(:abort):: #abort_transaction
140
- # #transaction(:commit):: #commit_transaction
141
- # #transaction(:name):: #transaction_name
142
- # #transaction:: #transaction_open?
122
+ # Alternative method for calling the transaction methods. An optional name
123
+ # can be specified for named transaction support.
124
+ #
125
+ # #transaction(:start):: #start_transaction
126
+ # #transaction(:rewind):: #rewind_transaction
127
+ # #transaction(:abort):: #abort_transaction
128
+ # #transaction(:commit):: #commit_transaction
129
+ # #transaction(:name):: #transaction_name
130
+ # #transaction:: #transaction_open?
143
131
  def transaction(action = nil, name = nil)
144
132
  @objects.each { |obj| obj.transaction(action, name) }
145
133
  end
146
134
  end
135
+
136
+ # vim: syntax=ruby
@@ -1,16 +1,5 @@
1
- #--
2
- # Transaction::Simple
3
- # Simple object transaction support for Ruby
4
- # http://rubyforge.org/projects/trans-simple/
5
- # Version 1.4.0
6
- #
7
- # Licensed under a MIT-style licence. See Licence.txt in the main
8
- # distribution for full licensing information.
9
- #
10
- # Copyright (c) 2003 - 2007 Austin Ziegler
11
- #
12
- # $Id: threadsafe.rb 50 2007-02-03 20:26:19Z austin $
13
- #++
1
+ # -*- ruby encoding: utf-8 -*-
2
+
14
3
  require 'transaction/simple'
15
4
  require 'thread'
16
5
 
@@ -20,28 +9,27 @@ module Transaction
20
9
  class TransactionThreadError < TransactionError; end
21
10
  end
22
11
 
23
- # = Transaction::Simple::ThreadSafe
24
- # Thread-safe simple object transaction support for Ruby.
25
- # Transaction::Simple::ThreadSafe is used in the same way as
26
- # Transaction::Simple. Transaction::Simple::ThreadSafe uses a Mutex object
27
- # to ensure atomicity at the cost of performance in threaded applications.
28
- #
29
- # Transaction::Simple::ThreadSafe will not wait to obtain a lock; if the
30
- # lock cannot be obtained immediately, a
31
- # Transaction::TransactionThreadError will be raised.
32
- #
33
- # Thanks to Mauricio Fernandez for help with getting this part working.
34
- #
35
- # Threadsafe transactions can be used in any place that normal
36
- # transactions would. The main difference would be in setup:
37
- #
38
- # require 'transaction/simple/threadsafe'
39
- #
40
- # x = "Hello, you."
41
- # x.extend(Transaction::Simple::ThreadSafe) # Threadsafe
42
- #
43
- # y = "Hello, you."
44
- # y.extend(Transaction::Simple) # Not threadsafe
12
+ # Thread-safe simple object transaction support for Ruby.
13
+ # Transaction::Simple::ThreadSafe is used in the same way as
14
+ # Transaction::Simple. Transaction::Simple::ThreadSafe uses a Mutex object
15
+ # to ensure atomicity at the cost of performance in threaded applications.
16
+ #
17
+ # Transaction::Simple::ThreadSafe will not wait to obtain a lock; if the
18
+ # lock cannot be obtained immediately, a Transaction::TransactionThreadError
19
+ # will be raised.
20
+ #
21
+ # Thanks to Mauricio Fernandez for help with getting this part working.
22
+ #
23
+ # Threadsafe transactions can be used in any place that normal transactions
24
+ # would. The main difference would be in setup:
25
+ #
26
+ # require 'transaction/simple/threadsafe'
27
+ #
28
+ # x = "Hello, you."
29
+ # x.extend(Transaction::Simple::ThreadSafe) # Threadsafe
30
+ #
31
+ # y = "Hello, you."
32
+ # y.extend(Transaction::Simple) # Not threadsafe
45
33
  module Transaction::Simple::ThreadSafe
46
34
  include Transaction::Simple
47
35
 
@@ -66,3 +54,5 @@ module Transaction::Simple::ThreadSafe
66
54
  EOS
67
55
  end
68
56
  end
57
+
58
+ # vim: syntax=ruby
@@ -1,24 +1,12 @@
1
- #--
2
- # Transaction::Simple
3
- # Simple object transaction support for Ruby
4
- # http://rubyforge.org/projects/trans-simple/
5
- # Version 1.4.0
6
- #
7
- # Licensed under a MIT-style licence. See Licence.txt in the main
8
- # distribution for full licensing information.
9
- #
10
- # Copyright (c) 2003 - 2007 Austin Ziegler
11
- #
12
- # $Id: group.rb 47 2007-02-03 15:02:51Z austin $
13
- #++
1
+ # -*- ruby encoding: utf-8 -*-
2
+
14
3
  require 'transaction/simple/threadsafe'
15
4
 
16
- # A transaction group is an object wrapper that manages a group of objects
17
- # as if they were a single object for the purpose of transaction
18
- # management. All transactions for this group of objects should be
19
- # performed against the transaction group object, not against individual
20
- # objects in the group. This is the threadsafe version of a transaction
21
- # group.
5
+ # A transaction group is an object wrapper that manages a group of objects
6
+ # as if they were a single object for the purpose of transaction management.
7
+ # All transactions for this group of objects should be performed against the
8
+ # transaction group object, not against individual objects in the group.
9
+ # This is the threadsafe version of a transaction group.
22
10
  class Transaction::Simple::ThreadSafe::Group < Transaction::Simple::Group
23
11
  def initialize(*objects)
24
12
  @objects = objects || []
@@ -34,3 +22,5 @@ class Transaction::Simple::ThreadSafe::Group < Transaction::Simple::Group
34
22
  end
35
23
  end
36
24
  end
25
+
26
+ # vim: syntax=ruby
@@ -0,0 +1,22 @@
1
+ # -*- ruby encoding: utf-8 -*-
2
+
3
+ # Provided by Nobu Nakada.
4
+ # You can use this code for previous versions.
5
+
6
+ unless defined?(instance_variable_defined?)
7
+ module Kernel
8
+ (t = Object.new).instance_eval { @instance_variable = 1 }
9
+ case t.instance_variables[0]
10
+ when Symbol
11
+ def instance_variable_defined?(var)
12
+ instance_variables.include?(var.to_sym)
13
+ end
14
+ when String
15
+ def instance_variable_defined?(var)
16
+ instance_variables.include?(var.to_s)
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ # vim: syntax=ruby
@@ -0,0 +1,42 @@
1
+ # -*- ruby encoding: utf-8 -*-
2
+
3
+ load 'special-dumpable.rb'
4
+
5
+ class Child
6
+ attr_accessor :parent
7
+ end
8
+
9
+ class Parent < String
10
+ include SpecialDumpable
11
+
12
+ attr_reader :children
13
+ def initialize(value)
14
+ super
15
+ @children = []
16
+ end
17
+
18
+ def << child
19
+ child.parent = self
20
+ @children << child
21
+ end
22
+ end
23
+
24
+ parent = Parent.new("gold")
25
+ puts "parent(#{parent}).object_id: #{parent.object_id}"
26
+ parent << Child.new
27
+ puts "parent(#{parent}).children[0].parent.object_id: #{parent.children[0].parent.object_id}"
28
+ puts "starting transaction with childcount #{parent.children.size}"
29
+ s = parent.special_dump
30
+ parent << Child.new
31
+ parent.gsub!(/gold/, 'pyrite')
32
+ puts "parent(#{parent}).children[1].parent.object_id: #{parent.children[1].parent.object_id}"
33
+ puts "aborting transaction with childcount #{parent.children.size}"
34
+ parent.special_restore s
35
+ puts "parent(#{parent})"
36
+ puts "aborted transaction with childcount #{parent.children.size}"
37
+ puts "parent.object_id: #{parent.object_id}"
38
+ puts "parent.children[0].parent.object_id: #{parent.children[0].parent.object_id}"
39
+ parent << Child.new
40
+ puts "parent.children[1].parent.object_id: #{parent.children[1].parent.object_id}"
41
+
42
+ # vim: syntax=ruby
@@ -0,0 +1,130 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- ruby encoding: utf-8 -*-
3
+ #
4
+ # SpecialDumpable - workaround for a problem in Transaction::Simple
5
+ #
6
+ # (C) 2006 Pit Capitain
7
+ #
8
+ # For a description of the problem see http://www.halostatue.ca/2006/10/22/
9
+ # ruby-conference-2006-day-1-evening-friday-20-october-2006/
10
+ # and http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/155092
11
+ #
12
+ # The workaround:
13
+ #
14
+ # The objects that are restored from Marshal shouldn't reference the newly
15
+ # created root object, but the original root object. After studying the
16
+ # source code of Marshal, I found that this could be achieved with a
17
+ # special _load method of the root object's class which returns the
18
+ # original root object. Then the original root object would be stored in
19
+ # the internal list of restored objects and references to the root object
20
+ # would use the original one.
21
+ #
22
+ # See SpecialDumpable::ClassMethods#_load
23
+ #
24
+ # In order to get at the original root object from a class method, we store
25
+ # its object_id in the Marshal string.
26
+ #
27
+ # See SpecialDumpable#_dump
28
+ #
29
+ # Note: this means that classes with their own _load and _dump methods
30
+ # cannot be used with this implementation. I think it should be possible to
31
+ # enhance the implementation to support these classes, too.
32
+ #
33
+ # Using those two methods, the objects restored from Marshal really
34
+ # reference the original root object. But this is not enough. We have to
35
+ # restore the instance variables of the root object, too. The _dump method
36
+ # from above only dumps the object_id of the root object, not its instance
37
+ # variables.
38
+ #
39
+ # For the instance variables, we create a new object and store the instance
40
+ # variables of the root object there. Then we not only serialize the root
41
+ # object, but an array with both the root object and the object with the
42
+ # instance variables.
43
+ #
44
+ # See SpecialDumpable#special_dump
45
+ #
46
+ # The root object dumps its object_id, and the object with the instance
47
+ # variables dumps the instance variables of the root object.
48
+ #
49
+ # When restoring the objects, Marshal creates an array with the root object
50
+ # plus an object with the restored instance variables of the root object.
51
+ # We only need to replace the current instance variables of the root object
52
+ # with the restored instance variables to get the desired behaviour.
53
+ #
54
+ # See SpecialDumpable#special_restore
55
+ #
56
+ # That's it.
57
+
58
+ module SpecialDumpable
59
+ def self.included base
60
+ base.extend ClassMethods
61
+ end
62
+
63
+ module ClassMethods
64
+ def _load source
65
+ ObjectSpace._id2ref source.to_i
66
+ end
67
+ end
68
+
69
+ def _dump limit
70
+ object_id.to_s
71
+ end
72
+
73
+ def special_dump
74
+ value_holder = Object.new
75
+ SpecialDumpable.copy_instance_variables self, value_holder
76
+ Marshal.dump [ self, value_holder ]
77
+ end
78
+
79
+ def special_restore source
80
+ self_that_can_be_ignored, value_holder = Marshal.restore source
81
+ SpecialDumpable.copy_instance_variables value_holder, self
82
+ self
83
+ end
84
+
85
+ def self.copy_instance_variables from, to
86
+ from.instance_variables.each do |var|
87
+ val = from.instance_variable_get var
88
+ to.instance_variable_set var, val
89
+ end
90
+ end
91
+
92
+ end
93
+
94
+ if __FILE__ == $0
95
+ class Child
96
+ attr_accessor :parent
97
+ end
98
+
99
+ class Parent
100
+ include SpecialDumpable
101
+
102
+ attr_reader :children
103
+ def initialize
104
+ @children = []
105
+ end
106
+
107
+ def << child
108
+ child.parent = self
109
+ @children << child
110
+ end
111
+ end
112
+
113
+ parent = Parent.new
114
+ puts "parent.object_id: #{parent.object_id}"
115
+ parent << Child.new
116
+ puts "parent.children[0].parent.object_id: #{parent.children[0].parent.object_id}"
117
+ puts "starting transaction with childcount #{parent.children.size}"
118
+ s = parent.special_dump
119
+ parent << Child.new
120
+ puts "parent.children[1].parent.object_id: #{parent.children[1].parent.object_id}"
121
+ puts "aborting transaction with childcount #{parent.children.size}"
122
+ parent.special_restore s
123
+ puts "aborted transaction with childcount #{parent.children.size}"
124
+ puts "parent.object_id: #{parent.object_id}"
125
+ puts "parent.children[0].parent.object_id: #{parent.children[0].parent.object_id}"
126
+ parent << Child.new
127
+ puts "parent.children[1].parent.object_id: #{parent.children[1].parent.object_id}"
128
+ end
129
+
130
+ # vim: syntax=ruby