Read about our upcoming Code of Conduct on this issue

resolvable_discussion.rb 2.69 KB
Newer Older
1
2
# frozen_string_literal: true

3
4
module ResolvableDiscussion
  extend ActiveSupport::Concern
5
  include ::Gitlab::Utils::StrongMemoize
6
7

  included do
Douwe Maan's avatar
Douwe Maan committed
8
9
10
11
12
13
14
15
16
    # A number of properties of this `Discussion`, like `first_note` and `resolvable?`, are memoized.
    # When this discussion is resolved or unresolved, the values of these properties potentially change.
    # To make sure all memoized values are reset when this happens, `update` resets all instance variables with names in
    # `memoized_variables`. If you add a memoized method in `ResolvableDiscussion` or any `Discussion` subclass,
    # please make sure the instance variable name is added to `memoized_values`, like below.
    cattr_accessor :memoized_values, instance_accessor: false do
      []
    end

Douwe Maan's avatar
Douwe Maan committed
17
18
19
20
21
22
23
24
    memoized_values.push(
      :resolvable,
      :resolved,
      :first_note,
      :first_note_to_resolve,
      :last_resolved_note,
      :last_note
    )
25

26
27
    delegate :potentially_resolvable?, to: :first_note

28
29
    delegate  :resolved_at,
              :resolved_by,
30
              :resolved_by_push?,
31
32
33
34
35
36

              to: :last_resolved_note,
              allow_nil: true
  end

  def resolvable?
37
38
39
    strong_memoize(:resolvable) do
      potentially_resolvable? && notes.any?(&:resolvable?)
    end
40
41
42
  end

  def resolved?
43
44
45
    strong_memoize(:resolved) do
      resolvable? && notes.none?(&:to_be_resolved?)
    end
46
47
48
  end

  def first_note
49
50
51
    strong_memoize(:first_note) do
      notes.first
    end
52
53
54
55
56
  end

  def first_note_to_resolve
    return unless resolvable?

57
58
59
    strong_memoize(:first_note_to_resolve) do
      notes.find(&:to_be_resolved?)
    end
60
61
62
63
64
  end

  def last_resolved_note
    return unless resolved?

65
    strong_memoize(:last_resolved_note) do
66
      resolved_notes.max_by(&:resolved_at)
67
    end
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
  end

  def resolved_notes
    notes.select(&:resolved?)
  end

  def to_be_resolved?
    resolvable? && !resolved?
  end

  def can_resolve?(current_user)
    return false unless current_user
    return false unless resolvable?

    current_user == self.noteable.author ||
      current_user.can?(:resolve_note, self.project)
  end

  def resolve!(current_user)
    return unless resolvable?

    update { |notes| notes.resolve!(current_user) }
  end

  def unresolve!
    return unless resolvable?

    update { |notes| notes.unresolve! }
  end
Douwe Maan's avatar
Douwe Maan committed
97
98
99
100
101
102
103
104
105
106

  private

  def update
    # Do not select `Note.resolvable`, so that system notes remain in the collection
    notes_relation = Note.where(id: notes.map(&:id))

    yield(notes_relation)

    # Set the notes array to the updated notes
107
    @notes = notes_relation.fresh.to_a # rubocop:disable Gitlab/ModuleWithInstanceVariables
Douwe Maan's avatar
Douwe Maan committed
108

109
110
    self.class.memoized_values.each do |name|
      clear_memoization(name)
Douwe Maan's avatar
Douwe Maan committed
111
112
    end
  end
113
end