svoop/rodbot

View on GitHub
lib/rodbot/db.rb

Summary

Maintainability
A
0 mins
Test Coverage
# frozen_string_literal: true

require 'json'

using Rodbot::Refinements

module Rodbot

  # Uniform key/value database interface for various backends
  #
  # Keys can be namespaced using the colon as separator:
  #
  # * 'color' - color key without namespace
  # * 'bike:color' - color key within the bike namespace
  # * 'vehicle:bike:color' - color key within... you get the idea
  #
  # Furthermore, keys can be entered either as colon separated keys or as
  # list of symbols. The following are therefore equivalent:
  #
  #   Rodbot.db.get('vehicle:bike:color')
  #   Rodbot.db.get(:vehicle, :bike, :color)
  #
  # Same goes for the star wildcard which is only allowed last:
  #
  #   Rodbot.db.scan('vehicle:*')
  #   Rodbot.db.scan(:vehicle, :*)
  #
  # The interface is simple and straightforward:
  #
  # @example Set a value
  #   Rodbot.db.set(:name) { 'John' }
  #
  # @example Set a value which expires after 30 seconds
  #   Rodbot.db.set(:name, expires_in: 30) { 'John' }
  #
  # @example Replace an existing value
  #   Rodbot.db.set(:name) { 'John' }
  #   Rodbot.db.set(:name) { 'Bob' }   # replaces John with Bob
  #
  # @example Update an existing value
  #   Rodbot.db.set(:name) { 'John' }
  #   Rodbot.db.set(:name) { |n| "#{n} Doe" }   # replaces John with John Doe
  #
  # @example Get a value
  #   Rodbot.db.set(:name) { 'John' }
  #   Rodbot.db.get(:name)   # => 'John'
  #
  # @example Delete a value
  #   Rodbot.db.set(:name) { 'John' }
  #   Rodbot.db.delete(:name)   # => 'John'
  #   Rodbot.db.get(:name)      # => nil
  #
  # @example Scan for keys
  #   Rodbot.db.set(:first_name) { 'John' }
  #   Rodbot.db.set(:last_name) { 'Doe' }
  #   Rodbot.db.scan(:*)   # => [:first_name, :last_name]
  #
  # @example Delete all keys
  #   Rodbot.db.flush
  #
  # Please note that {JSON} is used to serialize which has an influence on
  # what you get back after deserialization:
  #
  # * Primitive types such as String, Integer or TrueClass are preserved.
  # * Any other object is converted to String, incuding...
  # * Symbol values are converted to String
  # * Symbol elements in an array are converted to String
  # * Symbol values in a hash are converted to String
  #
  # However, hash keys are always converted to Symbol - mainly because Rubyland
  # favours Symbol keys over String keys for visual reasons.
  class Db
    attr_reader :url

    # @param url [String] connection URL of the backend
    def initialize(url)
      @url = url
      extend "rodbot/db/#{url.split('://').first}".constantize
    end

    private

    def serialize(object)
      object.to_json
    end

    def deserialize(string)
      JSON.parse(string, symbolize_names: true) if string
    end
  end
end