Add reusable script to refactor Phoenix test conn aliasing

Portable awk-based script that transforms conn shadowing patterns
into idiomatic pipe chains across 4 cases (body extraction, single
assert, pattern match assert, multi-use rename).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Willem van den Ende 2026-03-20 20:55:20 +00:00
parent f148fe4fcd
commit 5186edc2e9

188
refactor_conn_aliasing.sh Executable file
View File

@ -0,0 +1,188 @@
#!/usr/bin/env bash
set -euo pipefail
usage() {
cat <<'EOF'
Usage: refactor_conn_aliasing.sh [OPTIONS] FILE...
--dry-run Show diff without modifying files
--help Show usage
EOF
}
DRY_RUN=false
FILES=()
while [[ $# -gt 0 ]]; do
case "$1" in
--dry-run) DRY_RUN=true; shift ;;
--help) usage; exit 0 ;;
-*) echo "Unknown option: $1" >&2; usage >&2; exit 1 ;;
*) FILES+=("$1"); shift ;;
esac
done
if [[ ${#FILES[@]} -eq 0 ]]; then
echo "Error: no files specified" >&2
usage >&2
exit 1
fi
for file in "${FILES[@]}"; do
if [[ ! -f "$file" ]]; then
echo "Warning: $file not found, skipping" >&2
continue
fi
tmpfile=$(mktemp)
trap "rm -f '$tmpfile'" EXIT
awk '
# Detect trigger line: conn = VERB(conn, ARGS)
# where VERB is get/post/put/patch/delete/head/options
/^[[:space:]]*conn = (get|post|put|patch|delete|head|options)\(conn, / {
trigger_line = $0
# Extract leading whitespace
match($0, /^[[:space:]]*/)
indent = substr($0, RSTART, RLENGTH)
# Extract verb and args from: conn = verb(conn, args)
rest = $0
sub(/^[[:space:]]*conn = /, "", rest)
# rest is now: verb(conn, args)
paren_pos = index(rest, "(")
verb = substr(rest, 1, paren_pos - 1)
# args portion: everything after "conn, " up to the trailing ")"
inner = substr(rest, paren_pos + 1)
sub(/\)$/, "", inner)
# inner is: conn, args
sub(/^conn, /, "", inner)
args = inner
# Read the next non-blank line
triggered = 1
next
}
triggered == 1 {
# Skip blank lines, accumulating them
if ($0 ~ /^[[:space:]]*$/) {
blank_lines = blank_lines $0 "\n"
next
}
next_line = $0
triggered = 0
# Now look ahead: count how many subsequent lines (until scope boundary)
# reference "conn" — to decide Case 4 vs Cases 1-3
# We already have next_line. Check if next_line references conn.
# Then peek further lines.
# For simplicity: check if next_line matches Case 1, 2, or 3 patterns.
# If it does, check the line AFTER that for more conn references (Case 4 override).
# Case 1: var = helper(conn, status)
# helpers: html_response, json_response, text_response, response, redirected_to
case1 = 0
if (match(next_line, /^[[:space:]]*([a-z_]+) = (html_response|json_response|text_response|response|redirected_to)\(conn, [^)]+\)$/, m1)) {
case1 = 1
c1_var = m1[1]
c1_helper = m1[2]
# Extract status from helper(conn, status)
match(next_line, /\(conn, ([^)]+)\)/, m1s)
c1_status = m1s[1]
}
# Case 2: assert helper(conn, status) with optional =~ "..."
case2 = 0
if (match(next_line, /^[[:space:]]*assert (html_response|json_response|text_response|response|redirected_to)\(conn, ([^)]+)\)(.*)$/, m2)) {
case2 = 1
c2_helper = m2[1]
c2_status = m2[2]
c2_tail = m2[3]
}
# Case 3: assert %{...} = helper(conn, status)
case3 = 0
if (match(next_line, /^[[:space:]]*assert (%\{[^}]*\}) = (html_response|json_response|text_response|response|redirected_to)\(conn, ([^)]+)\)$/, m3)) {
case3 = 1
c3_pattern = m3[1]
c3_helper = m3[2]
c3_status = m3[3]
}
# If we matched Case 1, 2, or 3, emit the merged line
if (case1) {
print indent c1_var " = conn |> " verb "(" args ") |> " c1_helper "(" c1_status ")"
if (blank_lines != "") printf "%s", blank_lines
blank_lines = ""
next
}
if (case2) {
print indent "assert conn |> " verb "(" args ") |> " c2_helper "(" c2_status ")" c2_tail
if (blank_lines != "") printf "%s", blank_lines
blank_lines = ""
next
}
if (case3) {
print indent "assert " c3_pattern " = conn |> " verb "(" args ") |> " c3_helper "(" c3_status ")"
if (blank_lines != "") printf "%s", blank_lines
blank_lines = ""
next
}
# If next_line references conn at all, this is Case 4 territory
# (multiple uses without a recognized single-merge pattern)
if (next_line ~ /conn/) {
# Case 4: rename to response
print indent "response = conn |> " verb "(" args ")"
if (blank_lines != "") printf "%s", blank_lines
blank_lines = ""
# Replace conn with response in next_line
gsub(/conn/, "response", next_line)
print next_line
# Continue replacing conn->response in subsequent lines until scope boundary
renaming = 1
next
}
# No conn reference on next line — leave trigger unchanged (fallback)
print trigger_line
if (blank_lines != "") printf "%s", blank_lines
blank_lines = ""
print next_line
next
}
# Renaming mode for Case 4: replace conn with response until scope boundary
renaming == 1 {
# Scope boundary: blank line, "end", reduced indentation, or new conn = assignment
if ($0 ~ /^[[:space:]]*$/ || $0 ~ /^[[:space:]]*end$/ || $0 ~ /^[[:space:]]*conn =/) {
renaming = 0
print
next
}
gsub(/conn/, "response")
print
next
}
# Normal mode: pass through
{
if (blank_lines != "") {
printf "%s", blank_lines
blank_lines = ""
}
print
}
BEGIN { triggered = 0; renaming = 0; blank_lines = "" }
' "$file" > "$tmpfile"
if $DRY_RUN; then
diff -u "$file" "$tmpfile" || true
else
mv "$tmpfile" "$file"
echo "Refactored: $file"
fi
done