class Record

Attributes

attributes[R]
dirty[R]
errors[R]
id[R]

Public Class Methods

all(options={}) click to toggle source
# File lib/redisant/records.rb, line 58
def self.all options={}
  sort = options.delete :sort
  if sort
    index = self.find_index sort.to_s
    raise Redisant::InvalidArgument.new("Cannot order by #{sort}") unless index
    index.objects options
  else
    ids.map { |id| self.find id }
  end
end
any?() click to toggle source
# File lib/redisant/records.rb, line 69
def self.any?
  $redis.scard( id_key ) > 0
end
build(attributes=nil) click to toggle source

crud

# File lib/redisant/records.rb, line 115
def self.build attributes=nil
  object = self.new attributes
  object.save
  object
end
class_key(str) click to toggle source
# File lib/redisant/records.rb, line 183
def self.class_key str
  "#{self.name.downcase}:#{str}"
end
count() click to toggle source
# File lib/redisant/records.rb, line 89
def self.count
  Criteria.new(self).count
end
destroy_all() click to toggle source
# File lib/redisant/records.rb, line 170
def self.destroy_all
  keys = ids.map {|id| "#{self.name.downcase}:#{id}:attributes" }
  keys << id_key
  keys << class_key('ids:counter')
  $redis.del keys if keys.any?
end
exists?(id) click to toggle source
# File lib/redisant/records.rb, line 54
def self.exists? id
  $redis.sismember( id_key, id )
end
find(id) click to toggle source

query

# File lib/redisant/records.rb, line 38
def self.find id
  raise Redisant::InvalidArgument.new("Invalid argument") unless id
  return nil unless exists? id
  t = self.new id:id
  
  t.load
  t
end
find!(id) click to toggle source
# File lib/redisant/records.rb, line 47
def self.find! id
  raise Redisant::InvalidArgument.new("Not found") unless exists? id
  t = self.new id:id
  t.load
  t
end
first(attributes={}) click to toggle source
# File lib/redisant/records.rb, line 73
def self.first attributes={}
  Criteria.new(self).first attributes
end
id_key() click to toggle source

ids

# File lib/redisant/records.rb, line 261
def self.id_key
  class_key 'id'
end
ids() click to toggle source
# File lib/redisant/records.rb, line 278
def self.ids
  Criteria.new(self).ids
end
last(attributes={}) click to toggle source
# File lib/redisant/records.rb, line 77
def self.last attributes={}
  Criteria.new(self).last attributes
end
load(id) click to toggle source
# File lib/redisant/records.rb, line 31
def self.load id
  t = self.new id:id
  t.load
  t
end
new(attributes=nil) click to toggle source
# File lib/redisant/records.rb, line 16
def initialize attributes=nil
  raise Redisant::InvalidArgument.new('Wrong arguments') unless attributes==nil or attributes.is_a? Hash
  @id = attributes.delete(:id) if attributes
  @attributes = stringify_attributes(attributes) || {}
  @prev_attributes = {}
  @dirty = @attributes.keys
  setup_relations if respond_to? :setup_relations
  @id_saved = false
  @errors = nil
end
order(options) click to toggle source
# File lib/redisant/records.rb, line 97
def self.order options
  Criteria.new(self).order options
end
random() click to toggle source
# File lib/redisant/records.rb, line 81
def self.random
  Criteria.new(self).random
end
sort(options) click to toggle source
# File lib/redisant/records.rb, line 93
def self.sort options
  Criteria.new(self).sort options
end
where(attributes) click to toggle source
# File lib/redisant/records.rb, line 85
def self.where attributes
  Criteria.new(self).where attributes
end

Public Instance Methods

add_id() click to toggle source
# File lib/redisant/records.rb, line 282
def add_id
  raise Redisant::InvalidArgument.new('Cannot add empty id') unless @id
  return if @id_saved
  $redis.sadd self.class.id_key, @id.to_i
  @id_saved = true
end
attribute(key) click to toggle source

single attribute

# File lib/redisant/records.rb, line 188
def attribute key
  @attributes[key.to_s]
end
attributes=(attributes) click to toggle source

multiple attributes

# File lib/redisant/records.rb, line 208
def attributes= attributes
  raise Redisant::InvalidArgument.new("Invalid arguments") unless attributes.is_a? Hash
  attributes.each_pair do |key,value|
    if value != @attributes[key.to_s]
      @attributes[key.to_s] = value
      dirty key
    end
  end
end
class_name() click to toggle source
# File lib/redisant/records.rb, line 27
def class_name
  self.class.name.downcase
end
clean() click to toggle source
# File lib/redisant/records.rb, line 110
def clean
  @dirty = []
end
cleanup_attributes() click to toggle source
# File lib/redisant/records.rb, line 253
def cleanup_attributes
  # delete attribues in the hash that's not in our local attributes
  keys = $redis.hkeys member_key('attributes')
  delete = keys - @attributes.keys
  $redis.hdel member_key('attributes'), delete
end
destroy() click to toggle source
# File lib/redisant/records.rb, line 121
def destroy
  destroy_relations
  destroy_attributes
  remove_id
end
destroy_attributes() click to toggle source
# File lib/redisant/records.rb, line 247
def destroy_attributes
  $redis.del member_key('attributes')
  @attributes = nil
  update_search
end
dirty?() click to toggle source

dirty

# File lib/redisant/records.rb, line 102
def dirty?
  @dirty.any?
end
load() click to toggle source
# File lib/redisant/records.rb, line 131
def load
  load_attributes
  @id_saved = true
end
load_attributes(keys=nil) click to toggle source
# File lib/redisant/records.rb, line 218
def load_attributes keys=nil
  if keys
    keys = keys.map { |key| key.to_s }
    values = $redis.hmget(member_key('attributes'), keys)
    raw = keys.zip(values).to_h
  else
    raw = $redis.hgetall(member_key('attributes'))
  end
  decoded = decode_attributes(raw)
  @attributes = restore_attribute_types decoded
  keep_attributes
end
make_unique_id() click to toggle source
# File lib/redisant/records.rb, line 265
def make_unique_id
  return if @id
  #use optimistic concurrency control:
  #if id is taken, try again until we succeed
  while true
    id = $redis.incr(self.class.class_key('ids:counter')).to_i
    unless self.class.exists? id
      @id = id
      return
    end
  end
end
member_key(str) click to toggle source

keys

# File lib/redisant/records.rb, line 178
def member_key str
  raise Redisant::InvalidArgument.new('Cannot make key without id') unless @id
  "#{class_name}:#{@id}:#{str}"
end
new?() click to toggle source
# File lib/redisant/records.rb, line 127
def new?
  id == nil
end
remove_id() click to toggle source
# File lib/redisant/records.rb, line 289
def remove_id
  raise Redisant::InvalidArgument.new('Cannot remove empty id') unless @id
  $redis.srem self.class.id_key, @id
  @id = nil
  @id_saved = false
end
save() click to toggle source
# File lib/redisant/records.rb, line 136
def save
  return false unless validate
  make_unique_id
  add_id
  save_attributes
  true
end
save!() click to toggle source
# File lib/redisant/records.rb, line 144
def save!
  raise Redisant::ValidationFailed.new('Validation failed') unless save
end
save_attributes() click to toggle source
# File lib/redisant/records.rb, line 231
def save_attributes
  if dirty?
    synthesize_attributes
    $redis.hmset member_key('attributes'), encode_attributes
    clean
  end
  update_search
end
set_attribute(key, value) click to toggle source
# File lib/redisant/records.rb, line 192
def set_attribute key, value
  if value != @attributes[key.to_s]
    @attributes[key.to_s] = value
    dirty [key]
  end
end
update_attribute(key, value) click to toggle source
# File lib/redisant/records.rb, line 199
def update_attribute key, value
  if value != @attributes[key.to_s]
    @attributes[key.to_s] = value
    $redis.hset member_key('attributes'), key, value
    update_search
  end
end
update_attributes(attributes) click to toggle source
# File lib/redisant/records.rb, line 240
def update_attributes attributes
  raise Redisant::InvalidArgument.new("Invalid arguments") unless attributes.is_a? Hash
  @attributes.merge! stringify_attributes(attributes)
  dirty attributes.keys
  save_attributes
end
validate() click to toggle source
# File lib/redisant/records.rb, line 148
def validate
  @errors = nil
  self.class.attributes.each_pair do |key,options|
    v = attribute(key)
    if options[:required]
      if v==nil
        @errors ||= {}
        @errors[key] = "is required"
      end
    end
    if v && options[:unique]
      conditions = {}
      conditions[key] = v
      if self.class.where(conditions).count > 0
        @errors ||= {}
        @errors[key] = "must be unique"
      end
    end
  end
  @errors == nil
end

Private Instance Methods

decode_attributes(attributes) click to toggle source
# File lib/redisant/records.rb, line 325
def decode_attributes attributes
  decoded = {}
  attributes.each do |pair|
    if pair[1]==nil
      decoded[pair[0]] = nil
    else
      decoded[pair[0]] = JSON.parse pair[1], quirks_mode: true
    end
  end
  @attributes = decoded
end
destroy_relations() click to toggle source
# File lib/redisant/records.rb, line 348
def destroy_relations
  relations.values.each {|relation| relation.destroy }
  @relations = nil
end
encode_attributes() click to toggle source
# File lib/redisant/records.rb, line 316
def encode_attributes
  encoded = {}
  @dirty.each do |key|
    k = key.to_s
    encoded[k] = @attributes[k].to_json
  end
  encoded.flatten
end
keep_attributes() click to toggle source
# File lib/redisant/records.rb, line 357
def keep_attributes
  if @attributes
    @prev_attributes = @attributes.dup
  else
    @prev_attributes = nil
  end
end
restore_attribute_types(attributes) click to toggle source
# File lib/redisant/records.rb, line 337
def restore_attribute_types attributes
  restored = {}
  attributes.each_pair do |k,v|
    time_match = /(.+)_at$/.match k
    if time_match
      restored[k] = Time.parse v
    end
  end
  attributes.merge! restored
end
stringify_attributes(attributes) click to toggle source
# File lib/redisant/records.rb, line 353
def stringify_attributes attributes
  attributes.collect {|k,v| [k.to_s,v] }.to_h if attributes
end
synthesize_attributes() click to toggle source

redis can only sort by string or float to sort by eg. Time we store float version of required attributes

# File lib/redisant/records.rb, line 301
def synthesize_attributes
  synthesized = {}
  self.class.indexes.each_pair do |name,index|
    if @dirty.include? name
      if index.type=='float'
        # for Time objects to_f return number of seconds since epoch
        key = "#{name}:float"
        value = @attributes[name].to_f
        @attributes[key] = value
        dirty key
      end
    end
  end
end