lib/neo4j/active_node/has_n.rb in neo4j-5.0.15 vs lib/neo4j/active_node/has_n.rb in neo4j-5.1.0.rc.1
- old
+ new
@@ -22,11 +22,11 @@
# Default
def inspect
if @cached_result
@cached_result.inspect
else
- "<AssociationProxy @query_proxy=#{@query_proxy.inspect}>"
+ "#<AssociationProxy @query_proxy=#{@query_proxy.inspect}>"
end
end
extend Forwardable
%w(include? empty? count find first last ==).each do |delegated_method|
@@ -119,11 +119,11 @@
self.class.send(:association_query_proxy, name, {start_object: self}.merge!(options))
end
def association_proxy(name, options = {})
name = name.to_sym
- hash = [name, options.values_at(:node, :rel, :labels)].hash
+ hash = [name, options.values_at(:node, :rel, :labels, :rel_length)].hash
association_proxy_cache_fetch(hash) do
if result_cache = self.instance_variable_get('@source_query_proxy_result_cache')
result_by_previous_id = previous_proxy_results_by_previous_id(result_cache, name)
result_cache.inject(nil) do |proxy_to_return, object|
@@ -150,31 +150,23 @@
query_proxy = self.class.send(:association_query_proxy, association_name, previous_query_proxy: query_proxy, node: :next, optional: true)
Hash[*query_proxy.pluck('ID(previous)', 'collect(next)').flatten(1)]
end
- def handle_non_persisted_node(other_node)
- return unless Neo4j::Config[:autosave_on_assignment]
- other_node.try(:save)
- save
- end
-
- def validate_persisted_for_association!
- fail(Neo4j::ActiveNode::HasN::NonPersistedNodeError, 'Unable to create relationship with non-persisted nodes') unless self._persisted_obj
- end
-
module ClassMethods
- # :nocov:
# rubocop:disable Style/PredicateName
+
+ # :nocov:
def has_association?(name)
ActiveSupport::Deprecation.warn 'has_association? is deprecated and may be removed from future releases, use association? instead.', caller
association?(name)
end
- # rubocop:enable Style/PredicateName
# :nocov:
+ # rubocop:enable Style/PredicateName
+
def association?(name)
!!associations[name.to_sym]
end
def associations
@@ -193,12 +185,15 @@
end
# For defining an "has many" association on a model. This defines a set of methods on
# your model instances. For instance, if you define the association on a Person model:
#
- # has_many :out, :vehicles, type: :has_vehicle
#
+ # .. code-block:: ruby
+ #
+ # has_many :out, :vehicles, type: :has_vehicle
+ #
# This would define the following methods:
#
# **#vehicles**
# Returns a QueryProxy object. This is an Enumerable object and thus can be iterated
# over. It also has the ability to accept class-level methods from the Vehicle model
@@ -211,51 +206,64 @@
# **.vehicles**
# Returns a QueryProxy object. This would represent all ``Vehicle`` objects associated with
# either all ``Person`` nodes (if ``Person.vehicles`` is called), or all ``Vehicle`` objects
# associated with the ``Person`` nodes thus far represented in the QueryProxy chain.
# For example:
- # ``company.people.where(age: 40).vehicles``
#
+ # .. code-block:: ruby
+ #
+ # company.people.where(age: 40).vehicles
+ #
# Arguments:
# **direction:**
# **Available values:** ``:in``, ``:out``, or ``:both``.
#
# Refers to the relative to the model on which the association is being defined.
#
# Example:
- # ``Person.has_many :out, :posts, type: :wrote``
#
- # means that a `WROTE` relationship goes from a `Person` node to a `Post` node
+ # .. code-block:: ruby
#
+ # Person.has_many :out, :posts, type: :wrote
+ #
+ # means that a `WROTE` relationship goes from a `Person` node to a `Post` node
+ #
# **name:**
# The name of the association. The affects the methods which are created (see above).
# The name is also used to form default assumptions about the model which is being referred to
#
# Example:
- # ``Person.has_many :out, :posts, type: :wrote``
#
- # will assume a `model_class` option of ``'Post'`` unless otherwise specified
+ # .. code-block:: ruby
#
+ # Person.has_many :out, :posts, type: :wrote
+ #
+ # will assume a `model_class` option of ``'Post'`` unless otherwise specified
+ #
# **options:** A ``Hash`` of options. Allowed keys are:
# *type*: The Neo4j relationship type. This option is required unless either the
# `origin` or `rel_class` options are specified
#
# *origin*: The name of the association from another model which the `type` and `model_class`
# can be gathered.
#
# Example:
- # ``Person.has_many :out, :posts, origin: :author`` (`model_class` of `Post` is assumed here)
#
- # ``Post.has_one :in, :author, type: :has_author, model_class: 'Person'``
+ # .. code-block:: ruby
#
+ # # `model_class` of `Post` is assumed here
+ # Person.has_many :out, :posts, origin: :author
+ #
+ # Post.has_one :in, :author, type: :has_author, model_class: 'Person'
+ #
# *model_class*: The model class to which the association is referring. Can be either a
- # model object ``include`` ing ``ActiveNode`` or a string (or an ``Array`` of same).
- # **A string is recommended** to avoid load-time issues
+ # model object ``include`` ing ``ActiveNode`` or a Symbol/String (or an ``Array`` of same).
+ # **A Symbol or String is recommended** to avoid load-time issues
#
# *rel_class*: The ``ActiveRel`` class to use for this association. Can be either a
- # model object ``include`` ing ``ActiveRel`` or a string (or an ``Array`` of same).
- # **A string is recommended** to avoid load-time issues
+ # model object ``include`` ing ``ActiveRel`` or a Symbol/String (or an ``Array`` of same).
+ # **A Symbol or String is recommended** to avoid load-time issues
#
# *dependent*: Enables deletion cascading.
# **Available values:** ``:delete``, ``:delete_orphans``, ``:destroy``, ``:destroy_orphans``
# (note that the ``:destroy_orphans`` option is known to be "very metal". Caution advised)
#
@@ -285,17 +293,24 @@
private
def define_has_many_methods(name)
define_method(name) do |node = nil, rel = nil, options = {}|
- return [].freeze unless self._persisted_obj
+ # return [].freeze unless self._persisted_obj
+ if node.is_a?(Hash)
+ options = node
+ node = nil
+ end
+
association_proxy(name, {node: node, rel: rel, source_object: self, labels: options[:labels]}.merge!(options))
end
define_has_many_setter(name)
+ define_has_many_id_methods(name)
+
define_class_method(name) do |node = nil, rel = nil, options = {}|
association_proxy(name, {node: node, rel: rel, labels: options[:labels]}.merge!(options))
end
end
@@ -305,30 +320,82 @@
Neo4j::Transaction.run { association_proxy(name).replace_with(other_nodes) }
end
end
- def define_has_one_methods(name)
- define_method(name) do |node = nil, rel = nil|
- return nil unless self._persisted_obj
+ def define_has_many_id_methods(name)
+ define_method_unless_defined("#{name.to_s.singularize}_ids") do
+ association_proxy(name).pluck(:uuid)
+ end
- association_proxy(name, node: node, rel: rel).first
+ define_method_unless_defined("#{name.to_s.singularize}_ids=") do |ids|
+ association_proxy(name).replace_with(ids)
end
+ define_method_unless_defined("#{name.to_s.singularize}_neo_ids") do
+ association_proxy(name).pluck(:neo_id)
+ end
+ end
+
+ def define_method_unless_defined(method_name, &block)
+ define_method(method_name, block) unless respond_to?(method_name)
+ end
+
+ def define_has_one_methods(name)
+ define_has_one_getter(name)
+
define_has_one_setter(name)
+ define_has_one_id_methods(name)
+
define_class_method(name) do |node = nil, rel = nil, options = {}|
association_proxy(name, {node: node, rel: rel, labels: options[:labels]}.merge!(options))
end
end
+ def define_has_one_id_methods(name)
+ define_method("#{name}_id") do
+ association_proxy(name).pluck(:uuid).first
+ end
+
+ define_method_unless_defined("#{name}_id=") do |id|
+ association_proxy(name).replace_with(id)
+ end
+
+ define_method("#{name}_neo_id") do
+ association_proxy(name).pluck(:neo_id).first
+ end
+ end
+
+ def define_has_one_getter(name)
+ define_method(name) do |node = nil, rel = nil, options = {}|
+ return nil unless self._persisted_obj
+
+ if node.is_a?(Hash)
+ options = node
+ node = nil
+ end
+
+ # Return all results if a variable-length relationship length was given
+ results = association_proxy(name, {node: node, rel: rel}.merge!(options))
+ if options[:rel_length] && !options[:rel_length].is_a?(Fixnum)
+ results
+ else
+ results.first
+ end
+ end
+ end
+
def define_has_one_setter(name)
define_method("#{name}=") do |other_node|
- handle_non_persisted_node(other_node)
- validate_persisted_for_association!
- association_proxy_cache.clear # TODO: Should probably just clear for this association...
-
- Neo4j::Transaction.run { association_proxy(name).replace_with(other_node) }
+ if persisted?
+ other_node.save if other_node.respond_to?(:persisted?) && !other_node.persisted?
+ association_proxy_cache.clear # TODO: Should probably just clear for this association...
+ Neo4j::Transaction.run { association_proxy(name).replace_with(other_node) }
+ # handle_non_persisted_node(other_node)
+ else
+ association_proxy(name).defer_create(other_node, {}, :'=')
+ end
end
end
def define_class_method(*args, &block)
klass = class << self; self; end