From e167f28549b80141165f42b567073c5dd55f80c5 Mon Sep 17 00:00:00 2001
From: Robert Speicher <rspeicher@gmail.com>
Date: Wed, 29 Apr 2015 17:47:55 -0400
Subject: [PATCH] Update Taskable to use TaskList

---
 app/models/concerns/taskable.rb          | 45 ++++++++----------------
 spec/support/taskable_shared_examples.rb | 30 ++++++----------
 2 files changed, 25 insertions(+), 50 deletions(-)

diff --git a/app/models/concerns/taskable.rb b/app/models/concerns/taskable.rb
index bbb3b301a9f..6e04578a7df 100644
--- a/app/models/concerns/taskable.rb
+++ b/app/models/concerns/taskable.rb
@@ -1,51 +1,36 @@
+require 'task_list'
+
 # 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
 module Taskable
-  TASK_PATTERN_MD = /^(?<bullet> *[*-] *)\[(?<checked>[ xX])\]/.freeze
-  TASK_PATTERN_HTML = /^<li>(?<p_tag>\s*<p>)?\[(?<checked>[ xX])\]/.freeze
-
-  # Change the state of a task list item for this Taskable.  Edit the object's
-  # description by finding the nth task item and changing its checkbox
-  # placeholder to "[x]" if +checked+ is true, or "[ ]" if it's false.
-  # Note: task numbering starts with 1
-  def update_nth_task(n, checked)
-    index = 0
-    check_char = checked ? 'x' : ' '
+  # Called by `TaskList::Summary`
+  def task_list_items
+    return [] if description.blank?
 
-    # Do this instead of using #gsub! so that ActiveRecord detects that a field
-    # has changed.
-    self.description = self.description.gsub(TASK_PATTERN_MD) do |match|
-      index += 1
-      case index
-      when n then "#{$LAST_MATCH_INFO[:bullet]}[#{check_char}]"
-      else match
-      end
+    @task_list_items ||= description.scan(TaskList::Filter::ItemPattern).collect do |item|
+      # ItemPattern strips out the hyphen, but Item requires it. Rabble rabble.
+      TaskList::Item.new("- #{item}")
     end
+  end
 
-    save
+  def tasks
+    @tasks ||= TaskList.new(self)
   end
 
   # Return true if this object's description has any task list items.
   def tasks?
-    description && description.match(TASK_PATTERN_MD)
+    tasks.summary.items?
   end
 
   # Return a string that describes the current state of this Taskable's task
   # list items, e.g. "20 tasks (12 done, 8 unfinished)"
   def task_status
-    return nil unless description
-
-    num_tasks = 0
-    num_done = 0
-
-    description.scan(TASK_PATTERN_MD) do
-      num_tasks += 1
-      num_done += 1 unless $LAST_MATCH_INFO[:checked] == ' '
-    end
+    return '' if description.blank?
 
-    "#{num_tasks} tasks (#{num_done} done, #{num_tasks - num_done} unfinished)"
+    sum = tasks.summary
+    "#{sum.item_count} tasks (#{sum.complete_count} done, #{sum.incomplete_count} unfinished)"
   end
 end
diff --git a/spec/support/taskable_shared_examples.rb b/spec/support/taskable_shared_examples.rb
index 8e5e3a8aafc..77e0ed5f578 100644
--- a/spec/support/taskable_shared_examples.rb
+++ b/spec/support/taskable_shared_examples.rb
@@ -4,27 +4,13 @@
 #   subject { Issue or MergeRequest }
 shared_examples 'a Taskable' do
   before do
-    subject.description = <<EOT.gsub(/ {6}/, '')
+    subject.description = <<-EOT.strip_heredoc
       * [ ] Task 1
       * [x] Task 2
       * [x] Task 3
       * [ ] Task 4
       * [ ] Task 5
-EOT
-  end
-
-  it 'updates the Nth task correctly' do
-    subject.update_nth_task(1, true)
-    expect(subject.description).to match(/\[x\] Task 1/)
-
-    subject.update_nth_task(2, true)
-    expect(subject.description).to match('\[x\] Task 2')
-
-    subject.update_nth_task(3, false)
-    expect(subject.description).to match('\[ \] Task 3')
-
-    subject.update_nth_task(4, false)
-    expect(subject.description).to match('\[ \] Task 4')
+    EOT
   end
 
   it 'returns the correct task status' do
@@ -33,10 +19,14 @@ EOT
     expect(subject.task_status).to match('3 unfinished')
   end
 
-  it 'knows if it has tasks' do
-    expect(subject.tasks?).to be_truthy
+  describe '#tasks?' do
+    it 'returns true when object has tasks' do
+      expect(subject.tasks?).to eq true
+    end
 
-    subject.description = 'Now I have no tasks'
-    expect(subject.tasks?).to be_falsey
+    it 'returns false when object has no tasks' do
+      subject.description = 'Now I have no tasks'
+      expect(subject.tasks?).to eq false
+    end
   end
 end
-- 
GitLab