diff options
Diffstat (limited to 'app/models/concerns/resolvable_discussion.rb')
-rw-r--r-- | app/models/concerns/resolvable_discussion.rb | 103 |
1 files changed, 103 insertions, 0 deletions
diff --git a/app/models/concerns/resolvable_discussion.rb b/app/models/concerns/resolvable_discussion.rb new file mode 100644 index 00000000000..dd979e7bb17 --- /dev/null +++ b/app/models/concerns/resolvable_discussion.rb @@ -0,0 +1,103 @@ +module ResolvableDiscussion + extend ActiveSupport::Concern + + included do + # 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 + + memoized_values.push( + :resolvable, + :resolved, + :first_note, + :first_note_to_resolve, + :last_resolved_note, + :last_note + ) + + delegate :potentially_resolvable?, to: :first_note + + delegate :resolved_at, + :resolved_by, + + to: :last_resolved_note, + allow_nil: true + end + + def resolvable? + return @resolvable if @resolvable.present? + + @resolvable = potentially_resolvable? && notes.any?(&:resolvable?) + end + + def resolved? + return @resolved if @resolved.present? + + @resolved = resolvable? && notes.none?(&:to_be_resolved?) + end + + def first_note + @first_note ||= notes.first + end + + def first_note_to_resolve + return unless resolvable? + + @first_note_to_resolve ||= notes.find(&:to_be_resolved?) + end + + def last_resolved_note + return unless resolved? + + @last_resolved_note ||= resolved_notes.sort_by(&:resolved_at).last + 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 + + 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 + @notes = notes_relation.fresh.to_a + + self.class.memoized_values.each do |var| + instance_variable_set(:"@#{var}", nil) + end + end +end |