File: awardable.rb

package info (click to toggle)
gitlab 17.6.5-19
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 629,368 kB
  • sloc: ruby: 1,915,304; javascript: 557,307; sql: 60,639; xml: 6,509; sh: 4,567; makefile: 1,239; python: 406
file content (116 lines) | stat: -rw-r--r-- 3,257 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# frozen_string_literal: true

module Awardable
  extend ActiveSupport::Concern

  included do
    has_many :award_emoji, -> { includes(:user).order(:id) }, as: :awardable, inverse_of: :awardable, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent

    if self < Participable
      # By default we always load award_emoji user association
      participant :award_emoji
    end
  end

  class_methods do
    def awarded(user, opts = {})
      inner_query = inner_filter_query(user, opts)

      where(inner_query.exists)
    end

    def not_awarded(user, opts = {})
      inner_query = inner_filter_query(user, opts)

      where(inner_query.exists.not)
    end

    def order_upvotes_desc
      order_votes(AwardEmoji::UPVOTE_NAME, 'DESC')
    end

    def order_upvotes_asc
      order_votes(AwardEmoji::UPVOTE_NAME, 'ASC')
    end

    def order_downvotes_desc
      order_votes(AwardEmoji::DOWNVOTE_NAME, 'DESC')
    end

    # Order votes by emoji, optional sort order param `descending` defaults to true
    def order_votes(emoji_name, direction, base_class_name = base_class.name, awardable_id_column = :id)
      awardable_table = self.arel_table
      awards_table = AwardEmoji.arel_table

      join_clause = awardable_table
        .join(awards_table, Arel::Nodes::OuterJoin)
        .on(awards_table[:awardable_id].eq(awardable_table[awardable_id_column])
              .and(awards_table[:awardable_type].eq(base_class_name).and(awards_table[:name].eq(emoji_name))))
        .join_sources

      joins(join_clause).group(awardable_table[:id]).reorder(
        Arel.sql("COUNT(award_emoji.id) #{direction}")
      )
    end

    private

    # Fragment used to build queries when filtering objects by award emoji
    def inner_filter_query(user, opts = {})
      award_emoji_table = Arel::Table.new('award_emoji')

      emoji_name = opts[:name]
      base_class_name = opts[:base_class_name] || base_class.name
      awardable_id_column = opts[:awardable_id_column] || self.arel_table[:id]

      inner_query =
        award_emoji_table
          .project('true')
          .where(award_emoji_table[:user_id].eq(user.id))
          .where(award_emoji_table[:awardable_type].eq(base_class_name))
          .where(award_emoji_table[:awardable_id].eq(awardable_id_column))

      inner_query.where(award_emoji_table[:name].eq(emoji_name)) if emoji_name.present?

      inner_query
    end
  end

  def grouped_awards(with_thumbs: true)
    # By default we always load award_emoji user association
    awards = award_emoji.group_by(&:name)

    if with_thumbs && (!project || project.show_default_award_emojis?)
      awards[AwardEmoji::UPVOTE_NAME]   ||= []
      awards[AwardEmoji::DOWNVOTE_NAME] ||= []
    end

    awards
  end

  def downvotes
    award_emoji.downvotes.count
  end

  def upvotes
    award_emoji.upvotes.count
  end

  def emoji_awardable?
    true
  end

  def user_can_award?(current_user)
    Ability.allowed?(current_user, :award_emoji, self)
  end

  def user_authored?(current_user)
    author = self.respond_to?(:author) ? self.author : self.user

    author == current_user
  end

  def awarded_emoji?(emoji_name, current_user)
    award_emoji.named(emoji_name).awarded_by(current_user).exists?
  end
end