module Neo4j
module ActiveNode
module Query
# Methods related to returning nodes and rels from QueryProxy
module QueryProxyEnumerable
include Enumerable
# Just like every other each but it allows for optional params to support the versions that also return relationships.
# The node and rel params are typically used by those other methods but there's nothing stopping you from
# using `your_node.each(true, true)` instead of `your_node.each_with_rel`.
# @return [Enumerable] An enumerable containing some combination of nodes and rels.
def each(node = true, rel = nil, &block)
result(node, rel).each(&block)
end
def result(node = true, rel = nil)
return [].freeze if unpersisted_start_object?
@result_cache ||= {}
return result_cache_for(node, rel) if result_cache?(node, rel)
result = pluck_vars(node, rel)
set_instance_caches(result, node, rel)
@result_cache[[node, rel]] ||= result
end
def result_cache?(node = true, rel = nil)
!!result_cache_for(node, rel)
end
def result_cache_for(node = true, rel = nil)
(@result_cache || {})[[node, rel]]
end
def fetch_result_cache
@result_cache ||= yield
end
# When called at the end of a QueryProxy chain, it will return the resultant relationship objects intead of nodes.
# For example, to return the relationship between a given student and their lessons:
#
# .. code-block:: ruby
#
# student.lessons.each_rel do |rel|
#
# @return [Enumerable] An enumerable containing any number of applicable relationship objects.
def each_rel(&block)
block_given? ? each(false, true, &block) : to_enum(:each, false, true)
end
# When called at the end of a QueryProxy chain, it will return the nodes and relationships of the last link.
# For example, to return a lesson and each relationship to a given student:
#
# .. code-block:: ruby
#
# student.lessons.each_with_rel do |lesson, rel|
def each_with_rel(&block)
block_given? ? each(true, true, &block) : to_enum(:each, true, true)
end
# Does exactly what you would hope. Without it, comparing `bobby.lessons == sandy.lessons` would evaluate to false because it
# would be comparing the QueryProxy objects, not the lessons themselves.
def ==(other)
self.to_a == other
end
# For getting variables which have been defined as part of the association chain
def pluck(*args)
transformable_attributes = (model ? model.attribute_names : []) + %w(uuid neo_id)
arg_list = args.map do |arg|
arg = Neo4j::ActiveNode::Query::QueryProxy::Link.converted_key(model, arg)
if transformable_attributes.include?(arg.to_s)
{identity => arg}
else
arg
end
end
self.query.pluck(*arg_list)
end
protected
def ensure_distinct(node, force = false)
@distinct || force ? "DISTINCT(#{node})" : node
end
private
def pluck_vars(node, rel)
vars = []
vars << ensure_distinct(identity) if node
vars << @rel_var if rel
pluck(*vars)
end
def set_instance_caches(instance, node, rel)
instance.each do |object|
object.instance_variable_set('@source_query_proxy', self)
object.instance_variable_set('@source_proxy_result_cache', instance)
if node && rel && object.last.is_a?(Neo4j::ActiveRel)
object.last.instance_variable_set(association.direction == :in ? '@from_node' : '@to_node', object.first)
end
end
end
end
end
end
end