# frozen_string_literal: true require "core/extension" require "core/local" require "core/state" module Core # [public] Customized inspections for any object. # module Inspect require_relative "inspect/inspection" require_relative "inspect/version" extend Core::Extension extends dependencies: [Core::State] extends :definition do # [public] Sets one or more instance variables or instance methods as inspectable. # # without - An array of instance variables or instance methods that should not be inspected. # def inspects(*inspectables, without: []) if inspectables.any? mutate_state :__inspectables__ do |current_inspectables| inspectables.each do |inspectable| inspection = build_inspection(inspectable) current_inspectables[inspection.name] = inspection end current_inspectables end end if without.any? mutate_state :__uninspectables__ do |current_uninspectables| without.each do |inspectable| inspection = build_inspection(inspectable) current_uninspectables[inspection.name] = inspection end current_uninspectables end end end private def build_inspection(inspectable) case inspectable when Core::Inspect::Inspection inspectable else Core::Inspect::Inspection.new(name: inspectable) end end end applies do state :__inspectables__, default: {} state :__uninspectables__, default: {} end # [public] Inspects the object. # def inspect inspection = +"#<#{self.class}:0x#{__id__.to_s(16)}" if Inspect.inspecting?(self) "#{inspection} ...>" else Inspect.prevent_recursion(self) do each_inspectable do |inspectable| inspection << ", #{inspectable.name}=#{inspectable.resolve(self).inspect}" end inspection.strip << ">" end end end private def each_inspectable inspectables = if @__inspectables__.any? @__inspectables__ else instance_variables.map(&:to_s).reject { |instance_variable| instance_variable.start_with?("@_") }.each_with_object({}) { |instance_variable, hash| hash[instance_variable] = self.class.__send__(:build_inspection, instance_variable) } end inspectables.each_value do |inspectable| unless @__uninspectables__.include?(inspectable.name) yield inspectable end end end class << self include Core::Local def prevent_recursion(object) object_id = object.object_id inspected_objects[object_id] = true yield ensure inspected_objects.delete(object_id) end def inspecting?(object) inspected_objects[object.object_id] end def inspected_objects localized(:__corerb_inspected_objects__) || localize(:__corerb_inspected_objects__, {}) end end end end