Read about our upcoming Code of Conduct on this issue

resolvable_discussion.rb 2.63 KB
Newer Older
1
2
module ResolvableDiscussion
  extend ActiveSupport::Concern
3
  include ::Gitlab::Utils::StrongMemoize
4
5

  included do
Douwe Maan's avatar
Douwe Maan committed
6
7
8
9
10
11
12
13
14
    # 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
15
16
17
18
19
20
21
22
    memoized_values.push(
      :resolvable,
      :resolved,
      :first_note,
      :first_note_to_resolve,
      :last_resolved_note,
      :last_note
    )
23

24
25
    delegate :potentially_resolvable?, to: :first_note

26
27
    delegate  :resolved_at,
              :resolved_by,
28
              :resolved_by_push?,
29
30
31
32
33
34

              to: :last_resolved_note,
              allow_nil: true
  end

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

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

  def first_note
47
    notes.first
48
49
50
51
52
  end

  def first_note_to_resolve
    return unless resolvable?

53
54
55
    strong_memoize(:first_note_to_resolve) do
      notes.find(&:to_be_resolved?)
    end
56
57
58
59
60
  end

  def last_resolved_note
    return unless resolved?

61
62
63
    strong_memoize(:last_resolved_note) do
      resolved_notes.sort_by(&:resolved_at).last
    end
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
  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
93
94
95
96
97
98
99
100
101
102

  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
103
    @notes = notes_relation.fresh.to_a # rubocop:disable Gitlab/ModuleWithInstanceVariables
Douwe Maan's avatar
Douwe Maan committed
104
105
106
107
108

    self.class.memoized_values.each do |var|
      instance_variable_set(:"@#{var}", nil)
    end
  end
109
end