Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Grouping thread locals in the ActiveRecord scopes #10135

Merged
merged 1 commit into from

2 participants

@wangjohn

I'm grouping some thread locals under a registry object so that we can try to get the thread locals under control. I've brought the current_scope and ignore_default_scope locals together into a single object.

Accesses to these thread locals are now governed through the ScopeRegistry object.

@rafaelfranca
Owner

I think the main reason that @tenderlove want to do this is to lower the number of thread current variables and this current implementation is not changing anything.

I may be wrong but I think we have to do something like this:

class ScopeRegistry
  def self.current
    Thread.current["scope_registry"] ||= new
  end

  def initialize
    @current_scopes = {}
    @ignore_default_scopes = {}
  end

  def current_scope_for(class_name)
    @current_scopes[class_name]
  end

  def set_current_scope_for(class_name, value)
    @current_scopes[class_name] = value
  end

  ...
@wangjohn

@rafaelfranca Ah! Lowering the number of thread locals makes sense. I'll change the implementation to something like what you gave.

@wangjohn

@rafaelfranca I've rewritten the PR so that there is only a single thread local for all the scopes. There's now a single registry object which keeps track of everything.

@rafaelfranca rafaelfranca commented on the diff
activerecord/lib/active_record/scoping.rb
((7 lines not shown))
+ # 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
@rafaelfranca Owner

I don't think this class should be part of the public API, so I'd add a # :nodoc: here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
activerecord/lib/active_record/scoping.rb
((17 lines not shown))
+ # Now when you run:
+ #
+ # registry.value_for(:current_scope, "Board")
+ #
+ # You will obtain whatever was defined in +some_new_scope+.
+ class ScopeRegistry
+ def self.current
+ Thread.current["scope_registry"] ||= new
+ end
+
+ VALID_SCOPE_TYPES = [:current_scope, :ignore_default_scope]
+
+ attr_accessor :registry
+
+ def initialize
+ @registry = {}
@rafaelfranca Owner
@registry = Hash.new { |hash, key| hash[key] = {} }

So we don't need to loop on the VALID_SCOPE_TYPES

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@wangjohn wangjohn Grouping thread locals in the ActiveRecord scopes so that the
current_scope and ignore_default_scope locals are brought together under
a registry object.
5b945f2
@wangjohn

@rafaelfranca Thanks for the comments, I've updated the PR.

@rafaelfranca rafaelfranca merged commit e2e9eed into rails:master
@wangjohn wangjohn deleted the wangjohn:grouping_thread_locals branch
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Apr 8, 2013
  1. @wangjohn

    Grouping thread locals in the ActiveRecord scopes so that the

    wangjohn authored
    current_scope and ignore_default_scope locals are brought together under
    a registry object.
This page is out of date. Refresh to see the latest.
View
55 activerecord/lib/active_record/scoping.rb
@@ -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
@@ -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:
@rafaelfranca Owner

I don't think this class should be part of the public API, so I'd add a # :nodoc: here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ 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
View
4 activerecord/lib/active_record/scoping/default.rb
@@ -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
View
4 activerecord/test/cases/base_test.rb
@@ -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
Something went wrong with that request. Please try again.