gadzorg/gram2_api_server

View on GitHub
lib/gorg_rabbitmq_notifier/gorg_rabbitmq_notifier/active_record_extension.rb

Summary

Maintainability
A
55 mins
Test Coverage
require "active_support/concern"

module GorgRabbitmqNotifier::ActiveRecordExtension
  extend ActiveSupport::Concern

  included do
    # Class methods
    def self.rabbitmq_id(method_name = nil, &block)
      @rabbitmq_id_proc =
        (block_given? ? block : Proc.new { |o| o.send(method_name) })
    end

    def self.rabbitmq_resource_type(resource_type)
      @rabbitmq_resource_type = resource_type
    end

    def self.get_rabbitmq_resource_type
      @rabbitmq_resource_type || self.model_name.singular
    end

    around_save :capture_serialisation_changes
    # Instance methods
    def rabbitmq_id
      self.class.rabbitmq_id_proc.call(self)
    end

    def send_rabbitmq_notification(action, changes = {})
      message =
        GorgRabbitmqNotifier::NotificationMessage.new(
          resource_type: self.class.get_rabbitmq_resource_type,
          action: action.to_s,
          resource_id_attribute: "key",
          resource_id_value: rabbitmq_id,
          changes: changes,
        )
      message.commit!
    end

    protected

    def self.rabbitmq_id_proc
      @rabbitmq_id_proc
    end

    def capture_add_association(object)
      capture_association_modification(:add, object)
    end

    def capture_del_association(object)
      capture_association_modification(:delete, object)
    end

    def capture_association_modification(action, object)
      process_association(action, self, object)
      process_association(action, object, self)
    end

    def process_association(action, from_object, to_object)
      if to_object.respond_to? :rabbitmq_id
        from_serializer = ActiveModel::Serializer.serializer_for(from_object)
        to_serializer = ActiveModel::Serializer.serializer_for(to_object)
        association =
          from_serializer._reflections.find do |name, params|
            params.options[:serializer] == to_serializer
          end
        case association.class.to_s
        when "ActiveModel::Serializer::HasManyReflection"
          from_object.send_rabbitmq_notification(
            :updated,
            changes = {
              association.name => { action => [to_object.rabbitmq_id] }
            },
          )
        when "ActiveModel::Serializer::HasOneReflection"
          #ToDo
          nil
        when "ActiveModel::Serializer::BelongsToReflection"
          #ToDo
          nil
        end
      end
    end

    # This method doesn't use ActiveRecord's method "previous_changes"
    # because we need to capture changes on the serialized representation
    # of the resource.
    def capture_serialisation_changes
      serialiser = ActiveModel::Serializer.serializer_for(self)
      if self.id
        before = serialiser.new(self.clone.reload).attributes
        action = :updated
      else
        before = {}
        action = :created
      end

      GorgRabbitmqNotifier.batch do
        yield

        after = serialiser.new(self).attributes
        changes = deep_hash_diff(before, after)
        send_rabbitmq_notification(action, changes)
      end
    end

    def deep_hash_diff(a, b)
      (a.keys | b.keys).inject({}) do |diff, k|
        if a[k] != b[k]
          if a[k].respond_to?(:deep_diff) && b[k].respond_to?(:deep_diff)
            diff[k] = a[k].deep_diff(b[k])
          else
            diff[k] = [a[k], b[k]]
          end
        end
        diff
      end
    end
  end
end