Skip to content

Commit 152a953

Browse files
ErikSchierboomee7
andauthored
lint, sync, fmt: support the files.invalidator field (#577)
This new field allows a track to specify additional files that, when changed, cause a solution to become outdated. Upstream: - exercism/docs@322d63ecdbf6 - exercism/website@695307892fd4 Co-authored-by: ee7 <45465154+ee7@users.noreply.github.com>
1 parent 0832f81 commit 152a953

7 files changed

Lines changed: 31 additions & 3 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,7 @@ The canonical key order for an exercise `.meta/config.json` file is:
318318
- exemplar (Concept Exercises only)
319319
- example (Practice Exercises only)
320320
- [editor]
321+
- [invalidator]
321322
- [language_versions]
322323
- [forked_from] (Concept Exercises only)
323324
- [icon] (Concept Exercises only)

src/lint/concept_exercises.nim

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ proc hasValidFiles(data: JsonNode; path, exerciseDir: Path): bool =
1111
hasArrayOfFiles(d, "test", path, k, exerciseDir),
1212
hasArrayOfFiles(d, "exemplar", path, k, exerciseDir),
1313
hasArrayOfFiles(d, "editor", path, k, exerciseDir, isRequired = false),
14+
hasArrayOfFiles(d, "invalidator", path, k, exerciseDir, isRequired = false),
1415
]
1516
result = allTrue(checks)
1617

src/lint/practice_exercises.nim

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ proc hasValidFiles(data: JsonNode; path, exerciseDir: Path): bool =
1111
hasArrayOfFiles(d, "test", path, k, exerciseDir),
1212
hasArrayOfFiles(d, "example", path, k, exerciseDir),
1313
hasArrayOfFiles(d, "editor", path, k, exerciseDir, isRequired = false),
14+
hasArrayOfFiles(d, "invalidator", path, k, exerciseDir, isRequired = false),
1415
]
1516
result = allTrue(checks)
1617

src/lint/track_config.nim

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ proc hasValidFiles(data: JsonNode; path: Path): bool =
4747
hasArrayOfStrings(data[f], "editor", path, context = f,
4848
uniqueValues = true, isRequired = false,
4949
checkIsFilesPattern = true),
50+
hasArrayOfStrings(data[f], "invalidator", path, context = f,
51+
uniqueValues = true, isRequired = false,
52+
checkIsFilesPattern = true),
5053
]
5154
result = allTrue(checks)
5255
else:
@@ -340,6 +343,7 @@ type
340343
example*: seq[string]
341344
exemplar*: seq[string]
342345
editor*: seq[string]
346+
invalidator*: seq[string]
343347

344348
TrackConfig* = object
345349
slug*: string
@@ -713,11 +717,16 @@ proc checkFilePatternsOverlap(filePatterns: FilePatterns; trackSlug: string,
713717
("solution", "example"),
714718
("solution", "exemplar"),
715719
("solution", "editor"),
720+
("solution", "invalidator"),
716721
("test", "example"),
717722
("test", "exemplar"),
718723
("test", "editor"),
724+
("test", "invalidator"),
719725
("editor", "example"),
720726
("editor", "exemplar"),
727+
("editor", "invalidator"),
728+
("invalidator", "example"),
729+
("invalidator", "exemplar"),
721730
]
722731

723732
var seenFilePatterns = initTable[string, HashSet[string]](250)

src/sync/sync_common.nim

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ type
3434
exemplar*: seq[string]
3535
example*: seq[string]
3636
editor*: seq[string]
37+
invalidator*: seq[string]
3738

3839
TrackConfig* = object
3940
exercises*: Exercises
@@ -128,6 +129,7 @@ type
128129
# solution*: seq[string]
129130
# test*: seq[string]
130131
# editor*: seq[string]
132+
# invalidator*: seq[string]
131133
# case kind*: ExerciseKind
132134
# of ekConcept:
133135
# exemplar*: seq[string]
@@ -160,20 +162,23 @@ type
160162
fkExemplar = "exemplar"
161163
fkExample = "example"
162164
fkEditor = "editor"
165+
fkInvalidator = "invalidator"
163166

164167
ConceptExerciseFiles* = object
165168
originalKeyOrder: seq[FilesKey]
166169
solution*: seq[string]
167170
test*: seq[string]
168171
exemplar*: seq[string]
169172
editor*: seq[string]
173+
invalidator*: seq[string]
170174

171175
PracticeExerciseFiles* = object
172176
originalKeyOrder: seq[FilesKey]
173177
solution*: seq[string]
174178
test*: seq[string]
175179
example*: seq[string]
176180
editor*: seq[string]
181+
invalidator*: seq[string]
177182

178183
ConceptExerciseConfig* = object
179184
originalKeyOrder: seq[ExerciseConfigKey]
@@ -345,6 +350,8 @@ func filesKeyOrder(val: ConceptExerciseFiles | PracticeExerciseFiles;
345350
result = @[fkSolution, fkTest, fkEx]
346351
if prettyMode == pmFmt and val.editor.len > 0:
347352
result.add fkEditor
353+
if prettyMode == pmFmt and val.invalidator.len > 0:
354+
result.add fkInvalidator
348355
else:
349356
result = val.originalKeyOrder
350357
# If `solution` is missing, write it first.
@@ -366,6 +373,10 @@ func filesKeyOrder(val: ConceptExerciseFiles | PracticeExerciseFiles;
366373
let insertionIndex = result.find(fkEx) + 1
367374
result.insert(fkEditor, insertionIndex)
368375

376+
# If `invalidator` is missing and not empty, write it at the end.
377+
if fkInvalidator notin result and val.invalidator.len > 0:
378+
result.add fkInvalidator
379+
369380
func addFiles(s: var string; val: ConceptExerciseFiles | PracticeExerciseFiles;
370381
prettyMode: PrettyMode; indentLevel = 1) =
371382
## Appends the pretty-printed JSON for a `files` key with value `val` to `s`.
@@ -389,6 +400,8 @@ func addFiles(s: var string; val: ConceptExerciseFiles | PracticeExerciseFiles;
389400
s.addArray("example", val.example, indentLevel = inner)
390401
of fkEditor:
391402
s.addArray("editor", val.editor, indentLevel = inner)
403+
of fkInvalidator:
404+
s.addArray("invalidator", val.invalidator, indentLevel = inner)
392405

393406
s.removeComma()
394407
s.addNewlineAndIndent(indentLevel)

src/sync/sync_filepaths.nim

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ func update(f: var (ConceptExerciseFiles | PracticeExerciseFiles),
4444
update(f.solution, patterns.solution, slug)
4545
update(f.test, patterns.test, slug)
4646
update(f.editor, patterns.editor, slug)
47+
update(f.invalidator, patterns.invalidator, slug)
4748
when f is ConceptExerciseFiles:
4849
update(f.exemplar, patterns.exemplar, slug)
4950
when f is PracticeExerciseFiles:
@@ -61,7 +62,7 @@ func isSynced(f: ConceptExerciseFiles | PracticeExerciseFiles,
6162
genCond(exemplar)
6263
else:
6364
genCond(example)
64-
uniqueCond and genCond(solution) and genCond(test) and genCond(editor)
65+
uniqueCond and genCond(solution) and genCond(test) and genCond(editor) and genCond(invalidator)
6566

6667
type
6768
ExerciseConfig* = object
@@ -161,8 +162,8 @@ proc checkOrUpdateFilepaths*(seenUnsynced: var set[SyncKind];
161162
## Prints a message for each track exercise that:
162163
## - lacks a `.meta/config.json` file
163164
## - or has a `.meta/config.json` file with an missing/empty
164-
## `files.solution|test|editor|example|exemplar` array, when that value has
165-
## a pattern defined in the track-level `config.json` file.
165+
## `files.solution|test|editor|invalidator|example|exemplar` array, when
166+
## that value has a pattern defined in the track-level `config.json` file.
166167
##
167168
## Populates those values if `--update` was passed and the user confirms.
168169
##

tests/test_fmt.nim

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,8 @@ proc testFmt =
323323
delete(j["files"], "originalKeyOrder")
324324
if j["files"]["editor"].len == 0:
325325
delete(j["files"], "editor")
326+
if j["files"]["invalidator"].len == 0:
327+
delete(j["files"], "invalidator")
326328
when e is ConceptExerciseConfig:
327329
let val = j["forked_from"]
328330
if val.kind == JNull or (val.kind == JArray and val.len == 0):

0 commit comments

Comments
 (0)