Class: Yes::Core::Authorization::LookupCache

Inherits:
Object
  • Object
show all
Defined in:
lib/yes/core/authorization/lookup_cache.rb

Overview

Scoped memoization for the read-only lookups an authorization pass repeats.

Authorizing a batch of commands resolves the same two things once per command: the principal data (which depends only on the request's auth data) and the authorized resource (which the commands of a batch commonly share). Both are pure reads, and a batch is authorized in full before any of its commands is executed, so no write can invalidate them while the pass is running.

Caching is only active inside LookupCache.with_scope. Outside one, LookupCache.fetch just yields, so callers keep their uncached behaviour unless they opt in. The store lives in ActiveSupport::IsolatedExecutionState, which keeps it per thread/fiber, and LookupCache.with_scope always clears it on the way out so nothing leaks into the next request.

Cached values are shared by every LookupCache.fetch for the same key, so callers must treat them as read-only.

Examples:

Caching the lookups of one authorization pass

LookupCache.with_scope do
  commands.each { |command| authorizer_for(command).call(command, auth_data) }
end

Constant Summary collapse

STORE_KEY =
:yes_core_authorization_lookup_cache

Class Method Summary collapse

Class Method Details

.active? ⇒ Boolean

Returns whether a scope is currently open.

Returns:

  • (Boolean) —

    whether a scope is currently open



65
66
67
# File 'lib/yes/core/authorization/lookup_cache.rb', line 65

def active?
  ActiveSupport::IsolatedExecutionState.key?(STORE_KEY)
end

.fetch(key) { ... } ⇒ Object

Returns the value cached under key, computing it via the block on a miss. Without an open scope the block's value is returned uncached.

Parameters:

  • key (Object) —

    cache key

Yields:

  • computes the value when it is not cached yet

Returns:

  • (Object) —

    the cached or freshly computed value



55
56
57
58
59
60
61
62
# File 'lib/yes/core/authorization/lookup_cache.rb', line 55

def fetch(key)
  return yield unless active?

  store = ActiveSupport::IsolatedExecutionState[STORE_KEY]
  return store[key] if store.key?(key)

  store[key] = yield
end

.with_scope { ... } ⇒ Object

Runs the block with caching enabled, clearing the cache afterwards. A nested scope reuses the cache of the outermost one and leaves clearing to it.

Yields:

  • the block to run with caching enabled

Returns:

  • (Object) —

    the block's return value



37
38
39
40
41
42
43
44
45
46
47
# File 'lib/yes/core/authorization/lookup_cache.rb', line 37

def with_scope
  return yield if active?

  ActiveSupport::IsolatedExecutionState[STORE_KEY] = {}

  begin
    yield
  ensure
    ActiveSupport::IsolatedExecutionState.delete(STORE_KEY)
  end
end