From 51fca464b52461fdccf07d83da2facb315ff2b33 Mon Sep 17 00:00:00 2001 From: Guillem Arias Date: Thu, 14 May 2026 21:50:01 +0200 Subject: [PATCH] fix(goals): reconciler logs to Sentry + rename :extend route to :renew MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two Ruby idiom audit fixes. The Reconciler's outer `rescue StandardError` was logging at error level and moving on. Pipeline-protective (we don't want a Goal reconcile failure to break the Plaid/SimpleFIN/etc importer it's hooked into) but invisible — real bugs hid behind a warn log forever. Add `Sentry.capture_exception(e) if defined?(Sentry)` alongside the log, matching the pattern in `Account::Syncer`, `Sync`, `PlaidItem`, and the chart-series rescues this branch already added. Keep the rescue's protective function. `member do patch :extend end` shadows `Module#extend` — the controller action name competes with Ruby's most-common mixin entry point. `before_action :foo, only: %i[extend destroy]` reads as "extend this controller with :foo, only: …" to a casual reader, and stack traces against `def extend` look misleading. Rename to `:renew` (matches the existing copy: the button says "Extend 7 days," but the API verb is "renew the watching window"): - config/routes.rb: `patch :renew` - GoalPledgesController#extend → #renew - locale `goal_pledges.extend` → `goal_pledges.renew` - banner `extend_goal_pledge_path` → `renew_goal_pledge_path` - test refs updated The user-facing button text is unchanged. --- app/controllers/goal_pledges_controller.rb | 4 ++-- app/models/goal_pledge/reconciler.rb | 6 ++++++ app/views/goals/_pending_pledge_banner.html.erb | 2 +- config/locales/views/goal_pledges/en.yml | 2 +- config/routes.rb | 2 +- test/controllers/goal_pledges_controller_test.rb | 4 ++-- 6 files changed, 13 insertions(+), 7 deletions(-) diff --git a/app/controllers/goal_pledges_controller.rb b/app/controllers/goal_pledges_controller.rb index 66c80fec5..e33e00b1b 100644 --- a/app/controllers/goal_pledges_controller.rb +++ b/app/controllers/goal_pledges_controller.rb @@ -1,6 +1,6 @@ class GoalPledgesController < ApplicationController before_action :set_goal - before_action :set_pledge, only: %i[extend destroy] + before_action :set_pledge, only: %i[renew destroy] rescue_from ActiveRecord::RecordNotFound, with: :record_not_found def new @@ -32,7 +32,7 @@ class GoalPledgesController < ApplicationController end end - def extend + def renew @pledge.extend! redirect_to goal_path(@goal), notice: t(".success") rescue GoalPledge::NotOpenError diff --git a/app/models/goal_pledge/reconciler.rb b/app/models/goal_pledge/reconciler.rb index f700bc852..0a1afd837 100644 --- a/app/models/goal_pledge/reconciler.rb +++ b/app/models/goal_pledge/reconciler.rb @@ -32,7 +32,13 @@ class GoalPledge::Reconciler end end rescue StandardError => e + # Don't let an unexpected reconcile failure break the importer pipeline + # we're hooked into (ProviderImportAdapter / ReconciliationManager). + # Surface to Sentry so the actual bug doesn't hide behind a warn-level + # log; the inner narrow rescue handles known races without coming + # through here. Rails.logger.error("GoalPledge::Reconciler failed for entry ##{entry&.id}: #{e.class}: #{e.message}") + Sentry.capture_exception(e) if defined?(Sentry) end private diff --git a/app/views/goals/_pending_pledge_banner.html.erb b/app/views/goals/_pending_pledge_banner.html.erb index aafca32cc..b47baae3f 100644 --- a/app/views/goals/_pending_pledge_banner.html.erb +++ b/app/views/goals/_pending_pledge_banner.html.erb @@ -10,7 +10,7 @@
<%= render DS::Button.new( text: t("goals.show.pending_pledge.extend"), - href: extend_goal_pledge_path(pledge.goal, pledge), + href: renew_goal_pledge_path(pledge.goal, pledge), method: :patch, variant: "outline", size: "sm" diff --git a/config/locales/views/goal_pledges/en.yml b/config/locales/views/goal_pledges/en.yml index 7e5fe295c..3124561e1 100644 --- a/config/locales/views/goal_pledges/en.yml +++ b/config/locales/views/goal_pledges/en.yml @@ -12,7 +12,7 @@ en: preview_reached: "Hits your {target} target — goal reached." create: success: Pledge recorded. Sure will confirm it on the next sync. - extend: + renew: success: Pledge window extended 7 days. not_open: Only open pledges can be extended. destroy: diff --git a/config/routes.rb b/config/routes.rb index 068b3cfbf..c4c7cd9c3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -309,7 +309,7 @@ Rails.application.routes.draw do resources :pledges, only: %i[new create destroy], controller: "goal_pledges" do member do - patch :extend + patch :renew end end end diff --git a/test/controllers/goal_pledges_controller_test.rb b/test/controllers/goal_pledges_controller_test.rb index 4028eb585..9de4ad443 100644 --- a/test/controllers/goal_pledges_controller_test.rb +++ b/test/controllers/goal_pledges_controller_test.rb @@ -40,14 +40,14 @@ class GoalPledgesControllerTest < ActionDispatch::IntegrationTest test "extend pushes expires_at forward" do before = @pledge.expires_at - patch extend_goal_pledge_url(@goal, @pledge) + patch renew_goal_pledge_url(@goal, @pledge) assert_redirected_to goal_path(@goal) assert @pledge.reload.expires_at > before end test "extend on non-open pledge flashes alert" do pledge = goal_pledges(:matched_transfer) - patch extend_goal_pledge_url(@goal, pledge) + patch renew_goal_pledge_url(@goal, pledge) assert_redirected_to goal_path(@goal) assert flash[:alert].present? end