public
Last active

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

  • Download Gist
dm-postgres-adapter.rb
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
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
save_point.rb
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
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

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.