From 6908e73f72584719bddd175c8ca9a5ccd82d13c4 Mon Sep 17 00:00:00 2001 From: moveson Date: Mon, 3 Aug 2026 00:36:51 -0600 Subject: [PATCH 1/2] Tolerate legacy flat filter params on the spread view Crawlers hitting old cached URLs with the flat filter=combined format were triggering a 500: the search bar's params.dig(:filter, :search) raised on the String value. Search reads now go through prepared_params, whose filter accessors already guard against non-hash filter params, and the gender hidden field and clear-search helper guard their raw access explicitly. Fixes Scout error group 94101 (129 occurrences). Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01UXmhbeNGokL4pdgxB8bxuT --- app/helpers/events_helper.rb | 2 +- app/views/events/spread.html.erb | 9 +++++---- spec/system/results/spread_view_spec.rb | 7 +++++++ 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb index 68dbd2bcf..fc79c1cca 100644 --- a/app/helpers/events_helper.rb +++ b/app/helpers/events_helper.rb @@ -75,7 +75,7 @@ def link_to_beacon_button(view_object) def spread_path_without_search(view_object) query = request.query_parameters.deep_dup - query["filter"]&.delete("search") + query["filter"] = query["filter"].except("search") if query["filter"].is_a?(Hash) query.delete("filter") if query["filter"].blank? spread_event_path(view_object.event, query) end diff --git a/app/views/events/spread.html.erb b/app/views/events/spread.html.erb index bb6758ad7..3b0d065b7 100644 --- a/app/views/events/spread.html.erb +++ b/app/views/events/spread.html.erb @@ -40,17 +40,18 @@
+ <% gender_param = params[:filter].is_a?(ActionController::Parameters) ? params[:filter][:gender] : nil %> <%= form_tag spread_event_path(@presenter.event), method: :get do %> <%= hidden_field_tag :display_style, params[:display_style] if params[:display_style].present? %> <%= hidden_field_tag :sort, params[:sort] if params[:sort].present? %> - <%= hidden_field_tag "filter[gender]", params.dig(:filter, :gender) if params.dig(:filter, :gender).present? %> + <%= hidden_field_tag "filter[gender]", gender_param if gender_param.present? %>
<%= text_field_tag "filter[search]", - params.dig(:filter, :search), + prepared_params[:search], placeholder: "Name or bib number", autocomplete: "off", class: "form-control" %> - <% if params.dig(:filter, :search).present? %> + <% if prepared_params[:search].present? %> <%= link_to spread_path_without_search(@presenter), id: "spread-search-clear", class: "btn btn-outline-secondary input-group-text" do %> @@ -83,7 +84,7 @@
<% cache @presenter.cache_key do %> - <% if @presenter.effort_times_rows.empty? && params.dig(:filter, :search).present? %> + <% if @presenter.effort_times_rows.empty? && prepared_params[:search].present? %>

No entrants match this search

diff --git a/spec/system/results/spread_view_spec.rb b/spec/system/results/spread_view_spec.rb index 265192384..3dc42a93e 100644 --- a/spec/system/results/spread_view_spec.rb +++ b/spec/system/results/spread_view_spec.rb @@ -28,6 +28,13 @@ verify_efforts_present(subject_efforts) end + scenario "A visitor arrives via a legacy URL with a flat filter param" do + visit spread_event_path(event, filter: "combined", display_style: "elapsed", sort: "-overall_rank") + + expect(page).to have_content(event.name) + verify_efforts_present(subject_efforts) + end + scenario "A visitor searches for an entrant by name" do subject_effort = efforts(:hardrock_2015_tuan_jacobs) visit spread_event_path(event) From d1ee54d9ae39866659d4de45555b521d565dcb5b Mon Sep 17 00:00:00 2001 From: moveson Date: Mon, 3 Aug 2026 00:40:56 -0600 Subject: [PATCH 2/2] Redirect legacy flat filter params to the canonical format A flat filter string that names a gender (the old format) permanently redirects to the nested filter[gender] equivalent so crawlers update their cached URLs; an unrecognized flat filter redirects with the param dropped. The view-level guards remain as defense for arbitrary malformed params. Follows the precedent of the legacy split_name redirect in events#traffic. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01UXmhbeNGokL4pdgxB8bxuT --- app/controllers/events_controller.rb | 6 ++++++ spec/system/results/spread_view_spec.rb | 9 +++++++++ 2 files changed, 15 insertions(+) diff --git a/app/controllers/events_controller.rb b/app/controllers/events_controller.rb index 460bed0cf..c8ad3683c 100644 --- a/app/controllers/events_controller.rb +++ b/app/controllers/events_controller.rb @@ -151,6 +151,12 @@ def reassign # GET /events/1/spread def spread + if params[:filter].is_a?(String) + gender = params[:filter].presence_in(Effort.genders.keys << "combined") + return redirect_to request.params.merge(filter: gender ? { gender: gender } : nil), + status: :moved_permanently + end + @presenter = EventSpreadDisplay.new(event: @event, params: prepared_params, current_user: current_user) respond_to do |format| format.html diff --git a/spec/system/results/spread_view_spec.rb b/spec/system/results/spread_view_spec.rb index 3dc42a93e..ee2c53fb1 100644 --- a/spec/system/results/spread_view_spec.rb +++ b/spec/system/results/spread_view_spec.rb @@ -31,6 +31,15 @@ scenario "A visitor arrives via a legacy URL with a flat filter param" do visit spread_event_path(event, filter: "combined", display_style: "elapsed", sort: "-overall_rank") + expect(page).to have_current_path(%r{/spread\?.*filter%5Bgender%5D=combined}, url: true) + expect(page).to have_content(event.name) + verify_efforts_present(subject_efforts) + end + + scenario "A visitor arrives via a legacy URL with an unrecognized flat filter param" do + visit spread_event_path(event, filter: "bogus", display_style: "elapsed") + + expect(page).not_to have_current_path(/filter/, url: true) expect(page).to have_content(event.name) verify_efforts_present(subject_efforts) end