app/models/concerns/relationships.rb

Summary

Maintainability
A
35 mins
Test Coverage
module Relationships
  extend ActiveSupport::Concern

  included do
    attr_accessor :relations_attributes

    after_destroy :remove_relations_for_destroyed_related!

    def relations
      Relation.find_related(self.id)
    end

    def relations_values(splitted_type=false)
      relations_with_relateds.map do |r|
        return if r.related.nil?
        rel = r.relation
        {
          id:       rel.id,
          target:   {id: r.related.id, name: r.related.name },
          metadata: metadata_values(rel.metadata),
        }.merge(
          splitted_type ?
          {rel_type: rel.rel_type, direction: relation_direction(rel)} :
          {type: relation_type(rel)}
        )
      end
    end

    def save_relations_from_attributes
      destroy_removed_relations!
      relations_attributes.each do |r|
        rel = Relation.find_or_initialize_by id: r.id
        rel.assign_attributes related_ids: [self.id, r.target], rel_type: r.rel_type, direction: r.direction
        rel.save
        rel.upsert_metadata(r.metadata)
      end
    end

    private

      def remove_relations_for_destroyed_related!
        relations.destroy_all
      end

      def relations_with_relateds
        _relateds = get_all_relateds
        relations.map { |r| OpenStruct.new relation: r, related: _relateds[related_id_for_relation(r)] }
      end

      def related_id_for_relation(r)
        r.related_ids.first == self.id.to_s ? r.related_ids.second.to_i : r.related_ids.first.to_i
      end

      def get_all_relateds
        _ids = relations.map { |r| related_id_for_relation(r) }.uniq
        Hash[ *GeoData.select([:id, :name]).where('"geo_data"."id" IN (?)', _ids).map { |g| [g.id, g] }.flatten ]
      end

      def relation_type(r)
        "#{r.rel_type}_#{relation_direction(r)}"
      end

      def relation_direction(r)
        _id = r.related_ids.first == self.id.to_s ? r.direction : swap_direction(r.direction)
      end

      def swap_direction(direction)
        (direction.to_sym == :dir) ? "rev" : "dir"
      end

      def destroy_removed_relations!
        old_ids = relations.pluck :id
        new_ids = relations_attributes.map(&:id).compact
        (old_ids - new_ids).each { |_id| Relation.find(_id).destroy }
      end

      def metadata_values(_metadata)
        accepted_keys = [:description, :start_date, :end_date, :currency, :amount]
        _metadata.nil? ? {} : _metadata.attributes.select { |k,v| accepted_keys.include? k.to_sym }
      end

  end
end