diff --git a/CLAUDE.md b/CLAUDE.md index 9b844a7..fcc2373 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -115,6 +115,11 @@ Do not introduce a new framework, database, job runner, or test library without - Add database indexes for foreign keys and any column used for timeline ordering. - Use strong parameters, and scope queries through associations rather than `Model.find(params[:id])` on user-owned records. +- **A render-only action has no method.** Rails renders the matching template + whether or not the action is defined, so `def new; end` is inert. The route, + and the `only:` list of any filter that applies to it, already declare that + the action exists. Four such methods were deleted on 2026-09-01 (issue #79); + do not reintroduce them, and do not read their absence as an oversight. **Views** diff --git a/sonar-project.properties b/sonar-project.properties index 0e69d57..e669689 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -27,3 +27,17 @@ sonar.ruby.file.suffixes=.rb,.rake # to the SonarQube job as an artifact. This is the only coverage format the # Ruby analyser accepts — it does not read LCOV. sonar.ruby.coverage.reportPaths=web/coverage/coverage.json + +# Coverage describes the Ruby, because the Ruby is what SimpleCov can +# instrument. Without these two lines the dashboard read 76.8% while the suite +# covered ~99% of the Ruby: Sonar counts a source file with no coverage data as +# uncovered, and sonar.sources also holds Stimulus controllers and ERB +# templates, which SimpleCov cannot instrument at all. +# +# Both numbers were honest; they measured different things, and the headline was +# the one that understated the suite — which is how a metric stops being read. +# +# This narrows one metric, not the scan: every file below is still analysed for +# bugs, smells and vulnerabilities. It is also the prerequisite for making the +# quality gate blocking, since a gate on a number nobody believes is theatre. +sonar.coverage.exclusions=web/app/javascript/**,web/app/views/** diff --git a/web/app/controllers/passwords_controller.rb b/web/app/controllers/passwords_controller.rb index f95ec78..911fd16 100644 --- a/web/app/controllers/passwords_controller.rb +++ b/web/app/controllers/passwords_controller.rb @@ -3,9 +3,6 @@ class PasswordsController < ApplicationController before_action :set_user_by_token, only: %i[ edit update ] rate_limit to: 10, within: 3.minutes, only: :create, with: -> { redirect_to new_password_path, alert: "Try again later." } - def new - end - def create if user = User.find_by(email_address: params[:email_address]) PasswordsMailer.reset(user).deliver_later @@ -14,9 +11,6 @@ def create redirect_to new_session_path, notice: "Password reset instructions sent (if user with that email address exists)." end - def edit - end - def update if @user.update(params.permit(:password, :password_confirmation)) @user.sessions.destroy_all diff --git a/web/app/controllers/posts_controller.rb b/web/app/controllers/posts_controller.rb index 22ab19a..a1c8196 100644 --- a/web/app/controllers/posts_controller.rb +++ b/web/app/controllers/posts_controller.rb @@ -41,9 +41,6 @@ def create end end - def edit - end - def update if @post.update(post_params) redirect_to posts_path diff --git a/web/app/controllers/sessions_controller.rb b/web/app/controllers/sessions_controller.rb index 8b27e00..5ac99c6 100644 --- a/web/app/controllers/sessions_controller.rb +++ b/web/app/controllers/sessions_controller.rb @@ -3,9 +3,6 @@ class SessionsController < ApplicationController before_action :redirect_if_authenticated, only: %i[ new create ] rate_limit to: 10, within: 3.minutes, only: :create, with: -> { redirect_to new_session_path, alert: "Try again later." } - def new - end - def create if user = User.authenticate_by(params.permit(:email_address, :password)) start_new_session_for user diff --git a/web/config/initializers/opentelemetry.rb b/web/config/initializers/opentelemetry.rb index 3470978..3cfa9dc 100644 --- a/web/config/initializers/opentelemetry.rb +++ b/web/config/initializers/opentelemetry.rb @@ -27,6 +27,12 @@ tracing_requested = ENV["OTEL_TRACES_EXPORTER"] != "none" if tracing_requested || Rails.env.test? + # These requires stay here rather than at the top of the file, and Sonar's + # rubydre:S7816 is right that this is unusual — it is also the whole point of + # ADR 0009. Loading the SDK is what starts it; requiring it unconditionally + # would mean every rails command paid for instrumentation nobody asked for, + # which is a behaviour change rather than a tidy-up. The rule is refused here + # deliberately, with the reason in the file rather than only on a dashboard. require "opentelemetry/sdk" require "opentelemetry/instrumentation/rails" diff --git a/web/spec/rails_helper.rb b/web/spec/rails_helper.rb index fed1aa5..1f823f9 100644 --- a/web/spec/rails_helper.rb +++ b/web/spec/rails_helper.rb @@ -34,6 +34,10 @@ rescue ActiveRecord::PendingMigrationError => e abort e.to_s.strip end +# Support files are required after the Rails environment, not at the top of +# the file, because they reference application constants and RSpec config. +# Also refusing rubydre:S7816, for a different reason than the initializer: +# this is a glob, so there is no static require to hoist. Rails.root.glob('spec/support/**/*.rb').sort.each { |file| require file } RSpec.configure do |config|