class RuboCop::RSpec::ExpectOffense::AnnotatedSource

Parsed representation of code annotated with the `^^^ Message` style

Constants

ANNOTATION_PATTERN

Attributes

annotations[R]
lines[R]

Public Class Methods

new(lines, annotations) click to toggle source

@param lines [Array<String>] @param annotations [Array<(Integer, String)>]

each entry is the annotated line number and the annotation text

@note annotations are sorted so that reconstructing the annotation

text via {#to_s} is deterministic
# File lib/rubocop/rspec/expect_offense.rb, line 97
def initialize(lines, annotations)
  @lines       = lines.freeze
  @annotations = annotations.sort.freeze
end
parse(annotated_source) click to toggle source

@param annotated_source [String] string passed to the matchers

Separates annotation lines from source lines. Tracks the real source line number that each annotation corresponds to.

@return [AnnotatedSource]

# File lib/rubocop/rspec/expect_offense.rb, line 76
def self.parse(annotated_source)
  source      = []
  annotations = []

  annotated_source.each_line do |source_line|
    if source_line =~ ANNOTATION_PATTERN
      annotations << [source.size, source_line]
    else
      source << source_line
    end
  end

  new(source, annotations)
end

Public Instance Methods

plain_source() click to toggle source

Return the plain source code without annotations

@return [String]

# File lib/rubocop/rspec/expect_offense.rb, line 138
def plain_source
  lines.join
end
to_s() click to toggle source

Construct annotated source string (like what we parse)

Reconstruct a deterministic annotated source string. This is useful for eliminating semantically irrelevant annotation ordering differences.

@example standardization

source1 = AnnotatedSource.parse(<<-RUBY)
line1
^ Annotation 1
 ^^ Annotation 2
RUBY

source2 = AnnotatedSource.parse(<<-RUBY)
line1
 ^^ Annotation 2
^ Annotation 1
RUBY

source1.to_s == source2.to_s # => true

@return [String]

# File lib/rubocop/rspec/expect_offense.rb, line 125
def to_s
  reconstructed = lines.dup

  annotations.reverse_each do |line_number, annotation|
    reconstructed.insert(line_number, annotation)
  end

  reconstructed.join
end
with_offense_annotations(offenses) click to toggle source

Annotate the source code with the RuboCop offenses provided

@param offenses [Array<RuboCop::Cop::Offense>]

@return [self]

# File lib/rubocop/rspec/expect_offense.rb, line 147
def with_offense_annotations(offenses)
  offense_annotations =
    offenses.map do |offense|
      indent     = ' ' * offense.column
      carets     = '^' * offense.column_length

      [offense.line, "#{indent}#{carets} #{offense.message}\n"]
    end

  self.class.new(lines, offense_annotations)
end