1# power_assert.rb 2# 3# Copyright (C) 2014 Kazuki Tsujimoto 4 5begin 6 unless defined?(Byebug) 7 captured = false 8 TracePoint.new(:return, :c_return) do |tp| 9 captured = true 10 unless tp.binding and tp.return_value 11 raise '' 12 end 13 end.enable { __id__ } 14 raise '' unless captured 15 end 16rescue 17 raise LoadError, 'Fully compatible TracePoint API required' 18end 19 20require 'power_assert/version' 21require 'power_assert/configuration' 22require 'power_assert/context' 23 24module PowerAssert 25 POWER_ASSERT_LIB_DIR = File.dirname(caller_locations(1, 1).first.path) 26 INTERNAL_LIB_DIRS = {PowerAssert => POWER_ASSERT_LIB_DIR} 27 private_constant :POWER_ASSERT_LIB_DIR, :INTERNAL_LIB_DIRS 28 29 # For backward compatibility 30 IGNORED_LIB_DIRS = INTERNAL_LIB_DIRS 31 private_constant :IGNORED_LIB_DIRS 32 if respond_to?(:deprecate_constant) 33 deprecate_constant :IGNORED_LIB_DIRS 34 end 35 36 class << self 37 def start(assertion_proc_or_source, assertion_method: nil, source_binding: TOPLEVEL_BINDING) 38 if respond_to?(:clear_global_method_cache, true) 39 clear_global_method_cache 40 end 41 yield BlockContext.new(assertion_proc_or_source, assertion_method, source_binding) 42 end 43 44 def trace(frame) 45 begin 46 raise 'Byebug is not started yet' unless Byebug.started? 47 rescue NameError 48 raise "PowerAssert.#{__method__} requires Byebug" 49 end 50 ctx = TraceContext.new(frame._binding) 51 ctx.enable 52 ctx 53 end 54 55 def app_caller_locations 56 caller_locations.drop_while {|i| internal_file?(i.path) }.take_while {|i| ! internal_file?(i.path) } 57 end 58 59 def app_context? 60 top_frame = caller_locations.drop_while {|i| i.path.start_with?(POWER_ASSERT_LIB_DIR) }.first 61 top_frame and ! internal_file?(top_frame.path) 62 end 63 64 private 65 66 def internal_file?(file) 67 setup_internal_lib_dir(Byebug, :attach, 2) if defined?(Byebug) 68 setup_internal_lib_dir(PryByebug, :start_with_pry_byebug, 2, Pry) if defined?(PryByebug) 69 INTERNAL_LIB_DIRS.find do |_, dir| 70 file.start_with?(dir) 71 end 72 end 73 74 def setup_internal_lib_dir(lib, mid, depth, lib_obj = lib) 75 unless INTERNAL_LIB_DIRS.key?(lib) 76 INTERNAL_LIB_DIRS[lib] = lib_dir(lib_obj, mid, depth) 77 end 78 rescue NameError 79 end 80 81 def lib_dir(obj, mid, depth) 82 File.expand_path('../' * depth, obj.method(mid).source_location[0]) 83 end 84 85 if defined?(RubyVM) 86 CLEAR_CACHE_ISEQ = RubyVM::InstructionSequence.compile('using PowerAssert.const_get(:Empty)') 87 private_constant :CLEAR_CACHE_ISEQ 88 89 def clear_global_method_cache 90 CLEAR_CACHE_ISEQ.eval 91 end 92 end 93 end 94 95 module Empty 96 end 97 private_constant :Empty 98end 99