Module: Taskable

Included in:
Issuable
Defined in:
app/models/concerns/taskable.rb

Overview

Contains functionality for objects that can have task lists in their descriptions. Task list items can be added with Markdown like "* [x] Fix bugs".

Used by MergeRequest and Issue

Defined Under Namespace

Classes: Item

Constant Summary collapse

COMPLETED =
'completed'
INCOMPLETE =
'incomplete'
COMPLETE_PATTERN =
/\[[xX]\]/
INCOMPLETE_PATTERN =
/\[[[:space:]]\]/
ITEM_PATTERN =

Used by TaskListToggleService and WorkItems::TaskListReferenceReplacementService.

%r{
  ^
  (?:(?:>\s{0,4})*)               # optional blockquote characters
  ((?:\s*(?:[-+*]|(?:\d+[.)])))+) # list prefix (one or more) required - task item has to be always in a list
  \s+                             # whitespace prefix has to be always presented for a list item
  (                               # checkbox
    #{COMPLETE_PATTERN}|#{INCOMPLETE_PATTERN}
  )
  (\s.+)                          # followed by whitespace and some text.
}x

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.get_tasks(content) ⇒ Object



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'app/models/concerns/taskable.rb', line 30

def self.get_tasks(content)
  doc = Banzai::Pipeline::PlainMarkdownPipeline.call(content, {})[:output]

  items = []
  doc.xpath(Banzai::Filter::TaskListFilter::XPATH).each do |node|
    next if node.has_attribute?('data-inapplicable')

    text = Banzai::Filter::TaskListFilter.text_for_task_item_from_input(node)
    text = text.split('\n').first&.strip || ''

    source = Banzai::Filter::TaskListFilter.text_html_for_task_item_from_input(node)

    items << Taskable::Item.new(
      complete?: node.has_attribute?('checked'),
      text: text,
      source: source)
  end

  items
end

.get_updated_tasks(old_content:, new_content:) ⇒ Object



51
52
53
54
55
56
57
58
59
60
61
# File 'app/models/concerns/taskable.rb', line 51

def self.get_updated_tasks(old_content:, new_content:)
  old_tasks = get_tasks(old_content)
  new_tasks = get_tasks(new_content)

  new_tasks.select.with_index do |new_task, i|
    old_task = old_tasks[i]
    next unless old_task

    new_task.source == old_task.source && new_task.complete? != old_task.complete?
  end
end

Instance Method Details

#complete_task_list_item_countObject



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

def complete_task_list_item_count
  task_list_items.count(&:complete?)
end

#task_completion_statusObject



104
105
106
107
108
109
# File 'app/models/concerns/taskable.rb', line 104

def task_completion_status
  @task_completion_status ||= {
    count: task_list_items.count,
    completed_count: complete_task_list_item_count
  }
end

#task_list_itemsObject

Called by TaskList::Summary



64
65
66
67
68
# File 'app/models/concerns/taskable.rb', line 64

def task_list_items
  return [] if description.blank?

  @task_list_items ||= Taskable.get_tasks(description) # rubocop:disable Gitlab/ModuleWithInstanceVariables
end

#task_status(short: false) ⇒ Object

Return a string that describes the current state of this Taskable's task list items, e.g. "12 of 20 checklist items completed"



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'app/models/concerns/taskable.rb', line 81

def task_status(short: false)
  return '' if description.blank?

  checklist_item_noun = n_('checklist item', 'checklist items', task_list_items.count)
  if short
    format(s_('Tasks|%{complete_count}/%{total_count} %{checklist_item_noun}'),
      checklist_item_noun: checklist_item_noun,
      complete_count: complete_task_list_item_count,
      total_count: task_list_items.count)
  else
    format(s_('Tasks|%{complete_count} of %{total_count} %{checklist_item_noun} completed'),
      checklist_item_noun: checklist_item_noun,
      complete_count: complete_task_list_item_count,
      total_count: task_list_items.count)
  end
end

#task_status_shortObject

Return a short string that describes the current state of this Taskable's task list items -- for small screens



100
101
102
# File 'app/models/concerns/taskable.rb', line 100

def task_status_short
  task_status(short: true)
end

#tasks?Boolean

Return true if this object's description has any task list items.

Returns:

  • (Boolean)


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

def tasks?
  task_list_items.any?
end