Module: Parse::Cache::MonetaSurface

Included in:
KeyspacedStore, Redis, ScopedView
Defined in:
lib/parse/cache/moneta_surface.rb

Overview

The derived half of Moneta's store interface.

Moneta gets these from Moneta::Defaults, which the SDK's own store wrappers do not include. That gap was invisible for a long time because the Faraday caching middleware only ever calls the primitives. It is not invisible to applications: the README has documented Parse.cache["key"] = value and Parse.cache.fetch(...) for years, and neither existed on Redis. Anyone following the documentation got NoMethodError.

Required primitives. An including class must implement load, store, delete, and key?, each taking Moneta's options argument. load is the read primitive, NOT []: an earlier version of this module derived load from [] and consulted a moneta_backing_store that defaulted to self, so load(key, expires: 60) asked whether self responded to the method it was already executing and recursed until SystemStackError. Every option-carrying read went the same way, since fetch, values_at, slice, and fetch_values all route through it. A wrapper knows how to reach its own backing store; this module does not, and should not guess.

Optional capabilities that cannot be derived (create, increment, decrement, expire) are deliberately absent: they need backend support, and claiming them unconditionally would turn a feature check into a runtime error. Feature-detect those with respond_to?.

Instance Method Summary collapse

Instance Method Details

#[](key) ⇒ Object?

Parameters:

Returns:



34
35
36
# File 'lib/parse/cache/moneta_surface.rb', line 34

def [](key)
  load(key, {})
end

#[]=(key, value) ⇒ Object

Returns the stored value, so assignment chains as Ruby expects.

Parameters:

Returns:

  • (Object)

    the stored value, so assignment chains as Ruby expects.



42
43
44
45
# File 'lib/parse/cache/moneta_surface.rb', line 42

def []=(key, value)
  store(key, value, {})
  value
end

#fetch(key, default = nil, options = nil) ⇒ Object

Copied from Moneta::Defaults#fetch, deliberately line for line.

Its two shapes read the second positional differently: without a block it is the default value, with one it is the OPTIONS hash and the block supplies the fallback. Passing both a block and a third argument is an error, not something to silently absorb.

This has now been wrong twice from interpretation: first treating the second argument as a default in both shapes, which dropped the options, then accepting a block alongside a third argument and ignoring one of the two hashes. Reproducing the upstream method is cheaper than continuing to infer it.

Parameters:

  • key (String)
  • default (Object) (defaults to: nil)

    the fallback without a block, the options with one.

  • options (Hash, nil) (defaults to: nil)

Returns:

Raises:

  • (ArgumentError)

    when given both a block and options.



66
67
68
69
70
71
72
73
74
75
# File 'lib/parse/cache/moneta_surface.rb', line 66

def fetch(key, default = nil, options = nil)
  if block_given?
    raise ArgumentError, "Only one argument accepted if block is given" if options
    result = load(key, default || {})
    result == nil ? yield(key) : result
  else
    result = load(key, options || {})
    result == nil ? default : result
  end
end

#fetch_values(*keys, **options) ⇒ Array<Object>

Returns values in the order requested, with the block result substituted for misses.

Parameters:

Returns:

  • (Array<Object>)

    values in the order requested, with the block result substituted for misses.



87
88
89
90
91
92
93
# File 'lib/parse/cache/moneta_surface.rb', line 87

def fetch_values(*keys, **options)
  values = values_at(*keys, **options)
  return values unless block_given?
  keys.zip(values).map do |key, value|
    value == nil ? yield(key) : value
  end
end

#merge!(pairs, options = {}) ⇒ self Also known as: update

Returns matching Moneta, which returns the store.

Parameters:

  • pairs (Hash, Enumerable)

    key/value pairs to write.

Returns:

  • (self)

    matching Moneta, which returns the store.



109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/parse/cache/moneta_surface.rb', line 109

def merge!(pairs, options = {})
  pairs.each do |key, value|
    if block_given?
      # The existing value is read with the SAME options the write will
      # use, so a conflict block never decides against a value fetched
      # under different terms than the one being stored.
      existing = load(key, options)
      value = yield(key, existing, value) unless existing.nil?
    end
    store(key, value, options)
  end
  self
end

#slice(*keys, **options) ⇒ Array<Array(String, Object)>

Returns [key, value] pairs for the present keys only. Pairs rather than a Hash because that is what Moneta returns, and the point of this module is to behave the way a Moneta store does, not the way a Hash does.

Parameters:

Returns:

  • (Array<Array(String, Object)>)

    [key, value] pairs for the present keys only. Pairs rather than a Hash because that is what Moneta returns, and the point of this module is to behave the way a Moneta store does, not the way a Hash does.



100
101
102
103
104
105
# File 'lib/parse/cache/moneta_surface.rb', line 100

def slice(*keys, **options)
  keys.each_with_object([]) do |key, out|
    value = load(key, options)
    out << [key, value] unless value.nil?
  end
end

#values_at(*keys, **options) ⇒ Array<Object, nil>

Returns values in the order requested, nil for misses.

Parameters:

Returns:

  • (Array<Object, nil>)

    values in the order requested, nil for misses.



80
81
82
# File 'lib/parse/cache/moneta_surface.rb', line 80

def values_at(*keys, **options)
  keys.map { |key| load(key, options) }
end