Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save kristianmandrup/318350 to your computer and use it in GitHub Desktop.
Save kristianmandrup/318350 to your computer and use it in GitHub Desktop.
# Proposal for a Simulated::Transactions API, "A Simulated Transaction Log and Rollback API"
# The example shows how this common API could be used for Mongo DB and Mongoid, both transactionless persistence stores?
# This API could be used in testing frameworks such as rspec, test-unit, cucumber and the like
# in order to ensure running a test doesn't have side effects, even in the context of a transactionless persistence store
class DocA
include MongoMapper::Document
# include specific Simulated::Transactions model
# note, the testing framework should be responsible for adding this for any model
include Simulated::Transactions::MongoMapper
key :title, String
end
module MongoMapper
class Document
def self.add_simulated_transactions
include Simulated::Transactions::MongoMapper
end
end
end
# testing framework on initialization (instances of ActiveModel ?)
all_model_classes.each do |model_class|
model_class.add_simulated_transactions
end
class DocB
include Mongoid::Document
include Simulated::Transactions::Mongoid
field :first_name
end
# each transactionless persistence store has its own module below Simulated::Transactions
module Simulated
module Transactions
module MongoMapper
include SimulatedTransactionModel
# this module must then ensure versioning capability is added to each model where it is included
include ::MongoMapper::Versioned
end
end
end
module Simulated
module Transactions
module Mongoid
include SimulatedTransactionModel
include ::Mongoid::Versioned
end
end
end
module Simulated
module TransactionModel
def self.simulated_transaction_model?
true
end
def self.included(module_name)
# take advantage of naming convention !
define_method :transaction_index do
# module_name in these example is either Mongoid or MongoMapper :)
"Simulated::#{module_name}TransactionIndex".constantize.instance
end
end
# ensures this call_back handler is added to the method stack for after_save call_back
after_save :add_simulated_transaction_log
def add_simulated_transaction_log
index = transaction_index
index[self.object_id] ? index[self.object_id]++ : index[self.object_id] = 1
end
end
end
# each transactionless persistence store has its TransactionIndex subclass inheriting from Simulated::TransactionIndex
module Simulated
class MongoMapperTransactionIndex < TransactionIndex
def initialize
super
end
def self.instance
@instance ||= new
end
# call specific "previous" method of versioning module
def self.previous(transaction_obj)
transaction_obj.previous
end
end
end
module Simulated
class MongoidTransactionIndex < TransactionIndex
def initialize
super
end
def self.instance
@instance ||= new
end
# specific "previous" logic for its own versioning module
def self.previous(transaction_obj)
# is there a built-in ?
last_version = self.class.first(:conditions => { :_id => id, :version => transaction_obj.version -1 })
last_version ? last_version : nil
end
end
end
module Simulated
# The transaction log
class TransactionIndex
attr_writer :trans_index
def initialize
@index ||= {}
end
def after_save
index[self.object_id] ? index[self.object_id][:count]++ : index[self.object_id] = {:obj => self, :count => 1}
end
def self.rollback
index.each do |trans|
trans[:count].times do
# go to previous version, if no previous version it must be initial version, so then delete the record?
trans[:obj].delete if !previous(trans[:obj])
end
end
end
end
end
class Tester
def my_test
doc = Doc.create(:title=>"v1") # # => after_save {27 => {:obj => <obj:27>, :count => 1 }}
doc.title = 'hello'
Doc.save # => after_save {27 => {:obj => <obj:27>, :count => 1 }}
doc.title = 'goodbye'
Doc.save # => after_save {27 => {:obj => <obj:27>, :count => 2 }}
end
end
# Perform tests in class using Mongo DB with 'Mongo Mapper' and 'versioned' gems, do rollback at end using Simulated Transactions!
perform(Tester, :my_test)
def perform(clazz, method)
begin
clazz.send method
ensure
if class.respond_to? simulated_transaction_model?
TransactionIndex.rollback
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment