require 'lotus/utils/basic_object'
module Lotus
module Model
module Adapters
# It's raised when an adapter can't find the underlying database adapter.
#
# Example: When we try to use the SqlAdapter with a Postgres database
# but we didn't loaded the pg gem before.
#
# @see Lotus::Model::Adapters::SqlAdapter#initialize
#
# @since 0.1.0
class DatabaseAdapterNotFound < Lotus::Model::Error
end
# It's raised when an adapter does not support a feature.
#
# Example: When we try to get a connection string for the current database
# but the adapter has not implemented it.
#
# @see Lotus::Model::Adapters::Abstract#connection_string
#
# @since 0.3.0
class NotSupportedError < Lotus::Model::Error
end
# It's raised when an operation is requested to an adapter after it was
# disconnected.
#
# @since 0.5.0
class DisconnectedAdapterError < Lotus::Model::Error
def initialize
super "You have tried to perform an operation on a disconnected adapter"
end
end
# Represents a disconnected resource.
#
# When we use #disconnect for MemoryAdapter and
# FileSystemAdapter, we want to free underlying resources such
# as a mutex or a file descriptor.
#
# These adapters use to use anonymous descriptors that are destroyed by
# Ruby VM after each operation. Sometimes we need to clean the state and
# start fresh (eg. during a test suite or a deploy).
#
# Instead of assign nil to these instance variables, we assign this
# special type: DisconnectedResource.
#
# In case an operation is still performed after the adapter was disconnected,
# instead of see a generic NoMethodError for nil, a developer
# will face a specific message relative to the state of the adapter.
#
# @api private
# @since 0.5.0
#
# @see Lotus::Model::Adapters::Abstract#disconnect
# @see Lotus::Model::Adapters::MemoryAdapter#disconnect
# @see Lotus::Model::Adapters::FileSystemAdapter#disconnect
class DisconnectedResource < Utils::BasicObject
def method_missing(method_name, *)
::Kernel.raise DisconnectedAdapterError.new
end
end
# Abstract adapter.
#
# An adapter is a concrete implementation that allows a repository to
# communicate with a single database.
#
# Lotus::Model is shipped with Memory and SQL adapters.
# Third part adapters MUST implement the interface defined here.
# For convenience they may inherit from this class.
#
# These are low level details, and shouldn't be used directly.
# Please use a repository for entities persistence.
#
# @since 0.1.0
class Abstract
# Initialize the adapter
#
# @param mapper [Lotus::Model::Mapper] the object that defines the
# database to entities mapping
#
# @param uri [String] the optional connection string to the database
#
# @param options [Hash] a list of non-mandatory adapter options
#
# @since 0.1.0
def initialize(mapper, uri = nil, options = {})
@mapper = mapper
@uri = uri
@options = options
end
# Creates or updates a record in the database for the given entity.
#
# @param collection [Symbol] the target collection (it must be mapped).
# @param entity [Object] the entity to persist
#
# @return [Object] the entity
#
# @since 0.1.0
def persist(collection, entity)
raise NotImplementedError
end
# Creates a record in the database for the given entity.
# It should assign an id (identity) to the entity in case of success.
#
# @param collection [Symbol] the target collection (it must be mapped).
# @param entity [Object] the entity to create
#
# @return [Object] the entity
#
# @since 0.1.0
def create(collection, entity)
raise NotImplementedError
end
# Updates a record in the database corresponding to the given entity.
#
# @param collection [Symbol] the target collection (it must be mapped).
# @param entity [Object] the entity to update
#
# @return [Object] the entity
#
# @since 0.1.0
def update(collection, entity)
raise NotImplementedError
end
# Deletes a record in the database corresponding to the given entity.
#
# @param collection [Symbol] the target collection (it must be mapped).
# @param entity [Object] the entity to delete
#
# @since 0.1.0
def delete(collection, entity)
raise NotImplementedError
end
# Returns all the records for the given collection
#
# @param collection [Symbol] the target collection (it must be mapped).
#
# @return [Array] all the records
#
# @since 0.1.0
def all(collection)
raise NotImplementedError
end
# Returns a unique record from the given collection, with the given
# identity.
#
# @param collection [Symbol] the target collection (it must be mapped).
# @param id [Object] the identity of the object.
#
# @return [Object] the entity
#
# @since 0.1.0
def find(collection, id)
raise NotImplementedError
end
# Returns the first record in the given collection.
#
# @param collection [Symbol] the target collection (it must be mapped).
#
# @return [Object] the first entity
#
# @since 0.1.0
def first(collection)
raise NotImplementedError
end
# Returns the last record in the given collection.
#
# @param collection [Symbol] the target collection (it must be mapped).
#
# @return [Object] the last entity
#
# @since 0.1.0
def last(collection)
raise NotImplementedError
end
# Empties the given collection.
#
# @param collection [Symbol] the target collection (it must be mapped).
#
# @since 0.1.0
def clear(collection)
raise NotImplementedError
end
# Executes a command for the given query.
#
# @param query [Object] the query object to act on.
#
# @since 0.1.0
def command(query)
raise NotImplementedError
end
# Returns a query
#
# @param collection [Symbol] the target collection (it must be mapped).
# @param blk [Proc] a block of code to be executed in the context of
# the query.
#
# @return [Object]
#
# @since 0.1.0
def query(collection, &blk)
raise NotImplementedError
end
# Wraps the given block in a transaction.
#
# For performance reasons the block isn't in the signature of the method,
# but it's yielded at the lower level.
#
# Please note that it's only supported by some databases.
# For this reason, the options may vary from adapter to adapter.
#
# @param options [Hash] options for transaction
#
# @see Lotus::Model::Adapters::SqlAdapter#transaction
# @see Lotus::Model::Adapters::MemoryAdapter#transaction
#
# @since 0.2.3
def transaction(options = {})
raise NotImplementedError
end
# Returns a string which can be executed to start a console suitable
# for the configured database.
#
# @return [String] to be executed to start a database console
#
# @since 0.3.0
def connection_string
raise NotSupportedError
end
# Executes a raw command
#
# @param raw [String] the raw statement to execute on the connection
#
# @return [NilClass]
#
# @since 0.3.1
def execute(raw)
raise NotImplementedError
end
# Fetches raw records from
#
# @param raw [String] the raw query
# @param blk [Proc] an optional block that is yielded for each record
#
# @return [Enumerable, Array]
#
# @since 0.5.0
def fetch(raw, &blk)
raise NotImplementedError
end
# Disconnects the connection by freeing low level resources
#
# @since 0.5.0
def disconnect
raise NotImplementedError
end
end
end
end
end