lib/fie/commander.rb

Summary

Maintainability
A
0 mins
Test Coverage
require 'fie/state'
require 'fie/pools'
require 'redis'

module Fie
  class Commander < ActionCable::Channel::Base
    @@pools_subjects = Set.new
    @@disable_override = false

    def subscribed
      stream_from Commander.commander_name(params['identifier'])
    end

    def initialize_state(params)
      self.state = State.new \
        view_variables: params['view_variables'],
        controller_name: params['controller_name'],
        action_name: params['action_name'],
        uuid: self.params[:identifier]
      
      execute_js_function('Fie.triggerFieReadyEvent')
    end

    def unsubscribed
      redis.del Fie::Commander.commander_name(params[:identifier])
    end

    def initialize_pools
      ActionCable.server.broadcast \
        Fie::Commander.commander_name(params[:identifier]),
        command: 'subscribe_to_pools',
        parameters: {
          subjects: @@pools_subjects.to_a
        }
    end

    def publish(subject, object)
      Fie::Pools.publish_lazy(subject, object, params[:identifier])
    end

    def state
      Marshal.load redis.get(Fie::Commander.commander_name params[:identifier])
    end

    def state=(state)
      redis.set Fie::Commander.commander_name(params[:identifier]), Marshal.dump(state)
    end

    def modify_state_using_changelog(params)
      state.update_object_using_changelog(params['objects_changelog'])
    end

    def execute_js_function(name, *arguments)
      ActionCable.server.broadcast \
        Fie::Commander.commander_name(params[:identifier]),
        command: 'execute_function',
        parameters: {
          name: name,
          arguments: arguments
        }
    end

    def connected?
      commander_name = Commander.commander_name(params[:identifier])
      !redis.get(commander_name).nil?
    end

    private
      def redis
        $redis ||= Redis.new
      end

      def method_keywords_hash(method_name, params)
        method(method_name).parameters.map do |_, parameter_name|
          [parameter_name, params[parameter_name.to_s]]
        end.to_h
      end

    class << self
      def pool(subject, &block)
        @@pools_subjects.add(subject)

        pool_name = Fie::Pools.pool_name(subject)
        define_method("#{ pool_name }_callback") do |object:, sender_uuid: nil, lazy: false|
          @connection_uuid = self.params['identifier']

          unless @connection_uuid == sender_uuid
            @published_object = Marshal.load(object)
            instance_eval(&block)
          end

          if lazy
            Fie::Pools.publish subject, Marshal.load(object), sender_uuid: @connection_uuid
          end
        end
      end

      def commander_name(connection_uuid)
        "commander_#{ connection_uuid }"
      end

      def method_added(name)
        super_commander_method_names = [
          :subscribed,
          :refresh_view, 
          :identifier,
          :state,
          :unsubscribed,
          :modify_state_using_changelog,
          :execute_js_function,
          :initialize_pools,
          :publish
        ]

        unless @@disable_override || super_commander_method_names.include?(name)
          @@disable_override = true

          restructure_subclass_method_parameters(name)

          @@disable_override = false
        end
      end

      private
        def restructure_subclass_method_parameters(method_name)
          alias_method("sub_#{ method_name }", method_name)
          remove_method(method_name)
          define_method(method_name) do |params|
            if caller = params['caller']
              @caller = { value: caller['value'], id: caller['id'], class: caller['class'] }
            end
            
            @controller_name = params['controller_name']
            @action_name = params['action_name']
            @connection_uuid = self.params['identifier']

            ['caller', 'action', 'controller_name', 'action_name'].each { |param| params.delete param }

            if params.blank?
              self.send(:"sub_#{ method_name }")
            else
              method_keywords_hash = method_keywords_hash(:"sub_#{ method_name }", params)
              self.send(:"sub_#{ method_name }", method_keywords_hash)
            end
          end
        end
    end
  end
end