[Views] Declare strict locals on the remaining partials that take locals - #15191
Luke-Oldenburg wants to merge 4 commits into
Conversation
Every partial under app/views now declares strict locals. Declarations come from each partial's callers. defined?(x) checks become declared defaults with value checks, local_assigns[:x] reads become plain locals, and locals a partial never reads are dropped from its callers. Callers that relied on defined? treating false as present now pass the value that keeps today's output (hide_info, enable_linking, show_blankslate). application/_footer's help_message flag shadowed the help_message helper, so it becomes show_help_message. Also fixes is_blankslate being passed outside locals: on the donations page, and reads include_external/include_troubleshooting/is_email directly in the two _decline_reason partials. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
@claude review |
|
Claude finished @Luke-Oldenburg's task in 18s —— View job Review of #15191I found no blocking issues. I read the diff for the controllers and for the What I checked
Minor, non-blocking
|
Follow-up to #15191. Partials that still read local_assigns[:x] for a declared local now read the local. Those defaults are nil or false and only feed conditions, so nothing renders differently. Renames the class local to klass in application/_callout, comments/_item, increase_checks/_paper_check and wires/_requirements, since a local named class can't be read by name, and updates the callers. receipts/_receipt and receipts/_form_v3 keep local_assigns.__id__ as a per-render id. jquery-modal moves an opened modal out of its partial and won't open when two elements share an id, so an id built from the record would collide when the same receipt renders twice on /my/inbox, or when an upload re-renders the form. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
@claude review |
|
Claude finished @Luke-Oldenburg's task in 14s —— View job Review of #15191I found no blocking issues. I sampled the diff (the controllers, What I checked
Minor, non-blocking
|
9cbb1e4 to
01dc2c6
Compare
Note
This PR's last two commits (removing
local_assignsreads, and passing instance variables to partials as locals) moved into the stacked PRs below, so this PR is back to the version that was approved (its first two commits). Review and merge them in order. Each PR's base branch is the PR before it, so its diff shows only its own change.local_assigns@eventand row options through navs and transaction rows as strict localsSummary of the problem
This finishes the strict locals work from #15188, #15189 and #15190. The 100 partials left were the ones that take locals, plus
comments/reactions/react.turbo_stream.erb, a full template thatComment::ReactionsControllerrenders withlocals: { comment: }. After this, all 481 partials underapp/viewsdeclare strict locals.Most of them read their locals in ways that stop working once a declaration exists:
defined?(x)is true as soon asxis declared, so it can't mean "the caller passedx" anymore.local_assigns[:x]only holds what the caller passed. It never sees a declared default.A few of these hid bugs, and fixing them naively would have changed what renders.
Describe your changes
Each declaration comes from the partial's callers. A local is required when every caller passes it and the body reads it. Otherwise it gets a default: the file's existing
defined?(x) ? x : …orx ||= …value, ornil. As in #15188, the declaration goes on line 1 with a blank line after it.Reading locals directly
defined?(x)becomes a plain check onx, with anildefault. I checked the values every caller passes. Existing callers get the same result, except the three changed below and thehcb_codes/_iconattribute under Rendered output.local_assigns[:x]becomesx. [Views] Read strict locals instead of local_assigns #15200 does the same in the partials that already declared their locals.x ||= defaultmoves into the declaration where the default is a literal and no caller passesnil._callout'sicon ||=stays, because its default depends ontype.Callers that relied on
defined?treatingfalseas present. Each now passes the value that keeps today's output:reimbursement/expenses/_receiptspassedhide_info: false, andreceipts/_receipthid the info anyway. It now passeshide_info: true.payments/showpassedenable_linking: false, andreceipts/_form_v3showed "Select from Receipt Bin" anyway. It now passesenable_linking: true.CommentsController#destroypassedshow_blankslate: commentable.comments.empty?. Becausedefined?treated any value as set, the blank slate showed whenever the viewer'spolicy_scope(comments)was empty. It now passesshow_blankslate: true.Other fixes the declarations needed
application/_footerused ahelp_messagelocal as an on/off flag, buthelp_messageis also anApplicationHelpermethod. When a layout rendered the footer without that local, the bare name called the helper and printed the message. Whenlogins/_footerpassedhelp_message: nilorfalse, the message was hidden. Ahelp_message: nildeclaration would have blanked the footer everywhere, so the flag is nowshow_help_message: trueandlogins/_footerpassesfalse. I checked every other declared name against the view's methods. The others (current_user,open,possessive,session,tag,title) are either always passed or never used to call the helper.users/_navpassed one local literally namedlocal_assigns: { selected:, user:, open: }tousers/_settings_dropdown. That only worked because non-strict compilation swaps in the inner hash. It now passes the three locals directly.card_grant/pre_authorizations/_organizer_infodeclared its locals with braces, which Rails ignores. It now uses parentheses.hcb_codes/_tagsread@event, even though all 14 callers passevent: @event || @hcb_code.event. It now declaresevent:and reads the local. The transaction page and the admin ledger-audit task page both set@eventto that same value, so nothing renders differently. The nestedhcb_codes/_create_tagstill reads@eventitself, as it does for its other four callers.Unused locals dropped from callers
events_controller: the 9events/home/*frames read ivars, so onlytags_chart(tags:) andusers_chart(users:,event:) still get locals.eventon their second line, soevent:andshow_amount:are gone fromevents/account_number,events/transactions_listand bothReceiptsControllerbranches.show_new(admin/balances),upload_method:andshimmer:(pre-authorization page),donation:(donation goal and tiers),select_target:(hcb_codes/link_receipt),target:(deletion request form),current_user:(thereceipts/extractedbroadcast),stripe_card: StripeCard.new(card forms), andwhats_hcb:/help_message:(logins/_footer).events/home/_balance_transactionsand_team_statspassedevent: @eventoutsidelocals:, where Rails drops it. I removed it.Related fixes
events/donation_overviewpassedis_blankslate: trueoutsidelocals:, so the blank-slate share button never gotw-full. It's now insidelocals:. This is the one visible change.hcb_codes/_decline_reasonandledger/items/_decline_reasonreadlocal_assigns[:include_external], which ignores the declaredinclude_external: truedefault. They now read their locals directly. All six callers pass both flags, so nothing renders differently.Rendered output
hcb_codes/_iconno longer addsdata-action="click->transactions#select"to rows that aren't selectable. Both callers always passedselects:, sodefined?(selects)was always true and those rows gotdata-transaction=""or"false". Cmd-clicking one inside thetransactionscontroller randocument.getElementById("")and threw. Nothing on screen changes.The erb_lint
StrictLocalslinter is still off. Turning it on can be its own PR.Testing
app/,lib/,config/andspec/for any other reference to these partials, including broadcasts andrender @collection. Every one passes the required locals.rails runner. All 118 changed templates that declare strict locals compile, and so do the 13 changed full-page templates.transactions#selectattribute.mainand the new ones here, these cover:receipts/_receipt,receipts/_form_v3and transaction-row callerrender_activitiesand the Discord fallbacktransactions#selectattribute.hcb_codes/_tagschange, I attached temporary tags to all 116 HCB codes. I rendered each transaction page as an admin and as a regular user who was a reader on some events and a member on others, plus 40 admin ledger-audit task pages. All 272 pages match the first commit.bin/erb_lintandrubocopon the changed files: no offenses.rspec: 2474 examples, 0 failures on the first commit. After the_tagschange,spec/controllers spec/requests spec/mailers spec/integration: 558 examples, 0 failures.🤖 Generated with Claude Code