lib/neoid/relationship.rb in neoid-0.1.2 vs lib/neoid/relationship.rb in neoid-0.2.0

- old
+ new

@@ -1,7 +1,72 @@ module Neoid module Relationship + class << self + def from_hash(hash) + relationship = RelationshipLazyProxy.new(hash) + + relationship + end + + def included(receiver) + receiver.send :include, Neoid::ModelAdditions + receiver.send :include, InstanceMethods + receiver.extend ClassMethods + + initialize_relationship receiver if Neoid.env_loaded + + Neoid.relationship_models << receiver + end + + def meta_data + @meta_data ||= {} + end + + def initialize_relationship(rel_model) + rel_model.reflect_on_all_associations(:belongs_to).each do |belongs_to| + return if belongs_to.options[:polymorphic] + + # e.g. all has_many on User class + all_has_many = belongs_to.klass.reflect_on_all_associations(:has_many) + + # has_many (without through) on the side of the relationship that removes a relationship. e.g. User has_many :likes + this_has_many = all_has_many.find { |o| o.klass == rel_model } + next unless this_has_many + + # e.g. User has_many :likes, after_remove: ... + full_callback_name = "after_remove_for_#{this_has_many.name}" + belongs_to.klass.send(full_callback_name) << :neo_after_relationship_remove if belongs_to.klass.method_defined?(full_callback_name) + + # has_many (with through) on the side of the relationship that removes a relationship. e.g. User has_many :movies, through :likes + many_to_many = all_has_many.find { |o| o.options[:through] == this_has_many.name } + next unless many_to_many + + return if many_to_many.options[:as] # polymorphic are not supported here yet + + # user_id + foreign_key_of_owner = many_to_many.through_reflection.foreign_key + + # movie_id + foreign_key_of_record = many_to_many.source_reflection.foreign_key + + (Neoid::Relationship.meta_data ||= {}).tap do |data| + (data[belongs_to.klass.name.to_s] ||= {}).tap do |model_data| + model_data[many_to_many.klass.name.to_s] = [rel_model.name.to_s, foreign_key_of_owner, foreign_key_of_record] + end + end + + # e.g. User has_many :movies, through: :likes, before_remove: ... + full_callback_name = "before_remove_for_#{many_to_many.name}" + belongs_to.klass.send(full_callback_name) << :neo_before_relationship_through_remove if belongs_to.klass.method_defined?(full_callback_name) + + # e.g. User has_many :movies, through: :likes, after_remove: ... + full_callback_name = "after_remove_for_#{many_to_many.name}" + belongs_to.klass.send(full_callback_name) << :neo_after_relationship_through_remove if belongs_to.klass.method_defined?(full_callback_name) + end + end + end + # this is a proxy that delays loading of start_node and end_node from Neo4j until accessed. # the original Neography Relatioship loaded them on initialization class RelationshipLazyProxy < ::Neography::Relationship def start_node @start_node_from_db ||= @start_node = Neography::Node.load(@start_node, Neoid.db) @@ -10,45 +75,38 @@ def end_node @end_node_from_db ||= @end_node = Neography::Node.load(@end_node, Neoid.db) end end - def self.from_hash(hash) - relationship = RelationshipLazyProxy.new(hash) - - relationship - end - - module ClassMethods def delete_command :delete_relationship end end module InstanceMethods def neo_find_by_id - results = Neoid.db.get_relationship_auto_index(Neoid::UNIQUE_ID_KEY, self.neo_unique_id) + results = Neoid.db.get_relationship_auto_index(Neoid::UNIQUE_ID_KEY, neo_unique_id) relationship = results.present? ? Neoid::Relationship.from_hash(results[0]) : nil relationship end - + def _neo_save return unless Neoid.enabled? - + options = self.class.neoid_config.relationship_options - - start_item = self.send(options[:start_node]) - end_item = self.send(options[:end_node]) - + + start_item = send(options[:start_node]) + end_item = send(options[:end_node]) + return unless start_item && end_item # initialize nodes start_item.neo_node end_item.neo_node - data = self.to_neo.merge(ar_type: self.class.name, ar_id: self.id, Neoid::UNIQUE_ID_KEY => self.neo_unique_id) + data = to_neo.merge(ar_type: self.class.name, ar_id: id, Neoid::UNIQUE_ID_KEY => neo_unique_id) data.reject! { |k, v| v.nil? } rel_type = options[:type].is_a?(Proc) ? options[:type].call(self) : options[:type] gremlin_query = <<-GREMLIN @@ -73,91 +131,33 @@ } relationship GREMLIN - script_vars = { + script_vars = { unique_id_key: Neoid::UNIQUE_ID_KEY, relationship_data: data, - unique_id: self.neo_unique_id, + unique_id: neo_unique_id, start_node_unique_id: start_item.neo_unique_id, end_node_unique_id: end_item.neo_unique_id, rel_type: rel_type } - Neoid::logger.info "Relationship#neo_save #{self.class.name} #{self.id}" - + Neoid::logger.info "Relationship#neo_save #{self.class.name} #{id}" + relationship = Neoid.execute_script_or_add_to_batch gremlin_query, script_vars do |value| Neoid::Relationship.from_hash(value) end relationship end - + def neo_load(hash) Neoid::Relationship.from_hash(hash) end def neo_relationship _neo_representation - end - end - - def self.included(receiver) - receiver.send :include, Neoid::ModelAdditions - receiver.send :include, InstanceMethods - receiver.extend ClassMethods - - initialize_relationship receiver if Neoid.env_loaded - - Neoid.relationship_models << receiver - end - - def self.meta_data - @meta_data ||= {} - end - - def self.initialize_relationship(rel_model) - rel_model.reflect_on_all_associations(:belongs_to).each do |belongs_to| - return if belongs_to.options[:polymorphic] - - # e.g. all has_many on User class - all_has_many = belongs_to.klass.reflect_on_all_associations(:has_many) - - # has_many (without through) on the side of the relationship that removes a relationship. e.g. User has_many :likes - this_has_many = all_has_many.find { |o| o.klass == rel_model } - next unless this_has_many - - # e.g. User has_many :likes, after_remove: ... - full_callback_name = "after_remove_for_#{this_has_many.name}" - belongs_to.klass.send(full_callback_name) << :neo_after_relationship_remove if belongs_to.klass.method_defined?(full_callback_name) - - # has_many (with through) on the side of the relationship that removes a relationship. e.g. User has_many :movies, through :likes - many_to_many = all_has_many.find { |o| o.options[:through] == this_has_many.name } - next unless many_to_many - - return if many_to_many.options[:as] # polymorphic are not supported here yet - - # user_id - foreign_key_of_owner = many_to_many.through_reflection.foreign_key - - # movie_id - foreign_key_of_record = many_to_many.source_reflection.foreign_key - - (Neoid::Relationship.meta_data ||= {}).tap do |data| - (data[belongs_to.klass.name.to_s] ||= {}).tap do |model_data| - model_data[many_to_many.klass.name.to_s] = [rel_model.name.to_s, foreign_key_of_owner, foreign_key_of_record] - end - end - - # e.g. User has_many :movies, through: :likes, before_remove: ... - full_callback_name = "before_remove_for_#{many_to_many.name}" - belongs_to.klass.send(full_callback_name) << :neo_before_relationship_through_remove if belongs_to.klass.method_defined?(full_callback_name) - - - # e.g. User has_many :movies, through: :likes, after_remove: ... - full_callback_name = "after_remove_for_#{many_to_many.name}" - belongs_to.klass.send(full_callback_name) << :neo_after_relationship_through_remove if belongs_to.klass.method_defined?(full_callback_name) end end end end