specguard-ruby 0.3.25 → 0.3.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +4 -2
- data/lib/specguard/rspec/cli.rb +23 -18
- data/lib/specguard/rspec/finding.rb +16 -9
- data/lib/specguard/rspec/linter.rb +9 -5
- data/lib/specguard/rspec/scanner.rb +138 -17
- data/lib/specguard/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 788caaa70678a741f5ddb2aa3379d033369905ee89a7164f37527f0c64ecf3a5
|
|
4
|
+
data.tar.gz: 8c03300935002043fd6097d3779057ed0dec439b13108bdc440ced0fee71af95
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 138c404bb1691827932705550d146c5d34c908ef5c3581ef6f1aa6d210de46f8ba0ef349f81486bab4340ead74588858031012732f26a46e7df691ce4fce78ce
|
|
7
|
+
data.tar.gz: 26e5764b84b90f120caea35853e9205b1e1142bf3f3709b761f164acc7f294b2cda5aa6dcc6f89354e03f7bc750a6d3268050f1ef25961db606b6b1584e4bff8
|
data/README.md
CHANGED
|
@@ -48,7 +48,8 @@ bundle exec ruby -rminitest/specguard_plugin -e 'Minitest.extensions << "specgua
|
|
|
48
48
|
|
|
49
49
|
Validates `# @intent:` annotations in changed (or all) `*_spec.rb` files against the OpenTestIntent
|
|
50
50
|
JSON Schema. Exits `1` on a malformed annotation — or a well-formed but unreachable one (stacked
|
|
51
|
-
above another comment-form `@intent:` line, so the one-line lookback never claims it
|
|
51
|
+
above another comment-form `@intent:` line, so the one-line lookback never claims it, or trailing
|
|
52
|
+
on a `describe`/`context` group line, which no example's extraction can ever reach) — and
|
|
52
53
|
**never** fails on a *missing* one (adoption is opt-in and gradual).
|
|
53
54
|
|
|
54
55
|
```bash
|
|
@@ -334,7 +335,8 @@ Every way the backend can fail — the binary is missing, will not execute, exit
|
|
|
334
335
|
is not a verdict, or emits output that is not a report — is **exit 2**, the linter's "could not do
|
|
335
336
|
my job" code. It never becomes exit 1, which means "an annotation is malformed" — or well-formed
|
|
336
337
|
but unreachable: stacked above another comment-form `@intent:` line, so the one-line lookback
|
|
337
|
-
never claims it
|
|
338
|
+
never claims it, or trailing on a `describe`/`context` group line, which no example's extraction
|
|
339
|
+
can ever reach — and nothing else.
|
|
338
340
|
|
|
339
341
|
## The formatter — `SpecGuard::RSpecFormatter`
|
|
340
342
|
|
data/lib/specguard/rspec/cli.rb
CHANGED
|
@@ -126,10 +126,12 @@ module SpecGuard
|
|
|
126
126
|
# "Lint, don't require": a missing annotation is never an error.
|
|
127
127
|
EXIT_OK = 0
|
|
128
128
|
# One or more annotations are malformed — or well-formed but
|
|
129
|
-
# unreachable (SPGD-900: stacked above another comment-form
|
|
130
|
-
# line
|
|
131
|
-
#
|
|
132
|
-
#
|
|
129
|
+
# unreachable (SPGD-900/SPGD-1510: stacked above another comment-form
|
|
130
|
+
# `@intent:` line so the one-line lookback never claims it, or trailing
|
|
131
|
+
# on an example-group line no example's extraction can reach — a dead
|
|
132
|
+
# contract either way, and still a failure). The only code produced by
|
|
133
|
+
# inspecting content, and the only path that reaches it is a failed
|
|
134
|
+
# {Linter::Result}.
|
|
133
135
|
EXIT_MALFORMED = 1
|
|
134
136
|
# The linter could not do its job: bad flags, `--changed` outside a git
|
|
135
137
|
# repository, a validator that could not be resolved, or an unexpected
|
|
@@ -165,12 +167,14 @@ module SpecGuard
|
|
|
165
167
|
|
|
166
168
|
results = backend.check(selection.files)
|
|
167
169
|
|
|
168
|
-
# The structural pass (SPGD-900): annotations that are
|
|
169
|
-
# isolation but can never be extracted — stacked consecutive
|
|
170
|
+
# The structural pass (SPGD-900, SPGD-1510): annotations that are
|
|
171
|
+
# valid in isolation but can never be extracted — stacked consecutive
|
|
170
172
|
# comment-form `@intent:` lines, where the one-line lookback
|
|
171
|
-
# (SPGD-12 §2) claims only the line just above the example
|
|
172
|
-
#
|
|
173
|
-
#
|
|
173
|
+
# (SPGD-12 §2) claims only the line just above the example, and
|
|
174
|
+
# `@intent:` tokens trailing on an example-group line, which no
|
|
175
|
+
# extraction rule can ever reach. These are Linter::Results like any
|
|
176
|
+
# other, so both renderers and the exit code pick them up with no
|
|
177
|
+
# second path to keep in step.
|
|
174
178
|
results += unreachable_results(selection.files)
|
|
175
179
|
|
|
176
180
|
# Computed once, here, and handed to whichever renderer runs. `--json`
|
|
@@ -476,15 +480,16 @@ module SpecGuard
|
|
|
476
480
|
#
|
|
477
481
|
# The boundary is the set-difference the partition above already
|
|
478
482
|
# supports: a file counts as covered when it yielded any line-scoped
|
|
479
|
-
# result (an annotation, valid or not — the SPGD-900
|
|
480
|
-
# are line-scoped too and require an annotation to
|
|
481
|
-
# their file) or when it could not be read at all
|
|
482
|
-
# An unread file is NOT a zero-annotation file:
|
|
483
|
-
# summary clause and FAIL line, and calling it
|
|
484
|
-
# the same overstatement `summary_line` refuses
|
|
485
|
-
# findings need
|
|
486
|
-
#
|
|
487
|
-
#
|
|
483
|
+
# result (an annotation, valid or not — the SPGD-900/SPGD-1510
|
|
484
|
+
# unreachable findings are line-scoped too and require an annotation to
|
|
485
|
+
# exist, so they cover their file) or when it could not be read at all
|
|
486
|
+
# (KIND_READ → `unread`). An unread file is NOT a zero-annotation file:
|
|
487
|
+
# it already gets its own summary clause and FAIL line, and calling it
|
|
488
|
+
# annotation-free would be the same overstatement `summary_line` refuses
|
|
489
|
+
# to make. Unreachable findings need an annotation to exist — a stacked
|
|
490
|
+
# comment-form pair, or an `@intent:` token on a group line — impossible
|
|
491
|
+
# in a file with none, so `annotations ∪ unread` already covers every
|
|
492
|
+
# non-bare file and no third subtraction is needed.
|
|
488
493
|
#
|
|
489
494
|
# It is a NOTE on stderr, never a warning and never an exit code: "lint,
|
|
490
495
|
# don't require" (SPGD-12 §1) keeps a missing annotation a non-error.
|
|
@@ -48,15 +48,22 @@ module SpecGuard
|
|
|
48
48
|
# of ways an annotation can fail is written down in one place.
|
|
49
49
|
KIND_SCHEMA = :schema
|
|
50
50
|
|
|
51
|
-
# The annotation is well-formed in isolation but can never be EXTRACTED
|
|
52
|
-
#
|
|
53
|
-
#
|
|
54
|
-
#
|
|
55
|
-
#
|
|
56
|
-
#
|
|
57
|
-
#
|
|
58
|
-
#
|
|
59
|
-
#
|
|
51
|
+
# The annotation is well-formed in isolation but can never be EXTRACTED.
|
|
52
|
+
# Two shapes, both dead the moment they are written:
|
|
53
|
+
#
|
|
54
|
+
# * a comment-form `@intent:` stacked above another comment-form
|
|
55
|
+
# `@intent:` line, so the one-line lookback (SPGD-12 §2 —
|
|
56
|
+
# {AnnotationLookup} claims only the comment on the line immediately
|
|
57
|
+
# above an example) always skips it in favour of the lower line;
|
|
58
|
+
# * an `@intent:` trailing on an example-group line
|
|
59
|
+
# ({Scanner::GROUP_LINE} — SPGD-1510): a group line is no example's
|
|
60
|
+
# own and is not comment-only, so no extraction rule can ever reach
|
|
61
|
+
# it, and the example beneath it silently ingests unannotated.
|
|
62
|
+
#
|
|
63
|
+
# Either way the contract is dead metadata: counted by the linter,
|
|
64
|
+
# discarded by extraction. Produced by {Scanner}'s structural pass, not
|
|
65
|
+
# by {AnnotationScanner} — each annotation is scanned in isolation
|
|
66
|
+
# there, which is exactly why these defects are invisible to it.
|
|
60
67
|
KIND_UNREACHABLE = :unreachable
|
|
61
68
|
|
|
62
69
|
def initialize(file:, line:, intent: nil, problem: nil, kind: nil)
|
|
@@ -38,12 +38,16 @@ module SpecGuard
|
|
|
38
38
|
# `read` kind) and still reports it as `FAIL` with exit 1.
|
|
39
39
|
#
|
|
40
40
|
# `Finding::KIND_UNREACHABLE` — an annotation that is well-formed in
|
|
41
|
-
# isolation but
|
|
42
|
-
#
|
|
43
|
-
#
|
|
41
|
+
# isolation but can never be extracted, so the one-line lookback
|
|
42
|
+
# (SPGD-12 §2) never claims it — is also reported as a failure, and
|
|
43
|
+
# therefore also exits 1. Two shapes reach this kind: a comment-form
|
|
44
|
+
# `@intent:` stacked above another comment-form `@intent:` line (SPGD-900),
|
|
45
|
+
# and an `@intent:` trailing on an example-group line, which is no
|
|
46
|
+
# example's own and not comment-only (SPGD-1510). Nothing about either is
|
|
44
47
|
# malformed; the contract is dead metadata, counted by the linter and
|
|
45
|
-
# discarded by extraction, and the structural pass that flags
|
|
46
|
-
# (SPGD-900) reports it loudly rather than letting it pass as
|
|
48
|
+
# discarded by extraction, and the structural pass that flags them
|
|
49
|
+
# (SPGD-900, SPGD-1510) reports it loudly rather than letting it pass as
|
|
50
|
+
# clean.
|
|
47
51
|
module Linter
|
|
48
52
|
# One annotation's verdict. `problem` is set when discovery could not
|
|
49
53
|
# produce an intent at all; `reasons` when the schema rejected one.
|
|
@@ -258,25 +258,41 @@ module SpecGuard
|
|
|
258
258
|
#
|
|
259
259
|
# Everything above validates each annotation IN ISOLATION — one payload,
|
|
260
260
|
# one verdict. But the formatter's extraction contract
|
|
261
|
-
# ({AnnotationLookup}, SPGD-12 §2) is positional:
|
|
262
|
-
#
|
|
263
|
-
#
|
|
264
|
-
#
|
|
265
|
-
#
|
|
266
|
-
#
|
|
267
|
-
#
|
|
268
|
-
#
|
|
261
|
+
# ({AnnotationLookup}, SPGD-12 §2) is positional: an annotation reaches
|
|
262
|
+
# an example only through its own line (the trailing form, and only when
|
|
263
|
+
# that line IS the example's) or through the comment-form line
|
|
264
|
+
# IMMEDIATELY ABOVE it. Two shapes fall outside both and are dead the
|
|
265
|
+
# moment they are written — silently discarded by extraction while this
|
|
266
|
+
# pipeline counts them as valid annotations and the run exits 0:
|
|
267
|
+
#
|
|
268
|
+
# * the STACKED pair (SPGD-897, found in the audit): two consecutive
|
|
269
|
+
# comment-form `@intent:` lines above one `it` — the UPPER line is
|
|
270
|
+
# never claimed, because the one-line lookback takes only the line
|
|
271
|
+
# just above the example. Measured: 16 such stacked pairs shipped
|
|
272
|
+
# through `specguard-lint` exit 0, every upper contract dead.
|
|
273
|
+
# * the GROUP line (SPGD-1510, cost SPGD-1475 a full rework round): an
|
|
274
|
+
# `@intent:` trailing on a `describe`/`context`/… line. The line is
|
|
275
|
+
# no example's own, and it is not comment-only, so no lookback can
|
|
276
|
+
# ever claim it — the example beneath it silently ingests as
|
|
277
|
+
# unannotated. A describe insertion that swallowed a newline is how
|
|
278
|
+
# the shape is born.
|
|
279
|
+
#
|
|
280
|
+
# Both are reported loudly, not skipped — the same stance
|
|
269
281
|
# {AnnotationScanner} takes for NO_PAYLOAD.
|
|
270
282
|
#
|
|
271
283
|
# A comment-form annotation line is a comment-only line
|
|
272
284
|
# ({AnnotationLookup::COMMENT_LINE}) carrying the `@intent:` token. The
|
|
273
|
-
# trailing same-line form
|
|
274
|
-
#
|
|
275
|
-
#
|
|
285
|
+
# trailing same-line form is claimed ONLY when the line itself defines
|
|
286
|
+
# an example ({EXAMPLE_LINE}); there the annotation belongs to its own
|
|
287
|
+
# example's line and the lookback is not involved. On any other line the
|
|
288
|
+
# trailing form is dead too — an example-group line carrying it is now
|
|
289
|
+
# flagged by {GROUP_LINE}'s pass below, with the one-liner exemption
|
|
290
|
+
# ({EXAMPLE_ON_LINE}) keeping `describe "x" do it("y") { } end` claims
|
|
291
|
+
# intact.
|
|
276
292
|
#
|
|
277
293
|
# @param paths [Enumerable<String>]
|
|
278
|
-
# @return [Array<Finding>] one per unreachable
|
|
279
|
-
#
|
|
294
|
+
# @return [Array<Finding>] one per unreachable annotation, in
|
|
295
|
+
# file-then-line order. A file that cannot be read contributes
|
|
280
296
|
# nothing here — {ValidatorBackend} already reports read failures, and
|
|
281
297
|
# this pass has nothing positional to say about a file it never saw.
|
|
282
298
|
def unreachable_findings(paths)
|
|
@@ -300,6 +316,13 @@ module SpecGuard
|
|
|
300
316
|
"so the one-line lookback claims only the line just above the example " \
|
|
301
317
|
"and this annotation is silently discarded at extraction (SPGD-12 §2)"
|
|
302
318
|
|
|
319
|
+
UNREACHABLE_GROUP_ANNOTATION =
|
|
320
|
+
"unreachable annotation: this @intent: sits trailing on an example-group line " \
|
|
321
|
+
"(describe/context/...), and annotations attach to examples, never to groups — " \
|
|
322
|
+
"extraction (SPGD-12 §2) is one-line and example-anchored, so no example can ever " \
|
|
323
|
+
"claim it. Move it onto its example's `it` line, or to the comment line directly " \
|
|
324
|
+
"above that `it`"
|
|
325
|
+
|
|
303
326
|
# A comment-only line carrying an `@intent:` token — the only form an
|
|
304
327
|
# example on the NEXT line may claim ({AnnotationLookup::COMMENT_LINE}).
|
|
305
328
|
COMMENT_INTENT_LINE = /\A\s*#.*@intent:/
|
|
@@ -312,11 +335,32 @@ module SpecGuard
|
|
|
312
335
|
# Anchoring on the example is what keeps this pass off annotation
|
|
313
336
|
# corpora with no examples at all (the recorded-binary fixtures in
|
|
314
337
|
# spec/fixtures/): there is no lookback there to silently discard
|
|
315
|
-
# anything, so there is nothing to report. The
|
|
316
|
-
# for
|
|
317
|
-
#
|
|
338
|
+
# anything, so there is nothing to report. The defects this pass exists
|
|
339
|
+
# for are annotations an author believed were attached to a real
|
|
340
|
+
# example: stacked lines above it (SPGD-897), and the trailing form on
|
|
341
|
+
# a group line directly above it (SPGD-1510).
|
|
318
342
|
EXAMPLE_LINE = /\A\s*(?:it|specify)\b/
|
|
319
343
|
|
|
344
|
+
# @param text [String] source of one file
|
|
345
|
+
# @param file [String] path to record on each Finding
|
|
346
|
+
# @return [Array<Finding>] the structural findings for one file, in line
|
|
347
|
+
# order: one per comment-form `@intent:` line in a stacked run (see
|
|
348
|
+
# {stacked_findings_in_text}) plus one per group line carrying the
|
|
349
|
+
# trailing form (see {group_line_findings_in_text}). The two passes
|
|
350
|
+
# are disjoint by construction — one reads only comment-only lines,
|
|
351
|
+
# the other only lines that are not — so the merge cannot double-flag
|
|
352
|
+
# a line, and the sort restores the file-then-line order
|
|
353
|
+
# {unreachable_findings} promises.
|
|
354
|
+
def unreachable_findings_in_text(text, file:)
|
|
355
|
+
# A file that is not valid UTF-8 is reported once, loudly, by the
|
|
356
|
+
# backend as a read failure; nothing positional can be said about it,
|
|
357
|
+
# and `lines` below would raise on the invalid bytes.
|
|
358
|
+
return [] unless text.valid_encoding?
|
|
359
|
+
|
|
360
|
+
(stacked_findings_in_text(text, file: file) +
|
|
361
|
+
group_line_findings_in_text(text, file: file)).sort_by(&:line)
|
|
362
|
+
end
|
|
363
|
+
|
|
320
364
|
# @param text [String] source of one file
|
|
321
365
|
# @param file [String] path to record on each Finding
|
|
322
366
|
# @return [Array<Finding>] one per comment-form `@intent:` line in a
|
|
@@ -324,7 +368,7 @@ module SpecGuard
|
|
|
324
368
|
# lines immediately above an example — except the run's LAST line,
|
|
325
369
|
# which is the one the one-line lookback claims. For the canonical
|
|
326
370
|
# two-line stack above one `it`, that is the UPPER line.
|
|
327
|
-
def
|
|
371
|
+
def stacked_findings_in_text(text, file:)
|
|
328
372
|
# A file that is not valid UTF-8 is reported once, loudly, by the
|
|
329
373
|
# backend as a read failure; nothing positional can be said about it,
|
|
330
374
|
# and `lines` below would raise on the invalid bytes.
|
|
@@ -354,6 +398,83 @@ module SpecGuard
|
|
|
354
398
|
|
|
355
399
|
findings
|
|
356
400
|
end
|
|
401
|
+
|
|
402
|
+
# RSpec's example-group keywords, at the start of a line and optionally
|
|
403
|
+
# `RSpec.`-prefixed. A line matching this hosts a GROUP, not an example
|
|
404
|
+
# — and {AnnotationLookup}'s extraction can claim an annotation only
|
|
405
|
+
# from an example's own line or the comment-only line above it, so an
|
|
406
|
+
# `@intent:` written here is unreachable wherever it sits on the line.
|
|
407
|
+
GROUP_LINE =
|
|
408
|
+
/\A\s*(?:RSpec\.)?(?:describe|context|feature|shared_examples|shared_examples_for|shared_context|example_group)\b/
|
|
409
|
+
|
|
410
|
+
# A token WITH its payload opener: what the rest of the pipeline treats
|
|
411
|
+
# as an annotation on a group line. The `{` is load-bearing — a bare
|
|
412
|
+
# `@intent:` token is a {AnnotationScanner::NO_PAYLOAD} extraction
|
|
413
|
+
# failure the main pipeline already reports loudly, and (because
|
|
414
|
+
# extraction is marker-based, SPGD-8 §7) the token is ALSO found inside
|
|
415
|
+
# quoted strings: this repo's own suite carries
|
|
416
|
+
# `describe "unreachable stacked @intent: annotations"`, which is prose,
|
|
417
|
+
# not an annotation, and must not read as one. A line matching this is
|
|
418
|
+
# one the scanner captured a payload from (or began capturing one),
|
|
419
|
+
# which is the population this pass has standing to judge. The naive
|
|
420
|
+
# search inherits the scanner's string-literal limitation in the exotic
|
|
421
|
+
# direction only: a group line whose prose embeds `@intent: {` reads as
|
|
422
|
+
# annotated (a flag the pipeline independently agrees with, since
|
|
423
|
+
# extraction captures that literal too), never the reverse.
|
|
424
|
+
INTENT_WITH_PAYLOAD = /@intent:\s*\{/
|
|
425
|
+
|
|
426
|
+
# The one-liner exemption: a group line that ALSO defines an example on
|
|
427
|
+
# the same line is that example's own line, so its trailing annotation
|
|
428
|
+
# is claimed by the example and must not be flagged:
|
|
429
|
+
#
|
|
430
|
+
# describe "one" do it("x") { } end # @intent: { ... }
|
|
431
|
+
#
|
|
432
|
+
# Two guards keep prose from reading as an example call. The match runs
|
|
433
|
+
# against the code BEFORE the `@intent:` token — the payload is English
|
|
434
|
+
# an author wrote about a behavior ("...when it's given one"), and
|
|
435
|
+
# reading `it'` there as a call exempted the exact group-line shape
|
|
436
|
+
# this pass exists to flag. And the call must sit after a block opener
|
|
437
|
+
# (`do`, `{`, `;`), because the description string is part of the code
|
|
438
|
+
# side too: `describe "the cost of rendering it" do` ends in the word
|
|
439
|
+
# `it`, and `it"` is not a call. What this heuristic still cannot see:
|
|
440
|
+
# a description string containing the literal sequence `do it` /
|
|
441
|
+
# `do specify` (or `{ it`), as in `describe "how to do it right" do`,
|
|
442
|
+
# still reads as a call and is wrongly exempted — a missed flag, and
|
|
443
|
+
# only ever a missed flag: prose can turn the exemption ON, never a
|
|
444
|
+
# real example OFF. The one-line example's own payload is never
|
|
445
|
+
# consulted, so a payload can never exempt anything.
|
|
446
|
+
EXAMPLE_ON_LINE = /(?:\bdo\b|\{|;)\s*(?:it|specify)\b/
|
|
447
|
+
|
|
448
|
+
# @param text [String] source of one file
|
|
449
|
+
# @param file [String] path to record on each Finding
|
|
450
|
+
# @return [Array<Finding>] one per example-group line carrying an
|
|
451
|
+
# `@intent:` token, for lines that define no example of their own
|
|
452
|
+
# ({EXAMPLE_ON_LINE}). Comment-only lines are left to
|
|
453
|
+
# {stacked_findings_in_text}: a comment above a group is a different
|
|
454
|
+
# shape, deliberately out of scope here (SPGD-1510 flags the group
|
|
455
|
+
# line ITSELF).
|
|
456
|
+
def group_line_findings_in_text(text, file:)
|
|
457
|
+
# Same UTF-8 contract as the stacked pass above.
|
|
458
|
+
return [] unless text.valid_encoding?
|
|
459
|
+
|
|
460
|
+
text.lines.each_with_index.filter_map do |line, idx|
|
|
461
|
+
# Comment-only lines belong to the stacked pass; this pass reads the
|
|
462
|
+
# group line ITSELF. (A `#`-leading line could never match
|
|
463
|
+
# GROUP_LINE anyway — the guard states the division, not a filter.)
|
|
464
|
+
next if line.lstrip.start_with?("#")
|
|
465
|
+
next unless GROUP_LINE.match?(line)
|
|
466
|
+
next unless INTENT_WITH_PAYLOAD.match?(line)
|
|
467
|
+
# The exemption sees the code before the token; the payload and
|
|
468
|
+
# anything after it are prose and must never read as an example
|
|
469
|
+
# call. The token is guaranteed present (INTENT_WITH_PAYLOAD just
|
|
470
|
+
# matched), so `split` always yields the code side.
|
|
471
|
+
code = line.split("@intent:", 2).first
|
|
472
|
+
next if EXAMPLE_ON_LINE.match?(code)
|
|
473
|
+
|
|
474
|
+
Finding.new(file: file, line: idx + 1, problem: UNREACHABLE_GROUP_ANNOTATION,
|
|
475
|
+
kind: Finding::KIND_UNREACHABLE)
|
|
476
|
+
end
|
|
477
|
+
end
|
|
357
478
|
end
|
|
358
479
|
end
|
|
359
480
|
end
|
data/lib/specguard/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: specguard-ruby
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.26
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- specguard Agent
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-09-
|
|
11
|
+
date: 2026-09-26 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: json
|