require 'delegate' require 'dry/system/constants' require 'dry/events/publisher' module Dry module System module Plugins # @api public module Monitoring class Proxy < SimpleDelegator # @api private def self.for(target, key:, methods: [], &block) monitored_methods = if methods.empty? target.public_methods - Object.public_instance_methods else methods end Class.new(self) do extend Dry::Core::ClassAttributes include Dry::Events::Publisher[target.class.name] defines :monitored_methods attr_reader :__notifications__ monitored_methods(methods.empty? ? target.public_methods - Object.public_instance_methods : methods) monitored_methods.each do |meth| define_method(meth) do |*args, &block| object = __getobj__ opts = { target: key, object: object, method: meth, args: args } __notifications__.instrument(:monitoring, opts) do object.public_send(meth, *args, &block) end end end end end def initialize(target, notifications) super(target) @__notifications__ = notifications end end # @api private def self.extended(system) super system.use(:decorate) system.use(:notifications) system.after(:configure) do self[:notifications].register_event(:monitoring) end end # @api private def monitor(key, options = EMPTY_HASH, &block) notifications = self[:notifications] resolve(key).tap do |target| proxy = Proxy.for(target, options.merge(key: key)) if block proxy.monitored_methods.each do |meth| notifications.subscribe(:monitoring, target: key, method: meth, &block) end end decorate(key, decorator: proxy.new(target, notifications)) end end end end end end