Class: PowerAssert::Context
| Relationships & Source Files | |
| Namespace Children | |
|
Classes:
| |
| Inherits: | Object |
| Defined in: | lib/power_assert/context.rb |
Class Method Summary
Instance Attribute Summary
- #fired? ⇒ Boolean readonly private
Instance Method Summary
- #message
- #message_proc
- #yield
- #build_assertion_message(parser, return_values) private
- #column2display_offset(str) private
- #detect_path(parser, return_values) private
- #encoding_safe_rstrip(str) private
- #enum_count_by(enum, &blk) private
- #find_all_identified_calls(return_values, path) private
- #invoke_yield private
- #uniq_calls(paths) private
Constructor Details
.new(assertion_proc_or_source, assertion_method, source_binding) ⇒ Context
# File 'lib/power_assert/context.rb', line 10
def initialize(assertion_proc_or_source, assertion_method, source_binding) @fired = false @target_thread = Thread.current if assertion_proc_or_source.respond_to?(:to_proc) @assertion_proc = assertion_proc_or_source.to_proc line = nil else @assertion_proc = source_binding.eval "Proc.new {#{assertion_proc_or_source}}" line = assertion_proc_or_source end @parser = Parser::DUMMY @trace_call = TracePoint.new(:call, :c_call) do if PowerAssert.app_context? and Thread.current == @target_thread @trace_call.disable locs = PowerAssert.app_caller_locations path = locs.last.path lineno = locs.last.lineno if File.exist?(path) line ||= File.open(path) {|fp| fp.each_line.drop(lineno - 1).first } end if line @parser = Parser.new(line, path, lineno, @assertion_proc.binding, assertion_method.to_s, @assertion_proc) end end end method_id_set = nil @return_values = [] @trace_return = TracePoint.new(:return, :c_return) do |tp| unless method_id_set next unless Thread.current == @target_thread method_id_set = @parser.method_id_set end method_id = tp.callee_id next if ! method_id_set[method_id] next if tp.event == :c_return and not (@parser.lineno == tp.lineno and @parser.path == tp.path) locs = PowerAssert.app_caller_locations if (tp.event == :c_return && locs.length == 1 || tp.event == :return && locs.length <= 2) and Thread.current == @target_thread if @parser.path == locs.last.path and @parser.lineno == locs.last.lineno val = PowerAssert.configuration.lazy_inspection ? tp.return_value : InspectedValue.new(SafeInspectable.new(tp.return_value).inspect) @return_values << Value[method_id.to_s, val, locs.last.lineno, nil] end end rescue Exception => e warn "power_assert: [BUG] Failed to trace: #{e.class}: #{e.}" if e.respond_to?(:) warn e..gsub(/^/, 'power_assert: ') end end end
Instance Attribute Details
#fired? ⇒ Boolean (readonly, private)
[ GitHub ]
# File 'lib/power_assert/context.rb', line 90
def fired? @fired end
Instance Method Details
#build_assertion_message(parser, return_values) (private)
[ GitHub ]# File 'lib/power_assert/context.rb', line 94
def (parser, return_values) if PowerAssert.configuration. line = IRB::Color.colorize_code(parser.line, ignore_error: true) else line = parser.line end path = detect_path(parser, return_values) return line unless path c2d = column2display_offset(parser.line) return_values, methods_in_path = find_all_identified_calls(return_values, path) return_values.zip(methods_in_path) do |i, j| unless i.name == j.name warn "power_assert: [BUG] Failed to get column: #{i.name}" return line end i.display_offset = c2d[j.column] end refs_in_path = path.find_all {|i| i.type == :ref } ref_values = refs_in_path.map {|i| Value[i.name, parser.binding.eval(i.name), parser.lineno, i.column, c2d[i.column]] } vals = (return_values + ref_values).find_all(&:display_offset).sort_by(&:display_offset).reverse return line if vals.empty? fmt = (0..vals[0].display_offset).map do |i| if vals.find {|v| v.display_offset == i } "%<#{i}>s" else line[i] == "\t" ? "\t" : ' ' end end.join lines = [] lines << line.chomp lines << sprintf(fmt, vals.each_with_object({}) {|v, h| h[:"#{v.display_offset}"] = '|' }).chomp vals.each do |i| inspected_val = SafeInspectable.new(Inspector.new(i.value, i.display_offset)).inspect inspected_val.each_line do |l| map_to = vals.each_with_object({}) do |j, h| h[:"#{j.display_offset}"] = [l, '|', ' '][i.display_offset <=> j.display_offset] end lines << encoding_safe_rstrip(sprintf(fmt, map_to)) end end lines.join("\n") end
#column2display_offset(str) (private)
[ GitHub ]# File 'lib/power_assert/context.rb', line 185
def column2display_offset(str) display_offset = 0 str.each_char.with_object([]) do |c, r| c.bytesize.times do r << display_offset end display_offset += c.ascii_only? ? 1 : 2 # FIXME end end
#detect_path(parser, return_values) (private)
[ GitHub ]# File 'lib/power_assert/context.rb', line 140
def detect_path(parser, return_values) return parser.call_paths.flatten.uniq if parser.method_id_set.empty? all_paths = parser.call_paths return_value_names = return_values.map(&:name) uniq_calls = uniq_calls(all_paths) uniq_call = return_value_names.find {|i| uniq_calls.include?(i) } detected_paths = all_paths.find_all do |path| method_names = path.find_all {|ident| ident.type == :method }.map(&:name) break [path] if uniq_call and method_names.include?(uniq_call) return_value_names == method_names end return nil unless detected_paths.length == 1 detected_paths[0] end
#encoding_safe_rstrip(str) (private)
[ GitHub ]# File 'lib/power_assert/context.rb', line 174
def encoding_safe_rstrip(str) str.rstrip rescue ArgumentError, Encoding::CompatibilityError enc = str.encoding if enc.ascii_compatible? str.b.rstrip.force_encoding(enc) else str end end
#enum_count_by(enum, &blk) (private)
[ GitHub ]# File 'lib/power_assert/context.rb', line 170
def enum_count_by(enum, &blk) Hash[enum.group_by(&blk).map{|k, v| [k, v.length] }] end
#find_all_identified_calls(return_values, path) (private)
[ GitHub ]# File 'lib/power_assert/context.rb', line 160
def find_all_identified_calls(return_values, path) return_value_num_of_calls = enum_count_by(return_values, &:name) path_num_of_calls = enum_count_by(path.find_all {|ident| ident.type == :method }, &:name) identified_calls = return_value_num_of_calls.find_all {|name, num| path_num_of_calls[name] == num }.map(&:first) [ return_values.find_all {|val| identified_calls.include?(val.name) }, path.find_all {|ident| ident.type == :method and identified_calls.include?(ident.name) } ] end
#invoke_yield (private)
[ GitHub ]# File 'lib/power_assert/context.rb', line 82
def invoke_yield @trace_return.enable do @trace_call.enable do yield end end end
#message
[ GitHub ]# File 'lib/power_assert/context.rb', line 66
def raise 'call #yield at first' unless fired? @message ||= (@parser, @return_values).freeze end
#message_proc
[ GitHub ]# File 'lib/power_assert/context.rb', line 71
def #=> { message } end
#uniq_calls(paths) (private)
[ GitHub ]# File 'lib/power_assert/context.rb', line 155
def uniq_calls(paths) all_calls = enum_count_by(paths.map {|path| path.find_all {|ident| ident.type == :method }.map(&:name).uniq }.flatten) {|i| i } all_calls.find_all {|_, call_count| call_count == 1 }.map {|name, _| name } end
#yield
[ GitHub ]# File 'lib/power_assert/context.rb', line 75
def yield @fired = true invoke_yield(&@assertion_proc) end