razum2um/lurker

View on GitHub
lib/lurker/spy.rb

Summary

Maintainability
A
25 mins
Test Coverage
require 'json'

module Lurker
  class Spy
    class BlindError < StandardError; end

    attr_reader :block, :service
    attr_accessor :request, :response

    extend Forwardable
    delegate [:verb, :payload] => :request
    delegate [:status, :body] => :response

    def initialize(options={}, &block)
      @options = options
      @block = block

      @service = if defined?(Rails)
        rails_app_class = Rails.application.class
        rails_app_name = rails_app_class.respond_to?(:module_parent_name) ? rails_app_class.module_parent_name : rails_app_class.parent_name
        Service.new(Rails.root.join(DEFAULT_SERVICE_PATH).to_s, rails_app_name)
      else
        Service.default_service
      end
    end

    def call
      @request = @response = nil # fill in while test
      @block.call.tap do |result|
        if @request && @response
          @service.verify!(
            verb, endpoint_path, payload,
            extensions, status, body
          )

          @service.persist! if success?(result)
        end
      end
    end

    def endpoint_path
      [request.endpoint_path, suffix].compact.join('-')
    end

    def extensions
      extensions = {
        path_params: reordered_request_path_params,
        path_info: request.path_info,
        method: request.verb,
      }
      unless request.query_params.empty?
        extensions[:query_params] = request.query_params
      end
      if suffix.present?
        extensions[:suffix] = suffix.to_s
      end
      extensions.stringify_keys
    end

    def suffix
      if (suffix = @options[:suffix]).is_a?(String)
        suffix.gsub(/[^[[:alnum:]]]/, '_')
      end
    end

    def success?(result)
      if defined?(::Minitest::Test) && result.is_a?(::Minitest::Test)
        result.failure.nil?
      elsif result.is_a?(Exception)
        false
      else
        true
      end
    end
    private :success?

    def self.on(options={}, &block)
      require 'lurker/spec_helper' unless defined? Lurker::SpecHelper
      instance = new(options, &block)
      Thread.current[:lurker_spy] = instance
      instance.call
    ensure
      Thread.current[:lurker_spy] = nil
    end

    def self.enabled?
      current.present?
    end

    def self.current
      Thread.current[:lurker_spy]
    end

    private

    def reordered_request_path_params
      other_params = request.path_params.reject do |k, _|
        k == 'controller' || k == 'action'
      end

      {
        'controller' => request.path_params['controller'],
        'action' => request.path_params['action']
      }.merge!(other_params)
    end
  end
end