module Redcord::Serializer::ClassMethods

Constants

TIME_TYPES

Redis only allows range queries on floats. To allow range queries on the Ruby Time type, encode_attr_value and decode_attr_value will implicitly encode and decode Time attributes to a float.

Public Instance Methods

coerce_and_set_id(redis_hash, id) click to toggle source
# File lib/redcord/serializer.rb, line 194
def coerce_and_set_id(redis_hash, id)
  # Coerce each serialized result returned from Redis back into Model
  # instance
  instance = TypeCoerce.send(:[], self).new.from(from_redis_hash(redis_hash))
  instance.send(:id=, id)
  instance
end
decode_attr_value(attribute, val) click to toggle source
# File lib/redcord/serializer.rb, line 47
def decode_attr_value(attribute, val)
  if !val.blank? && TIME_TYPES.include?(props[attribute][:type])
    val = val.to_i
    nsec = val >= 0 ? val % 1_000_000_000 : -val % 1_000_000_000

    Time.zone.at(val / 1_000_000_000).change(nsec: nsec)
  else
    val
  end
end
encode_attr_value(attribute, val) click to toggle source
# File lib/redcord/serializer.rb, line 34
def encode_attr_value(attribute, val)
  if !val.blank? && TIME_TYPES.include?(props[attribute][:type])
    time_in_nano_sec = val.to_i * 1_000_000_000
    time_in_nano_sec >= 0 ? time_in_nano_sec + val.nsec : time_in_nano_sec - val.nsec
  elsif val.is_a?(Float)
    # Encode as round-trippable float64
    '%1.16e' % [val]
  else
    val
  end
end
encode_range_index_attr_val(attribute, val) click to toggle source
# File lib/redcord/serializer.rb, line 162
def encode_range_index_attr_val(attribute, val)
  if val.is_a?(Redcord::RangeInterval)
    # nil is treated as -inf and +inf. This is supported in Redis sorted
    # sets so clients aren't required to know the highest and lowest scores
    # in a range
    min_val = !val.min ? '-inf' : encode_attr_value(attribute, val.min)
    max_val = !val.max ? '+inf' : encode_attr_value(attribute, val.max)

    # In Redis, by default min and max is closed. You can prefix the score
    # with '(' to specify an open interval.
    min_val = val.min_exclusive ? '(' + min_val.to_s : min_val.to_s
    max_val = val.max_exclusive ? '(' + max_val.to_s : max_val.to_s
    [min_val, max_val]
  else
    # Equality queries for range indices are be passed to redis as a range
    # [val, val].
    encoded_val = encode_attr_value(attribute, val)
    [encoded_val, encoded_val]
  end
end
from_redis_hash(args) click to toggle source
# File lib/redcord/serializer.rb, line 223
def from_redis_hash(args)
  args.map { |key, val| [key, decode_attr_value(key.to_sym, val)] }.to_h
end
get_attr_type(attr_key) click to toggle source
# File lib/redcord/serializer.rb, line 184
def get_attr_type(attr_key)
  props[attr_key][:type_object]
end
model_key() click to toggle source
# File lib/redcord/serializer.rb, line 203
def model_key
  "Redcord:#{name}"
end
to_redis_hash(args) click to toggle source
# File lib/redcord/serializer.rb, line 212
def to_redis_hash(args)
  args.map do |key, val|
    [key.to_sym, encode_attr_value(key.to_sym, val)]
  end.to_h
end
validate_and_adjust_custom_index_query_conditions(query_conditions) click to toggle source
# File lib/redcord/serializer.rb, line 106
def validate_and_adjust_custom_index_query_conditions(query_conditions)
  adjusted_query_conditions = query_conditions.clone
  query_conditions.each do |attr_key, condition|
    if !condition.is_a?(Array)
      adjusted_query_conditions[attr_key] = [condition, condition]
    elsif condition[0].to_s[0] == '(' or condition[1].to_s[0] == '('
      raise(Redcord::CustomIndexInvalidQuery, "Custom index doesn't support exclusive ranges")
    end
  end
  adjusted_query_conditions
end
validate_attr_type(attr_val, attr_type) click to toggle source
# File lib/redcord/serializer.rb, line 146
def validate_attr_type(attr_val, attr_type)
  if (attr_type.is_a?(Class) && !attr_val.is_a?(attr_type)) ||
     (attr_type.is_a?(T::Types::Base) && !attr_type.valid?(attr_val))
    raise(
      Redcord::WrongAttributeType,
      "Expected type #{attr_type}, got #{attr_val.class.name}",
    )
  end
end
validate_index_attributes(attr_keys, custom_index_name: nil) click to toggle source
# File lib/redcord/serializer.rb, line 79
def validate_index_attributes(attr_keys, custom_index_name: nil)
  custom_index_attributes = class_variable_get(:@@custom_index_attributes)[custom_index_name]
  attr_keys.each do |attr_key|
    next if attr_key == shard_by_attribute

    if !custom_index_attributes.empty?
      if !custom_index_attributes.include?(attr_key)
        raise(
          Redcord::AttributeNotIndexed,
          "#{attr_key} is not a part of #{custom_index_name} index.",
        )
      end
    else
      if !class_variable_get(:@@index_attributes).include?(attr_key) &&
        !class_variable_get(:@@range_index_attributes).include?(attr_key)
        raise(
          Redcord::AttributeNotIndexed,
          "#{attr_key} is not an indexed attribute.",
        )
      end
    end
  end
end
validate_range_attr_types(attr_val, attr_type) click to toggle source
# File lib/redcord/serializer.rb, line 124
def validate_range_attr_types(attr_val, attr_type)
  # Validate attribute types for range index attributes
  if attr_val.is_a?(Redcord::RangeInterval)
    validate_attr_type(
      attr_val.min,
      T.cast(T.nilable(attr_type), T::Types::Base),
    )
    validate_attr_type(
      attr_val.max,
      T.cast(T.nilable(attr_type), T::Types::Base),
    )
  else
    validate_attr_type(attr_val, attr_type)
  end
end
validate_types_and_encode_query(attr_key, attr_val) click to toggle source
# File lib/redcord/serializer.rb, line 59
def validate_types_and_encode_query(attr_key, attr_val)
  # Validate attribute types for index attributes
  attr_type = get_attr_type(attr_key)
  if class_variable_get(:@@index_attributes).include?(attr_key) || attr_key == shard_by_attribute
    validate_attr_type(attr_val, attr_type)
  else
    validate_range_attr_types(attr_val, attr_type)

    # Range index attributes need to be further encoded into a format
    # understood by the Lua script.
    unless attr_val.nil?
      attr_val = encode_range_index_attr_val(attr_key, attr_val)
    end
  end
  attr_val
end