From d29fb8bf7647c28c1f32a3d9cd1d15ad599f3358 Mon Sep 17 00:00:00 2001 From: Tim Fischbach Date: Tue, 18 Aug 2026 09:56:59 +0200 Subject: [PATCH 1/8] Build new options hash when serializing Active Support freezes the options it passes to as_json since Rails 8, so extending the caller's hash raised FrozenError when Active Admin rendered JSON downloads. --- app/models/concerns/pageflow/serialization_blacklist.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/models/concerns/pageflow/serialization_blacklist.rb b/app/models/concerns/pageflow/serialization_blacklist.rb index 53d85f8d16..34972327a1 100644 --- a/app/models/concerns/pageflow/serialization_blacklist.rb +++ b/app/models/concerns/pageflow/serialization_blacklist.rb @@ -2,10 +2,8 @@ module Pageflow # @api private module SerializationBlacklist def serializable_hash(options = nil) - options ||= {} - - options[:except] = Array(options[:except]) - options[:except].concat(blacklist_for_serialization) + options = (options || {}).dup + options[:except] = Array(options[:except]) + blacklist_for_serialization super end From 167c4ce3e7b0ebb8b989d39aaa9dfb56e0a87c3a Mon Sep 17 00:00:00 2001 From: Tim Fischbach Date: Tue, 18 Aug 2026 09:56:59 +0200 Subject: [PATCH 2/8] Let deprecations derive their own callstack Rails 8 dropped support for passing the result of caller to ActiveSupport::Deprecation#warn. Pageflow::Deprecation.warn already falls back to caller_locations of the very same frame. --- lib/pageflow/configuration.rb | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/pageflow/configuration.rb b/lib/pageflow/configuration.rb index 073c4920cb..7cfa00c394 100644 --- a/lib/pageflow/configuration.rb +++ b/lib/pageflow/configuration.rb @@ -627,8 +627,7 @@ def page_types def register_page_type(page_type) Pageflow::Deprecation.warn( 'Pageflow::Configuration#register_page_type is deprecated. ' \ - 'Use config.page_types.register instead.', - caller + 'Use config.page_types.register instead.' ) page_types.register(page_type) end @@ -637,13 +636,13 @@ def register_page_type(page_type) # Please change your forms accordingly. def paperclip_filesystem_root Pageflow::Deprecation.warn( - 'Pageflow::Configuration#paperclip_filesystem_root is deprecated.', caller + 'Pageflow::Configuration#paperclip_filesystem_root is deprecated.' ) end def paperclip_filesystem_root=(_val) Pageflow::Deprecation.warn( - 'Pageflow::Configuration#paperclip_filesystem_root is deprecated.', caller + 'Pageflow::Configuration#paperclip_filesystem_root is deprecated.' ) end From 6a3baf07137078e416671bb3ded4e3a433413e52 Mon Sep 17 00:00:00 2001 From: Tim Fischbach Date: Tue, 18 Aug 2026 09:56:59 +0200 Subject: [PATCH 3/8] Assign perma ids without locking unsaved entries Rails 8.1 raises on increment! for new records. Revision components can be saved while their entry still is, since the before_save callback that assigns the perma id is registered ahead of the belongs_to autosave. --- .../concerns/pageflow/auto_generated_perma_id.rb | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/app/models/concerns/pageflow/auto_generated_perma_id.rb b/app/models/concerns/pageflow/auto_generated_perma_id.rb index c8e888a7fe..b5b932464c 100644 --- a/app/models/concerns/pageflow/auto_generated_perma_id.rb +++ b/app/models/concerns/pageflow/auto_generated_perma_id.rb @@ -14,10 +14,17 @@ def ensure_perma_id entry = entry_for_auto_generated_perma_id - entry.with_lock do - entry.increment!(:perma_id_counter) - self.perma_id = entry.perma_id_counter - end + self.perma_id = + if entry.new_record? + # No other process can see the entry yet, so there is nothing + # to lock. + entry.increment(:perma_id_counter).perma_id_counter + else + entry.with_lock do + entry.increment!(:perma_id_counter) + entry.perma_id_counter + end + end end def entry_for_auto_generated_perma_id From f687619326d69578fbf036735d8eb2dbd2d85e51 Mon Sep 17 00:00:00 2001 From: Tim Fischbach Date: Tue, 18 Aug 2026 09:56:59 +0200 Subject: [PATCH 4/8] Load dummy app routes after booting Rails 8 only loads routes on demand while eager loading is off, so the Active Admin controller constants that controller specs name were missing as soon as the spec files were loaded. --- spec/support/pageflow/dummy/app.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/spec/support/pageflow/dummy/app.rb b/spec/support/pageflow/dummy/app.rb index d88c455223..491878a8c9 100644 --- a/spec/support/pageflow/dummy/app.rb +++ b/spec/support/pageflow/dummy/app.rb @@ -18,6 +18,17 @@ def generate end require(File.join(ENV.fetch('RAILS_ROOT', nil), 'config', 'environment')) + + load_routes + end + + # Routes are only loaded on demand unless eager loading is + # enabled. Active Admin defines its controllers while routes are + # drawn, and specs reference those constants as they are loaded. + def load_routes + return unless Rails.application.respond_to?(:reload_routes_unless_loaded) + + Rails.application.reload_routes_unless_loaded end def directory From 81a71442ccf289ceea6a6715784c63381a6fe534 Mon Sep 17 00:00:00 2001 From: Tim Fischbach Date: Tue, 18 Aug 2026 09:56:59 +0200 Subject: [PATCH 5/8] Skip install generators plugins do not provide Rails 8 exits when asked for a generator that does not exist, which aborted dummy app generation for engines like pageflow_paged. --- spec/support/pageflow/dummy/rails_template.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/spec/support/pageflow/dummy/rails_template.rb b/spec/support/pageflow/dummy/rails_template.rb index 10cd7367f3..f760e2d94d 100644 --- a/spec/support/pageflow/dummy/rails_template.rb +++ b/spec/support/pageflow/dummy/rails_template.rb @@ -55,8 +55,12 @@ def source_paths generate 'pageflow:install', '--force' -if ENV['PAGEFLOW_PLUGIN_ENGINE'].present? - generate "#{ENV['PAGEFLOW_PLUGIN_ENGINE']}:install", '--force' +# Invoking a generator that does not exist aborts the template, and not +# every plugin engine comes with an install generator. +plugin_engine = ENV['PAGEFLOW_PLUGIN_ENGINE'].presence + +if plugin_engine && Rails::Generators.find_by_namespace('install', plugin_engine) + generate "#{plugin_engine}:install", '--force' end # Devise needs default_url_options for generating mails. From ae774931201bda7104515835a063349caebd12df Mon Sep 17 00:00:00 2001 From: Tim Fischbach Date: Fri, 4 Sep 2026 18:32:26 +0200 Subject: [PATCH 6/8] Require state_machine_job 3.3 Earlier versions restrict Active Job to < 8. --- pageflow.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pageflow.gemspec b/pageflow.gemspec index ad88c65901..f5ec88f91a 100644 --- a/pageflow.gemspec +++ b/pageflow.gemspec @@ -54,7 +54,7 @@ Gem::Specification.new do |s| s.add_dependency 'state_machines-activerecord', '~> 0.9.0' # Trigger resque jobs with a state machine - s.add_dependency 'state_machine_job', '~> 3.0' + s.add_dependency 'state_machine_job', '~> 3.3' # File attachments s.add_dependency 'kt-paperclip', '~> 7.2' From 33366283084c5cb4b55af5e770a6ce74ad45ca12 Mon Sep 17 00:00:00 2001 From: Tim Fischbach Date: Tue, 18 Aug 2026 09:56:59 +0200 Subject: [PATCH 7/8] Support Rails 8.0 and 8.1 Move the supported range to >= 7.2, < 8.2 and derive the gemspec requirement from it. Outside that range PAGEFLOW_RAILS_VERSION now drops the rails constraint entirely, so both older and newer versions can still be tried out. CI covers both ends of the range. --- .github/workflows/reusable-workflow-rspec.yml | 2 +- .github/workflows/tests.yml | 2 +- README.md | 2 +- lib/pageflow/rails_version.rb | 15 +++++++++++++-- pageflow.gemspec | 3 ++- 5 files changed, 18 insertions(+), 6 deletions(-) diff --git a/.github/workflows/reusable-workflow-rspec.yml b/.github/workflows/reusable-workflow-rspec.yml index 3fe50bd6a7..05ef4a1188 100644 --- a/.github/workflows/reusable-workflow-rspec.yml +++ b/.github/workflows/reusable-workflow-rspec.yml @@ -15,7 +15,7 @@ jobs: include: - ruby-version: 3.4 - ruby-version: 3.4 - rails-version: "~> 7.2" + rails-version: "~> 8.1.0" env: PAGEFLOW_RAILS_VERSION: ${{ matrix.rails-version }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1ddf3396cb..a63ea7a5e3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -16,7 +16,7 @@ jobs: strategy: fail-fast: false matrix: - rails-version: ['~> 7.1.0', '~> 7.2.0'] + rails-version: ['~> 7.2.0', '~> 8.1.0'] job: - pageflow - pageflow_js diff --git a/README.md b/README.md index 0761d95abd..e920661f36 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ Pageflow runs in environments with: * Ruby >= 3.4 * Node >= 22 -* Rails 7.1 +* Rails >= 7.2, < 8.2 * Redis server (for Resque) * A database server supported by Active Record (tested with MySQL) * ImageMagick diff --git a/lib/pageflow/rails_version.rb b/lib/pageflow/rails_version.rb index 313e1daad8..5c14190ed0 100644 --- a/lib/pageflow/rails_version.rb +++ b/lib/pageflow/rails_version.rb @@ -3,16 +3,27 @@ module Pageflow module RailsVersion extend self + SUPPORTED = ['>= 7.2', '< 8.2'].freeze + DEFAULT = '~> 7.2.0'.freeze + def detect - from_env || '~> 7.2.0' + from_env || DEFAULT + end + + def requirement + experimental? ? [] : SUPPORTED end def experimental? - detect != '~> 7.2.0' + !Gem::Requirement.new(SUPPORTED).satisfied_by?(requested_version) end private + def requested_version + Gem::Requirement.new(detect).requirements.first.last + end + def from_env ENV['PAGEFLOW_RAILS_VERSION'] if ENV['PAGEFLOW_RAILS_VERSION'] != '' end diff --git a/pageflow.gemspec b/pageflow.gemspec index f5ec88f91a..6d222d1acb 100644 --- a/pageflow.gemspec +++ b/pageflow.gemspec @@ -2,6 +2,7 @@ lib = File.expand_path('lib', __dir__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'pageflow/version' +require 'pageflow/rails_version' Gem::Specification.new do |s| s.name = 'pageflow' @@ -30,7 +31,7 @@ Gem::Specification.new do |s| s.required_ruby_version = '>= 3.2' - s.add_dependency 'rails', ['>= 7.1.2', '< 7.3'] + s.add_dependency 'rails', *Pageflow::RailsVersion.requirement # Framework for admin interface s.add_dependency 'activeadmin', '~> 3.0' From d18e802558352108b40278d2e59ce29e5a64f323 Mon Sep 17 00:00:00 2001 From: Tim Fischbach Date: Thu, 10 Sep 2026 07:24:24 +0200 Subject: [PATCH 8/8] Skip Active Storage in dummy app Pageflow does not use Active Storage. Rails 8.1 raises while loading it when the ruby-vips gem is present but libvips is older than 8.13, since it cannot disable libvips' unfuzzed operations in that case. Pageflow depends on ruby-vips for its own webp processor, so dummy app generation aborted on CI test images shipping an older libvips. --- spec/support/pageflow/dummy/app.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/support/pageflow/dummy/app.rb b/spec/support/pageflow/dummy/app.rb index 491878a8c9..4671327493 100644 --- a/spec/support/pageflow/dummy/app.rb +++ b/spec/support/pageflow/dummy/app.rb @@ -41,7 +41,7 @@ def template_path end def rails_new_options - result = '--skip-test-unit --skip-bundle --database=mysql' + result = '--skip-test-unit --skip-bundle --database=mysql --skip-active-storage' result << ' --skip-javascript' result end