Skip to content

Instantly share code, notes, and snippets.

@amkisko
Created December 2, 2025 09:38
Show Gist options
  • Select an option

  • Save amkisko/09a50b10e4ea82b0ac835307a940fe60 to your computer and use it in GitHub Desktop.

Select an option

Save amkisko/09a50b10e4ea82b0ac835307a940fe60 to your computer and use it in GitHub Desktop.

Revisions

  1. amkisko created this gist Dec 2, 2025.
    142 changes: 142 additions & 0 deletions debug_support.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,142 @@
    RSpec.configure do |config|
    config.before do |example|
    @spec_file_path = example.metadata[:example_group][:file_path]
    @spec_line_number = example.metadata[:example_group][:line_number]

    def spec_dirname
    File.dirname(@spec_file_path)
    end

    def spec_basename
    File.basename(@spec_file_path)
    end

    if ENV["DEBUG"]
    Rails.logger.level = 0
    puts "Running #{@spec_file_path}:#{@spec_line_number}"
    end
    end
    end

    with_sql = ENV["DEBUG_SQL"]
    with_stack = ENV["DEBUG_STACK"]
    debug_requested = with_sql || with_stack

    if debug_requested
    require "amazing_print"
    def pp(*, **) = ENV["PRETTY"] ? ap(*, **) : puts(*, **)

    RSpec.configure do |config|
    config.around do |example|
    if with_sql
    subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |event|
    payload = event.payload[:sql]
    # next if payload.match?(/^(SELECT|SET|SHOW|BEGIN|COMMIT|ROLLBACK|RELEASE|SAVEPOINT)/)
    # next if payload.include?("audits")
    next if payload.include?("pg_attribute")

    type_casted_binds = if event.payload[:type_casted_binds].is_a?(Array)
    event.payload[:type_casted_binds]
    elsif event.payload[:type_casted_binds].is_a?(Proc)
    event.payload[:type_casted_binds].call
    else
    []
    end

    if type_casted_binds.is_a?(Array)
    type_casted_binds.each_with_index.reverse_each do |bind, index|
    payload = payload.gsub("$#{index + 1}", "'#{bind}'")
    end
    end

    pp(event: "sql.active_record", sql: payload)
    end
    end

    if with_stack
    trace = TracePoint.new do |tp|
    next if !tp.path&.start_with?(Rails.root.to_s)
    next if tp.path&.include?(Rails.root.join("spec/support").to_s)

    if tp.event == :call
    method_name = tp.method_id
    defined_class = tp.defined_class
    args = []
    kwargs = {}

    begin
    # Try to get method object - handle both instance and class methods
    method_obj = begin
    if defined_class.singleton_class?
    defined_class.method(method_name)
    else
    tp.self.method(method_name)
    end
    rescue
    # Fallback: try instance method
    begin
    defined_class.instance_method(method_name)
    rescue
    nil
    end
    end

    if method_obj&.respond_to?(:parameters)
    params = method_obj.parameters
    binding = tp.binding

    params.each do |param_type, param_name|
    next unless binding.local_variable_defined?(param_name)

    value = binding.local_variable_get(param_name)
    case param_type
    when :req, :opt
    args << value
    when :key, :keyreq
    kwargs[param_name] = value
    when :keyrest
    kwargs.merge!(value || {})
    when :rest
    args.concat(value || [])
    end
    end
    end
    rescue
    # If we can't extract arguments, just continue without them
    end

    # Format method display: use . for class methods, # for instance methods
    method_display = if defined_class.singleton_class?
    "#{defined_class}.#{method_name}"
    else
    "#{defined_class}##{method_name}"
    end

    output = {
    event: tp.event,
    method: method_display,
    path: "#{tp.path}:#{tp.lineno}"
    }
    output[:args] = args if args.any?
    output[:kwargs] = kwargs if kwargs.any?

    pp(output)
    elsif tp.event == :raise
    pp({
    event: tp.event,
    raised_exception: tp.raised_exception,
    path: "#{tp.path}:#{tp.lineno}",
    method_id: tp.method_id
    })
    end
    end
    trace.enable
    end

    example.run

    trace.disable if with_stack
    ActiveSupport::Notifications.unsubscribe(subscriber) if with_sql
    end
    end
    end