module Neo4j::ActiveNode::HasN::ClassMethods

rubocop:disable Metrics/ModuleLength

Public Instance Methods

association?(name) click to toggle source

rubocop:enable Naming/PredicateName

    # File lib/neo4j/active_node/has_n.rb
293 def association?(name)
294   !!associations[name.to_sym]
295 end
associations() click to toggle source
    # File lib/neo4j/active_node/has_n.rb
301 def associations
302   (@associations ||= parent_associations.dup)
303 end
associations_keys() click to toggle source
    # File lib/neo4j/active_node/has_n.rb
305 def associations_keys
306   @associations_keys ||= associations.keys
307 end
has_association?(name) click to toggle source

:nocov:

    # File lib/neo4j/active_node/has_n.rb
284 def has_association?(name)
285   ActiveSupport::Deprecation.warn 'has_association? is deprecated and may be removed from future releases, use association? instead.', caller
286 
287   association?(name)
288 end
has_many(direction, name, options = {}) click to toggle source

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:

.. 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
(including calls to association methods)

**#vehicles=**

Takes an array of Vehicle objects and replaces all current ``:HAS_VEHICLE`` relationships
with new relationships refering to the specified objects

.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:

.. 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:

  .. 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:

  .. 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:

    .. 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 a
    Symbol/String (or an ``Array`` of same) with the name of the `ActiveNode` class,
    `false` to specify any model, or nil to specify that it should be guessed.

  *rel_class*: The ``ActiveRel`` class to use for this association.  Can be either a
    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)
    # File lib/neo4j/active_node/has_n.rb
392 def has_many(direction, name, options = {}) # rubocop:disable Naming/PredicateName
393   name = name.to_sym
394   build_association(:has_many, direction, name, options)
395 
396   define_has_many_methods(name, options)
397 end
has_one(direction, name, options = {}) click to toggle source

For defining an “has one” 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_one :out, :vehicle, type: :has_vehicle

This would define the methods: “#vehicle“, “#vehicle=“, and “.vehicle“.

See :ref:`#has_many <Neo4j/ActiveNode/HasN/ClassMethods#has_many>` for anything not specified here

    # File lib/neo4j/active_node/has_n.rb
409 def has_one(direction, name, options = {}) # rubocop:disable Naming/PredicateName
410   name = name.to_sym
411   build_association(:has_one, direction, name, options)
412 
413   define_has_one_methods(name, options)
414 end
parent_associations() click to toggle source
    # File lib/neo4j/active_node/has_n.rb
297 def parent_associations
298   superclass == Object ? {} : superclass.associations
299 end

Private Instance Methods

add_association(name, association_object) click to toggle source
    # File lib/neo4j/active_node/has_n.rb
600 def add_association(name, association_object)
601   fail "Association `#{name}` defined for a second time. "\
602        'Associations can only be defined once' if duplicate_association?(name)
603   associations[name] = association_object
604 end
association_proxy(name, options = {}) click to toggle source
    # File lib/neo4j/active_node/has_n.rb
560 def association_proxy(name, options = {})
561   AssociationProxy.new(association_query_proxy(name, options))
562 end
association_query_proxy(name, options = {}) click to toggle source
    # File lib/neo4j/active_node/has_n.rb
544 def association_query_proxy(name, options = {})
545   previous_query_proxy = options[:previous_query_proxy] || current_scope
546   query_proxy = previous_query_proxy || default_association_query_proxy
547   Neo4j::ActiveNode::Query::QueryProxy.new(association_target_class(name),
548                                            associations[name],
549                                            {session: neo4j_session,
550                                             query_proxy: query_proxy,
551                                             context: "#{query_proxy.context || self.name}##{name}",
552                                             optional: query_proxy.optional?,
553                                             association_labels: options[:labels],
554                                             source_object: query_proxy.source_object}.merge!(options)).tap do |query_proxy_result|
555                                               target_classes = association_target_classes(name)
556                                               return query_proxy_result.as_models(target_classes) if target_classes
557                                             end
558 end
association_target_class(name) click to toggle source
    # File lib/neo4j/active_node/has_n.rb
564 def association_target_class(name)
565   target_classes_or_nil = associations[name].target_classes_or_nil
566 
567   return if !target_classes_or_nil.is_a?(Array) || target_classes_or_nil.size != 1
568 
569   target_classes_or_nil[0]
570 end
association_target_classes(name) click to toggle source
    # File lib/neo4j/active_node/has_n.rb
572 def association_target_classes(name)
573   target_classes_or_nil = associations[name].target_classes_or_nil
574 
575   return if !target_classes_or_nil.is_a?(Array) || target_classes_or_nil.size <= 1
576 
577   target_classes_or_nil
578 end
build_association(macro, direction, name, options) click to toggle source
    # File lib/neo4j/active_node/has_n.rb
585 def build_association(macro, direction, name, options)
586   options[:model_class] = options[:model_class].name if options[:model_class] == self
587   Neo4j::ActiveNode::HasN::Association.new(macro, direction, name, options).tap do |association|
588     add_association(name, association)
589     create_reflection(macro, name, association, self)
590   end
591 
592   @associations_keys = nil
593 
594 # Re-raise any exception with added class name and association name to
595 # make sure error message is helpful
596 rescue StandardError => e
597   raise e.class, "#{e.message} (#{self.class}##{name})"
598 end
default_association_query_proxy() click to toggle source
    # File lib/neo4j/active_node/has_n.rb
580 def default_association_query_proxy
581   Neo4j::ActiveNode::Query::QueryProxy.new("::#{self.name}".constantize, nil,
582                                            session: neo4j_session, query_proxy: nil, context: self.name.to_s)
583 end
define_class_method(*args, &block) click to toggle source
    # File lib/neo4j/active_node/has_n.rb
537 def define_class_method(*args, &block)
538   klass = class << self; self; end
539   klass.instance_eval do
540     define_method(*args, &block)
541   end
542 end
define_has_many_id_methods(name) click to toggle source
    # File lib/neo4j/active_node/has_n.rb
454 def define_has_many_id_methods(name)
455   define_method_unless_defined("#{name.to_s.singularize}_ids") do
456     association_proxy(name).result_ids
457   end
458 
459   define_method_unless_defined("#{name.to_s.singularize}_ids=") do |ids|
460     clear_deferred_nodes_for_association(name)
461     association_proxy(name).replace_with(Array(ids).reject(&:blank?))
462   end
463 
464   define_method_unless_defined("#{name.to_s.singularize}_neo_ids") do
465     association_proxy(name).pluck(:neo_id)
466   end
467 end
define_has_many_methods(name, association_options) click to toggle source
    # File lib/neo4j/active_node/has_n.rb
418 def define_has_many_methods(name, association_options)
419   default_options = association_options.slice(:labels)
420 
421   define_method(name) do |node = nil, rel = nil, options = {}|
422     # return [].freeze unless self._persisted_obj
423 
424     options, node = node, nil if node.is_a?(Hash)
425 
426     options = default_options.merge(options)
427 
428     association_proxy(name, {node: node, rel: rel, source_object: self, labels: options[:labels]}.merge!(options))
429   end
430 
431   define_has_many_setter(name)
432 
433   define_has_many_id_methods(name)
434 
435   define_class_method(name) do |node = nil, rel = nil, options = {}|
436     options, node = node, nil if node.is_a?(Hash)
437 
438     options = default_options.merge(options)
439 
440     association_proxy(name, {node: node, rel: rel, labels: options[:labels]}.merge!(options))
441   end
442 end
define_has_many_setter(name) click to toggle source
    # File lib/neo4j/active_node/has_n.rb
444 def define_has_many_setter(name)
445   define_method("#{name}=") do |other_nodes|
446     association_proxy_cache.clear
447 
448     clear_deferred_nodes_for_association(name)
449 
450     self.class.run_transaction { association_proxy(name).replace_with(other_nodes) }
451   end
452 end
define_has_one_getter(name, default_options) click to toggle source
    # File lib/neo4j/active_node/has_n.rb
505 def define_has_one_getter(name, default_options)
506   define_method(name) do |node = nil, rel = nil, options = {}|
507     options, node = node, nil if node.is_a?(Hash)
508 
509     options = default_options.merge(options)
510 
511     association_proxy = association_proxy(name, {node: node, rel: rel}.merge!(options))
512 
513     # Return all results if options[:chainable] == true or a variable-length relationship length was given
514     if options[:chainable] || (options[:rel_length] && !options[:rel_length].is_a?(Integer))
515       association_proxy
516     else
517       o = association_proxy.result.first
518       self.class.send(:association_target_class, name).try(:nodeify, o) || o
519     end
520   end
521 end
define_has_one_id_methods(name) click to toggle source
    # File lib/neo4j/active_node/has_n.rb
491 def define_has_one_id_methods(name)
492   define_method_unless_defined("#{name}_id") do
493     association_proxy(name).result_ids.first
494   end
495 
496   define_method_unless_defined("#{name}_id=") do |id|
497     association_proxy(name).replace_with(id)
498   end
499 
500   define_method_unless_defined("#{name}_neo_id") do
501     association_proxy(name).pluck(:neo_id).first
502   end
503 end
define_has_one_methods(name, association_options) click to toggle source
    # File lib/neo4j/active_node/has_n.rb
473 def define_has_one_methods(name, association_options)
474   default_options = association_options.slice(:labels)
475 
476   define_has_one_getter(name, default_options)
477 
478   define_has_one_setter(name)
479 
480   define_has_one_id_methods(name)
481 
482   define_class_method(name) do |node = nil, rel = nil, options = {}|
483     options, node = node, nil if node.is_a?(Hash)
484 
485     options = default_options.merge(options)
486 
487     association_proxy(name, {node: node, rel: rel, labels: options[:labels]}.merge!(options))
488   end
489 end
define_has_one_setter(name) click to toggle source
    # File lib/neo4j/active_node/has_n.rb
523 def define_has_one_setter(name)
524   define_method("#{name}=") do |other_node|
525     if persisted?
526       other_node.save if other_node.respond_to?(:persisted?) && !other_node.persisted?
527       association_proxy_cache.clear # TODO: Should probably just clear for this association...
528       self.class.run_transaction { association_proxy(name).replace_with(other_node) }
529       # handle_non_persisted_node(other_node)
530     else
531       defer_create(name, other_node, clear: true)
532       other_node
533     end
534   end
535 end
define_method_unless_defined(method_name, &block) click to toggle source
    # File lib/neo4j/active_node/has_n.rb
469 def define_method_unless_defined(method_name, &block)
470   define_method(method_name, block) unless method_defined?(method_name)
471 end
duplicate_association?(name) click to toggle source
    # File lib/neo4j/active_node/has_n.rb
606 def duplicate_association?(name)
607   associations.key?(name) && parent_associations[name] != associations[name]
608 end