Class: Mutant::Warnings Private

Inherits:
Object
  • Object
show all
Defined in:
lib/mutant/warnings.rb

Overview

This class is part of a private API. You should avoid using this class if possible, as it may be removed or be changed in the future.

Class to capture warnings generated by Kernel#warn and Ruby itself.

Note this API is fundamentally impure:

  • Unlike almost all of classes in the mutant code base it has internal mutable state

  • Its therefore NOT thread safe

  • And worst: Each instance, even after its not referenced anymore by user code: Leaks permanent global state as instances of this class hook itself into the ‘Warning` module.

So ideally only make one instance, and re-use it.

Also note, a more canonical implementation would prepend modules and simply call ‘super` in the event the capture is disabled. This sadly does not work as it would inference with various bad players in the ruby ecosystem that do not adhere to the semantics outlined in the documentation.

See: ruby-doc.org/core-2.6.3/Warning.html

For example in case rubygems is active it adds its own hook to warnings, that would in case of the super implementation cause infinite recursion.

Reproduction for this case is as simple as:

“‘ require ’rubygems’

module Warning

def warn(*)
  super
end

end “‘

For that reason we do have to use the original method capture to dispatch in disabled state.

ignore :reek:RepeatedConditional

Examples:

capture = Mutant::Warnings.new(Warning)

# Note that this test case shows we can capture warnings generated from C
def ruby_warning
  Class.new do
    undef :initialize
  end
end

messages = capture.call do
  ruby_warning
end

messages # => ["some_file.rb:44: warning: undefining `initialize' may cause serious problems\n"]

Defined Under Namespace

Classes: RecursiveUseError

Instance Method Summary collapse

Constructor Details

#initialize(warning) ⇒ undefined

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Initialize object

Parameters:

  • warning (Module)

    the module to integrate against



69
70
71
72
73
74
75
76
77
78
# File 'lib/mutant/warnings.rb', line 69

def initialize(warning)
  @disabled = true
  @messages = []
  @original = warning.public_method(:warn)

  capture = method(:capture)
  warning.module_eval do
    module_function define_method(:warn, &capture)
  end
end

Instance Method Details

#callArray<String>

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Run a block with warning collection enabled

Returns:

  • (Array<String>)


83
84
85
86
87
88
89
90
91
# File 'lib/mutant/warnings.rb', line 83

def call
  assert_no_recursion
  @disabled = nil
  yield
  IceNine.deep_freeze(@messages.dup)
ensure
  @disabled = true
  @messages.clear
end