import: annotate the empty collections the groups slot builds
Steep's three: an empty hash is a type until something says which,
and a two-element literal is an Array until something says it is a
pair -- once for the empty groups file an import opens with, once for
the hash #groups reads back, once for the id-to-label pairs the
review screen looks names up in.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KrREeoPLXx1qsYYq7qkL8h
diff --git a/lib/pro_tacts/import/staged.rb b/lib/pro_tacts/import/staged.rb
index 978ef39..033f12c 100644
--- a/lib/pro_tacts/import/staged.rb
+++ b/lib/pro_tacts/import/staged.rb
@@ -60,7 +60,8 @@ module ProTacts
# Empty rather than absent, so every slot of an import that
# exists is a file that exists and a write can say which of
# the two it found (#write).
- File.binwrite(directory / file_name(GROUPS), JSON.generate({}))
+ nothing = {} #: Hash[String, Array[String]]
+ File.binwrite(directory / file_name(GROUPS), JSON.generate(nothing))
id
end
@@ -106,13 +107,19 @@ module ProTacts
# will not parse is a broken assumption JSON says so about.
#: (String id) -> Hash[String, Array[String]]
def self.groups(id)
+ chosen = {} #: Hash[String, Array[String]]
raw = read(id, GROUPS)
- return {} if raw.nil?
+ return chosen if raw.nil?
parsed = JSON.parse(raw)
- return {} unless parsed.is_a?(Hash)
+ return chosen unless parsed.is_a?(Hash)
- parsed.to_h { |index, ids| [index.to_s, (ids.is_a?(Array) ? ids : []).map(&:to_s)] }
+ parsed.each do |index, ids|
+ next unless ids.is_a?(Array)
+
+ chosen[index.to_s] = ids.map(&:to_s)
+ end
+ chosen
end
# The import, gone: the last step of a landing, and what keeps
diff --git a/lib/pro_tacts/web/import.rb b/lib/pro_tacts/web/import.rb
index 62d10d3..c8f92ad 100644
--- a/lib/pro_tacts/web/import.rb
+++ b/lib/pro_tacts/web/import.rb
@@ -118,7 +118,11 @@ module ProTacts
# By name, because the row is read rather than submitted: a
# group the walk ticked and someone then deleted is simply not
# among these.
- names = store.all_groups.to_h { |group| [group.id, group.label] }
+ names = store.all_groups.to_h { |group|
+ # A pair is a two-element array until something says
+ # otherwise, and an inline annotation needs its own line.
+ [group.id, group.label] #: [String, String]
+ }
rows = landing.each_with_index.map { |card, index|
# A literal of several elements is an Array until something
# says otherwise, and an inline annotation needs its own line.