lib/jsonapi/relationship.rb in jsonapi-resources-0.10.7 vs lib/jsonapi/relationship.rb in jsonapi-resources-0.11.0.beta2

- old
+ new

@@ -1,11 +1,13 @@ +# frozen_string_literal: true + module JSONAPI class Relationship attr_reader :acts_as_set, :foreign_key, :options, :name, - :class_name, :polymorphic, :always_include_optional_linkage_data, + :class_name, :polymorphic, :always_include_optional_linkage_data, :exclude_linkage_data, :parent_resource, :eager_load_on_include, :custom_methods, - :inverse_relationship, :allow_include, :use_related_resource_records_for_joins + :inverse_relationship, :allow_include, :hidden, :use_related_resource_records_for_joins attr_writer :allow_include attr_accessor :_routed, :_warned_missing_route @@ -13,11 +15,11 @@ @name = name.to_s @options = options @acts_as_set = options.fetch(:acts_as_set, false) == true @foreign_key = options[:foreign_key] ? options[:foreign_key].to_sym : nil @parent_resource = options[:parent_resource] - @relation_name = options.fetch(:relation_name, @name) + @relation_name = options[:relation_name] @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') @polymorphic_types ||= options[:polymorphic_relations] @@ -26,20 +28,24 @@ use_related_resource_records_for_joins_default = if options[:relation_name] false else JSONAPI.configuration.use_related_resource_records_for_joins end - + @use_related_resource_records_for_joins = options.fetch(:use_related_resource_records_for_joins, use_related_resource_records_for_joins_default) == true + @hidden = options.fetch(:hidden, false) == true + + @exclude_linkage_data = options[:exclude_linkage_data] @always_include_optional_linkage_data = options.fetch(:always_include_optional_linkage_data, false) == true - @eager_load_on_include = options.fetch(:eager_load_on_include, false) == true + @eager_load_on_include = options.fetch(:eager_load_on_include, true) == true @allow_include = options[:allow_include] @class_name = nil - @inverse_relationship = nil + @inverse_relationship = options[:inverse_relationship]&.to_sym + @_routed = false @_warned_missing_route = false exclude_links(options.fetch(:exclude_links, JSONAPI.configuration.default_exclude_links)) @@ -64,46 +70,60 @@ # :nocov: @table_name ||= resource_klass._table_name # :nocov: end + def inverse_relationship + unless @inverse_relationship + @inverse_relationship ||= if resource_klass._relationship(@parent_resource._type.to_s.singularize).present? + @parent_resource._type.to_s.singularize.to_sym + elsif resource_klass._relationship(@parent_resource._type).present? + @parent_resource._type.to_sym + else + nil + end + end + + @inverse_relationship + end + def self.polymorphic_types(name) @poly_hash ||= {}.tap do |hash| 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| + klass.reflect_on_all_associations(:has_many).select { |r| r.options[:as] }.each do |reflection| (hash[reflection.options[:as]] ||= []) << klass.name.underscore end end end end @poly_hash[name.to_sym] end def resource_types if polymorphic? && belongs_to? - @polymorphic_types ||= self.class.polymorphic_types(@relation_name).collect {|t| t.pluralize} + @polymorphic_types ||= self.class.polymorphic_types(_relation_name).collect { |t| t.pluralize } else [resource_klass._type.to_s.pluralize] end end def type @type ||= resource_klass._type.to_sym end def relation_name(options) - case @relation_name - when Symbol - # :nocov: - @relation_name - # :nocov: - when String - @relation_name.to_sym - when Proc - @relation_name.call(options) + case _relation_name + when Symbol + # :nocov: + _relation_name + # :nocov: + when String + _relation_name.to_sym + when Proc + _relation_name.call(options) end end def belongs_to? # :nocov: @@ -115,18 +135,18 @@ @options[:readonly] end def exclude_links(exclude) case exclude - when :default, "default" - @_exclude_links = [:self, :related] - when :none, "none" - @_exclude_links = [] - when Array - @_exclude_links = exclude.collect {|link| link.to_sym} - else - fail "Invalid exclude_links" + when :default, "default" + @_exclude_links = [:self, :related] + when :none, "none" + @_exclude_links = [] + when Array + @_exclude_links = exclude.collect { |link| link.to_sym } + else + fail "Invalid exclude_links" end end def _exclude_links @_exclude_links ||= [] @@ -134,21 +154,32 @@ def exclude_link?(link) _exclude_links.include?(link.to_sym) end + def _relation_name + @relation_name || @name + end + class ToOne < Relationship attr_reader :foreign_key_on def initialize(name, options = {}) super @class_name = options.fetch(:class_name, name.to_s.camelize) @foreign_key ||= "#{name}_id".to_sym @foreign_key_on = options.fetch(:foreign_key_on, :self) - if parent_resource - @inverse_relationship = options.fetch(:inverse_relationship, parent_resource._type) + # if parent_resource + # @inverse_relationship = options.fetch(:inverse_relationship, parent_resource._type) + # end + + if options.fetch(:create_implicit_polymorphic_type_relationships, true) == true && polymorphic? + # Setup the implicit relationships for the polymorphic types and exclude linkage data + setup_implicit_relationships_for_polymorphic_types end + + @polymorphic_type_relationship_for = options[:polymorphic_type_relationship_for] end def to_s # :nocov: useful for debugging "#{parent_resource}.#{name}(#{belongs_to? ? 'BelongsToOne' : 'ToOne'})" @@ -159,29 +190,48 @@ # :nocov: foreign_key_on == :self # :nocov: end + def hidden? + @hidden || @polymorphic_type_relationship_for.present? + end + def polymorphic_type "#{name}_type" if polymorphic? end + 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}" + return + end + + types.each do |type| + parent_resource.has_one(type.to_s.underscore.singularize, + exclude_linkage_data: exclude_linkage_data, + polymorphic_type_relationship_for: name) + end + end + def include_optional_linkage_data? + return false if @exclude_linkage_data @always_include_optional_linkage_data || JSONAPI::configuration.always_include_to_one_linkage_data end def allow_include?(context = nil) strategy = if @allow_include.nil? JSONAPI.configuration.default_allow_include_to_one else @allow_include end - if !!strategy == strategy #check for boolean + if !!strategy == strategy # check for boolean return strategy elsif strategy.is_a?(Symbol) || strategy.is_a?(String) - parent_resource.send(strategy, context) + parent_resource_klass.send(strategy, context) else strategy.call(context) end end end @@ -192,21 +242,25 @@ def initialize(name, options = {}) super @class_name = options.fetch(:class_name, name.to_s.camelize.singularize) @foreign_key ||= "#{name.to_s.singularize}_ids".to_sym @reflect = options.fetch(:reflect, true) == true - if parent_resource - @inverse_relationship = options.fetch(:inverse_relationship, parent_resource._type.to_s.singularize.to_sym) - end + # if parent_resource + # @inverse_relationship = options.fetch(:inverse_relationship, parent_resource._type.to_s.singularize.to_sym) + # end end def to_s # :nocov: useful for debugging - "#{parent_resource}.#{name}(ToMany)" + "#{parent_resource_klass}.#{name}(ToMany)" # :nocov: end + def hidden? + @hidden + end + def include_optional_linkage_data? # :nocov: @always_include_optional_linkage_data || JSONAPI::configuration.always_include_to_many_linkage_data # :nocov: end @@ -216,13 +270,13 @@ JSONAPI.configuration.default_allow_include_to_many else @allow_include end - if !!strategy == strategy #check for boolean + if !!strategy == strategy # check for boolean return strategy elsif strategy.is_a?(Symbol) || strategy.is_a?(String) - parent_resource.send(strategy, context) + parent_resource_klass.send(strategy, context) else strategy.call(context) end end