diff --git a/changelog/new_add_railsactioncontrollerflashbeforerender.md b/changelog/new_add_railsactioncontrollerflashbeforerender.md new file mode 100644 index 0000000000..fd97953452 --- /dev/null +++ b/changelog/new_add_railsactioncontrollerflashbeforerender.md @@ -0,0 +1 @@ +* [#759](https://github.com/rubocop/rubocop-rails/pull/759): Add new `Rails/ActionControllerFlashBeforeRender` cop. ([@americodls][]) diff --git a/config/default.yml b/config/default.yml index 284b45cb11..f69e5528a5 100644 --- a/config/default.yml +++ b/config/default.yml @@ -65,6 +65,16 @@ Rails: Enabled: true DocumentationBaseURL: https://docs.rubocop.org/rubocop-rails +Rails/ActionControllerFlashBeforeRender: + Description: 'Use `flash.now` instead of `flash` before `render`.' + StyleGuide: 'https://rails.rubystyle.guide/#flash-before-render' + Reference: 'https://api.rubyonrails.org/classes/ActionController/FlashBeforeRender.html' + Enabled: 'pending' + SafeAutocorrect: true + VersionAdded: '2.15' + Include: + - '**/test/**/*.rb' + Rails/ActionControllerTestCase: Description: 'Use `ActionDispatch::IntegrationTest` instead of `ActionController::TestCase`.' StyleGuide: 'https://rails.rubystyle.guide/#integration-testing' diff --git a/lib/rubocop/cop/rails/action_controller_flash_before_render.rb b/lib/rubocop/cop/rails/action_controller_flash_before_render.rb new file mode 100644 index 0000000000..632d65c28a --- /dev/null +++ b/lib/rubocop/cop/rails/action_controller_flash_before_render.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module Rails + # Using `flash` assignment before `render` will persist the message for too long. + # Check https://guides.rubyonrails.org/action_controller_overview.html#flash-now + # + # @example + # # bad + # flash[:alert] = "Warning!" + # render :index + # + # # good + # flash.now[:alert] = "Warning!" + # render :index + # + class ActionControllerFlashBeforeRender < Base + extend AutoCorrector + + MSG = 'Use `flash.now` before `render`.' + + def_node_search :flash_before_render?, <<~PATTERN + ^(send (send nil? :flash) :[]= ...) + PATTERN + + def_node_search :render?, <<~PATTERN + (send nil? :render ...) + PATTERN + + RESTRICT_ON_SEND = [:flash].freeze + + def on_send(node) + expression = flash_before_render?(node) + + return unless expression + + context = node.parent.parent + return unless context.descendants.any? { render?(_1) } + + add_offense(node) do |corrector| + corrector.replace(node, 'flash.now') + end + end + end + end + end +end diff --git a/lib/rubocop/cop/rails_cops.rb b/lib/rubocop/cop/rails_cops.rb index 2f1e0996d1..c81b246b95 100644 --- a/lib/rubocop/cop/rails_cops.rb +++ b/lib/rubocop/cop/rails_cops.rb @@ -9,6 +9,7 @@ require_relative 'mixin/target_rails_version' require_relative 'rails/action_controller_test_case' +require_relative 'rails/action_controller_flash_before_render' require_relative 'rails/action_filter' require_relative 'rails/active_record_aliases' require_relative 'rails/active_record_callbacks_order' diff --git a/spec/rubocop/cop/rails/action_controller_flash_before_render_spec.rb b/spec/rubocop/cop/rails/action_controller_flash_before_render_spec.rb new file mode 100644 index 0000000000..3fb8998174 --- /dev/null +++ b/spec/rubocop/cop/rails/action_controller_flash_before_render_spec.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +RSpec.describe RuboCop::Cop::Rails::ActionControllerFlashBeforeRender, :config do + it 'registers an offense when using `flash` before `render`' do + expect_offense(<<~RUBY) + flash[:alert] = "msg" + ^^^^^ Use `flash.now` before `render`. + render :index + RUBY + + expect_correction(<<~RUBY) + flash.now[:alert] = "msg" + render :index + RUBY + end + + it 'does not register an offense when using `flash.now` before `render`' do + expect_no_offenses(<<~RUBY) + flash.now[:alert] = "msg" + render :index + RUBY + end + + it 'does not register an offense when using `flash` before `redirect_to`' do + expect_no_offenses(<<~RUBY) + flash[:alert] = "msg" + redirect_to "https://www.rubygems.org" + RUBY + end +end