Instance Variable scoping is so damn important; check this out:
- When self is a class, they are instance variables of classes(class instance variables).
- When self is a object, they are instance variables of objects(instance variables).
class ImSoCleverClass
@a_class_i_var = "I am a class level instance variable. All threads can see me."
@@a_class_var = "I am a class variable, I am available through each instance of the object but shared/same between all instantiated objects. - yes, this includes threads"
def self.class_level_memo(msg = "class level fart")
@class_level_memo ||= "#{msg}, oh look, a class level Memoization, this is the same as the class instance variable and is of course, shared across threads"
end
class << self
def lol_why(msg = "anotehr class level fart")
@lol_why ||= "#{msg}, good ol class << self, damn homie, you are one clever rubyist! (class level instanve variable. same as all the above)"
end
end
def initialize(name = "local fart")
@object_i_var = "Yay! Each instance has a unique version of me, #{name}! Thread safe! (unless of course you are passing me around to each thread manually, then we will have to set some ground rules!)"
end
def call
# object level scope fun!
puts @object_i_var # will see it's own message, owned by the object!
puts @@a_class_var # will also see this, but so will every instance of this object, and it will be the same!
puts "@a_class_i_var defined? #{!!defined? @a_class_i_var}" # cant see this one!!! Get to this by accessing the class itself. Example: self.class.a_class_i_var
puts "@class_level_memo defined? #{!!defined? @class_level_memo}" # cant see, assigned to the class, not the object
puts "@lol_why defined? #{!!defined? @lol_why}" #same as above
end
def self.call(msg)
# Class level fun time!
puts @object_i_var || "cant see @object_i_var, this is scoped to each individual object instance"
puts @@a_class_var
puts @a_class_i_var
puts class_level_memo(msg)
puts lol_why(msg)
end
end
puts "Object Instantiation:"
ImSoCleverClass.new("local fart").call
puts "\n\nClass Level Fun:"
ImSoCleverClass.call("another fart")
puts "\n\nNotice how msg still equals `another fart`:"
ImSoCleverClass.call("ignored fart")
- If you follow rails conventions, and avoid global and class level instance variables, you're mostly* fine.
- (*) But are you're gems?
- If you are using
Thread.current
, you're probably going to introduce another bug, even if you are attempting to fix an unsafe gem problem. - Consider wrapping unsafe object access in a mutex
https://workingwithruby.com/wwrt/intro https://bearmetal.eu/theden/how-do-i-know-whether-my-rails-app-is-thread-safe-or-not/