Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Grouping thread locals in the ActiveRecord scopes #10135

Merged
merged 1 commit into from Apr 8, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
55 changes: 53 additions & 2 deletions activerecord/lib/active_record/scoping.rb
Expand Up @@ -9,11 +9,11 @@ module Scoping

module ClassMethods
def current_scope #:nodoc:
Thread.current["#{base_class}_current_scope"]
ScopeRegistry.current.value_for(:current_scope, base_class.to_s)
end

def current_scope=(scope) #:nodoc:
Thread.current["#{base_class}_current_scope"] = scope
ScopeRegistry.current.set_value_for(:current_scope, base_class.to_s, scope)
end
end

Expand All @@ -24,5 +24,56 @@ def populate_with_current_scope_attributes
send("#{att}=", value) if respond_to?("#{att}=")
end
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.current
# 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+.
class ScopeRegistry # :nodoc:
def self.current
Thread.current["scope_registry"] ||= new
end

VALID_SCOPE_TYPES = [:current_scope, :ignore_default_scope]

attr_accessor :registry

def initialize
@registry = Hash.new { |hash, key| hash[key] = {} }
end

# Obtains the value for a given +scope_name+ and +variable_name+.
def value_for(scope_type, variable_name)
raise_invalid_scope_type!(scope_type)
@registry[scope_type][variable_name]
end

# Sets the +value+ for a given +scope_type+ and +variable_name+.
def set_value_for(scope_type, variable_name, value)
raise_invalid_scope_type!(scope_type)
@registry[scope_type][variable_name] = value
end

private

def raise_invalid_scope_type!(scope_type)
if !VALID_SCOPE_TYPES.include?(scope_type)
raise ArgumentError, "Invalid scope type '#{scope_type}' sent to the registry. Scope types must be included in VALID_SCOPE_TYPES"
end
end
end
end
end
4 changes: 2 additions & 2 deletions activerecord/lib/active_record/scoping/default.rb
Expand Up @@ -120,11 +120,11 @@ def build_default_scope # :nodoc:
end

def ignore_default_scope? # :nodoc:
Thread.current["#{self}_ignore_default_scope"]
ScopeRegistry.current.value_for(:ignore_default_scope, self)
end

def ignore_default_scope=(ignore) # :nodoc:
Thread.current["#{self}_ignore_default_scope"] = ignore
ScopeRegistry.current.set_value_for(:ignore_default_scope, self, ignore)
end

# The ignore_default_scope flag is used to prevent an infinite recursion
Expand Down
4 changes: 2 additions & 2 deletions activerecord/test/cases/base_test.rb
Expand Up @@ -1377,9 +1377,9 @@ def test_current_scope_is_reset
UnloadablePost.send(:current_scope=, UnloadablePost.all)

UnloadablePost.unloadable
assert_not_nil Thread.current[:UnloadablePost_current_scope]
assert_not_nil ActiveRecord::Scoping::ScopeRegistry.current.value_for(:current_scope, "UnloadablePost")
ActiveSupport::Dependencies.remove_unloadable_constants!
assert_nil Thread.current[:UnloadablePost_current_scope]
assert_nil ActiveRecord::Scoping::ScopeRegistry.current.value_for(:current_scope, "UnloadablePost")
ensure
Object.class_eval{ remove_const :UnloadablePost } if defined?(UnloadablePost)
end
Expand Down