Search code examples
ruby-on-railsrubyactivesupport

Instance variables in ActiveSupport::Concern


I have read that modules (in this case ActiveSupport::Concern) in ruby are shared amongst all instances of the class initialised. So if that were true it would mean any instance variables would be shared among all of the instances in memory.

module SetPassword
  extend ActiveSupport::Concern
  attr_reader :password
  def password=(new_password)
    @password = new_password
    self.crypted_password = password_digest(new_password, salt)
  end
end

class User
  include SetPassword
end

u = User.new; u.password = 'abcdef'
u2 = User.new; u2.password = '123456'

Is the code above safe to use? Or would the second user override the first user?


Solution

  • Module#include is under the hood calling Module#append_features. That means, nothing is shared and this might be easily checked (ActiveSupport::Concern has nothing to do with the code you are to check.)

    module SetPassword
      attr_reader :password
      def password=(new_password)
        @password = new_password
        puts <<~EOS
        Setting password to: #{@password} (id: #{@password.__id__}).
        Object: #{self.class.name} (#{self.__id__}).
        Method: #{__callee__} on #{self.method(__callee__).__id__}.
        EOS
      end 
    end
    
    class User
      include SetPassword
    end
    
    u1 = User.new; u1.password = 'abcdef'
    u2 = User.new; u2.password = '123456'
    

    The code above demonstrates that everything (the password itself, the instance variable and even the method on it’s own) is different:

    ▶ u1 = User.new; u1.password = 'abcdef'
    #⇒ Setting password to: abcdef (id: 46968741459360).
    #  Object: User (46968741459420).
    #  Method: password= on 46968741459040.
    
    ▶ u2 = User.new; u2.password = '123456'
    #⇒ Setting password to: 123456 (id: 46968734231740).
    #  Object: User (46968734232020).
    #  Method: password= on 46968734230640.