class Pupa::Processor::Connection::PostgreSQLAdapter
A proxy class to save plain old Ruby objects to PostgreSQL.
Attributes
Public Class Methods
@param [String] database_url the database URL
# File lib/pupa/processor/connection_adapters/postgresql_adapter.rb, line 13 def initialize(database_url) @raw_connection = Sequel.connect(database_url) end
Public Instance Methods
Finds a document matching the selection criteria.
The selection criteria must set a ‘_type` key in order to determine the collection to query.
@param [Hash] selector the selection criteria @return [Hash,nil] the matched document, or nil @raises [Pupa::Errors::TooManyMatches] if multiple documents are found
# File lib/pupa/processor/connection_adapters/postgresql_adapter.rb, line 25 def find(selector) collection_name = collection_name_from_class_name(selector[:_type].camelize) if selector.except(:_type).empty? raise Errors::EmptySelectorError, "selector is empty during find in collection #{collection_name}" end collection = raw_connection[collection_name] query = collection.filter(symbolize_keys(selector)) case query.count when 0 nil when 1 stringify_keys(query.first) else raise Errors::TooManyMatches, "selector matches multiple documents during find in collection #{collection_name}: #{JSON.dump(selector)}" end end
Inserts or replaces a document in PostgreSQL.
@param [Object] object an object @return [Array] whether the object was inserted and the object’s database ID @raises [Pupa::Errors::TooManyMatches] if multiple documents would be updated
# File lib/pupa/processor/connection_adapters/postgresql_adapter.rb, line 48 def save(object) fingerprint = symbolize_keys(object.fingerprint) # Sequel needs symbols selector = if fingerprint.key?(:$or) && fingerprint.size == 1 Sequel.or(reject_subdocument_criteria(fingerprint[:$or])) else reject_subdocument_criteria(fingerprint) end collection_name = collection_name_from_class_name(object.class.to_s) if fingerprint.empty? raise Errors::EmptySelectorError, "selector is empty during save in collection #{collection_name} for #{object._id}" end collection = raw_connection[collection_name] query = collection.filter(selector) # Run query before callbacks to avoid e.g. timestamps in the selector. case query.count when 0 object.run_callbacks(:save) do object.run_callbacks(:create) do collection.insert(object.to_h(persist: true)) [true, object._id.to_s] end end when 1 # Make the document available to the callbacks. # @see https://github.com/jpmckinney/pupa-ruby/issues/17 object.document = stringify_keys(query.first) object.run_callbacks(:save) do query.update(object.to_h(persist: true).except(:_id)) [false, object.document['_id'].to_s] end else raise Errors::TooManyMatches, "selector matches multiple documents during save in collection #{collection_name} for #{object._id}: #{JSON.dump(selector)}" end end
Private Instance Methods
Returns the name of the collection in which to save the object.
@param [String] class_name the name of the object’s class @return [String] the name of the collection in which to save the object
# File lib/pupa/processor/connection_adapters/postgresql_adapter.rb, line 91 def collection_name_from_class_name(class_name) class_name.demodulize.underscore.pluralize.to_sym end
# File lib/pupa/processor/connection_adapters/postgresql_adapter.rb, line 95 def reject_subdocument_criteria(object) case object when Hash array = [] object.each do |key,value| unless key.to_s['.'] # @todo Support MongoDB subdocument criteria. array += [key, reject_subdocument_criteria(value)] end end array when Array object.map do |value| reject_subdocument_criteria(value) end.reject(&:empty?) else object end end