Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: more flexible polymorphic types lookup #1434

Merged
merged 8 commits into from
Jan 25, 2024
Merged
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
2 changes: 1 addition & 1 deletion lib/jsonapi/relationship.rb
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ def polymorphic_type
def setup_implicit_relationships_for_polymorphic_types(exclude_linkage_data: true)
types = self.class.polymorphic_types(_relation_name)
unless types.present?
warn "No polymorphic types found for #{parent_resource.name} #{_relation_name}"
warn "[POLYMORPHIC TYPE] No polymorphic types found for #{parent_resource.name} #{_relation_name}"
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

👀

return
end

Expand Down
4 changes: 4 additions & 0 deletions lib/jsonapi/resources/railtie.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ class Railtie < ::Rails::Railtie
::JSONAPI::MimeTypes.parser.call(body)
}
end

initializer "jsonapi_resources.initialize", after: :initialize do
JSONAPI::Utils::PolymorphicTypesLookup.polymorphic_types_lookup_clear!
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

👀

end
end
end
end
71 changes: 62 additions & 9 deletions lib/jsonapi/utils/polymorphic_types_lookup.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,79 @@ module Utils
module PolymorphicTypesLookup
extend self

def polymorphic_types(name)
polymorphic_types_lookup[name.to_sym]
singleton_class.attr_accessor :build_polymorphic_types_lookup_strategy
self.build_polymorphic_types_lookup_strategy =
:build_polymorphic_types_lookup_from_object_space
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

👀


def polymorphic_types(name, rebuild: false)
polymorphic_types_lookup(rebuild: rebuild).fetch(name.to_sym, &handle_polymorphic_type_name_found)
end

def handle_polymorphic_type_name_found
@handle_polymorphic_type_name_found ||= lambda do |name|
warn "[POLYMORPHIC TYPE NOT FOUND] No polymorphic types found for #{name}"
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

👀

nil
end
end

def polymorphic_types_lookup
def polymorphic_types_lookup(rebuild: false)
polymorphic_types_lookup_clear! if rebuild
@polymorphic_types_lookup ||= build_polymorphic_types_lookup
end

def polymorphic_types_lookup_clear!
@polymorphic_types_lookup = nil
end

def build_polymorphic_types_lookup
{}.tap do |hash|
public_send(build_polymorphic_types_lookup_strategy)
end

def build_polymorphic_types_lookup_from_descendants
{}.tap do |lookup|
ActiveRecord::Base
.descendants
.select(&:name)
.reject(&:abstract_class)
.select(&:model_name).map {|klass|
add_polymorphic_types_lookup(klass: klass, lookup: lookup)
}
end
end

def build_polymorphic_types_lookup_from_object_space
{}.tap do |lookup|
ObjectSpace.each_object do |klass|
next unless Module === klass
if ActiveRecord::Base > klass
klass.reflect_on_all_associations(:has_many).select { |r| r.options[:as] }.each do |reflection|
(hash[reflection.options[:as]] ||= []) << klass.name.underscore
end
end
next unless ActiveRecord::Base > klass
add_polymorphic_types_lookup(klass: klass, lookup: lookup)
end
end
end

# TODO(BF): Consider adding the following conditions
# is_active_record_inspectable = true
# is_active_record_inspectable &&= klass.respond_to?(:reflect_on_all_associations, true)
# is_active_record_inspectable &&= format_polymorphic_klass_type(klass).present?
# return unless is_active_record_inspectable
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

👀

def add_polymorphic_types_lookup(klass:, lookup:)
klass.reflect_on_all_associations(:has_many).select { |r| r.options[:as] }.each do |reflection|
(lookup[reflection.options[:as]] ||= []) << format_polymorphic_klass_type(klass).underscore
end
end

# TODO(BF): Consider adding the following conditions
# klass.name ||
# begin
# klass.model_name.name
# rescue ArgumentError => ex
# # klass.base_class may be nil
# warn "[POLYMORPHIC TYPE] #{__callee__} #{klass} #{ex.inspect}"
# nil
# end
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

👀

def format_polymorphic_klass_type(klass)
klass.name
end
end
end
end
35 changes: 35 additions & 0 deletions test/unit/utils/polymorphic_types_lookup_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
require File.expand_path('../../../test_helper', __FILE__)

class PolymorphicTypesLookupTest < ActiveSupport::TestCase
def setup
JSONAPI::Utils::PolymorphicTypesLookup.polymorphic_types_lookup_clear!
end

def test_build_polymorphic_types_lookup_from_object_space
expected = {
:imageable=>["product", "document"]
}
actual = JSONAPI::Utils::PolymorphicTypesLookup.build_polymorphic_types_lookup_from_object_space
actual_keys = actual.keys.sort
assert_equal(actual_keys, expected.keys.sort)
actual_keys.each do |actual_key|
actual_values = actual[actual_key].sort
expected_values = expected[actual_key].sort
assert_equal(actual_values, expected_values)
end
end

def test_build_polymorphic_types_lookup_from_descendants
expected = {
:imageable=>["document", "product"]
}
actual = JSONAPI::Utils::PolymorphicTypesLookup.build_polymorphic_types_lookup_from_descendants
actual_keys = actual.keys.sort
assert_equal(actual_keys, expected.keys.sort)
actual_keys.each do |actual_key|
actual_values = actual[actual_key].sort
expected_values = expected[actual_key].sort
assert_equal(actual_values, expected_values)
end
end
end
Loading