class FMCache::Engine

Constants

DEFAULT_TTL

Attributes

client[R]
decoder[R]
encoder[R]
fm_parser[R]

Public Class Methods

new( client:, fm_parser:, ttl: DEFAULT_TTL, notifier: nil, json_serializer: nil, id_key_prefix: nil ) click to toggle source

@param [Redis | MockRRedis] client @param [Proc] fm_parser @param [Integer] ttl @param [Proc, nil] notifier @param [#dump#load, nil] json_serializer @param [String, nil] id_key_prefix

# File lib/fmcache/engine.rb, line 11
def initialize(
  client:,
  fm_parser:,
  ttl:             DEFAULT_TTL,
  notifier:        nil,
  json_serializer: nil,
  id_key_prefix:   nil
)
  @client     = Client.new(client, notifier)
  @fm_parser  = wrap(fm_parser)
  @ttl        = ttl
  @id_key_gen = IdKeyGen.new(id_key_prefix)
  @encoder    = Encoder.new(@id_key_gen)
  @decoder    = Decoder.new(@fm_parser)
  @jsonizer   = Jsonizer.new(json_serializer)
end

Public Instance Methods

delete(ids:) click to toggle source

@param [<Integer | String>] ids

# File lib/fmcache/engine.rb, line 91
def delete(ids:)
  ids = ids.map(&:to_i)
  client.del(keys: @id_key_gen.to_keys(ids))
end
fetch(ids:, field_mask:, &block) click to toggle source

@param [<Integer | String>] ids @param [FieldMaskParser::Node] field_mask @yieldparam [<Integer>, FieldMaskParser::Node] ids, field_mask @yieldreturn [<Hash>] @return [<Hash>]

# File lib/fmcache/engine.rb, line 62
def fetch(ids:, field_mask:, &block)
  ids = ids.map(&:to_i)
  normalize!(field_mask)

  values, incomplete_values, incomplete_info = read(ids: ids, field_mask: field_mask)
  return values if incomplete_info.ids.size == 0

  # NOTE: get new data
  d = block.call(incomplete_info.ids, incomplete_info.field_mask)
  write(values: d, field_mask: incomplete_info.field_mask)

  older = encode(incomplete_values, field_mask)
  newer = encode(d,                 incomplete_info.field_mask)

  v, i_v, i_i = decode(older.deep_merge(newer), field_mask)

  if i_i.ids.size == 0
    r = values + v + i_v
  else
    # NOTE: Fallback to block.call with full field_mask
    d2 = block.call(i_i.ids, field_mask)
    write(values: d2, field_mask: field_mask)
    r = values + d2
  end

  Helper.sort(r, ids)
end
read(ids:, field_mask:) click to toggle source

@param [<Integer | String>] ids @param [FieldMaskParser::Node] field_mask @return [<Hash>, <Hash>, IncompleteInfo]

# File lib/fmcache/engine.rb, line 42
def read(ids:, field_mask:)
  ids = ids.map(&:to_i)
  normalize!(field_mask)

  keys   = @id_key_gen.to_keys(ids)
  fields = Helper.to_fields(field_mask)
  h = client.get(keys: keys, fields: fields)

  with_id, with_no_id = split(h)
  v, i_v, i_i = decode(@jsonizer.dejsonize(with_id), field_mask)
  with_no_id_list = @id_key_gen.to_ids(with_no_id.keys)

  return v, i_v, merge(i_i, with_no_id_list, field_mask)
end
write(values:, field_mask:) click to toggle source

@param [<Hash>] values @param [FieldMaskParser::Node] field_mask @return [Boolean]

# File lib/fmcache/engine.rb, line 33
def write(values:, field_mask:)
  normalize!(field_mask)
  h = encode(values, field_mask)
  client.set(values: @jsonizer.jsonize(h), ttl: @ttl)
end

Private Instance Methods

decode(hash, field_mask) click to toggle source

@param [{ String => { String => <Hash> } }] hash @param [FieldMaskParser::Node] field_mask @return [<Hash>, <Hash>, IncompleteInfo]

# File lib/fmcache/engine.rb, line 101
def decode(hash, field_mask)
  decoder.decode(hash, field_mask)
end
encode(values, field_mask) click to toggle source

@param [<Hash>] values @param [FieldMaskParser::Node] field_mask @rerturn [{ String => { String => <Hash> } }]

# File lib/fmcache/engine.rb, line 108
def encode(values, field_mask)
  encoder.encode(values, field_mask)
end
merge(incomplete_info, with_no_id_list, field_mask) click to toggle source

@param [IncompleteInfo] incomplete_info @param [<Integer>] with_no_id_list @param [FieldMaskParser::Node] field_mask @return [IncompleteInfo]

# File lib/fmcache/engine.rb, line 151
def merge(incomplete_info, with_no_id_list, field_mask)
  if with_no_id_list.size == 0
    return incomplete_info
  end

  ids = incomplete_info.ids + with_no_id_list
  fields = Set.new(Helper.to_fields(incomplete_info.field_mask)) | Set.new(Helper.to_fields(field_mask))

  IncompleteInfo.new(ids: ids, field_mask: fm_parser.call(fields))
end
normalize!(field_mask) click to toggle source

@param [FieldMaskParser::Node] field_mask

# File lib/fmcache/engine.rb, line 123
def normalize!(field_mask)
  if !field_mask.attrs.include?(:id)
    field_mask.attrs << :id
  end
  field_mask.assocs.each do |a|
    normalize!(a)
  end
end
split(h) click to toggle source
# File lib/fmcache/engine.rb, line 132
def split(h)
  with_id    = {}
  with_no_id = {}

  h.each do |k, v|
    if v.fetch("id").nil?
      with_no_id[k] = v
    else
      with_id[k] = v
    end
  end

  return with_id, with_no_id
end
wrap(fm_parser) click to toggle source

@param [Proc] fm_parser @return [Proc]

# File lib/fmcache/engine.rb, line 114
def wrap(fm_parser)
  -> (fields) {
    n = fm_parser.call(fields)
    normalize!(n)
    n
  }
end