kvokka/activevalidation

View on GitHub
lib/active_validation/internal/observers/manifest.rb

Summary

Maintainability
A
35 mins
Test Coverage
# frozen_string_literal: true

module ActiveValidation
  module Internal
    module Observers
      class Manifest
        DISABLED_VERIFIER = :verifier_is_not_enabled
        RECENT_FAILURE = :it_was_failed_too_recently
        NOT_FOUND = :not_found
        ALREADY_INSTALLED = :already_installed
        INSTALLED = :installed
        UNINSTALLED = :uninstalled

        attr_reader :verifier, :installed_manifests, :last_failed_attempt_time

        delegate :manifest, :failed_attempt_retry_time, :enabled?, to: :verifier

        def initialize(verifier)
          @verifier = verifier
          @installed_manifests = Concurrent::Set.new
          @lock = Concurrent::ReadWriteLock.new
          @last_failed_attempt_time = nil
        end

        # Install manifest and store the result. Load priority:
        #   * `manifest_id` from the arguments
        #   * `verifier.manifest` - defined by user in verifier block
        #   * `current_manifest` - the latest manifest with current version
        #
        # The process will be skipped if:
        #   - `verifier` not `enabled?`
        #   - The last failed attempt was too recent.
        #
        # @return Symbol
        def install(manifest_id: nil)
          return DISABLED_VERIFIER unless enabled?
          return RECENT_FAILURE if attempt_to_was_failed_recently?

          found = lock.with_read_lock { find(manifest_id: manifest_id) } || current_manifest
          return  NOT_FOUND unless found
          return ALREADY_INSTALLED if found.installed?

          install! internal_manifest: found
        end

        # Uninstall the manifest.
        #
        # @return Symbol
        def uninstall(manifest_id:)
          lock.with_write_lock do
            internal_manifest = find(manifest_id: manifest_id)
            return NOT_FOUND unless internal_manifest&.installed?

            internal_manifest.uninstall
            installed_manifests.delete internal_manifest
          end
          UNINSTALLED
        end

        # We omit the error and only save the error time to prevent
        # DB flooding, when there is no any manifest.
        #
        # We do not know all possible errors so we can not be more
        # specific here.
        #
        # @return [Internal::Manifest]
        def current_manifest
          verifier.current_manifest.to_internal_manifest
        rescue StandardError => _e
          @last_failed_attempt_time = Time.now
          nil
        end

        # We need this method for been able to restore manifest lookup
        #
        # @return nil
        def reset_last_failed_attempt_time
          @last_failed_attempt_time = nil
        end

        # The actual install process
        #
        # @return Symbol
        def install!(internal_manifest:)
          lock.with_write_lock do
            return ALREADY_INSTALLED if find(manifest_id: internal_manifest.id)&.installed?

            internal_manifest.install
            installed_manifests << internal_manifest
          end
          INSTALLED
        end

        private

        attr_reader :lock

        def find(manifest_id: nil)
          return unless manifest_id

          @installed_manifests.detect do |manifest|
            manifest.id == manifest_id
          end
        end

        def attempt_to_was_failed_recently?
          return unless last_failed_attempt_time

          Time.now < @last_failed_attempt_time + failed_attempt_retry_time
        end
      end
    end
  end
end