Class: SpecGuard::RSpec::Linter

Inherits:
Object
  • Object
show all
Defined in:
lib/specguard/rspec/linter.rb

Overview

Applies the schema to the Findings discovery produced, and says which ones the run must fail on.

This is the join between the two halves of the linter: discovery decides what an annotation is, the schema decides whether it is valid, and Result is the single shape the reporter and the exit code are both derived from — so "what was printed" and "what we exited with" cannot drift apart.

What counts as a failure

Three things reach the same verdict from different directions:

* the payload could not be captured off the line at all
(`Finding::KIND_EXTRACTION`) — a typo'd annotation;
* it was captured but is not JSON even after normalization
(`Finding::KIND_PARSE`);
* it parsed but violates the schema.

All three are "an annotation is malformed", which the contract fixes at exit 1. A missing annotation is not among them — "lint, don't require" (SPGD-12 §1): a file with no @intent: at all is clean.

The one case that is arguably not an annotation problem

Finding::KIND_READ — a spec file that could not be opened or is not valid UTF-8 — is also reported as a failure, and therefore also exits 1. That is a deliberate choice, not an oversight: validate-intent classifies it separately (its own read kind) and still reports it as FAIL with exit 1.

The CLASSIFICATION and the EXIT CODE are what match, and both are asserted rather than described, in spec/specguard/rspec/validator_backend_spec.rb — which runs this linter and the binary's recorded report over the same inputs and compares them. Each read-failure shape below has its own two-sided block there, so the shared half and the surviving difference are both pinned: converging either one fails that file.

The MESSAGE TEXT does not match on any of the three read-failure shapes, and an earlier version of this comment claimed it did — it quoted a FAIL bad_spec.rb — could not read file: ... line as if the two tools emitted the same bytes. They do not:

* a file that is not valid UTF-8 — the binary says `input is not
well-formed UTF-8 (PROTOCOL.md §1.1 requires it)`;
`Scanner.scan_text` says `invalid UTF-8 byte sequence`. Both name the
condition rather than an offset, and both refuse the file, which is
what PROTOCOL.md §1.1 requires; neither wording is specified.
* a file that does not exist — the binary reports it on *stderr* as
`error: no file(s) match '<path>'` (its arguments are glob patterns);
`Scanner.scan_file` reports it on stdout as a read failure of that
path (its arguments are paths).
* a path that exists and is not a regular file — the binary's glob
filters it away, so it answers exactly as it does for a name matching
nothing: a `no-match` finding under `--json`. `no file at this path`
is not the binary's wording but this gem's, minted in
`ValidatorBackend#no_match_result`, because Go's "no file(s) match
<pattern>" is a statement about a glob pattern this CLI does not
have. `Scanner.scan_file` opened it and has an errno, so it reports
`Is a directory @ io_fread - <path>`. The distinction between a
missing path and a non-regular one is Ruby's alone; the binary does
not draw it and the backend cannot recover it.

The line the contract actually draws is the linter is broken (2) versus the input it was pointed at is bad (1). An unopenable file named on the command line is the second. The kind is carried through rather than flattened into prose so this stays a visible decision that a later ticket can reverse in one place.

Defined Under Namespace

Classes: Result

Instance Method Summary collapse

Constructor Details

#initialize(schema) ⇒ Linter

Returns a new instance of Linter.



115
116
117
# File 'lib/specguard/rspec/linter.rb', line 115

def initialize(schema)
  @schema = schema
end

Instance Method Details

#check(findings) ⇒ Array<Result>

Returns in the order the findings were discovered.

Parameters:

Returns:

  • (Array<Result>)

    in the order the findings were discovered



121
122
123
# File 'lib/specguard/rspec/linter.rb', line 121

def check(findings)
  findings.map { |finding| check_one(finding) }
end

#check_one(finding) ⇒ Result

Parameters:

Returns:



127
128
129
130
131
132
133
134
135
136
# File 'lib/specguard/rspec/linter.rb', line 127

def check_one(finding)
  unless finding.extracted?
    return Result.new(file: finding.file, line: finding.line,
                      kind: finding.kind, problem: finding.problem)
  end

  reasons = @schema.violations(finding.intent)
  Result.new(file: finding.file, line: finding.line, reasons: reasons,
             kind: reasons.empty? ? nil : Finding::KIND_SCHEMA)
end