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

new(name, uri_or_options) click to toggle source

@api public

Calls superclass method
# 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

create(resources) click to toggle source

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
delete(collection) click to toggle source

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(statement, *bind_values) click to toggle source

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
read(query) click to toggle source

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
select(statement, *bind_values) click to toggle source

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
update(attributes, collection) click to toggle source

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

close_connection(connection) click to toggle source

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
normalized_uri() click to toggle source

@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
open_connection() click to toggle source

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

select_field(reader) click to toggle source

@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
select_fields(reader, fields) click to toggle source

@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
with_connection() { |connection = open_connection| ... } click to toggle source

@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