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

feat: handle polymorphism support #501

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
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
12 changes: 6 additions & 6 deletions app/controllers/forest_liana/associations_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def index

respond_to do |format|
format.json { render_jsonapi(getter) }
format.csv { render_csv(getter, @association.klass) }
format.csv { render_csv(getter, SchemaUtils.association_ref(@association)) }
end
rescue => error
FOREST_LOGGER.error "Association Index error: #{error}\n#{format_stacktrace(error)}"
Expand Down Expand Up @@ -104,21 +104,21 @@ def resource_params
end

def is_sti_model?
@is_sti_model ||= (@association.klass.inheritance_column.present? &&
@association.klass.columns.any? { |column| column.name == @association.klass.inheritance_column })
@is_sti_model ||= (SchemaUtils.association_ref(@association).inheritance_column.present? &&
SchemaUtils.association_ref(@association).columns.any? { |column| column.name == SchemaUtils.association_ref(@association).inheritance_column })
end

def get_record record
is_sti_model? ? record.becomes(@association.klass) : record
is_sti_model? ? record.becomes(SchemaUtils.association_ref(@association)) : record
end

def render_jsonapi getter
fields_to_serialize = fields_per_model(params[:fields], @association.klass)
fields_to_serialize = fields_per_model(params[:fields], SchemaUtils.association_ref(@association))
records = getter.records.map { |record| get_record(record) }

includes = getter.includes_for_serialization
if fields_to_serialize && includes.length > 0
association_name = ForestLiana.name_for(@association.klass)
association_name = ForestLiana.name_for(SchemaUtils.association_ref(@association))
fields_to_serialize[association_name] += ",#{includes.join(',')}"
end

Expand Down
4 changes: 2 additions & 2 deletions app/deserializers/forest_liana/resource_deserializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,13 @@ def extract_relationships
#if data.blank?
#@attributes[name] = nil
#else
#@attributes[name] = association.klass.find(data[:id])
#@attributes[name] = SchemaUtils.association_ref(association).find(data[:id])
#end

# ActionController::Parameters do not inherit from Hash anymore
# since Rails 5.
if (data.is_a?(Hash) || data.is_a?(ActionController::Parameters)) && data[:id]
@attributes[name] = association.klass.find(data[:id])
@attributes[name] = SchemaUtils.association_ref(association).find(data[:id])
elsif data.blank?
@attributes[name] = nil
end
Expand Down
2 changes: 1 addition & 1 deletion app/helpers/forest_liana/query_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ module ForestLiana
module QueryHelper
def self.get_one_associations(resource)
SchemaUtils.one_associations(resource)
.select { |association| SchemaUtils.model_included?(association.klass) }
.select { |association| !SchemaUtils.polymorphic?(association) && SchemaUtils.model_included?(association.klass) }
end

def self.get_one_association_names_symbol(resource)
Expand Down
2 changes: 1 addition & 1 deletion app/serializers/forest_liana/serializer_factory.rb
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ def mixpanel_integration?

SchemaUtils.associations(active_record_class).each do |a|
begin
if SchemaUtils.model_included?(a.klass)
if SchemaUtils.model_included?(SchemaUtils.association_ref(a))
serializer.send(serializer_association(a), a.name) {
if [:has_one, :belongs_to].include?(a.macro)
begin
Expand Down
2 changes: 1 addition & 1 deletion app/services/forest_liana/belongs_to_updater.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def initialize(resource, association, params)
def perform
begin
@record = @resource.find(@params[:id])
new_value = @association.klass.find(@data[:id]) if @data && @data[:id]
new_value = SchemaUtils.association_ref(@association).find(@data[:id]) if @data && @data[:id]
@record.send("#{@association.name}=", new_value)

@record.save
Expand Down
2 changes: 1 addition & 1 deletion app/services/forest_liana/has_many_associator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def perform

if @data.is_a?(Array)
@data.each do |record_added|
associated_records << @association.klass.find(record_added[:id])
associated_records << SchemaUtils.association_ref(@association).find(record_added[:id])
end
end
end
Expand Down
6 changes: 3 additions & 3 deletions app/services/forest_liana/has_many_dissociator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ def perform
if !record_ids.nil? && record_ids.any?
if remove_association
record_ids.each do |id|
associated_records.delete(@association.klass.find(id))
associated_records.delete(SchemaUtils.association_ref(@association).find(id))
end
end

if @with_deletion
record_ids = record_ids.select { |record_id| @association.klass.exists?(record_id) }
@association.klass.destroy(record_ids)
record_ids = record_ids.select { |record_id| SchemaUtils.association_ref(@association).exists?(record_id) }
SchemaUtils.association_ref(@association).destroy(record_ids)
end
end
end
Expand Down
4 changes: 2 additions & 2 deletions app/services/forest_liana/has_many_getter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ def records
private

def compute_includes
@includes = @association.klass
@includes = SchemaUtils.association_ref(@association)
.reflect_on_all_associations
.select do |association|
inclusion = !association.options[:polymorphic] &&
inclusion = !SchemaUtils.polymorphic?(association) &&
SchemaUtils.model_included?(association.klass) &&
[:belongs_to, :has_and_belongs_to_many].include?(association.macro)

Expand Down
2 changes: 1 addition & 1 deletion app/services/forest_liana/leaderboard_stat_getter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ class LeaderboardStatGetter < StatGetter
def initialize(resource, params)
@resource = resource
@params = params
@model_relationship = @resource.reflect_on_association(@params[:relationship_field]).klass
@model_relationship = @resource.reflect_on_association(@params[:relationship_field]).klass
compute_includes()
@label_field = @params[:label_field]
@aggregate = @params[:aggregate].downcase
Expand Down
4 changes: 2 additions & 2 deletions app/services/forest_liana/resource_creator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def perform
end
set_has_many_relationships
rescue ActiveRecord::StatementInvalid => exception
# NOTICE: SQL request cannot be executed properly
# NOTICE: SQL request cannot be executed properly
@errors = [{ detail: exception.cause.error }]
rescue ForestLiana::Errors::SerializeAttributeBadFormat => exception
@errors = [{ detail: exception.message }]
Expand All @@ -41,7 +41,7 @@ def set_has_many_relationships
if data.is_a?(Array)
data.each do |x|
existing_records = @record.send(name)
new_record = association.klass.find(x[:id])
new_record = SchemaUtils.association_ref(association).find(x[:id])
if !existing_records.include?(new_record)
existing_records << new_record
end
Expand Down
2 changes: 1 addition & 1 deletion app/services/forest_liana/resources_getter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ def compute_includes
.map { |field| field.split('.').first.to_sym }

includes_has_many = SchemaUtils.many_associations(@resource)
.select { |association| SchemaUtils.model_included?(association.klass) }
.select { |association| SchemaUtils.model_included?(SchemaUtils.association_ref(association)) }
.map(&:name)

includes_for_smart_search = includes_for_smart_search & includes_has_many
Expand Down
29 changes: 21 additions & 8 deletions app/services/forest_liana/schema_adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ def add_associations
SchemaUtils.associations(@model).each do |association|
begin
# NOTICE: Delete the association if the targeted model is excluded.
if !SchemaUtils.model_included?(association.klass)
if !SchemaUtils.model_included?(SchemaUtils.association_ref(association))
field = collection.fields.find do |x|
x[:field] == association.foreign_key
end
Expand Down Expand Up @@ -272,14 +272,27 @@ def inverse_of(association)
automatic_inverse_of(association)
end

def polymorphic_inverse_of(association)
active_models = ActiveRecord::Base.descendants
active_models.each do |model|
a = model.reflect_on_all_associations.select{|a| a.options[:as] == association.name }
unless a.empty?
return a.first.name
end
end
end

def automatic_inverse_of(association)
name = association.active_record.name.demodulize.underscore
if SchemaUtils.polymorphic?(association)
polymorphic_inverse_of(association)
else
name = association.active_record.name.demodulize.underscore
inverse_association = association.klass.reflections.keys.find do |k|
k.to_s == name || k.to_s == name.pluralize
end

inverse_association = association.klass.reflections.keys.find do |k|
k.to_s == name || k.to_s == name.pluralize
inverse_association.try(:to_s)
end

inverse_association.try(:to_s)
end

def get_schema_for_column(column)
Expand Down Expand Up @@ -313,7 +326,7 @@ def get_schema_for_association(association)
field: association.name.to_s,
type: get_type_for_association(association),
relationship: get_relationship_type(association),
reference: "#{ForestLiana.name_for(association.klass)}.id",
reference: "#{ForestLiana.name_for(SchemaUtils.association_ref(association))}.id",
inverse_of: inverse_of(association),
is_filterable: !is_many_association(association),
is_sortable: true,
Expand Down Expand Up @@ -500,7 +513,7 @@ def add_validations(column_schema, column)
end

def get_reference_for(association)
if association.options[:polymorphic] == true
if SchemaUtils.polymorphic?(association)
'*.id'
else
"#{ForestLiana.name_for(association.klass)}.id"
Expand Down
10 changes: 9 additions & 1 deletion app/services/forest_liana/schema_utils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ class SchemaUtils
def self.associations(active_record_class)
active_record_class.reflect_on_all_associations.select do |association|
begin
!polymorphic?(association) && !is_active_type?(association.klass)
!is_active_type?(SchemaUtils.association_ref(association))
rescue
FOREST_LOGGER.warn "Unknown association #{association.name} on class #{active_record_class.name}"
false
Expand Down Expand Up @@ -58,6 +58,14 @@ def self.polymorphic?(association)
association.options[:polymorphic]
end

def self.association_ref(association)
if self.polymorphic?(association)
association.active_record
else
association.klass
end
end

def self.find_model_from_abstract_class(abstract_class, collection_name)
abstract_class.subclasses.find do |subclass|
if subclass.abstract_class?
Expand Down