module Semantic # use the Zeitwerk autoloader to reattach_appender for development autoreloading feature class DevLoader def initialize(session_key) @session_key = session_key @subscribers = {} RailsSemanticLogger::ActionController::LogSubscriber.logger.level = :fatal # useful for remanent Rack::Log started launch end def launch once_and_reload do append_ansi_formatter Semantic::NotificationUtil.clear_subscribers(/\.action_controller$/) Semantic::NotificationUtil.clear_subscribers(/\.action_view$/) reset_subscribers register_action_controller register_action_view end end private def once_and_reload(&) yield Rails.autoloaders.main.on_load('ApplicationController', &) end def append_ansi_formatter SemanticLogger.clear_appenders! formatter = Semantic::AnsiFormatter.new SemanticLogger.add_appender(io: $stdout, formatter:, filter: ->(log) { !formatter.reject(log) }) end def register_action_controller sub_instance = Semantic::Subscribers::ActionController.new(@session_key) register_hook(sub_instance, :start_processing) register_hook(sub_instance, :process_action, :finish_processing) register_hook(sub_instance, :redirect_to) %i[send_file send_data halted_callback unpermitted_parameters send_stream write_fragment read_fragment expire_fragment exist_fragment?].each do |hook| register_hook(sub_instance, hook, :any_hook) end end def register_action_view sub_instance = Semantic::Subscribers::ActionView.new %i[render_template render_partial render_collection render_layout].each do |hook| register_hook(sub_instance, hook) end end def register_hook(sub_instance, hook, method = hook) @subscribers[sub_instance.class] ||= [] @subscribers[sub_instance.class] << ActiveSupport::Notifications.subscribe("#{hook}.#{sub_instance.event_group}") do |event| sub_instance.send(method, event) end end def reset_subscribers return if @subscribers.empty? @subscribers.each_pair do |clazz, subs| # puts "reset #{subs.size} subscribers for class <#{clazz}>" subs.each { |sub| ActiveSupport::Notifications.unsubscribe(sub) } subs.clear end end end end