Path: | Readme |
Last Update: | Thu May 05 12:16:49 -0400 2005 |
Transaction::Simple provides a generic way to add active transaction support to objects. The transaction methods added by this module will work with most objects, excluding those that cannot be Marshal-ed (bindings, procedure objects, IO instances, or singleton objects).
The transactions supported by Transaction::Simple are not backend transaction; that is, they are not associated with any sort of data store. They are "live" transactions occurring in memory and in the object itself. This is to allow "test" changes to be made to an object before making the changes permanent.
Transaction::Simple can handle an "infinite" number of transaction levels (limited only by memory). If I open two transactions, commit the second, but abort the first, the object will revert to the original version.
Transaction::Simple supports "named" transactions, so that multiple levels of transactions can be committed, aborted, or rewound by referring to the appropriate name of the transaction. Names may be any object except nil.
Version 1.3.0 of Transaction::Simple adds transaction groups. A transaction group is an object wrapper that manages a group of objects as if they were a single object for the purpose of transaction management. All transactions for this group of objects should be performed against the transaction group object, not against individual objects in the group.
Copyright: Copyright © 2003 - 2005 by Austin Ziegler Version: 1.3.0 Licence: MIT-Style
Thanks to David Black and Mauricio Fernández for their help with this library.
include 'transaction/simple' v = "Hello, you." # -> "Hello, you." v.extend(Transaction::Simple) # -> "Hello, you." v.start_transaction # -> ... (a Marshal string) v.transaction_open? # -> true v.gsub!(/you/, "world") # -> "Hello, world." v.rewind_transaction # -> "Hello, you." v.transaction_open? # -> true v.gsub!(/you/, "HAL") # -> "Hello, HAL." v.abort_transaction # -> "Hello, you." v.transaction_open? # -> false v.start_transaction # -> ... (a Marshal string) v.start_transaction # -> ... (a Marshal string) v.transaction_open? # -> true v.gsub!(/you/, "HAL") # -> "Hello, HAL." v.commit_transaction # -> "Hello, HAL." v.transaction_open? # -> true v.abort_transaction # -> "Hello, you." v.transaction_open? # -> false
v = "Hello, you." # -> "Hello, you." v.extend(Transaction::Simple) # -> "Hello, you." v.start_transaction(:first) # -> ... (a Marshal string) v.transaction_open? # -> true v.transaction_open?(:first) # -> true v.transaction_open?(:second) # -> false v.gsub!(/you/, "world") # -> "Hello, world." v.start_transaction(:second) # -> ... (a Marshal string) v.gsub!(/world/, "HAL") # -> "Hello, HAL." v.rewind_transaction(:first) # -> "Hello, you." v.transaction_open? # -> true v.transaction_open?(:first) # -> true v.transaction_open?(:second) # -> false v.gsub!(/you/, "world") # -> "Hello, world." v.start_transaction(:second) # -> ... (a Marshal string) v.gsub!(/world/, "HAL") # -> "Hello, HAL." v.transaction_name # -> :second v.abort_transaction(:first) # -> "Hello, you." v.transaction_open? # -> false v.start_transaction(:first) # -> ... (a Marshal string) v.gsub!(/you/, "world") # -> "Hello, world." v.start_transaction(:second) # -> ... (a Marshal string) v.gsub!(/world/, "HAL") # -> "Hello, HAL." v.commit_transaction(:first) # -> "Hello, HAL." v.transaction_open? # -> false
v = "Hello, you." # -> "Hello, you." Transaction::Simple.start(v) do |tv| # v has been extended with Transaction::Simple and an unnamed # transaction has been started. tv.transaction_open? # -> true tv.gsub!(/you/, "world") # -> "Hello, world." tv.rewind_transaction # -> "Hello, you." tv.transaction_open? # -> true tv.gsub!(/you/, "HAL") # -> "Hello, HAL." # The following breaks out of the transaction block after # aborting the transaction. tv.abort_transaction # -> "Hello, you." end # v still has Transaction::Simple applied from here on out. v.transaction_open? # -> false Transaction::Simple.start(v) do |tv| tv.start_transaction # -> ... (a Marshal string) tv.transaction_open? # -> true tv.gsub!(/you/, "HAL") # -> "Hello, HAL." # If #commit_transaction were called without having started a # second transaction, then it would break out of the transaction # block after committing the transaction. tv.commit_transaction # -> "Hello, HAL." tv.transaction_open? # -> true tv.abort_transaction # -> "Hello, you." end v.transaction_open? # -> false
require 'transaction/simple/group' x = "Hello, you." y = "And you, too." g = Transaction::Simple::Group.new(x, y) g.start_transaction(:first) # -> [ x, y ] g.transaction_open?(:first) # -> true x.transaction_open?(:first) # -> true y.transaction_open?(:first) # -> true x.gsub!(/you/, "world") # -> "Hello, world." y.gsub!(/you/, "me") # -> "And me, too." g.start_transaction(:second) # -> [ x, y ] x.gsub!(/world/, "HAL") # -> "Hello, HAL." y.gsub!(/me/, "Dave") # -> "And Dave, too." g.rewind_transaction(:second) # -> [ x, y ] x # -> "Hello, world." y # -> "And me, too." x.gsub!(/world/, "HAL") # -> "Hello, HAL." y.gsub!(/me/, "Dave") # -> "And Dave, too." g.commit_transaction(:second) # -> [ x, y ] x # -> "Hello, HAL." y # -> "And Dave, too." g.abort_transaction(:first) # -> [ x, y ] x = -> "Hello, you." y = -> "And you, too."
Threadsafe version of Transaction::Simple and Transaction::Simple::Group exist; these are loaded from ‘transaction/simple/threadsafe’ and ‘transaction/simple/threadsafe/group’, respectively, and are represented in Ruby code as Transaction::Simple::ThreadSafe and Transaction::Simple::ThreadSafe::Group, respectively.
While Transaction::Simple is very useful, it has some severe limitations that must be understood. Transaction::Simple: