Skip to content

Commit ae9017b

Browse files
authored
fix: more flexible polymorphic types lookup (#1434)
* fix: more flexible polymorphic types lookup * test: add polymorphic lookup tests they pass on v-11-dev I'm going to look into the existing lookup warnings now ``` [POLYMORPHIC TYPE NOT FOUND] No polymorphic types found for fileable [POLYMORPHIC TYPE] No polymorphic types found for FilePropertiesResource fileable [POLYMORPHIC TYPE NOT FOUND] No polymorphic types found for respondent [POLYMORPHIC TYPE] No polymorphic types found for QuestionResource respondent [POLYMORPHIC TYPE NOT FOUND] No polymorphic types found for respondent [POLYMORPHIC TYPE] No polymorphic types found for AnswerResource respondent [POLYMORPHIC TYPE NOT FOUND] No polymorphic types found for keepable [POLYMORPHIC TYPE] No polymorphic types found for KeeperResource keepable ``` * Revert "test: add polymorphic lookup tests" This reverts commit 0979a7243b6bc816dd2327d3ff23f70209c52dce. * feat: easily clear the lookup * feat: add a descendents strategy * test: polymorphic type lookup * feat: make polymorphic type lookup configurable * feat: clear polymorphic lookup after initialize
1 parent e4c9707 commit ae9017b

File tree

4 files changed

+102
-10
lines changed

4 files changed

+102
-10
lines changed

lib/jsonapi/relationship.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ def polymorphic_type
193193
def setup_implicit_relationships_for_polymorphic_types(exclude_linkage_data: true)
194194
types = self.class.polymorphic_types(_relation_name)
195195
unless types.present?
196-
warn "No polymorphic types found for #{parent_resource.name} #{_relation_name}"
196+
warn "[POLYMORPHIC TYPE] No polymorphic types found for #{parent_resource.name} #{_relation_name}"
197197
return
198198
end
199199

lib/jsonapi/resources/railtie.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ class Railtie < ::Rails::Railtie
1818
::JSONAPI::MimeTypes.parser.call(body)
1919
}
2020
end
21+
22+
initializer "jsonapi_resources.initialize", after: :initialize do
23+
JSONAPI::Utils::PolymorphicTypesLookup.polymorphic_types_lookup_clear!
24+
end
2125
end
2226
end
2327
end

lib/jsonapi/utils/polymorphic_types_lookup.rb

Lines changed: 62 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,79 @@ module Utils
55
module PolymorphicTypesLookup
66
extend self
77

8-
def polymorphic_types(name)
9-
polymorphic_types_lookup[name.to_sym]
8+
singleton_class.attr_accessor :build_polymorphic_types_lookup_strategy
9+
self.build_polymorphic_types_lookup_strategy =
10+
:build_polymorphic_types_lookup_from_object_space
11+
12+
def polymorphic_types(name, rebuild: false)
13+
polymorphic_types_lookup(rebuild: rebuild).fetch(name.to_sym, &handle_polymorphic_type_name_found)
14+
end
15+
16+
def handle_polymorphic_type_name_found
17+
@handle_polymorphic_type_name_found ||= lambda do |name|
18+
warn "[POLYMORPHIC TYPE NOT FOUND] No polymorphic types found for #{name}"
19+
nil
20+
end
1021
end
1122

12-
def polymorphic_types_lookup
23+
def polymorphic_types_lookup(rebuild: false)
24+
polymorphic_types_lookup_clear! if rebuild
1325
@polymorphic_types_lookup ||= build_polymorphic_types_lookup
1426
end
1527

28+
def polymorphic_types_lookup_clear!
29+
@polymorphic_types_lookup = nil
30+
end
31+
1632
def build_polymorphic_types_lookup
17-
{}.tap do |hash|
33+
public_send(build_polymorphic_types_lookup_strategy)
34+
end
35+
36+
def build_polymorphic_types_lookup_from_descendants
37+
{}.tap do |lookup|
38+
ActiveRecord::Base
39+
.descendants
40+
.select(&:name)
41+
.reject(&:abstract_class)
42+
.select(&:model_name).map {|klass|
43+
add_polymorphic_types_lookup(klass: klass, lookup: lookup)
44+
}
45+
end
46+
end
47+
48+
def build_polymorphic_types_lookup_from_object_space
49+
{}.tap do |lookup|
1850
ObjectSpace.each_object do |klass|
1951
next unless Module === klass
20-
if ActiveRecord::Base > klass
21-
klass.reflect_on_all_associations(:has_many).select { |r| r.options[:as] }.each do |reflection|
22-
(hash[reflection.options[:as]] ||= []) << klass.name.underscore
23-
end
24-
end
52+
next unless ActiveRecord::Base > klass
53+
add_polymorphic_types_lookup(klass: klass, lookup: lookup)
2554
end
2655
end
2756
end
57+
58+
# TODO(BF): Consider adding the following conditions
59+
# is_active_record_inspectable = true
60+
# is_active_record_inspectable &&= klass.respond_to?(:reflect_on_all_associations, true)
61+
# is_active_record_inspectable &&= format_polymorphic_klass_type(klass).present?
62+
# return unless is_active_record_inspectable
63+
def add_polymorphic_types_lookup(klass:, lookup:)
64+
klass.reflect_on_all_associations(:has_many).select { |r| r.options[:as] }.each do |reflection|
65+
(lookup[reflection.options[:as]] ||= []) << format_polymorphic_klass_type(klass).underscore
66+
end
67+
end
68+
69+
# TODO(BF): Consider adding the following conditions
70+
# klass.name ||
71+
# begin
72+
# klass.model_name.name
73+
# rescue ArgumentError => ex
74+
# # klass.base_class may be nil
75+
# warn "[POLYMORPHIC TYPE] #{__callee__} #{klass} #{ex.inspect}"
76+
# nil
77+
# end
78+
def format_polymorphic_klass_type(klass)
79+
klass.name
80+
end
2881
end
2982
end
3083
end
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
require File.expand_path('../../../test_helper', __FILE__)
2+
3+
class PolymorphicTypesLookupTest < ActiveSupport::TestCase
4+
def setup
5+
JSONAPI::Utils::PolymorphicTypesLookup.polymorphic_types_lookup_clear!
6+
end
7+
8+
def test_build_polymorphic_types_lookup_from_object_space
9+
expected = {
10+
:imageable=>["product", "document"]
11+
}
12+
actual = JSONAPI::Utils::PolymorphicTypesLookup.build_polymorphic_types_lookup_from_object_space
13+
actual_keys = actual.keys.sort
14+
assert_equal(actual_keys, expected.keys.sort)
15+
actual_keys.each do |actual_key|
16+
actual_values = actual[actual_key].sort
17+
expected_values = expected[actual_key].sort
18+
assert_equal(actual_values, expected_values)
19+
end
20+
end
21+
22+
def test_build_polymorphic_types_lookup_from_descendants
23+
expected = {
24+
:imageable=>["document", "product"]
25+
}
26+
actual = JSONAPI::Utils::PolymorphicTypesLookup.build_polymorphic_types_lookup_from_descendants
27+
actual_keys = actual.keys.sort
28+
assert_equal(actual_keys, expected.keys.sort)
29+
actual_keys.each do |actual_key|
30+
actual_values = actual[actual_key].sort
31+
expected_values = expected[actual_key].sort
32+
assert_equal(actual_values, expected_values)
33+
end
34+
end
35+
end

0 commit comments

Comments
 (0)