Skip to content

Instantly share code, notes, and snippets.

@xaviershay
Created September 26, 2010 15:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save xaviershay/598001 to your computer and use it in GitHub Desktop.
Save xaviershay/598001 to your computer and use it in GitHub Desktop.
This is a sample patch for three dm gems - data_objects, do_postgres, and dm-postgres-adapter. It is not ready to be applied, since it is currently unspecced and untested. The purpose of this patch is to solicit feedback as to the architecture, to ensure
require 'dm-transactions/adapters/dm-do-adapter'
module DataMapper
class Transaction
module PostgresAdapter
include DataObjectsAdapter
# Produces a fresh transaction primitive for this Adapter. Fake nested
# transactions by using save points.
#
# Used by Transaction to perform its various tasks.
#
# @return [Object]
# a new Object that responds to :close, :begin, :commit,
# and :rollback,
#
# @api private
def transaction_primitive
if current_transaction
DataObjects::SavePoint.create_for_uri(normalized_uri, current_connection)
else
DataObjects::Transaction.create_for_uri(normalized_uri)
end
end
end
end
end
require 'digest'
require 'digest/sha2'
# JUSTIFY: Why create a new data object type for save points?
#
# A save point is analgous to a transaction, and a transaction has its own
# DataObject. Save points are supported by [] do databases, and [are|are
# not] part of the SQL spec.
module DataObjects
# JUSTIFY: Why not inherit from Transaction?
#
# Even though they share a lot of behaviour, a SavePoint does not satisfy
# LSP for transaction, both because it can't be used in place of a transaction
# (save points require you to be inside a transaction), and as such because
# of the required for the extra connection parameter.
#
# There is a case to be made for refactoring the common behaviour into a mixin.
class SavePoint
# The host name. Note, this relies on the host name being configured and resolvable using DNS
HOST = "#{Socket::gethostbyname(Socket::gethostname)[0]}" rescue "localhost"
@@counter = 0
# The connection object allocated for this transaction
attr_reader :connection
# A unique ID for this transaction
attr_reader :id
# Instantiate the Transaction subclass that's appropriate for this uri scheme
def self.create_for_uri(uri, connection)
uri = uri.is_a?(String) ? URI::parse(uri) : uri
DataObjects.const_get(uri.scheme.capitalize)::SavePoint.new(uri)
end
# JUSTIFY: Why passing in the connection? Transaction doesn't need this...
#
# Unlike the similar Transaction primitive, which is only created once
# per connection and reused for each transaction, this primitive will
# be created once for each save point, since each save point requires
# its own unique identify. As a result, the extra connection paramater
# is required to allow the existing connection to be reused.
def initialize(uri, connection)
@connection = connection
@id = Digest::SHA256.hexdigest("#{HOST}:#{$$}:#{Time.now.to_f}:#{@@counter += 1}")
end
def begin
run %{SAVEPOINT "#{@id}"}
end
def commit
run %{RELEASE SAVEPOINT "#{@id}"}
end
def rollback
run %{ROLLBACK TO SAVEPOINT "#{@id}"}
end
private
# JUSTIFY: Transaction doesn't have this extract method refactoring, why here?
#
# Because I see no reason for this not also to be applied to Transaction
def run(cmd)
connection.create_command(cmd).execute_non_query
end
end
end
@jpr5
Copy link

jpr5 commented Dec 16, 2010

This has been effectively merged in:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment