lib/rom/repository/relation_proxy/combine.rb in rom-repository-0.3.1 vs lib/rom/repository/relation_proxy/combine.rb in rom-repository-1.0.0.beta1

- old
+ new

@@ -1,5 +1,7 @@ +require 'dry/core/inflector' + module ROM class Repository class RelationProxy # Provides convenient methods for composing relations # @@ -54,16 +56,34 @@ def combine(*args) options = args[0].is_a?(Hash) ? args[0] : args combine_opts = Hash.new { |h, k| h[k] = {} } - options.each do |(type, relations)| - if relations - combine_opts[type] = combine_opts_from_relations(relations) + options.each do |key, value| + if key == :one || key == :many + if value.is_a?(Hash) + value.each do |name, spec| + if spec.is_a?(Array) + combine_opts[key][name] = spec + else + _, (curried, keys) = combine_opts_from_relations(spec).to_a[0] + combine_opts[key][name] = [curried, keys] + end + end + else + _, (curried, keys) = combine_opts_from_relations(value).to_a[0] + combine_opts[key][curried.combine_tuple_key(key)] = [curried, keys] + end else - result, curried, keys = combine_opts_for_assoc(type) - combine_opts[result][type] = [curried, keys] + if value.is_a?(Array) + curried = combine_from_assoc(key, registry[key]).combine(*value) + result, _, keys = combine_opts_for_assoc(key) + combine_opts[result][key] = [curried, keys] + else + result, curried, keys = combine_opts_for_assoc(key, value) + combine_opts[result][key] = [curried, keys] + end end end nodes = combine_opts.flat_map do |type, relations| relations.map { |name, (relation, keys)| @@ -95,22 +115,25 @@ combine_opts[type] = case parents when Hash parents.each_with_object({}) { |(name, parent), r| keys = combine_keys(parent, relation, :parent) - r[name] = [parent, keys] + curried = combine_from_assoc_with_fallback(name, parent, keys) + r[name] = [curried, keys] } when Array parents.each_with_object({}) { |parent, r| - tuple_key = parent.combine_tuple_key(type) keys = combine_keys(parent, relation, :parent) - r[tuple_key] = [parent, keys] + tuple_key = parent.combine_tuple_key(type) + curried = combine_from_assoc_with_fallback(parent.name, parent, keys) + r[tuple_key] = [curried, keys] } else - tuple_key = parents.combine_tuple_key(type) keys = combine_keys(parents, relation, :parent) - { tuple_key => [parents, keys] } + tuple_key = parents.combine_tuple_key(type) + curried = combine_from_assoc_with_fallback(parents.name, parents, keys) + { tuple_key => [curried, keys] } end end combine(combine_opts) end @@ -136,22 +159,25 @@ combine_opts[type] = case children when Hash children.each_with_object({}) { |(name, child), r| keys = combine_keys(relation, child, :children) - r[name] = [child, keys] + curried = combine_from_assoc_with_fallback(name, child, keys) + r[name] = [curried, keys] } when Array - parents.each_with_object({}) { |child, r| - tuple_key = parent.combine_tuple_key(type) + children.each_with_object({}) { |child, r| keys = combine_keys(relation, child, :children) - r[tuple_key] = [parent, keys] + tuple_key = child.combine_tuple_key(type) + curried = combine_from_assoc_with_fallback(child.name, child, keys) + r[tuple_key] = [curried, keys] } else - tuple_key = children.combine_tuple_key(type) keys = combine_keys(relation, children, :children) - { tuple_key => [children, keys] } + curried = combine_from_assoc_with_fallback(children.name, children, keys) + tuple_key = children.combine_tuple_key(type) + { tuple_key => [curried, keys] } end end combine(combine_opts) end @@ -183,42 +209,48 @@ # # It returns a mapping like `name => [preloadable_relation, combine_keys]` # and this mapping is used by `combine` to build a full relation graph # # @api private - def combine_opts_from_relations(relations) - relations.each_with_object({}) do |(name, (other, keys)), h| - h[name] = - if other.curried? - [other, keys] - else - rel = combine_from_assoc(name, other) { other.combine_method(relation, keys) } - [rel, keys] - end + def combine_opts_from_relations(*relations) + relations.each_with_object({}) do |spec, h| + # We assume it's a child relation + keys = combine_keys(relation, spec, :children) + rel = combine_from_assoc_with_fallback(spec.name, spec, keys) + h[spec.name.relation] = [rel, keys] end end + # @api private + def combine_from_assoc_with_fallback(name, other, keys) + combine_from_assoc(name, other) do + other.combine_method(relation, keys) + end + end + # Try to get a preloadable relation from a defined association # # If association doesn't exist we call the fallback block # # @return [RelationProxy] # # @api private def combine_from_assoc(name, other, &fallback) + return other if other.curried? associations.try(name) { |assoc| other.for_combine(assoc) } or fallback.call end # Extract result (either :one or :many), preloadable relation and its keys # by using given association name # # This is used when a flat list of association names was passed to `combine` # # @api private - def combine_opts_for_assoc(name) + def combine_opts_for_assoc(name, opts = nil) assoc = relation.associations[name] curried = registry[assoc.target.relation].for_combine(assoc) + curried = curried.combine(opts) unless opts.nil? keys = assoc.combine_keys(__registry__) [assoc.result, curried, keys] end # Build a preloadable relation for relation graph @@ -256,10 +288,10 @@ # @return [Symbol] # # @api private def combine_tuple_key(result) if result == :one - Inflector.singularize(base_name.relation).to_sym + Dry::Core::Inflector.singularize(base_name.relation).to_sym else base_name.relation end end