leoniv/ass_launcher

View on GitHub
lib/ass_launcher/enterprise/ole.rb

Summary

Maintainability
A
0 mins
Test Coverage
# encoding: utf-8

require 'ass_launcher/enterprise/ole/ole_binaries'

module AssLauncher
  module Enterprise
    # 1C:Enterprise ole objects layer
    module Ole
      # Wrapper for {OLE_CLIENT_TYPES}
      def self.ole_client(type)
      fail ArgumentError, "Invalid ole type `#{type}'. Use types:"\
        " #{OLE_CLIENT_TYPES.keys}" unless OLE_CLIENT_TYPES.key? type
        OLE_CLIENT_TYPES[type]
      end

      # 1C Infobase External Connection
      class IbConnection
        attr_reader :__ole__
        protected :__ole__

        # (see OleBinaries::AbstractAssOleBinary#initialize)
        def initialize(requirement)
          @requirement = requirement.to_s
        end

        # Open connection in to infobase described in conn_str
        # @param conn_str [Support::ConnectionString::Server,
        #  Support::ConnectionString::File, String]
        def __open__(conn_str)
          return true if __opened__?
          __init_ole__(__ole_binary__.ole.connect(__cs__(conn_str)))
          true
        end

        def __init_ole__(ole)
          @__ole__ = ole
        end
        private :__init_ole__

        # Try close connection.
        # @note *It* *not* *guaranteed* *real* *closing* *connection!*
        #  Connection keep alive while have any alive WIN32OLE refs
        #  generated this connection. {WIN32OLE#\_\_ass_ole_free\_\_} try kill
        #  refs but it work not always
        # @see WIN32OLE
        def __close__
          return true if __closed__?
          __ole__.send :__ass_ole_free__
          @__ole__ = nil
          true
        end

        # True if connection closed
        def __closed__?
          __ole__.nil?
        end

        # True if connection opened
        def __opened__?
          !__closed__?
        end

        # Set 1C Ole server properties
        def __configure_com_connector__(opts)
          opts.each do |k, v|
            __ole_binary__.ole.setproperty(k, v)
          end
        end

        def __cs__(conn_str)
          return conn_str.to_ole_string if conn_str.respond_to? :to_ole_string
          conn_str.to_s
        end
        protected :__cs__

        def __ole_binary__
          @__ole_binary__ ||= OleBinaries::COMConnector.new(@requirement)
        end
        protected :__ole_binary__

        # Try call ole method
        # @raise [RuntimeError] if object closed
        def method_missing(method, *args)
          fail "Attempt call method for closed object #{self.class.name}"\
            if __closed__?
          __ole__.send(method, *args)
        end
        protected :method_missing
      end

      # IWorkingProcessConnection
      class WpConnection < IbConnection
        # Connection with 1C Server working process described in uri
        # @param uri [URI, String]
        def __open__(uri)
          return true if __opened__?
          __init_ole__(__ole_binary__.ole.connectworkingprocess(uri.to_s))
          true
        end
      end

      # Wrapper for IServerAgentConnection
      class AgentConnection < IbConnection
        # Connection with 1C Server agent described in uri
        # @param (see WpConnection#__open__)
        def __open__(uri)
          return true if __opened__?
          __init_ole__(__ole_binary__.ole.connectagent(uri.to_s))
          true
        end
      end

      class ApplicationConnectError < StandardError; end

      # Wrapper for V8xc.Application ole object
      class ThinApplication < IbConnection
        # Array of objects with opened connection for close all
        def self.objects
          @objects ||= []
        end

        # Close all opened connectons
        def self.close_all
          objects.each(&:__close__)
        end

        def initialize(requirement)
          super
          @opened = false
        end

        # (see IbConnection#__open__)
        # @raise [ApplicationConnectError]
        def __open__(conn_str)
          return true if __opened__?
          __try_open__(conn_str)
          self.class.objects << self
          __opened__?
        end

        def __try_open__(conn_str)
          @opened = __ole_binary__.ole.connect(__cs__(conn_str))
          fail ApplicationConnectError unless __opened__?
        rescue StandardError => e
          @opened = false
          @__ole_binary__ = nil
          raise e
        end
        protected :__try_open__

        def __ole__
          __ole_binary__.ole
        end
        protected :__ole__

        def __opened__?
          @opened
        end

        def __closed__?
          !__opened__?
        end

        def __close__
          return true if __closed__?
          # rubocop:disable HandleExceptions
          begin
            __ole__.terminate
          rescue
            # NOP
          ensure
            @__ole_binary__ = nil
            @opened = false
            ThinApplication.objects.delete(self)
          end
          # rubocop:enable HandleExceptions
          true
        end

        def __ole_binary__
          @__ole_binary__ ||= OleBinaries::ThinApplication.new(@requirement)
        end
        protected :__ole_binary__
      end

      # Wrapper for V8x.Application ole object
      class ThickApplication < ThinApplication
        def __ole_binary__
          @__ole_binary__ ||= OleBinaries::ThickApplication.new(@requirement)
        end
        protected :__ole_binary__
      end

      # Define type of 1C OLE clients
      OLE_CLIENT_TYPES = {
        external: AssLauncher::Enterprise::Ole::IbConnection,
        wprocess: AssLauncher::Enterprise::Ole::WpConnection,
        sagent: AssLauncher::Enterprise::Ole::AgentConnection,
        thin: AssLauncher::Enterprise::Ole::ThinApplication,
        thick: AssLauncher::Enterprise::Ole::ThickApplication
      }
    end
  end
end