deliveroo/routemaster

View on GitHub
routemaster/models/subscription.rb

Summary

Maintainability
A
0 mins
Test Coverage
require 'routemaster/models'
require 'routemaster/mixins/redis'
require 'routemaster/mixins/assert'
require 'routemaster/mixins/log'

module Routemaster
  module Models
    # The relation between a Subscriber and a Topic
    class Subscription
      include Mixins::Redis
      include Mixins::Assert
      include Mixins::Log

      attr_reader :subscriber, :topic

      def initialize(subscriber:, topic:)
        @subscriber = subscriber
        @topic = topic
      end

      def save
        _persist(:sadd, 'subscribed to')
        self
      end

      def destroy
        _persist(:srem, 'unsubscribed from')
        self
      end

      def ==(other)
        subscriber == other.subscriber && topic == other.topic
      end

      alias_method :eql?, :==

      module ClassMethods
        include Mixins::Assert

        def find(subscriber:, topic:)
          return unless exists?(subscriber: subscriber, topic: topic)
          new(subscriber: subscriber, topic: topic)
        end

        def exists?(subscriber:, topic:)
          _redis.sismember(_key_topic(topic), subscriber.name)
        end

        def where(subscriber: nil, topic: nil)
          _assert(subscriber.nil? ^ topic.nil?, 'exactly one or subscriber or topic must be provided')
          if subscriber
            Set.new _redis.smembers(_key_subscriber(subscriber)).map { |name|
              topic = Topic.find(name)
              _assert !!topic, "topic '#{name}' should exist"
              new(subscriber: subscriber, topic: topic)
            }
          else
            names = _redis.smembers(_key_topic(topic))
            Set.new Subscriber.where(name: names).map { |sub|
              new(subscriber: sub, topic: topic)
            }
          end
        end

        private

        def _key_subscriber(subscriber)
          "topics:#{subscriber.name}"
        end

        def _key_topic(topic)
          "subscribers:#{topic.name}"
        end
      end
      extend ClassMethods

      private

      def _key_subscriber
        self.class.send(__method__, @subscriber)
      end

      def _key_topic
        self.class.send(__method__, @topic)
      end

      def _persist(op, msg)
        res = _redis.multi { |m|
          m.public_send op, _key_topic, @subscriber.name
          m.public_send op, _key_subscriber, @topic.name
        }

        res.any? and _log.info {
          "'#{@subscriber.name}' #{msg} '#{@topic.name}'"
        }
        self
      end
    end
  end
end