# frozen_string_literal: true require_relative 'global_context_registry/current' require_relative 'global_context_registry/current_instance' require_relative 'global_context_registry/registry_dependent_on' # Handles storing of global state that may be swapped out or unset at # various points. We use this in dandelion to store the current user, # org, catalog, etc. # # Note: You must ensure your entries are .dup'able. # module RailsMultitenant module GlobalContextRegistry extend self # Set this global def set(symbol, value) globals[symbol] = value end alias_method :[]=, :set # delete this global def delete(symbol) globals.delete(symbol) end # Pass with a generator block for the value def fetch(symbol) result = globals[symbol] unless result result = yield globals[symbol] = result end result end # get the global identified by the symbol def get(symbol) globals[symbol] end alias_method :[], :get # merge the given values into the registry def merge!(values) globals.merge!(values) end # Duplicate the registry def duplicate_registry globals.transform_values do |value| value.nil? || value.is_a?(Integer) ? value : value.dup end end # Run a block of code with an the given registry def with_isolated_registry(registry = {}) prior_globals = new_registry(registry) yield ensure self.globals = prior_globals end # Run a block of code with the given values merged into the current registry def with_merged_registry(values = {}) prior_globals = new_registry(globals.merge(values)) yield ensure self.globals = prior_globals end # Prefer .with_isolated_registry to the following two methods. # Note: these methods are intended for use in a manner like .with_isolated_registry, # but in contexts where around semantics are not allowed. # Set a new, by default empty registry, returning the previous one. def new_registry(registry = {}) priors = globals self.globals = registry priors end # Replace the registry with one you previously took away with .new_registry def replace_registry(registry) self.globals = registry end # Run a block of code that disregards scoping during read queries def with_unscoped_queries # disabling Style/ExplicitBlockArgument for performance reasons with_merged_registry(__use_unscoped_queries: true) do # rubocop:disable Style/ExplicitBlockArgument yield end end def use_unscoped_queries? self[:__use_unscoped_queries] == true end # Prefer .with_unscoped_queries to the following two methods. # Note: these methods are intended for use in a manner like .with_admin_registry, # but in contexts where around semantics are not allowed. def disable_scoped_queries self[:__use_unscoped_queries] = true end def enable_scoped_queries self[:__use_unscoped_queries] = nil end private @dependencies = {} def add_dependency(parent, dependent) (@dependencies[parent] ||= []) << dependent end def dependencies_for(klass) @dependencies[klass] || [] end def globals registry = Thread.current[:global_context_registry] unless registry registry = {} Thread.current[:global_context_registry] = registry end registry end def globals=(value) Thread.current[:global_context_registry] = value end end end