# frozen_string_literal: true require "active_support/core_ext/module/delegation" module ActiveRecord module Scoping extend ActiveSupport::Concern included do include Default include Named end module ClassMethods # :nodoc: # Collects attributes from scopes that should be applied when creating # an AR instance for the particular class this is called on. def scope_attributes all.scope_for_create end # Are there attributes associated with this scope? def scope_attributes? current_scope end def current_scope(skip_inherited_scope = false) ScopeRegistry.current_scope(self, skip_inherited_scope) end def current_scope=(scope) ScopeRegistry.set_current_scope(self, scope) end def global_current_scope(skip_inherited_scope = false) ScopeRegistry.global_current_scope(self, skip_inherited_scope) end def global_current_scope=(scope) ScopeRegistry.set_global_current_scope(self, scope) end def scope_registry ScopeRegistry.instance end end def populate_with_current_scope_attributes # :nodoc: return unless self.class.scope_attributes? attributes = self.class.scope_attributes _assign_attributes(attributes) if attributes.any? end def initialize_internals_callback # :nodoc: super populate_with_current_scope_attributes end # This class stores the +:current_scope+ and +:ignore_default_scope+ values # for different classes. The registry is stored as either a thread or fiber # local depending on the application configuration. # # This class allows you to store and get the scope values on different # classes and different types of scopes. For example, if you are attempting # to get the current_scope for the +Board+ model, then you would use the # following code: # # registry = ActiveRecord::Scoping::ScopeRegistry # registry.set_current_scope(Board, some_new_scope) # # Now when you run: # # registry.current_scope(Board) # # You will obtain whatever was defined in +some_new_scope+. class ScopeRegistry # :nodoc: class << self delegate :current_scope, :set_current_scope, :ignore_default_scope, :set_ignore_default_scope, :global_current_scope, :set_global_current_scope, to: :instance def instance ActiveSupport::IsolatedExecutionState[:active_record_scope_registry] ||= new end end def initialize @current_scope = {} @ignore_default_scope = {} @global_current_scope = {} end def current_scope(model, skip_inherited_scope = false) value_for(@current_scope, model, skip_inherited_scope) end def set_current_scope(model, value) set_value_for(@current_scope, model, value) end def ignore_default_scope(model, skip_inherited_scope = false) value_for(@ignore_default_scope, model, skip_inherited_scope) end def set_ignore_default_scope(model, value) set_value_for(@ignore_default_scope, model, value) end def global_current_scope(model, skip_inherited_scope = false) value_for(@global_current_scope, model, skip_inherited_scope) end def set_global_current_scope(model, value) set_value_for(@global_current_scope, model, value) end private # Obtains the value for a given +scope_type+ and +model+. def value_for(scope_type, model, skip_inherited_scope = false) return scope_type[model.name] if skip_inherited_scope klass = model base = model.base_class while klass != base value = scope_type[klass.name] return value if value klass = klass.superclass end scope_type[klass.name] end # Sets the +value+ for a given +scope_type+ and +model+. def set_value_for(scope_type, model, value) scope_type[model.name] = value end end end end