From 4a10213984e543da9dd39930e78b31236980c7e8 Mon Sep 17 00:00:00 2001 From: Koji Takao Date: Wed, 21 Jan 2026 22:18:41 +0900 Subject: [PATCH] Add Rails 8.1 compatibility improvements This commit addresses several Rails 8.1 compatibility issues: 1. **CSV gem dependency**: Added 'csv' gem to Gemfile for Rails 8.1+ (removed from standard library). Required by acts_as_resource_controller.rb. 2. **HTTP status code compatibility**: Updated :unprocessable_entity to use conditional :unprocessable_content for Rails 8.0+ to avoid deprecation warnings while maintaining backward compatibility with earlier Rails versions. - Added unprocessable_status helper in JSONAPI::Exceptions::Error - Updated ValidationErrors and SaveFailed exceptions - Updated test assertions in controller_test.rb and test helpers 3. **Database adapter improvements**: Simplified db_true helper in join_manager_test.rb to use ActiveRecord::Base.connection.quoted_true for database-agnostic boolean value handling. These changes maintain full backward compatibility with Rails 6.1, 7.0, and 7.1 while preparing for Rails 8.0+. Based on upstream PR: https://github.com/cerebris/jsonapi-resources/pull/1485 Co-Authored-By: Cassie Simpson --- Gemfile | 1 + lib/jsonapi/exceptions.rb | 16 ++++++++++++++-- test/controllers/controller_test.rb | 18 +++++++++--------- test/helpers/functional_helpers.rb | 10 ++++++++++ test/test_helper.rb | 10 ++++++++++ .../join_manager_test.rb | 14 +------------- 6 files changed, 45 insertions(+), 24 deletions(-) diff --git a/Gemfile b/Gemfile index 215340d0..f052a191 100644 --- a/Gemfile +++ b/Gemfile @@ -28,6 +28,7 @@ when 'master' gem 'arel', { git: 'https://github.com/rails/arel.git' } when 'default' gem 'railties', '>= 6.0' + gem 'csv' # Required by Rails 8.1+ (removed from standard library) else gem 'railties', "~> #{version}" end \ No newline at end of file diff --git a/lib/jsonapi/exceptions.rb b/lib/jsonapi/exceptions.rb index e917118c..7b271501 100644 --- a/lib/jsonapi/exceptions.rb +++ b/lib/jsonapi/exceptions.rb @@ -18,6 +18,18 @@ def errors raise NotImplementedError, "Subclass of Error must implement errors method" # :nocov: end + + private + + # Rails 8.0+ deprecated :unprocessable_entity in favor of :unprocessable_content + # This helper maintains backward compatibility with earlier Rails versions + def unprocessable_status + if Rails::VERSION::MAJOR >= 8 + :unprocessable_content + else + :unprocessable_entity + end + end end class Errors < Error @@ -498,7 +510,7 @@ def errors def json_api_error(attr_key, message) create_error_object(code: JSONAPI::VALIDATION_ERROR, - status: :unprocessable_entity, + status: unprocessable_status, title: message, detail: detail(attr_key, message), source: { pointer: pointer(attr_key) }, @@ -532,7 +544,7 @@ def general_error?(attr_key) class SaveFailed < Error def errors [create_error_object(code: JSONAPI::SAVE_FAILED, - status: :unprocessable_entity, + status: unprocessable_status, title: I18n.translate('jsonapi-resources.exceptions.save_failed.title', default: 'Save failed or was cancelled'), detail: I18n.translate('jsonapi-resources.exceptions.save_failed.detail', diff --git a/test/controllers/controller_test.rb b/test/controllers/controller_test.rb index adf86c77..cbb72916 100644 --- a/test/controllers/controller_test.rb +++ b/test/controllers/controller_test.rb @@ -761,7 +761,7 @@ def test_create_link_to_missing_object } } - assert_response :unprocessable_entity + assert_response unprocessable_status # TODO: check if this validation is working assert_match /author - can't be blank/, response.body assert_nil response.location @@ -864,7 +864,7 @@ def test_create_with_invalid_data } } - assert_response :unprocessable_entity + assert_response unprocessable_status assert_equal "/data/relationships/author", json_response['errors'][0]['source']['pointer'] assert_equal "can't be blank", json_response['errors'][0]['title'] @@ -2019,7 +2019,7 @@ def test_delete_with_validation_error_base assert_equal "can't destroy me", json_response['errors'][0]['title'] assert_equal "/data", json_response['errors'][0]['source']['pointer'] - assert_response :unprocessable_entity + assert_response unprocessable_status end def test_delete_with_validation_error_attr @@ -2028,7 +2028,7 @@ def test_delete_with_validation_error_attr assert_equal "is locked", json_response['errors'][0]['title'] assert_equal "/data/attributes/title", json_response['errors'][0]['source']['pointer'] - assert_response :unprocessable_entity + assert_response unprocessable_status end def test_delete_single @@ -2631,7 +2631,7 @@ def test_create_validations_missing_attribute } } - assert_response :unprocessable_entity + assert_response unprocessable_status assert_equal 2, json_response['errors'].size assert_equal JSONAPI::VALIDATION_ERROR, json_response['errors'][0]['code'] assert_equal JSONAPI::VALIDATION_ERROR, json_response['errors'][1]['code'] @@ -2653,7 +2653,7 @@ def test_update_validations_missing_attribute } } - assert_response :unprocessable_entity + assert_response unprocessable_status assert_equal 1, json_response['errors'].size assert_equal JSONAPI::VALIDATION_ERROR, json_response['errors'][0]['code'] assert_match /name - can't be blank/, response.body @@ -3183,7 +3183,7 @@ def test_create_with_invalid_data } } - assert_response :unprocessable_entity + assert_response unprocessable_status assert_equal "/data/attributes/spouse-name", json_response['errors'][0]['source']['pointer'] assert_equal "can't be blank", json_response['errors'][0]['title'] @@ -3781,7 +3781,7 @@ def test_save_model_callbacks_fail } } - assert_response :unprocessable_entity + assert_response unprocessable_status assert_match /Save failed or was cancelled/, json_response['errors'][0]['detail'] end end @@ -4079,7 +4079,7 @@ def test_delete_with_validation_error_base_on_resource assert_equal "can't destroy me", json_response['errors'][0]['title'] assert_equal "/data/attributes/base", json_response['errors'][0]['source']['pointer'] - assert_response :unprocessable_entity + assert_response unprocessable_status end end diff --git a/test/helpers/functional_helpers.rb b/test/helpers/functional_helpers.rb index 3d6dc9d3..b971a92d 100644 --- a/test/helpers/functional_helpers.rb +++ b/test/helpers/functional_helpers.rb @@ -55,5 +55,15 @@ module FunctionalHelpers def json_response JSON.parse(@response.body) end + + # Rails 8.0+ deprecated :unprocessable_entity in favor of :unprocessable_content + # This helper maintains backward compatibility in tests + def unprocessable_status + if Rails::VERSION::MAJOR >= 8 + :unprocessable_content + else + :unprocessable_entity + end + end end end \ No newline at end of file diff --git a/test/test_helper.rb b/test/test_helper.rb index 191e181b..7a1bb952 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -585,6 +585,16 @@ def assert_jsonapi_response(expected_status, msg = nil) assert_equal expected_status, status, msg end + # Rails 8.0+ deprecated :unprocessable_entity in favor of :unprocessable_content + # This helper maintains backward compatibility in tests + def unprocessable_status + if Rails::VERSION::MAJOR >= 8 + :unprocessable_content + else + :unprocessable_entity + end + end + def assert_jsonapi_get(url, msg = "GET response must be 200") get url, headers: { 'Accept' => JSONAPI::MEDIA_TYPE } assert_jsonapi_response 200, msg diff --git a/test/unit/active_relation_resource_finder/join_manager_test.rb b/test/unit/active_relation_resource_finder/join_manager_test.rb index 19ba1a83..5dfc9629 100644 --- a/test/unit/active_relation_resource_finder/join_manager_test.rb +++ b/test/unit/active_relation_resource_finder/join_manager_test.rb @@ -4,19 +4,7 @@ class JoinTreeTest < ActiveSupport::TestCase def db_true - case ActiveRecord::Base.connection.adapter_name - when 'SQLite' - if Rails::VERSION::MAJOR >= 8 && Rails::VERSION::MINOR >= 1 - # Rails 8.1+ SQLite uses TRUE instead of 1 - "TRUE" - elsif Rails::VERSION::MAJOR >= 6 || (Rails::VERSION::MAJOR >= 5 && ActiveRecord::VERSION::MINOR >= 2) - "1" - else - "'t'" - end - when 'PostgreSQL' - 'TRUE' - end + ActiveRecord::Base.connection.quoted_true end def test_no_added_joins