transaction-simple 1.4.0 → 1.4.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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