File: //usr/local/rvm/src/ruby-2.7.4/spec/mspec/lib/mspec/runner/actions/constants_leak_checker.rb
class ConstantsLockFile
LOCK_FILE_NAME = '.mspec.constants'
def self.load
if File.exist?(LOCK_FILE_NAME)
File.readlines(LOCK_FILE_NAME).map(&:chomp)
else
[]
end
end
def self.dump(ary)
contents = ary.map(&:to_s).uniq.sort.join("\n") + "\n"
File.write(LOCK_FILE_NAME, contents)
end
end
class ConstantLeakError < StandardError
end
class ConstantsLeakCheckerAction
def initialize(save)
@save = save
@check = !save
@constants_locked = ConstantsLockFile.load
@exclude_patterns = MSpecScript.get(:toplevel_constants_excludes) || []
end
def register
MSpec.register :start, self
MSpec.register :before, self
MSpec.register :after, self
MSpec.register :finish, self
end
def start
@constants_start = constants_now
end
def before(state)
@constants_before = constants_now
end
def after(state)
constants = remove_excludes(constants_now - @constants_before - @constants_locked)
if @check && !constants.empty?
MSpec.protect 'Constants leak check' do
raise ConstantLeakError, "Top level constants leaked: #{constants.join(', ')}"
end
end
end
def finish
constants = remove_excludes(constants_now - @constants_start - @constants_locked)
if @save
ConstantsLockFile.dump(@constants_locked + constants)
end
if @check && !constants.empty?
MSpec.protect 'Global constants leak check' do
raise ConstantLeakError, "Top level constants leaked in the whole test suite: #{constants.join(', ')}"
end
end
end
private
def constants_now
Object.constants.map(&:to_s)
end
def remove_excludes(constants)
constants.reject { |name|
@exclude_patterns.any? { |pattern| pattern === name }
}
end
end