-
Notifications
You must be signed in to change notification settings - Fork 532
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
Changes from all commits
e67d676
d14cbad
221244b
658f2bb
5bec2e0
7946f83
ed41c8c
93ac314
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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! | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👀 |
||
end | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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}" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👀 |
||
def format_polymorphic_klass_type(klass) | ||
klass.name | ||
end | ||
end | ||
end | ||
end |
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👀