1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00
rails--rails/activerecord/lib/active_record/scoping.rb
Aaron Patterson 73c18888ad
Use methods to get ScopeRegistry values rather than symbols
We're spending time validating symbol parameters of the ScopeRegistry.
It's an internal class, and we can stop validating symbols by converting
to methods (you'll automatically get an error if you try to call a
method that doesn't exist).  Second, since we only have 3 things to keep
track of, rather than keep those things in a hash, just break it out in
to 3 instance variables.  (This is absolutely not a memory bottleneck,
but technically this patch will save some memory as the 3 ivars will be
embedded in the object rather than require a full st_table for the
original wrapper hash)
2021-04-02 12:14:57 -07:00

123 lines
3.7 KiB
Ruby

# frozen_string_literal: true
require "active_support/per_thread_registry"
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 a thread local, which is
# accessed through +ScopeRegistry.current+.
#
# 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_value_for(:current_scope, Board, some_new_scope)
#
# Now when you run:
#
# registry.value_for(:current_scope, Board)
#
# You will obtain whatever was defined in +some_new_scope+. The #value_for
# and #set_value_for methods are delegated to the current ScopeRegistry
# object, so the above example code can also be called as:
#
# ActiveRecord::Scoping::ScopeRegistry.set_value_for(:current_scope,
# Board, some_new_scope)
class ScopeRegistry # :nodoc:
extend ActiveSupport::PerThreadRegistry
VALID_SCOPE_TYPES = [:current_scope, :ignore_default_scope, :global_current_scope]
def initialize
@current_scope = {}
@ignore_default_scope = {}
@global_current_scope = {}
end
VALID_SCOPE_TYPES.each do |type|
class_eval <<-eorb, __FILE__, __LINE__
def #{type}(model, skip_inherited_scope = false)
value_for(@#{type}, model, skip_inherited_scope)
end
def set_#{type}(model, value)
set_value_for(@#{type}, model, value)
end
eorb
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
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