ruby 一个简单的redis orm

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ruby 一个简单的redis orm相关的知识,希望对你有一定的参考价值。

# encoding: utf-8

require 'redis'
require 'thread'

begin
  require "active_model/model/validations"
rescue LoadError
end

module RedisPersistence
  # = Redis \Persistence
  #
  # Usage:
  #
  # class User
  #   include RedisPersistence
  #   self.redis = Redis.new
  #
  #   attribute :age, ->(i) { i.to_i }
  #   attribute :name
  #
  #   validates :name, presence: true
  # end
  #
  # u = User.new(key: 1, age: 10, name: 'test')
  # u.new_record?       # => true
  # u.valid?            # => true
  # u.save              # => true
  # User.count          # => 1
  #
  # u = User.find(1)
  # u.persisted?        # => true
  # u.age               # => 10
  #
  def self.included(base)
    base.extend ClassMethods
    base.include ::ActiveModel::Validations
  end

  class RedisPersistenceError < StandardError; end
  class RedisConnectionError < RedisPersistenceError; end
  class StoreKeyMissing < RedisPersistenceError; end
  class RecordAlreadyExist < RedisPersistenceError; end
  class RecordNotSaved < RedisPersistenceError; end
  class RecordNotDestroyed < RecordNotSaved; end
  class UnknownAttributeError < RedisPersistenceError; end

  module ClassMethods
    def redis
      raise RedisConnectionError if !connected?
      @redis
    end

    def redis=(redis)
      @redis     = redis
      @connected = true
    end

    def connected?
      !!(@redis && @connected)
    end

    def mutex
      @@mutex ||= Mutex.new
    end

    def synchronize(&block)
      mutex.synchronize(&block)
    end

    def namespace
      @namespace ||= self.name
    end

    def store_key(key)
      "#{namespace}:#{key}"
    end

    def counter
      "#{namespace}/counter"
    end

    def keys(key)
      redis.keys(key)
    end

    def count
      redis.get(counter).to_i
    end

    def find(key)
      return nil if !exist?(key)
      raw_data = redis.hgetall(store_key(key))
      raw_data = raw_data.merge(key: key, _new_record: false)

      new(raw_data.to_h)
    end

    def exist?(key)
      redis.exists(store_key(key))
    end

    def delete(key)
      return false if !exist?(key)
      synchronize do
        if redis.del(store_key(key)) == 1
          redis.incrby(counter, -1)
          true
        else
          false
        end
      end
    end

    def delete_all
      keys("#{namespace}:*").each { |k| redis.del(k) }
      redis.del(counter)

      true
    end

    def insert(key, attributes = nil)
      flattened_attributes = attributes.to_a.flatten
      synchronize do
        redis.hmset(store_key(key), flattened_attributes)
        redis.incrby(counter, 1)
      end
    end

    def insert_row(key, field, value)
      redis.hset(store_key(key), field, value)
    end

    def attribute(name, block = nil)
      attributes << name if !attributes.include?(name)

      if block
        define_method(name) { block.call(@attributes[name]) }
      else
        define_method(name) { @attributes[name] }
      end

      define_method(:"#{name}=") { |value|  @attributes[name] = value }
    end

    # ==== Examples
    #   # Create a single new object
    #   User.create(key: '123', first_name: 'Jamie')
    #
    #   # Create an Array of new objects
    #   User.create([{ key: '123', first_name: 'Jamie' }, { key: '456', first_name: 'Jeremy' }])
    def create(attributes = nil)
      if attributes.is_a?(Array)
        attributes.collect { |attr| create(attr) }
      else
        object = new(attributes)
        object.save
        object
      end
    end

    def create!(attributes = nil)
      if attributes.is_a?(Array)
        attributes.collect { |attr| create!(attr) }
      else
        object = new(attributes)
        object.save!
        object
      end
    end

    protected

      def attributes
        @attributes ||= []
      end
  end

  attr_reader :key, :attributes

  def initialize(attributes = {})
    @attributes  = {}
    @_destroyed  = false
    @_new_record = attributes.delete(:_new_record).nil? ? true : false
    @key         = attributes.delete(:key)

    raise StoreKeyMissing if !@key

    _assign_attributes(attributes)

    super()
  end

  def new_record?
    @_new_record
  end

  def destroyed?
    @_destroyed
  end

  def persisted?
    !(@_new_record || @_destroyed)
  end

  def save(*args)
    create_or_update(*args)
  end

  def save!(*args)
    create_or_update(*args) || raise(RecordNotSaved)
  end

  def delete
    if persisted?
      result = self.class.delete(key)
      return false if !result
    end

    @_destroyed = true
    freeze
  end
  alias :destroy :delete

  def destroy!
    destroy || raise(RecordNotDestroyed)
  end

  def update(attributes)
    _assign_attributes(attributes)
    save
  end

  alias update_attributes update

  def update!(attributes)
    _assign_attributes(attributes)
    save!
  end

  alias update_attributes! update!

  def reload
    fresh_object = self.class.find(key)
    @_new_record = false

    _assign_attributes(fresh_object.attributes)

    self
  end

  private

    def _assign_attributes(attributes)
      attributes.each do |k, v|
        _assign_attribute(k, v)
      end
    end

    def _assign_attribute(k, v)
      if respond_to?("#{k}=")
        public_send("#{k}=", v)
      else
        raise UnknownAttributeError.new
      end
    end

    def create_or_update(*args)
      result = new_record? ? _create_record : _update_record(*args)
      result != false
    end

    def _update_record(attributes = self.attributes)
      return false if !key || !valid?

      attributes.each do |field, value|
        self.class.insert_row(key, field, value)
      end
      true
    end

    def _create_record(attributes = self.attributes)
      return false if !key || self.class.exist?(key) || !valid?

      self.class.insert(key, attributes)
      @_new_record = false
      true
    end

end

以上是关于ruby 一个简单的redis orm的主要内容,如果未能解决你的问题,请参考以下文章

redis离线集群安装

Redis环境简单部署(集群/双机)

是否可以使用 Ohm 更新模型属性,而 Redis DB 是 Ruby?

与 Ruby on Rails 相关的 ORM 是啥?

哪个 Ruby ORM 支持 PostgreSQL 数组数据类型?

Redis 3.2.1集群 —— Redis-trib.rb工具详解(含原理)