module TrackOpenInstances::ClassMethods

Contains class-level methods added to classes including TrackOpenInstances

Public Instance Methods

add_open_instance(instance) click to toggle source

Adds an instance to the tracking list (thread-safe)

Typically called automatically by the instance’s ‘initialize` method.

@param instance [Object] The instance to add

@return [void]

@api private

# File lib/track_open_instances.rb, line 137
def add_open_instance(instance)
  @open_instances_mutex.synchronize do
    @open_instances[instance] = OpenInstance.new(instance, caller_locations(3))
  end
end
assert_no_open_instances() click to toggle source

Asserts that no instances of the class remain unclosed

Commonly used in test suite teardown blocks to enure that all resources were released.

@example

# In RSpec teardown (e.g., after(:each))
MyResource.assert_no_open_instances

@raise [RuntimeError] If any instances are found unclosed with a report of unclosed instances

@return [void]

@api public

# File lib/track_open_instances.rb, line 249
def assert_no_open_instances
  report = open_instances_report
  raise(report.to_s) if report
end
open_instance_count() click to toggle source

The number of currently open instances

@example

res1 = MyResource.new
res2 = MyResource.new
MyResource.open_instance_count #=> 2

@return [Integer]

@api public

# File lib/track_open_instances.rb, line 169
def open_instance_count
  @open_instances_mutex.synchronize do
    @open_instances.size
  end
end
open_instances() click to toggle source

@!attribute [r] open_instances

Direct access to the internal list of tracked instances

Note: This returns all instances ever tracked unless explicitly removed. Use ‘open_instances` for checking leaks. Direct use is uncommon.

@example

# Assuming MyResource includes TrackOpenInstances
res1 = MyResource.new
res2 = MyResource.new
res1.close
MyResource.open_instances.keys #=> [#<MyResource... object_id=res2>] (after res1 removed)

@return [Hash{Object => OpenInstance}] The raw list of currently tracked instances

@api private

# File lib/track_open_instances.rb, line 122
def open_instances
  @open_instances_mutex.synchronize do
    @open_instances.dup.freeze
  end
end
open_instances_report() click to toggle source

Generates a report string listing unclosed instances and their creation stacks

Useful for debugging resource leaks. Returns nil if no instances are unclosed.

@example

res = MyResource.new
puts MyResource.open_instances_report
There is 1 open MyResource instance(s):
- object_id=701
  Created at:
    (caller stack line 1)
    (caller stack line 2)\n..."

@return [String, nil] A formatted report string or nil if none are open

@api public

# File lib/track_open_instances.rb, line 191
def open_instances_report
  @open_instances_mutex.synchronize do
    return nil if @open_instances.count.zero?

    String.new.tap do |report|
      report << open_instances_report_header
      report << open_instances_report_body
    end
  end
end
open_instances_report_body() click to toggle source

The body of the report detailing each open instance

@return [String]

@api private

# File lib/track_open_instances.rb, line 222
def open_instances_report_body
  String.new.tap do |body|
    @open_instances.each do |instance, open_instance|
      body << " - object_id=#{instance.object_id}\n"
      body << "   Call stack when created:\n"
      open_instance.creation_stack.each do |location|
        body << "     #{location.path}:#{location.lineno}:in `#{location.label}'\n"
      end
    end
  end
end
open_instances_report_header() click to toggle source

The header string for the report

@return [String]

@api private

# File lib/track_open_instances.rb, line 208
def open_instances_report_header
  count = @open_instances.count
  class_name = name || 'anonymous class'

  "There #{count == 1 ? 'is' : 'are'} #{count} " \
    "open #{class_name} instance#{count == 1 ? '' : 's'}:\n"
end
remove_open_instance(instance) click to toggle source

Removes an instance from the tracking list (thread-safe)

Typically called automatically by the instance’s ‘close` method.

@param instance [Object] The instance to remove

@return [void]

@api private

# File lib/track_open_instances.rb, line 152
def remove_open_instance(instance)
  @open_instances_mutex.synchronize do
    @open_instances.delete(instance)
  end
end