Class: Discussion

Inherits:
Object
  • Object
show all
Includes:
GlobalID::Identification, ResolvableDiscussion
Defined in:
app/models/discussion.rb

Overview

A non-diff discussion on an issue, merge request, commit, or snippet, consisting of `DiscussionNote` notes.

A discussion of this type can be resolvable.

Constant Summary collapse

CACHE_VERSION =

Bump this if we need to refresh the cached versions of discussions

1

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from ResolvableDiscussion

#can_resolve?, #clear_memoized_values, #first_note, #first_note_to_resolve, #last_resolved_note, #resolvable?, #resolve!, #resolved?, #resolved_by_push?, #resolved_notes, #to_be_resolved?, #unresolve!

Methods included from Gitlab::Utils::StrongMemoize

#clear_memoization, #strong_memoize, #strong_memoized?

Constructor Details

#initialize(notes, context_noteable = nil) ⇒ Discussion

Returns a new instance of Discussion.


105
106
107
108
# File 'app/models/discussion.rb', line 105

def initialize(notes, context_noteable = nil)
  @notes = notes
  @context_noteable = context_noteable
end

Instance Attribute Details

#context_noteableObject (readonly)

Returns the value of attribute context_noteable.


13
14
15
# File 'app/models/discussion.rb', line 13

def context_noteable
  @context_noteable
end

#notesObject (readonly)

Returns the value of attribute notes.


13
14
15
# File 'app/models/discussion.rb', line 13

def notes
  @notes
end

Class Method Details

.base_discussion_id(note) ⇒ Object


79
80
81
82
# File 'app/models/discussion.rb', line 79

def self.base_discussion_id(note)
  noteable_id = note.noteable_id || note.commit_id
  [:discussion, note.noteable_type.try(:underscore), noteable_id]
end

.build(notes, context_noteable = nil) ⇒ Object


41
42
43
# File 'app/models/discussion.rb', line 41

def self.build(notes, context_noteable = nil)
  notes.first.discussion_class(context_noteable).new(notes, context_noteable)
end

.build_collection(notes, context_noteable = nil) ⇒ Object


45
46
47
48
# File 'app/models/discussion.rb', line 45

def self.build_collection(notes, context_noteable = nil)
  grouped_notes = notes.group_by { |n| n.discussion_id(context_noteable) }
  grouped_notes.values.map { |notes| build(notes, context_noteable) }
end

.build_discussion_id(note) ⇒ Object

Returns an array of discussion ID components


75
76
77
# File 'app/models/discussion.rb', line 75

def self.build_discussion_id(note)
  [*base_discussion_id(note), SecureRandom.hex]
end

.build_discussions(discussion_ids, context_noteable = nil, preload_note_diff_file: false) ⇒ Object


50
51
52
53
54
55
56
# File 'app/models/discussion.rb', line 50

def self.build_discussions(discussion_ids, context_noteable = nil, preload_note_diff_file: false)
  notes = Note.where(discussion_id: discussion_ids).fresh
  notes = notes.inc_note_diff_file if preload_note_diff_file

  grouped_notes = notes.group_by { |n| n.discussion_id }
  grouped_notes.transform_values { |notes| Discussion.build(notes, context_noteable) }
end

.discussion_id(note) ⇒ Object

Returns an alphanumeric discussion ID based on `build_discussion_id`


70
71
72
# File 'app/models/discussion.rb', line 70

def self.discussion_id(note)
  Digest::SHA1.hexdigest(build_discussion_id(note).join("-"))
end

.lazy_find(discussion_id) ⇒ Object


58
59
60
61
62
63
64
65
66
67
# File 'app/models/discussion.rb', line 58

def self.lazy_find(discussion_id)
  BatchLoader.for(discussion_id).batch do |discussion_ids, loader|
    results = Note.where(discussion_id: discussion_ids).fresh.to_a.group_by(&:discussion_id)
    results.each do |discussion_id, notes|
      next if notes.empty?

      loader.call(discussion_id, Discussion.build(notes))
    end
  end
end

.note_classObject


101
102
103
# File 'app/models/discussion.rb', line 101

def self.note_class
  DiscussionNote
end

.override_discussion_id(note) ⇒ Object

When notes on a commit are displayed in context of a merge request that contains that commit, these notes are to be displayed as if they were part of one discussion, even though they were actually individual notes on the commit with different discussion IDs, so that it's clear that these are not notes on the merge request itself.

To turn a list of notes into a list of discussions, they are grouped by discussion ID, so to get these out-of-context notes to end up in the same discussion, we need to get them to return the same `discussion_id` when this grouping happens. To enable this, `Note#discussion_id` calls out to the `override_discussion_id` method on the appropriate `Discussion` subclass, as determined by the `discussion_class` method on `Note` or a subclass of `Note`.

If no override is necessary, return `nil`. For the case described above, see `OutOfContextDiscussion.override_discussion_id`.


97
98
99
# File 'app/models/discussion.rb', line 97

def self.override_discussion_id(note)
  nil
end

Instance Method Details

#==(other) ⇒ Object


114
115
116
117
118
119
# File 'app/models/discussion.rb', line 114

def ==(other)
  other.class == self.class &&
    other.context_noteable == self.context_noteable &&
    other.id == self.id &&
    other.notes == self.notes
end

#cache_keyObject


173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'app/models/discussion.rb', line 173

def cache_key
  # Need to use the notes' cache key so cache will be invalidated when note
  # within a discussion has been deleted or has different data after post
  # processing of content.
  notes_sha = Digest::SHA1.hexdigest(notes.map(&:post_processed_cache_key).join(':'))

  [
    CACHE_VERSION,
    id,
    notes_sha,
    resolved_at
  ].join(':')
end

#can_convert_to_discussion?Boolean

Returns:

  • (Boolean)

153
154
155
# File 'app/models/discussion.rb', line 153

def can_convert_to_discussion?
  false
end

#collapsed?Boolean

Returns:

  • (Boolean)

161
162
163
# File 'app/models/discussion.rb', line 161

def collapsed?
  resolved?
end

#declarative_policy_delegateObject


33
34
35
# File 'app/models/discussion.rb', line 33

def declarative_policy_delegate
  first_note
end

#diff_discussion?Boolean

Returns:

  • (Boolean)

145
146
147
# File 'app/models/discussion.rb', line 145

def diff_discussion?
  false
end

#expanded?Boolean

Returns:

  • (Boolean)

165
166
167
# File 'app/models/discussion.rb', line 165

def expanded?
  !collapsed?
end

#idObject Also known as: to_param


133
134
135
# File 'app/models/discussion.rb', line 133

def id
  first_note.discussion_id(context_noteable)
end

#individual_note?Boolean

Returns:

  • (Boolean)

149
150
151
# File 'app/models/discussion.rb', line 149

def individual_note?
  false
end

#last_noteObject


157
158
159
# File 'app/models/discussion.rb', line 157

def last_note
  @last_note ||= notes.last
end

#last_updated_atObject


121
122
123
# File 'app/models/discussion.rb', line 121

def last_updated_at
  last_note.created_at
end

#last_updated_byObject


125
126
127
# File 'app/models/discussion.rb', line 125

def last_updated_by
  last_note.author
end

#on_image?Boolean

Returns:

  • (Boolean)

110
111
112
# File 'app/models/discussion.rb', line 110

def on_image?
  false
end

#project_idObject


37
38
39
# File 'app/models/discussion.rb', line 37

def project_id
  project&.id
end

#reply_attributesObject


169
170
171
# File 'app/models/discussion.rb', line 169

def reply_attributes
  first_note.slice(:type, :noteable_type, :noteable_id, :commit_id, :discussion_id)
end

#reply_idObject


137
138
139
140
141
# File 'app/models/discussion.rb', line 137

def reply_id
  # To reply to this discussion, we need the actual discussion_id from the database,
  # not the potentially overwritten one based on the noteable.
  first_note.discussion_id
end

#updated?Boolean

Returns:

  • (Boolean)

129
130
131
# File 'app/models/discussion.rb', line 129

def updated?
  last_updated_at != created_at
end