module ActiveGraph::Node::HasN::ClassMethods

rubocop:disable Metrics/ModuleLength

Public Instance Methods

association?(name) click to toggle source

rubocop:enable Naming/PredicateName

    # File lib/active_graph/node/has_n.rb
292 def association?(name)
293   !!associations[name.to_sym]
294 end
associations() click to toggle source
    # File lib/active_graph/node/has_n.rb
300 def associations
301   (@associations ||= parent_associations.dup)
302 end
associations_keys() click to toggle source
    # File lib/active_graph/node/has_n.rb
304 def associations_keys
305   @associations_keys ||= associations.keys
306 end
has_association?(name) click to toggle source

:nocov:

    # File lib/active_graph/node/has_n.rb
283 def has_association?(name)
284   ActiveSupport::Deprecation.warn 'has_association? is deprecated and may be removed from future releases, use association? instead.', caller
285 
286   association?(name)
287 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 `Node` class,
    `false` to specify any model, or nil to specify that it should be guessed.

  *rel_class*: The ``Relationship`` class to use for this association.  Can be either a
    model object ``include`` ing ``Relationship`` 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/active_graph/node/has_n.rb
391 def has_many(direction, name, options = {}) # rubocop:disable Naming/PredicateName
392   name = name.to_sym
393   build_association(:has_many, direction, name, options)
394 
395   define_has_many_methods(name, options)
396 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 <ActiveGraph/Node/HasN/ClassMethods#has_many>` for anything not specified here

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

Private Instance Methods

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