lib/jsonapi/include_directives.rb in jsonapi-resources-0.10.7 vs lib/jsonapi/include_directives.rb in jsonapi-resources-0.11.0.beta2
- old
+ new
@@ -1,50 +1,108 @@
+# frozen_string_literal: true
+
module JSONAPI
class IncludeDirectives
# Construct an IncludeDirectives Hash from an array of dot separated include strings.
# For example ['posts.comments.tags']
# will transform into =>
# {
- # posts: {
- # include_related: {
- # comments:{
- # include_related: {
- # tags: {
- # include_related: {}
- # }
+ # include_related: {
+ # posts: {
+ # include: true,
+ # include_related: {
+ # comments: {
+ # include: true,
+ # include_related: {
+ # tags: {
+ # include: true,
+ # include_related: {},
+ # include_in_join: true
+ # }
+ # },
+ # include_in_join: true
# }
- # }
+ # },
+ # include_in_join: true
# }
# }
# }
- def initialize(resource_klass, includes_array)
+ def initialize(resource_klass, includes_array, force_eager_load: false)
@resource_klass = resource_klass
+ @force_eager_load = force_eager_load
@include_directives_hash = { include_related: {} }
includes_array.each do |include|
parse_include(include)
end
end
def include_directives
@include_directives_hash
end
- private
+ def [](name)
+ @include_directives_hash[name]
+ end
- def parse_include(include)
- path = JSONAPI::Path.new(resource_klass: @resource_klass,
- path_string: include,
- ensure_default_field: false,
- parse_fields: false)
+ def model_includes
+ get_includes(@include_directives_hash)
+ end
+ private
+
+ def get_related(current_path)
current = @include_directives_hash
+ current_resource_klass = @resource_klass
+ current_path.split('.').each do |fragment|
+ fragment = fragment.to_sym
- path.segments.each do |segment|
- relationship_name = segment.relationship.name.to_sym
+ if current_resource_klass
+ current_relationship = current_resource_klass._relationship(fragment)
+ current_resource_klass = current_relationship.try(:resource_klass)
+ else
+ raise JSONAPI::Exceptions::InvalidInclude.new(current_resource_klass, current_path)
+ end
- current[:include_related][relationship_name] ||= { include_related: {} }
- current = current[:include_related][relationship_name]
+ include_in_join = @force_eager_load || !current_relationship || current_relationship.eager_load_on_include
+
+ current[:include_related][fragment] ||= { include: false, include_related: {}, include_in_join: include_in_join }
+ current = current[:include_related][fragment]
+ end
+ current
+ end
+
+ def get_includes(directive, only_joined_includes = true)
+ ir = directive[:include_related]
+ ir = ir.select { |_k,v| v[:include_in_join] } if only_joined_includes
+
+ ir.map do |name, sub_directive|
+ sub = get_includes(sub_directive, only_joined_includes)
+ sub.any? ? { name => sub } : name
+ end
+ end
+
+ def parse_include(include)
+ parts = include.split('.')
+ local_path = ''
+
+ parts.each do |name|
+ local_path += local_path.length > 0 ? ".#{name}" : name
+ related = get_related(local_path)
+ related[:include] = true
+ end
+ end
+
+ def delve_paths(obj)
+ case obj
+ when Array
+ obj.map{|elem| delve_paths(elem)}.flatten(1)
+ when Hash
+ obj.map{|k,v| [[k]] + delve_paths(v).map{|path| [k] + path } }.flatten(1)
+ when Symbol, String
+ [[obj]]
+ else
+ raise "delve_paths cannot descend into #{obj.class.name}"
end
rescue JSONAPI::Exceptions::InvalidRelationship => _e
raise JSONAPI::Exceptions::InvalidInclude.new(@resource_klass, include)
end