Class: Magick::Adapters::Base

Inherits:
Object
  • Object
show all
Defined in:
lib/magick/adapters/base.rb

Direct Known Subclasses

ActiveRecord, Memory, Redis

Instance Method Summary collapse

Instance Method Details

#all_features ⇒ Object

Raises:

  • (NotImplementedError)


22
23
24
# File 'lib/magick/adapters/base.rb', line 22

def all_features
  raise NotImplementedError, "#{self.class} must implement #all_features"
end

#delete(feature_name) ⇒ Object

Raises:

  • (NotImplementedError)


14
15
16
# File 'lib/magick/adapters/base.rb', line 14

def delete(feature_name)
  raise NotImplementedError, "#{self.class} must implement #delete"
end

#delete_key(feature_name, key) ⇒ Object

Remove a single key from a feature's data, leaving the rest intact. Subclasses MUST implement this — there is no way to express "delete one key" in terms of the other primitives, and retention (pruning the version history hot window) depends on it.

Raises:

  • (NotImplementedError)


71
72
73
74
# File 'lib/magick/adapters/base.rb', line 71

def delete_key(feature_name, key)
  raise NotImplementedError,
        "#{self.class} must implement #delete_key (feature=#{feature_name}, key=#{key})"
end

#exists?(feature_name) ⇒ Boolean

Returns:

  • (Boolean)

Raises:

  • (NotImplementedError)


18
19
20
# File 'lib/magick/adapters/base.rb', line 18

def exists?(feature_name)
  raise NotImplementedError, "#{self.class} must implement #exists?"
end

#get(feature_name, key) ⇒ Object

Raises:

  • (NotImplementedError)


6
7
8
# File 'lib/magick/adapters/base.rb', line 6

def get(feature_name, key)
  raise NotImplementedError, "#{self.class} must implement #get"
end

#get_all_data(feature_name) ⇒ Object

Load all keys for a single feature in one call (override for efficiency)



27
28
29
# File 'lib/magick/adapters/base.rb', line 27

def get_all_data(feature_name)
  {}
end

#load_all_features_data ⇒ Object

Bulk load all features' data in one call (override for efficiency)



32
33
34
# File 'lib/magick/adapters/base.rb', line 32

def load_all_features_data
  {}
end

#load_features_data_with_prefix(prefix) ⇒ Object

Bulk load every feature whose name starts with prefix, and every feature whose name starts with none of prefixes. Bulk callers are split this way because the reserved bookkeeping namespaces (version snapshots, audit history) must stay out of the paths that only ever want features (cache preload, the Admin UI source refresh), while the version archive wants exactly its own namespace and nothing else.

Adapters backed by a queryable store SHOULD override these to filter at the source: the defaults below are correct, but they still read the rows they then throw away, which is the cost the split exists to avoid.



46
47
48
49
# File 'lib/magick/adapters/base.rb', line 46

def load_features_data_with_prefix(prefix)
  prefix = prefix.to_s
  load_all_features_data.select { |feature_name, _| feature_name.to_s.start_with?(prefix) }
end

#load_features_data_without_prefixes(prefixes) ⇒ Object



51
52
53
54
55
56
# File 'lib/magick/adapters/base.rb', line 51

def load_features_data_without_prefixes(prefixes)
  prefixes = Array(prefixes).map(&:to_s)
  load_all_features_data.reject do |feature_name, _|
    prefixes.any? { |prefix| feature_name.to_s.start_with?(prefix) }
  end
end

#next_sequence(feature_name, key, floor: 0) ⇒ Object

Atomically allocate the next number in a monotonic counter stored under (feature_name, key) and return it. The result is always greater than both the stored value and floor; callers pass the highest number they can already see as floor so a store that lost its counter (Redis flush, restored dump, upgrade from a release that had none) resumes above the surviving data instead of restarting at 1 and overwriting it.

Adapters backed by a store SHARED BETWEEN PROCESSES must override this with a genuinely atomic primitive (Redis HINCRBY, a row lock, a database sequence). This default is a plain read-modify-write: it is correct for a store only one process can reach, and will hand the same number to two processes that call it concurrently.



88
89
90
91
92
# File 'lib/magick/adapters/base.rb', line 88

def next_sequence(feature_name, key, floor: 0)
  value = [get(feature_name, key).to_i, floor.to_i].max + 1
  set(feature_name, key, value)
  value
end

#set(feature_name, key, value) ⇒ Object

Raises:

  • (NotImplementedError)


10
11
12
# File 'lib/magick/adapters/base.rb', line 10

def set(feature_name, key, value)
  raise NotImplementedError, "#{self.class} must implement #set"
end

#set_all_data(feature_name, data_hash) ⇒ Object

Bulk set multiple keys for a feature in one call. Subclasses MUST implement this — a no-op default silently drops bulk writes, which is why this used to cause hard-to-diagnose lost updates for custom adapters (audit P2-Co6).

Raises:

  • (NotImplementedError)


62
63
64
65
# File 'lib/magick/adapters/base.rb', line 62

def set_all_data(feature_name, data_hash)
  raise NotImplementedError,
        "#{self.class} must implement #set_all_data (feature=#{feature_name}, keys=#{data_hash.keys.inspect})"
end