Skip to content

Commit 94e5218

Browse files
committed
Rails 8_1 Compatibility
Fixes #1479 Rails 8.1 changed `ActionDispatch::Routing::Mapper::Resources::Resource#initialize` to require keyword args (only:, except:, **options). The JSONAPI‑Resources DSL still passes a positional options hash, so jsonapi_relationships never runs and relationship routes are missing. Adding the ** operator to `**options` converts the hash into keyword args. Tests that fail without this change: `test/controllers/controller_test.rb` I ran into several other deprecations. * `ActiveSupport::Deprecation.silenced` will not work the way it was set up in the tests because `#silenced` now works on an instance of `Deprecators` instead of globally. I created a module `JSONAPI.deprecator` so that the gem can use its own instance of Deprecator and the tests can work as designed. * `:unprocessable_entity` was deprecated in favor of `:unprocessable_content` * Used lazy method evaluation for `Configuration` because Rails 8.1 does not guarantee class-body initialization order, so @configuration was returning `nil`. * My team uses an active_record oracle adapter. Oracle unquoted identifiers are automatically stored as UPPERCASE. Rails 8.1 no longer normalizes the case automatically for queries, so I added a condition for using the oracle adapter. * `length` is no longer a valid column constraint. Used `limit` instead * `references:` is not a valid option key for ActiveRecord migrations. Excplicitly pass a foreign_key to the things table instead. * `:none` is not a valid active_record schema format in Rails 8.1 * Disabled foreign key constraints for SQLite tests * Use `ActiveRecord::Base.connection.quoted_true` as it is database agnostic There are a few warnings in the test suite that I did not address because they were unrelated to this change.
1 parent e92afc6 commit 94e5218

File tree

14 files changed

+90
-58
lines changed

14 files changed

+90
-58
lines changed

Gemfile

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ version = ENV['RAILS_VERSION'] || 'default'
1111
platforms :ruby do
1212
gem 'pg'
1313

14-
if version.start_with?('4.2', '5.0')
15-
gem 'sqlite3', '~> 1.3.13'
14+
if version.start_with?('8.1')
15+
gem 'sqlite3', '>= 2.1'
1616
else
17-
gem 'sqlite3', '~> 1.4'
17+
gem 'sqlite3', '~> 1.3.13'
1818
end
1919
end
2020

@@ -24,6 +24,7 @@ when 'master'
2424
gem 'arel', { git: 'https://github.com/rails/arel.git' }
2525
when 'default'
2626
gem 'railties', '>= 6.0'
27+
gem 'csv'
2728
else
2829
gem 'railties', "~> #{version}"
29-
end
30+
end

lib/jsonapi-resources.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,4 @@
4343
require 'jsonapi/resource_set'
4444
require 'jsonapi/path'
4545
require 'jsonapi/path_segment'
46+
require 'jsonapi/deprecator'

lib/jsonapi/active_relation_resource.rb

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -847,7 +847,16 @@ def alias_table_field(table, field, quoted = false)
847847
end
848848

849849
def quote(field)
850-
"\"#{field.to_s}\""
850+
if oracle?
851+
"\"#{field.to_s.upcase}\""
852+
else
853+
"\"#{field.to_s}\""
854+
end
855+
end
856+
857+
def oracle?
858+
defined?(ActiveRecord::Base.connection) &&
859+
ActiveRecord::Base.connection.adapter_name =~ /oracle/i
851860
end
852861

853862
def apply_filters(records, filters, options = {})

lib/jsonapi/basic_resource.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -547,7 +547,7 @@ def attribute(attribute_name, options = {})
547547
check_reserved_attribute_name(attr)
548548

549549
if (attr == :id) && (options[:format].nil?)
550-
ActiveSupport::Deprecation.warn('Id without format is no longer supported. Please remove ids from attributes, or specify a format.')
550+
JSONAPI.deprecator.warn('Id without format is no longer supported. Please remove ids from attributes, or specify a format.')
551551
end
552552

553553
check_duplicate_attribute_name(attr) if options[:format].nil?
@@ -609,7 +609,7 @@ def has_one(*attrs)
609609
end
610610

611611
def belongs_to(*attrs)
612-
ActiveSupport::Deprecation.warn "In #{name} you exposed a `has_one` relationship "\
612+
JSONAPI.deprecator.warn"In #{name} you exposed a `has_one` relationship "\
613613
" using the `belongs_to` class method. We think `has_one`" \
614614
" is more appropriate. If you know what you're doing," \
615615
" and don't want to see this warning again, override the" \

lib/jsonapi/configuration.rb

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -241,18 +241,18 @@ def default_processor_klass_name=(default_processor_klass_name)
241241
end
242242

243243
def allow_include=(allow_include)
244-
ActiveSupport::Deprecation.warn('`allow_include` has been replaced by `default_allow_include_to_one` and `default_allow_include_to_many` options.')
244+
JSONAPI.deprecator.warn('`allow_include` has been replaced by `default_allow_include_to_one` and `default_allow_include_to_many` options.')
245245
@default_allow_include_to_one = allow_include
246246
@default_allow_include_to_many = allow_include
247247
end
248248

249249
def whitelist_all_exceptions=(allow_all_exceptions)
250-
ActiveSupport::Deprecation.warn('`whitelist_all_exceptions` has been replaced by `allow_all_exceptions`')
250+
JSONAPI.deprecator.warn('`whitelist_all_exceptions` has been replaced by `allow_all_exceptions`')
251251
@allow_all_exceptions = allow_all_exceptions
252252
end
253253

254254
def exception_class_whitelist=(exception_class_allowlist)
255-
ActiveSupport::Deprecation.warn('`exception_class_whitelist` has been replaced by `exception_class_allowlist`')
255+
JSONAPI.deprecator.warn('`exception_class_whitelist` has been replaced by `exception_class_allowlist`')
256256
@exception_class_allowlist = exception_class_allowlist
257257
end
258258

@@ -315,11 +315,13 @@ def exception_class_whitelist=(exception_class_allowlist)
315315

316316
class << self
317317
attr_accessor :configuration
318-
end
319318

320-
@configuration ||= Configuration.new
319+
def configuration
320+
@configuration ||= Configuration.new
321+
end
322+
end
321323

322324
def self.configure
323-
yield(@configuration)
325+
yield(configuration)
324326
end
325327
end

lib/jsonapi/deprecator.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module JSONAPI
2+
def self.deprecator
3+
@deprecator ||= ActiveSupport::Deprecation.new('1.0', 'jsonapi-resources')
4+
end
5+
end

lib/jsonapi/exceptions.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,7 @@ def errors
498498

499499
def json_api_error(attr_key, message)
500500
create_error_object(code: JSONAPI::VALIDATION_ERROR,
501-
status: :unprocessable_entity,
501+
status: :unprocessable_content,
502502
title: message,
503503
detail: detail(attr_key, message),
504504
source: { pointer: pointer(attr_key) },
@@ -532,7 +532,7 @@ def general_error?(attr_key)
532532
class SaveFailed < Error
533533
def errors
534534
[create_error_object(code: JSONAPI::SAVE_FAILED,
535-
status: :unprocessable_entity,
535+
status: :unprocessable_content,
536536
title: I18n.translate('jsonapi-resources.exceptions.save_failed.title',
537537
default: 'Save failed or was cancelled'),
538538
detail: I18n.translate('jsonapi-resources.exceptions.save_failed.detail',

lib/jsonapi/routing_ext.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ def jsonapi_resource(*resources, &_block)
4646
options[:except] << :destroy unless options[:except].include?(:destroy) || options[:except].include?('destroy')
4747
end
4848

49-
resource @resource_type, options do
49+
resource @resource_type, **options do
5050
# :nocov:
5151
if @scope.respond_to? :[]=
5252
# Rails 4
@@ -59,7 +59,7 @@ def jsonapi_resource(*resources, &_block)
5959
end
6060
else
6161
# Rails 5
62-
jsonapi_resource_scope(SingletonResource.new(@resource_type, api_only?, @scope[:shallow], options), @resource_type) do
62+
jsonapi_resource_scope(SingletonResource.new(@resource_type, api_only?, @scope[:shallow], **options), @resource_type) do
6363
if block_given?
6464
yield
6565
else
@@ -121,7 +121,7 @@ def jsonapi_resources(*resources, &_block)
121121
options[:except] << :destroy unless options[:except].include?(:destroy) || options[:except].include?('destroy')
122122
end
123123

124-
resources @resource_type, options do
124+
resources @resource_type, **options do
125125
# :nocov:
126126
if @scope.respond_to? :[]=
127127
# Rails 4
@@ -133,7 +133,7 @@ def jsonapi_resources(*resources, &_block)
133133
end
134134
else
135135
# Rails 5
136-
jsonapi_resource_scope(Resource.new(@resource_type, api_only?, @scope[:shallow], options), @resource_type) do
136+
jsonapi_resource_scope(Resource.new(@resource_type, api_only?, @scope[:shallow], **options), @resource_type) do
137137
if block_given?
138138
yield
139139
else

test/controllers/controller_test.rb

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -761,7 +761,7 @@ def test_create_link_to_missing_object
761761
}
762762
}
763763

764-
assert_response :unprocessable_entity
764+
assert_response :unprocessable_content
765765
# TODO: check if this validation is working
766766
assert_match /author - can't be blank/, response.body
767767
assert_nil response.location
@@ -864,7 +864,7 @@ def test_create_with_invalid_data
864864
}
865865
}
866866

867-
assert_response :unprocessable_entity
867+
assert_response :unprocessable_content
868868

869869
assert_equal "/data/relationships/author", json_response['errors'][0]['source']['pointer']
870870
assert_equal "can't be blank", json_response['errors'][0]['title']
@@ -2019,7 +2019,7 @@ def test_delete_with_validation_error_base
20192019

20202020
assert_equal "can't destroy me", json_response['errors'][0]['title']
20212021
assert_equal "/data", json_response['errors'][0]['source']['pointer']
2022-
assert_response :unprocessable_entity
2022+
assert_response :unprocessable_content
20232023
end
20242024

20252025
def test_delete_with_validation_error_attr
@@ -2028,7 +2028,7 @@ def test_delete_with_validation_error_attr
20282028

20292029
assert_equal "is locked", json_response['errors'][0]['title']
20302030
assert_equal "/data/attributes/title", json_response['errors'][0]['source']['pointer']
2031-
assert_response :unprocessable_entity
2031+
assert_response :unprocessable_content
20322032
end
20332033

20342034
def test_delete_single
@@ -2631,7 +2631,7 @@ def test_create_validations_missing_attribute
26312631
}
26322632
}
26332633

2634-
assert_response :unprocessable_entity
2634+
assert_response :unprocessable_content
26352635
assert_equal 2, json_response['errors'].size
26362636
assert_equal JSONAPI::VALIDATION_ERROR, json_response['errors'][0]['code']
26372637
assert_equal JSONAPI::VALIDATION_ERROR, json_response['errors'][1]['code']
@@ -2653,7 +2653,7 @@ def test_update_validations_missing_attribute
26532653
}
26542654
}
26552655

2656-
assert_response :unprocessable_entity
2656+
assert_response :unprocessable_content
26572657
assert_equal 1, json_response['errors'].size
26582658
assert_equal JSONAPI::VALIDATION_ERROR, json_response['errors'][0]['code']
26592659
assert_match /name - can't be blank/, response.body
@@ -3183,7 +3183,7 @@ def test_create_with_invalid_data
31833183
}
31843184
}
31853185

3186-
assert_response :unprocessable_entity
3186+
assert_response :unprocessable_content
31873187

31883188
assert_equal "/data/attributes/spouse-name", json_response['errors'][0]['source']['pointer']
31893189
assert_equal "can't be blank", json_response['errors'][0]['title']
@@ -3779,7 +3779,7 @@ def test_save_model_callbacks_fail
37793779
}
37803780
}
37813781

3782-
assert_response :unprocessable_entity
3782+
assert_response :unprocessable_content
37833783
assert_match /Save failed or was cancelled/, json_response['errors'][0]['detail']
37843784
end
37853785
end
@@ -4077,7 +4077,7 @@ def test_delete_with_validation_error_base_on_resource
40774077

40784078
assert_equal "can't destroy me", json_response['errors'][0]['title']
40794079
assert_equal "/data/attributes/base", json_response['errors'][0]['source']['pointer']
4080-
assert_response :unprocessable_entity
4080+
assert_response :unprocessable_content
40814081
end
40824082
end
40834083

test/fixtures/active_record.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
end
5353

5454
create_table :posts, force: true do |t|
55-
t.string :title, length: 255
55+
t.string :title, limit: 255
5656
t.text :body
5757
t.integer :author_id
5858
t.integer :parent_post_id
@@ -324,8 +324,8 @@
324324

325325
create_table :related_things, force: true do |t|
326326
t.string :name
327-
t.references :from, references: :thing
328-
t.references :to, references: :thing
327+
t.references :from, foreign_key: { to_table: :things }
328+
t.references :to, foreign_key: { to_table: :things }
329329

330330
t.timestamps null: false
331331
end

0 commit comments

Comments
 (0)