Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 61 additions & 3 deletions .github/workflows/ruby.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,36 +31,94 @@ jobs:
- '3.0'
- 3.1
- 3.2
- 3.3
- 3.4
rails:
- 7.0.4
- 6.1.7
- 8.1.2
- 8.0.4
- 7.2.3
- 7.1.6
- 7.0.10
- 6.1.7.10
- 6.0.6
- 5.2.8.1
- 5.1.7
database_url:
- postgresql://postgres:password@localhost:5432/test
- sqlite3:test_db
exclude:
# Ruby 3.4 exclusions (Rails 7.2+)
- ruby: 3.4
rails: 7.1.6
- ruby: 3.4
rails: 7.0.10
- ruby: 3.4
rails: 6.1.7.10
- ruby: 3.4
rails: 6.0.6
- ruby: 3.4
rails: 5.2.8.1
- ruby: 3.4
rails: 5.1.7
# Ruby 3.3 exclusions (Rails 6.1+)
- ruby: 3.3
rails: 6.0.6
- ruby: 3.3
rails: 5.2.8.1
- ruby: 3.3
rails: 5.1.7
# Ruby 3.2 exclusions (Rails 7.0+)
- ruby: 3.2
rails: 6.0.6
- ruby: 3.2
rails: 5.2.8.1
- ruby: 3.2
rails: 5.1.7
# Ruby 3.1 exclusions (Rails 6.1+)
- ruby: 3.1
rails: 8.1.2
- ruby: 3.1
rails: 8.0.4
- ruby: 3.1
rails: 6.0.6
- ruby: 3.1
rails: 5.2.8.1
- ruby: 3.1
rails: 5.1.7
# Ruby 3.0 exclusions (Rails 6.1+)
- ruby: '3.0'
rails: 8.1.2
- ruby: '3.0'
rails: 8.0.4
- ruby: '3.0'
rails: 7.2.3
- ruby: '3.0'
rails: 6.0.6
- ruby: '3.0'
rails: 5.2.8.1
- ruby: '3.0'
rails: 5.1.7
# Ruby 2.7 exclusions (Rails 5.1-7.0)
- ruby: 2.7
rails: 8.1.2
- ruby: 2.7
rails: 8.0.4
- ruby: 2.7
rails: 7.2.3
- ruby: 2.7
rails: 7.1.6
# Ruby 2.6 exclusions (Rails 5.1-6.1)
- ruby: 2.6
rails: 8.1.2
- ruby: 2.6
rails: 8.0.4
- ruby: 2.6
rails: 7.2.3
- ruby: 2.6
rails: 7.1.6
- ruby: 2.6
rails: 7.0.4
rails: 7.0.10
# PostgreSQL not supported on Rails 5.1.7
- database_url: postgresql://postgres:password@localhost:5432/test
rails: 5.1.7
env:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ test/version_tmp
tmp
coverage
test/log
log/*.log
test_db
test_db-journal
.idea
Expand Down
3 changes: 3 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ platforms :ruby do

if version.start_with?('4.2', '5.0')
gem 'sqlite3', '~> 1.3.13'
elsif version.start_with?('8.')
# Rails 8.0+ requires sqlite3 >= 2.1
gem 'sqlite3', '>= 2.1'
else
gem 'sqlite3', '~> 1.4'
end
Expand Down
1 change: 1 addition & 0 deletions jsonapi-resources.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,5 @@ Gem::Specification.new do |spec|
spec.add_dependency 'activerecord', '>= 5.1'
spec.add_dependency 'railties', '>= 5.1'
spec.add_dependency 'concurrent-ruby'
spec.add_dependency 'csv' # Required for Ruby 3.4+ (no longer a default gem)
end
8 changes: 4 additions & 4 deletions lib/jsonapi/acts_as_resource_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -63,16 +63,16 @@ def index_related_resources

def get_related_resource
# :nocov:
ActiveSupport::Deprecation.warn "In #{self.class.name} you exposed a `get_related_resource`"\
" action. Please use `show_related_resource` instead."
JSONAPI.warn_deprecated "In #{self.class.name} you exposed a `get_related_resource`"\
" action. Please use `show_related_resource` instead."
show_related_resource
# :nocov:
end

def get_related_resources
# :nocov:
ActiveSupport::Deprecation.warn "In #{self.class.name} you exposed a `get_related_resources`"\
" action. Please use `index_related_resources` instead."
JSONAPI.warn_deprecated "In #{self.class.name} you exposed a `get_related_resources`"\
" action. Please use `index_related_resources` instead."
index_related_resources
# :nocov:
end
Expand Down
12 changes: 6 additions & 6 deletions lib/jsonapi/basic_resource.rb
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,7 @@ def attribute(attribute_name, options = {})
check_reserved_attribute_name(attr)

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

check_duplicate_attribute_name(attr) if options[:format].nil?
Expand Down Expand Up @@ -609,11 +609,11 @@ def has_one(*attrs)
end

def belongs_to(*attrs)
ActiveSupport::Deprecation.warn "In #{name} you exposed a `has_one` relationship "\
" using the `belongs_to` class method. We think `has_one`" \
" is more appropriate. If you know what you're doing," \
" and don't want to see this warning again, override the" \
" `belongs_to` class method on your resource."
JSONAPI.warn_deprecated "In #{name} you exposed a `has_one` relationship "\
" using the `belongs_to` class method. We think `has_one`" \
" is more appropriate. If you know what you're doing," \
" and don't want to see this warning again, override the" \
" `belongs_to` class method on your resource."
_add_relationship(Relationship::ToOne, *attrs)
end

Expand Down
32 changes: 24 additions & 8 deletions lib/jsonapi/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ def exception_class_allowed?(e)
end

def default_processor_klass=(default_processor_klass)
ActiveSupport::Deprecation.warn('`default_processor_klass` has been replaced by `default_processor_klass_name`.')
JSONAPI.warn_deprecated('`default_processor_klass` has been replaced by `default_processor_klass_name`.')
@default_processor_klass = default_processor_klass
end

Expand All @@ -241,18 +241,18 @@ def default_processor_klass_name=(default_processor_klass_name)
end

def allow_include=(allow_include)
ActiveSupport::Deprecation.warn('`allow_include` has been replaced by `default_allow_include_to_one` and `default_allow_include_to_many` options.')
JSONAPI.warn_deprecated('`allow_include` has been replaced by `default_allow_include_to_one` and `default_allow_include_to_many` options.')
@default_allow_include_to_one = allow_include
@default_allow_include_to_many = allow_include
end

def whitelist_all_exceptions=(allow_all_exceptions)
ActiveSupport::Deprecation.warn('`whitelist_all_exceptions` has been replaced by `allow_all_exceptions`')
JSONAPI.warn_deprecated('`whitelist_all_exceptions` has been replaced by `allow_all_exceptions`')
@allow_all_exceptions = allow_all_exceptions
end

def exception_class_whitelist=(exception_class_allowlist)
ActiveSupport::Deprecation.warn('`exception_class_whitelist` has been replaced by `exception_class_allowlist`')
JSONAPI.warn_deprecated('`exception_class_whitelist` has been replaced by `exception_class_allowlist`')
@exception_class_allowlist = exception_class_allowlist
end

Expand Down Expand Up @@ -314,12 +314,28 @@ def exception_class_whitelist=(exception_class_allowlist)
end

class << self
attr_accessor :configuration
end
attr_writer :configuration

@configuration ||= Configuration.new
Copy link
Collaborator

@bf4 bf4 Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's revert this change

def configuration
@configuration ||= Configuration.new
end
end

def self.configure
yield(@configuration)
yield(configuration)
end

# Rails 7.2+ made ActiveSupport::Deprecation.warn a private method
# This helper provides backward-compatible deprecation warnings
def self.warn_deprecated(message)
if defined?(ActiveSupport::Deprecation) && ActiveSupport::Deprecation.respond_to?(:warn)
# Rails < 7.2
ActiveSupport::Deprecation.warn(message)
else
# Rails 7.2+ or fallback - use standard warning with deprecation formatting
# Rails 7.2 doesn't provide a public API for custom deprecation warnings
# So we use Kernel#warn with a deprecation prefix
warn "[DEPRECATION] #{message}"
end
end
end
24 changes: 22 additions & 2 deletions lib/jsonapi/error.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,26 @@ module JSONAPI
class Error
attr_accessor :title, :detail, :id, :href, :code, :source, :links, :status, :meta

# Rack 3.0+ deprecated :unprocessable_entity in favor of :unprocessable_content
# This mapping ensures compatibility across Rack versions
DEPRECATED_STATUS_SYMBOLS = {
unprocessable_entity: :unprocessable_content
}.freeze

def self.status_code_for(status_symbol)
return nil if status_symbol.nil?

# Try the symbol directly first
code = Rack::Utils::SYMBOL_TO_STATUS_CODE[status_symbol]

# If not found and it's a deprecated symbol, try the new symbol
if code.nil? && DEPRECATED_STATUS_SYMBOLS.key?(status_symbol)
code = Rack::Utils::SYMBOL_TO_STATUS_CODE[DEPRECATED_STATUS_SYMBOLS[status_symbol]]
end

code&.to_s
end

def initialize(options = {})
@title = options[:title]
@detail = options[:detail]
Expand All @@ -17,7 +37,7 @@ def initialize(options = {})
@source = options[:source]
@links = options[:links]

@status = Rack::Utils::SYMBOL_TO_STATUS_CODE[options[:status]].to_s
@status = self.class.status_code_for(options[:status])
@meta = options[:meta]
end

Expand Down Expand Up @@ -48,7 +68,7 @@ def update_with_overrides(error_object_overrides)

if error_object_overrides[:status]
# :nocov:
@status = Rack::Utils::SYMBOL_TO_STATUS_CODE[error_object_overrides[:status]].to_s
@status = self.class.status_code_for(error_object_overrides[:status])
# :nocov:
end
@meta = error_object_overrides[:meta] || @meta
Expand Down
2 changes: 1 addition & 1 deletion lib/jsonapi/relationship.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def initialize(name, options = {})
@polymorphic = options.fetch(:polymorphic, false) == true
@polymorphic_types = options[:polymorphic_types]
if options[:polymorphic_relations]
ActiveSupport::Deprecation.warn('Use polymorphic_types instead of polymorphic_relations')
JSONAPI.warn_deprecated('Use polymorphic_types instead of polymorphic_relations')
@polymorphic_types ||= options[:polymorphic_relations]
end

Expand Down
38 changes: 31 additions & 7 deletions lib/jsonapi/routing_ext.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,18 +48,30 @@ def jsonapi_resource(*resources, &_block)

resource @resource_type, options do
# :nocov:
if @scope.respond_to? :[]=
if @scope.respond_to?(:[]=)
# Rails 4
@scope[:jsonapi_resource] = @resource_type

if block_given?
yield
else
jsonapi_relationships
end
elsif Rails::VERSION::MAJOR >= 8 && Rails::VERSION::MINOR >= 1
# Rails 8.1+
# Rails 8.1 changed Scope to not support []= and Resource.new signature
# Use instance variable to track resource type
@jsonapi_resource_type = @resource_type
if block_given?
yield
else
jsonapi_relationships
end
else
# Rails 5
jsonapi_resource_scope(SingletonResource.new(@resource_type, api_only?, @scope[:shallow], options), @resource_type) do
# Rails 5-8.0
resource_arg = SingletonResource.new(@resource_type, api_only?, @scope[:shallow], options)

jsonapi_resource_scope(resource_arg, @resource_type) do
if block_given?
yield
else
Expand Down Expand Up @@ -123,17 +135,29 @@ def jsonapi_resources(*resources, &_block)

resources @resource_type, options do
# :nocov:
if @scope.respond_to? :[]=
if @scope.respond_to?(:[]=)
# Rails 4
@scope[:jsonapi_resource] = @resource_type
if block_given?
yield
else
jsonapi_relationships
end
elsif Rails::VERSION::MAJOR >= 8 && Rails::VERSION::MINOR >= 1
# Rails 8.1+
# Rails 8.1 changed Scope to not support []= and Resource.new signature
# Use instance variable to track resource type
@jsonapi_resource_type = @resource_type
if block_given?
yield
else
jsonapi_relationships
end
else
# Rails 5
jsonapi_resource_scope(Resource.new(@resource_type, api_only?, @scope[:shallow], options), @resource_type) do
# Rails 5-8.0
resource_arg = Resource.new(@resource_type, api_only?, @scope[:shallow], options)

jsonapi_resource_scope(resource_arg, @resource_type) do
if block_given?
yield
else
Expand Down Expand Up @@ -277,7 +301,7 @@ def jsonapi_resource_scope(resource, resource_type) #:nodoc:
private

def resource_type_with_module_prefix(resource = nil)
resource_name = resource || @scope[:jsonapi_resource]
resource_name = resource || @scope[:jsonapi_resource] || @jsonapi_resource_type
[@scope[:module], resource_name].compact.collect(&:to_s).join('/')
end
end
Expand Down
6 changes: 4 additions & 2 deletions test/controllers/controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3682,14 +3682,16 @@ def test_warn_on_joined_to_many
get :index, params: {fields: {posts: 'id,title'}}
assert_response :success
end
assert_equal(err, "Performance issue detected: `Api::V4::PostResource.records` returned non-normalized results in `Api::V4::PostResource.find_fragments`.\n")
assert_match(/Performance issue detected: `Api::V4::PostResource.records` returned non-normalized results in `Api::V4::PostResource.find_fragments`\./, err)

JSONAPI.configuration.warn_on_performance_issues = false
_out, err = capture_subprocess_io do
get :index, params: {fields: {posts: 'id,title'}}
assert_response :success
end
assert_empty err
# On older Ruby/Rails combinations, there may be deprecation warnings
# but we should not see the performance issue warning
refute_match(/Performance issue detected/, err)

ensure
JSONAPI.configuration = original_config
Expand Down
Loading