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