Module: SimpleCov::SourceFile::RubyDataParser
| Relationships & Source Files | |
| Defined in: | lib/simplecov/source_file/ruby_data_parser.rb |
Overview
Coverage.result reports condition and method keys as Ruby
arrays. When the resultset is round-tripped through JSON those
array keys become their stringified inspect form, so this parser
walks the literal back into a real Array without using eval (see
#801). The grammar covers symbols, strings, integers, unary minus,
and constant paths — every shape Coverage ever emits.
Class Method Summary
-
.call(structure)
mod_func
Tests use the real data structures (except for integration tests) so no need to put them through here.
-
.parse_array_string(str)
mod_func
Parse a string like '[:if, 0, 3, 4, 3, 21]' or '["ClassName",
:method1, 2, 2, 5, 5]' back into a Ruby array. - .parse_element(node) mod_func
- .parse_integer_node(node) mod_func
- .parse_symbol_node(node) mod_func
-
.quote_inspected_class_segments(str)
mod_func
Methodcoverage keys can contain inspect-format class references like#or#, which aren't valid Ruby syntax. -
.string_literal_text(string_content)
mod_func
Concatenate the text fragments of a
:string_contentnode. -
.unescape_ruby(raw)
mod_func
Undo the same backslash-prefix escapes the previous hand-rolled parser undid:
\X→Xfor any X.
Class Method Details
.call(structure) (mod_func)
Tests use the real data structures (except for integration tests) so no need to put them through here.
# File 'lib/simplecov/source_file/ruby_data_parser.rb', line 18
def call(structure) return structure if structure.is_a?(Array) parse_array_string(structure.to_s) end
.parse_array_string(str) (mod_func)
Parse a string like '[:if, 0, 3, 4, 3, 21]' or
'["ClassName", :method1, 2, 2, 5, 5]' back into a Ruby array.
# File 'lib/simplecov/source_file/ruby_data_parser.rb', line 26
def parse_array_string(str) # Try plain Ripper first; only pre-quote `#<...>` inspect segments # if the input isn't already valid Ruby (otherwise we corrupt # `"#<Class:Foo>"` strings that *are* valid Ruby literals — exactly # the shape simplecov-on-simplecov method-coverage keys take). sexp = Ripper.sexp(str) || Ripper.sexp(quote_inspected_class_segments(str)) # simplecov:disable — defensive: Ripper.sexp returning nil from both passes requires malformed input array_node = sexp&.dig(1, 0) # simplecov:enable raise ArgumentError, "expected array literal: #{str.inspect}" unless array_node && array_node[0] == :array Array(array_node[1]).map { |element| parse_element(element) } end
.parse_element(node) (mod_func)
[ GitHub ]# File 'lib/simplecov/source_file/ruby_data_parser.rb', line 40
def parse_element(node) case node[0] when :@int, :unary then parse_integer_node(node) when :symbol_literal, :dyna_symbol then parse_symbol_node(node) when :string_literal then unescape_ruby(string_literal_text(node[1])) when :var_ref then node.dig(1, 1) # `Foo` when :const_path_ref then "#{parse_element(node[1])}::#{node[2][1]}" # `Foo::Bar` else # simplecov:disable — defensive fallback for unexpected Ripper node shapes raise ArgumentError, "unexpected element: #{node.inspect}" # simplecov:enable end end
.parse_integer_node(node) (mod_func)
[ GitHub ]# File 'lib/simplecov/source_file/ruby_data_parser.rb', line 54
def parse_integer_node(node) node[0] == :@int ? node[1].to_i : -node[2][1].to_i end
.parse_symbol_node(node) (mod_func)
[ GitHub ]# File 'lib/simplecov/source_file/ruby_data_parser.rb', line 58
def parse_symbol_node(node) if node[0] == :symbol_literal node.dig(1, 1, 1).to_sym else unescape_ruby(string_literal_text(node[1])).to_sym end end
.quote_inspected_class_segments(str) (mod_func)
Method coverage keys can contain inspect-format class references
like # or #, which aren't valid Ruby
syntax. Wrap them in quotes so Ripper can parse the surrounding
array literal; downstream we treat them as opaque strings.
# File 'lib/simplecov/source_file/ruby_data_parser.rb', line 83
def quote_inspected_class_segments(str) str.gsub(/#<[^>]*>/) { |segment| %("#{segment.gsub('"', '\\"')}") } end
.string_literal_text(string_content) (mod_func)
Concatenate the text fragments of a :string_content node. Ripper
may emit zero, one, or many :@tstring_content children depending
on the literal.
# File 'lib/simplecov/source_file/ruby_data_parser.rb', line 69
def string_literal_text(string_content) Array(string_content[1..]).map { |child| child[1] }.join end
.unescape_ruby(raw) (mod_func)
Undo the same backslash-prefix escapes the previous hand-rolled
parser undid: \X → X for any X.
# File 'lib/simplecov/source_file/ruby_data_parser.rb', line 75
def unescape_ruby(raw) raw.gsub(/\\(.)/) { ::Regexp.last_match(1) } end