Module: ImLost

Defined in:
lib/im-lost.rb,
lib/im-lost/version.rb

Overview

If you have overlooked something again and don’t really understand what your code is doing. If you have to maintain this application but can’t really find your way around and certainly can’t track down that stupid error. If you feel lost in all that code, here’s the gem to help you out!

ImLost helps you by analyzing function calls of objects, informing you about exceptions and logging your way through your code. In short, ImLost is your debugging helper!

Defined Under Namespace

Classes: TimerStore

Constant Summary collapse

VERSION =

The version number of the gem.

'1.2.3'

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.caller_locationsBoolean

Enables/disables to include code location into traced call information. This is enabled by default.

Returns:

  • (Boolean)

    whether code location will be included



21
22
23
# File 'lib/im-lost.rb', line 21

def caller_locations
  @caller_locations
end

.output#puts

The output device used to write information. This should be an ‘IO` device or any other object responding to `#<<` like a Logger.

‘STDERR` is configured by default.

Examples:

Write to a file

ImLost.output = File.new('./trace', 'w')

Write temporary into a memory stream

require 'stringio'

original = ImLost.output
begin
  ImLost.output = StringIO.new
  # ... collect trace information
  puts(ImLost.output.string) # or whatever
ensure
  ImLost.output = original
end

Returns:

  • (#puts)

    the output device



51
52
53
# File 'lib/im-lost.rb', line 51

def output
  @output
end

.timerTimerStore (readonly)

Returns the timer store used to estimate the runtime of your code.

Returns:

  • (TimerStore)

    the timer store used to estimate the runtime of your code



67
68
69
# File 'lib/im-lost.rb', line 67

def timer
  @timer
end

.trace_callsBoolean

Enables/disables tracing of method calls. This is enabled by default.

Returns:

  • (Boolean)

    whether method calls will be traced



76
# File 'lib/im-lost.rb', line 76

def trace_calls = @trace_calls.enabled?

.trace_resultsBoolean

Enables/disables tracing of returned values of method calls. This is enabled by default.

Returns:

  • (Boolean)

    whether return values will be traced



129
# File 'lib/im-lost.rb', line 129

def trace_results = @trace_results.enabled?

Class Method Details

.heretrue .here(test) ⇒ Object .here { ... } ⇒ Object

Print the call location conditionally.

Examples:

Print current location

ImLost.here

Print current location when instance variable is empty

ImLost.here(@name.empty?)

Print current location when instance variable is nil or empty

ImLost.here { @name.nil? || @name.empty? }

Overloads:

  • .heretrue

    Prints the call location.

    Returns:

    • (true)
  • .here(test) ⇒ Object

    Prints the call location when given argument is truthy.

    Parameters:

    • test (Object)

    Returns:

    • (Object)

      test

  • .here { ... } ⇒ Object

    Prints the call location when given block returns a truthy result.

    Yields:

    • When the block returns a truthy result the location will be print

    Yield Returns:

    • (Object)

      return result



165
166
167
168
169
170
# File 'lib/im-lost.rb', line 165

def here(test = true)
  return test if !test || (block_given? && !(test = yield))
  loc = Kernel.caller_locations(1, 1)[0]
  @output << "* #{loc.path}:#{loc.lineno}\n"
  test
end

.trace(*args) ⇒ Array<Object> .trace(*args) {|args| ... } ⇒ Object

Trace objects.

The given arguments can be any object instance or module or class.

Examples:

Trace method calls of an instance variable for a while

ImLost.trace(@file)
# ...
ImLost.untrace(@file)

Temporary trace method calls

File.open('test.txt', 'w') do |file|
  ImLost.trace(file) do
    file << 'hello '
    file.puts(:world!)
  end
end

# output will look like
#   > IO#<<(?)
#     /examples/test.rb:1
#   > IO#write(*)
#     /examples/test.rb:1
#   > IO#puts(*)
#     /examples/test.rb:2
#   > IO#write(*)
#     /examples/test.rb:2

Overloads:

  • .trace(*args) ⇒ Array<Object>

    Start tracing the given objects.

    Parameters:

    • args ([Object])

      one or more objects to be traced

    Returns:

    • (Array<Object>)

      the traced object(s)

    See Also:

  • .trace(*args) {|args| ... } ⇒ Object

    Traces the given object(s) inside the block only. The object(s) will not be traced any longer after the block call.

    Parameters:

    • args ([Object])

      one or more objects to be traced

    Yield Parameters:

    • args (Object)

      the traced object(s)

    Yield Returns:

    • (Object)

      return result



214
215
216
217
218
# File 'lib/im-lost.rb', line 214

def trace(*args, &block)
  return block&.call if args.empty?
  return args.size == 1 ? _trace(args[0]) : _trace_all(args) unless block
  args.size == 1 ? _trace_b(args[0], &block) : _trace_all_b(args, &block)
end

.trace_exceptions(with_locations: true) ⇒ Object

Traces exceptions raised within a given block.

Examples:

Trace exception and rescue handling

ImLost.trace_exceptions do
  File.write('/', 'test')
rescue SystemCallError
  raise('something went wrong!')
end

# output will look like
#   x Errno::EEXIST: File exists @ rb_sysopen - /
#     /examples/test.rb:2
#   ! Errno::EEXIST: File exists @ rb_sysopen - /
#     /examples/test.rb:3
#   x RuntimeError: something went wrong!
#     /examples/test.rb:4

Parameters:

  • with_locations (Boolean) (defaults to: true)

    whether the locations should be included into the exception trace information

Yield Returns:

  • (Object)

    return result



108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/im-lost.rb', line 108

def trace_exceptions(with_locations: true)
  return unless block_given?
  begin
    we = @trace_exceptions.enabled?
    el = @exception_locations
    @exception_locations = with_locations
    @trace_exceptions.enable unless we
    yield
  ensure
    @trace_exceptions.disable unless we
    @exception_locations = el
  end
end

.traced?(obj) ⇒ Boolean

Test if a given object is currently traced.

Parameters:

  • arg (Object)

    object to be tested

Returns:

  • (Boolean)

    wheter the object is beeing traced



226
# File 'lib/im-lost.rb', line 226

def traced?(obj) = @trace.key?(obj)

.untrace(*args) ⇒ Array<Object>?

Stop tracing objects.

Examples:

Trace some objects for some code lines

traced_obj = ImLost.trace(@file, @client)
# ...
ImLost.untrace(*traced_obj)

Parameters:

  • args ([]Object)

    ] one or more objects which should not longer be traced

Returns:

  • (Array<Object>)

    the object(s) which are not longer be traced

  • (nil)

    when none of the objects was traced before

See Also:



243
244
245
246
# File 'lib/im-lost.rb', line 243

def untrace(*args)
  args = args.filter_map { @trace.delete(_1) }
  args.size < 2 ? args[0] : args
end

.untrace_all!self

Stop tracing any object. When you are really lost and just like to stop tracing of all your objects.

Returns:

  • (self)

    itself

See Also:



256
257
258
259
# File 'lib/im-lost.rb', line 256

def untrace_all!
  @trace = {}.compare_by_identity
  self
end

.vars(object) ⇒ Object

Note:

The dedicated handling of ‘Fiber` is platform dependent!

Inspect internal variables of a given object.

When the given object is

  • a ‘Binding` it prints the local variables of the binding

  • a ‘Thread` it prints the fiber-local and thread variables

  • the current ‘Fiber` it prints the fibers’ storage

Be aware that only the current fiber can be inspected.

When the given object can not be inspected it prints an error message.

Examples:

Inspect current instance variables

@a = 22
b = 20
c = @a + b
ImLost.vars(self)
# => print value of `@a`

Inspect local variables

@a = 22
b = 20
c = @a + b
ImLost.vars(binding)
# => print values of `b` and 'c'

Inspect a thread’s variables

th = Thread.new { th[:var1] += 20 }
th[:var1] = 22
ImLost.vars(th)
# => print value of `var1`
th.join
ImLost.vars(th)

Inspect the current fiber’s storage

Fiber[:var1] = 22
Fiber[:var2] = 20
Fiber[:var3] = Fiber[:var1] + Fiber[:var2]
ImLost.vars(Fiber.current)

Parameters:

  • object (Object)

    which instance variables should be print

Returns:

  • (Object)

    the given object



307
308
309
310
311
312
313
314
315
316
317
318
319
320
# File 'lib/im-lost.rb', line 307

def vars(object)
  out = LineStore.new
  traced = @trace.delete(object)
  return _local_vars(out, object) if Binding === object
  out.location(Kernel.caller_locations(1, 1)[0])
  return _thread_vars(out, object) if Thread === object
  return _fiber_vars(out, object) if @fiber_support && Fiber === object
  return _instance_vars(out, object) if defined?(object.instance_variables)
  out << '  !!! unable to retrieve vars'
  object
ensure
  @trace[traced] = traced if traced
  @output << out
end