From efa55ef4c8ed83cfbdcf385a88c407135c0c2c76 Mon Sep 17 00:00:00 2001 From: Igor Fedoronchuk Date: Mon, 19 Oct 2026 18:58:58 +0200 Subject: [PATCH] Let an app move the notice flash key once `flash_key:` was per resource, so an application that renders its own flash key for "why this page is cut down" had to repeat it on every index declaring a notice - or reach for a prepend on ResourceDSL to move the default. `ActiveAdminFilterExtensions.notice_flash_key` is that default. It is read when the notice is written rather than when the resource is registered, so initializer order does not decide it and a test can move it around one request. What the resource declared still wins. --- README.md | 12 ++++++++- lib/active_admin_filter_extensions.rb | 15 +++++++++++ lib/active_admin_filter_extensions/notice.rb | 10 +++++-- .../redirect.rb | 2 +- lib/active_admin_filter_extensions/version.rb | 2 +- spec/integration/filter_defaults_spec.rb | 27 +++++++++++++++++++ 6 files changed, 63 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 96499f5..cb68fc8 100644 --- a/README.md +++ b/README.md @@ -170,7 +170,17 @@ end ``` The message is flashed only when the defaults actually took effect. Pass `flash_key:` to use -something other than `:notice`. +something other than `:notice`, or move the default for the whole application once: + +```ruby +# config/initializers/active_admin_filter_extensions.rb +ActiveAdminFilterExtensions.notice_flash_key = :filters_notice +``` + +Worth doing where the layout renders its own key for "why this page is cut down": Active Admin +writes action results to `:notice`, and on a shared key the arriving message wins, so a batch +action's result would silence the notice. The key is read when the notice is written and +`flash_key:` on the resource still beats it. ## Required filters: the mandatory window diff --git a/lib/active_admin_filter_extensions.rb b/lib/active_admin_filter_extensions.rb index 0f12088..4d23a9b 100644 --- a/lib/active_admin_filter_extensions.rb +++ b/lib/active_admin_filter_extensions.rb @@ -17,6 +17,21 @@ require "active_admin_filter_extensions/notice" module ActiveAdminFilterExtensions + class << self + # Where a filters notice goes when the resource does not say. Active Admin writes action + # results to :notice, and an app whose layout renders its own key for "why this page is + # cut down" would otherwise repeat `flash_key:` on every index that declares a notice: + # + # ActiveAdminFilterExtensions.notice_flash_key = :notice_message + # + # Read when the notice is written, not when the resource is registered, so initializer + # order does not decide it. + attr_writer :notice_flash_key + + def notice_flash_key + @notice_flash_key ||= :notice + end + end end # `add_filter` stores whatever options it is given without a whitelist, so `default:` needs no diff --git a/lib/active_admin_filter_extensions/notice.rb b/lib/active_admin_filter_extensions/notice.rb index 67da408..5440628 100644 --- a/lib/active_admin_filter_extensions/notice.rb +++ b/lib/active_admin_filter_extensions/notice.rb @@ -12,7 +12,12 @@ module ActiveAdminFilterExtensions # message is only flashed when the defaults actually took effect, so it stays quiet once the # admin has filtered for themselves. module Notice - def default_filters_notice(message, flash_key: :notice) + # The key this resource's notice goes under: what it declared, else the app-wide default. + def self.flash_key_for(config) + config.default_filters_notice_flash_key || ActiveAdminFilterExtensions.notice_flash_key + end + + def default_filters_notice(message, flash_key: nil) # Declared on the resource rather than closed over, so that the redirect - which answers # the request the notice belongs on with a 302 - can find it and carry it across. config.default_filters_notice = message @@ -27,7 +32,8 @@ def default_filters_notice(message, flash_key: :notice) # default slice - the notice on top of it would be noise. next if filter_policy_errors.present? - flash.now[flash_key] = ::MethodOrProcHelper.render_in_context(self, message) + flash.now[Notice.flash_key_for(active_admin_config)] = + ::MethodOrProcHelper.render_in_context(self, message) end end end diff --git a/lib/active_admin_filter_extensions/redirect.rb b/lib/active_admin_filter_extensions/redirect.rb index df1b570..dd9f778 100644 --- a/lib/active_admin_filter_extensions/redirect.rb +++ b/lib/active_admin_filter_extensions/redirect.rb @@ -71,7 +71,7 @@ def self.included(base) filter_policy_errors.empty? && filter_defaults_engaged? # On a shared key - the default is :notice, the same key a batch action writes - the # arriving message wins: it names what just happened, the notice can wait. - key = active_admin_config.default_filters_notice_flash_key + key = Notice.flash_key_for(active_admin_config) flash[key] = ::MethodOrProcHelper.render_in_context(self, message) if flash[key].blank? end diff --git a/lib/active_admin_filter_extensions/version.rb b/lib/active_admin_filter_extensions/version.rb index 560ba51..93c00d7 100644 --- a/lib/active_admin_filter_extensions/version.rb +++ b/lib/active_admin_filter_extensions/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module ActiveAdminFilterExtensions - VERSION = "1.0.1" + VERSION = "1.1.0" end diff --git a/spec/integration/filter_defaults_spec.rb b/spec/integration/filter_defaults_spec.rb index 74d55e3..4efa8dd 100644 --- a/spec/integration/filter_defaults_spec.rb +++ b/spec/integration/filter_defaults_spec.rb @@ -191,6 +191,33 @@ expect(page).to have_no_content("Showing published posts by default") end + + describe "the app-wide key" do + # Deliberately a key no resource declares, so an example cannot pass by coincidence: + # NoticePost's own key is :filters_notice. + around do |example| + previous = ActiveAdminFilterExtensions.notice_flash_key + ActiveAdminFilterExtensions.notice_flash_key = :app_wide_notice + example.run + ActiveAdminFilterExtensions.notice_flash_key = previous + end + + it "takes a notice that named no key of its own" do + # The resource says only the message; the app said where its notices go. Asserted on + # the rendered flash class, which is what the layout keys off. + visit "/admin/colliding_notice_posts" + + expect(page).to have_css(".flash_app_wide_notice", text: "Showing published posts by default") + expect(page).to have_no_css(".flash_notice") + end + + it "loses to the key the resource declared" do + visit "/admin/notice_posts" + + expect(page).to have_css(".flash_filters_notice", text: "Showing published posts by default") + expect(page).to have_no_css(".flash_app_wide_notice") + end + end end describe "requests that carry no q of their own" do