class DataMapper::Adapters::DataObjectsAdapter
DataObjectsAdapter is the base class for all adapers for relational databases. If you want to add support for a new RDBMS, it makes sense to make your adapter class inherit from this class.
By inheriting from DataObjectsAdapter, you get a copy of all the standard sub-modules (Quoting, Coersion and Queries) in your own Adapter. You can extend and overwrite these copies without affecting the originals.
Public Class Methods
@api public
# File lib/dm-do-adapter/adapter.rb, line 265 def initialize(name, uri_or_options) super # Default the driver-specific logger to DataMapper's logger if driver_module = DataObjects.const_get(normalized_uri.scheme.capitalize) driver_module.logger = DataMapper.logger if driver_module.respond_to?(:logger=) end end
Public Instance Methods
For each model instance in resources, issues an SQL INSERT (or equivalent) statement to create a new record in the data store for the instance
Note that this method does not update the identity map. If a plugin needs to use an adapter directly, it is up to plugin developer to keep the identity map up to date.
@param [Enumerable(Resource)] resources
The list of resources (model instances) to create
@return [Integer]
The number of records that were actually saved into the database
@api semipublic
# File lib/dm-do-adapter/adapter.rb, line 82 def create(resources) name = self.name resources.each do |resource| model = resource.model serial = model.serial(name) attributes = resource.dirty_attributes properties = [] bind_values = [] # make the order of the properties consistent model.properties(name).each do |property| next unless attributes.key?(property) bind_value = attributes[property] # skip insering NULL for columns that are serial or without a default next if bind_value.nil? && (property.serial? || !property.default?) # if serial is being set explicitly, do not set it again if property.equal?(serial) serial = nil end properties << property bind_values << bind_value end statement = insert_statement(model, properties, serial) result = with_connection do |connection| connection.create_command(statement).execute_non_query(*bind_values) end if result.affected_rows == 1 && serial serial.set!(resource, result.insert_id) end end end
Constructs and executes DELETE statement for given query
@param [Collection] collection
collection of records to be deleted
@return [Integer]
the number of records deleted
@api semipublic
# File lib/dm-do-adapter/adapter.rb, line 207 def delete(collection) query = collection.query statement, bind_values = delete_statement(query) with_connection do |connection| connection.create_command(statement).execute_non_query(*bind_values) end.affected_rows end
Execute non-SELECT SQL query
@param [String] statement
the SQL statement
@param [Array] *bind_values
optional bind values to merge into the statement
@return [DataObjects::Result]
result with number of affected rows, and insert id if any
@api public
# File lib/dm-do-adapter/adapter.rb, line 60 def execute(statement, *bind_values) with_connection do |connection| command = connection.create_command(statement) command.execute_non_query(*bind_values) end end
Constructs and executes SELECT query, then instantiates one or many object from result set.
@param [Query] query
composition of the query to perform
@return [Array]
result set of the query
@api semipublic
# File lib/dm-do-adapter/adapter.rb, line 133 def read(query) fields = query.fields types = fields.map { |property| property.primitive } statement, bind_values = select_statement(query) records = [] with_connection do |connection| command = connection.create_command(statement) command.set_types(types) # Handle different splat semantics for nil on 1.8 and 1.9 reader = if bind_values command.execute_reader(*bind_values) else command.execute_reader end begin while reader.next! records << Hash[ fields.zip(reader.values) ] end ensure reader.close end end records end
Retrieve results using an SQL SELECT statement
@param [String] statement
the SQL SELECT statement
@param [Array] *bind_values
optional bind values to merge into the statement
@return [Array]
if fields > 1, return an Array of Struct objects if fields == 1, return an Array of objects
@api public
# File lib/dm-do-adapter/adapter.rb, line 32 def select(statement, *bind_values) with_connection do |connection| reader = connection.create_command(statement).execute_reader(*bind_values) fields = reader.fields begin if fields.size > 1 select_fields(reader, fields) else select_field(reader) end ensure reader.close end end end
Constructs and executes UPDATE statement for given attributes and a query
@param [Hash(Property => Object)] attributes
hash of attribute values to set, keyed by Property
@param [Collection] collection
collection of records to be updated
@return [Integer]
the number of records updated
@api semipublic
# File lib/dm-do-adapter/adapter.rb, line 176 def update(attributes, collection) query = collection.query properties = [] bind_values = [] # make the order of the properties consistent query.model.properties(name).each do |property| next unless attributes.key?(property) properties << property bind_values << attributes[property] end statement, conditions_bind_values = update_statement(properties, query) bind_values.concat(conditions_bind_values) with_connection do |connection| connection.create_command(statement).execute_non_query(*bind_values) end.affected_rows end
Protected Instance Methods
Takes connection and closes it
@api semipublic
# File lib/dm-do-adapter/adapter.rb, line 257 def close_connection(connection) connection.close if connection.respond_to?(:close) end
@api private
# File lib/dm-do-adapter/adapter.rb, line 219 def normalized_uri @normalized_uri ||= begin keys = [ :adapter, :user, :password, :host, :port, :path, :fragment, :scheme, :query, :username, :database ] query = DataMapper::Ext::Hash.except(@options, keys) query = nil if query.empty? # Better error message in case port is no Numeric value port = @options[:port].nil? ? nil : @options[:port].to_int DataObjects::URI.new( :scheme => @options[:adapter], :user => @options[:user] || @options[:username], :password => @options[:password], :host => @options[:host], :port => port, :path => @options[:path] || @options[:database], :query => query, :fragment => @options[:fragment] ).freeze end end
Instantiates new connection object
@api semipublic
# File lib/dm-do-adapter/adapter.rb, line 250 def open_connection DataObjects::Connection.new(normalized_uri) end
Private Instance Methods
@api private
# File lib/dm-do-adapter/adapter.rb, line 299 def select_field(reader) results = [] while reader.next! results << reader.values.at(0) end results end
@api private
# File lib/dm-do-adapter/adapter.rb, line 285 def select_fields(reader, fields) fields = fields.map { |field| DataMapper::Inflector.underscore(field).to_sym } struct = Struct.new(*fields) results = [] while reader.next! results << struct.new(*reader.values) end results end
@api private
# File lib/dm-do-adapter/adapter.rb, line 275 def with_connection yield connection = open_connection rescue Exception => exception DataMapper.logger.error(exception.to_s) if DataMapper.logger raise ensure close_connection(connection) end