# frozen_string_literal: true module AnyCable module Rails module Connections # Wrap `request.session` to lazily load values provided # in the RPC call (set by the previous calls) class SessionProxy attr_reader :rack_session, :socket_session def initialize(rack_session, socket_session) @rack_session = rack_session @socket_session = JSON.parse(socket_session).with_indifferent_access end %i[has_key? [] []= fetch delete dig].each do |mid| class_eval <<~CODE, __FILE__, __LINE__ + 1 def #{mid}(*args, **kwargs, &block) restore_key! args.first rack_session.#{mid}(*args, **kwargs, &block) end CODE end alias_method :include?, :has_key? alias_method :key?, :has_key? %i[update merge! to_hash].each do |mid| class_eval <<~CODE, __FILE__, __LINE__ + 1 def #{mid}(*args, **kwargs, &block) restore! rack_session.#{mid}(*args, **kwargs, &block) end CODE end alias_method :to_h, :to_hash def keys rack_session.keys + socket_session.keys end # Delegate both publuc and private methods to rack_session def respond_to_missing?(name, include_private = false) return false if name == :marshal_dump || name == :_dump rack_session.respond_to?(name, include_private) || super end def method_missing(method, ...) if rack_session.respond_to?(method, true) rack_session.send(method, ...) else super end end # This method is used by StimulusReflex to obtain `@by` def instance_variable_get(name) super || rack_session.instance_variable_get(name) end private def restore! socket_session.keys.each(&method(:restore_key!)) end def restore_key!(key) return unless socket_session.key?(key) val = socket_session.delete(key) rack_session[key] = if val.is_a?(String) GlobalID::Locator.locate(val) || val else val end end end end end end