weathermen/soundstorm

View on GitHub
app/models/paper_trail/version.decorator

Summary

Maintainability
Test Coverage
# frozen_string_literal: true

module PaperTrail
  decorate Version do
    decorated do
      after_create :broadcast

      delegate :actor, to: :user
      delegate :activity_id, :as_activity, to: :item

      scope :unpublished, -> { where(broadcasted_at: nil) }
    end

    # Users that are following this change.
    #
    # @return [ActiveRecord::Relation<User>]
    def followers
      return [] if user.blank?
      user.followers(User)
    end

    # Users from remote Soundstorm instances that are following this
    # change.
    def remote_followers
      followers.reject do |follower|
        follower.host == Soundstorm::HOST
      end
    end

    # User who performed this change.
    #
    # @return [User] or `nil` if the whodunnit field is blank.
    def user
      return if whodunnit.blank?
      GlobalID::Locator.locate(whodunnit)
    end

    # Test if this change is a track upload.
    #
    # @return [Boolean] Whether the item being changed is a Track
    #                   and event is "create".
    def upload?
      item.is_a?(Track) && event == 'create'
    end

    # Denote what kind of change this is on the frontend.
    #
    # @return [String] "uploaded" or "created"
    def verb
      case event
      when 'create'
        upload? ? 'uploaded' : 'created'
      when 'update'
        'updated'
      when 'destroy'
        'deleted'
      end
    end

    # Type of the event being broadcast.
    #
    # @return [String] Class name of the event type.
    def type
      event.classify
    end

    # The message object used to broadcast over ActivityPub.
    #
    # @return [ActivityPub::Message]
    def message
      ActivityPub::Message.new(
        id: activity_id,
        type: type,
        published: created_at,
        actor: actor,
        payload: as_activity
      )
    end

    # Whether this activity has been broadcast.
    #
    # @return [Boolean] whether `broadcasted_at` is present.
    def broadcasted?
      broadcasted_at.present?
    end

    # Whether this activity should be broadcasted in ActivityPub.
    #
    # @return [Boolean] `true` if not broadcasted and remote
    def broadcastable?
      !broadcasted? && remote?
    end

    # Whether any remote users should receive a broadcast of this
    # activity.
    def remote?
      remote_followers.any?
    end

    private

    # Publish this event to all ActivityPub subscribers.
    #
    # @return [BroadcastMessageJob] or `nil` if not broadcasting
    def broadcast
      BroadcastMessageJob.perform_later(self) if broadcastable?
    end
  end
end