opf/openproject

View on GitHub
lib_static/open_project/acts/favorable.rb

Summary

Maintainability
A
0 mins
Test Coverage
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2024 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++

module OpenProject
  module Acts
    module Favorable
      def self.included(base)
        base.extend ClassMethods
      end

      module ClassMethods
        # Marks an ActiveRecord::Model as favorable
        # A favorable model has association with users (watchers) that marked it as favorite.
        #
        # This also creates the routes necessary for favoring/unfavoring by
        # adding the model's name to routes. This e.g leads to the following
        # routes when marking issues as watchable:
        #   POST:     projects/identifier/favorite
        #   DELETE:   projects/identifier/favorite
        #
        # acts_as_favorable expects that the including module defines a +visible?(user)+ method,
        # as it's used to identify whether a user can actually favorite the object.
        def acts_as_favorable
          return if included_modules.include?(::OpenProject::Acts::Favorable::InstanceMethods)
          OpenProject::Acts::Favorable::Registry.add(self)

          class_eval do
            has_many :favorites, as: :favored, dependent: :delete_all, validate: false
            has_many :favoring_users, through: :favorites, source: :user, validate: false

            scope :favored_by, ->(user_id) {
              includes(:favorites)
                .where(favorites: { user_id: })
            }
          end

          send :prepend, ::OpenProject::Acts::Favorable::InstanceMethods
        end
      end

      module InstanceMethods
        def self.prepended(base)
          base.extend ClassMethods
        end

        def add_favoring_user(user)
          return if favorites.exists?(user_id: user.id)

          favorites << Favorite.new(user:, favored: self)
        end

        def remove_favoring_user(user)
          favorites.where(user:).delete_all
        end

        def set_favored(user, favored: true)
          favored ? add_favoring_user(user) : remove_favoring_user(user)
        end

        def favored_by?(user)
          favorites.exists?(user:)
        end
      end
    end
  end
end