Todo.txt TDD Part 3
As mentioned at the end of Part 2, after the creation date, the rest of the string is called the Description. It can contain projects that start with a plus sign(+) or contexts that start with an at symbol(@) or key/value pairs with a colon(:). We’ll test the projects piece now.
Sub TEST_Projects() Dim clsTodo As CTodo Set clsTodo = New CTodo clsTodo.Raw = "(A) 2016-04-30 Call Mom+Dad due:2016-05-30" Debug.Assert clsTodo.Projects.Count = 0 Set clsTodo = New CTodo clsTodo.Raw = "(A) 2016-05-20 2016-04-30 Call Mom @Phone +Family due:2016-05-30" Debug.Assert clsTodo.Projects.Count = 1 Debug.Assert clsTodo.Projects(1).Tag = "Family" Set clsTodo = New CTodo clsTodo.Raw = "(A) 2016-04-30 Call Mom +Phone +Family due:2016-05-30" Debug.Assert clsTodo.Projects.Count = 2 Debug.Print "TEST_Projects" End Sub
I’m testing zero, one, and two projects. Now let’s update Raw to make this pass
Public Property Let Raw(ByVal sRaw As String) Dim vaSplit As Variant Dim lNext As Long Dim i As Long Dim clsProject As CProject vaSplit = Split(sRaw, Space(1)) Me.Complete = vaSplit(0) = "x" If vaSplit(0) = "x" Then lNext = lNext + 1 End If If vaSplit(lNext) Like "([A-Z])" Then Me.Priority = Mid$(vaSplit(lNext), 2, 1) lNext = lNext + 1 End If If IsDate(vaSplit(lNext)) Then If IsDate(vaSplit(lNext + 1)) Then Me.CompleteDate = DateValue(vaSplit(lNext)) Me.CreationDate = DateValue(vaSplit(lNext + 1)) lNext = lNext + 2 Else Me.CreationDate = DateValue(vaSplit(lNext)) lNext = lNext + 1 End If End If For i = lNext To UBound(vaSplit) If Left$(vaSplit(i), 1) = "+" Then Set clsProject = New CProject clsProject.Tag = Mid$(vaSplit(i), 2, Len(vaSplit(i))) Me.Projects.Add clsProject End If Next i End Property
This loops through the rest of the elements of the split array and looks for a plus sign at the start. If it finds one, it creates a Project instance and adds it to the Projects collection class. The contexts will be handled similarly.
Sub TEST_Contexts() Dim clsTodo As CTodo Set clsTodo = New CTodo clsTodo.Raw = "(A) 2016-04-30 Call Mom@home due:2016-05-30" Debug.Assert clsTodo.Contexts.Count = 0 Set clsTodo = New CTodo clsTodo.Raw = "(A) 2016-05-20 2016-04-30 Call Mom @Phone +Family due:2016-05-30" Debug.Assert clsTodo.Contexts.Count = 1 Debug.Assert clsTodo.Contexts(1).Tag = "Phone" Set clsTodo = New CTodo clsTodo.Raw = "(A) 2016-04-30 Call Mom @Phone @Family due:2016-05-30" Debug.Assert clsTodo.Contexts.Count = 2 Debug.Print "TEST_Contexts" End Sub
Public Property Let Raw(ByVal sRaw As String) Dim vaSplit As Variant Dim lNext As Long Dim i As Long Dim clsProject As CProject Dim clsContext As CContext vaSplit = Split(sRaw, Space(1)) Me.Complete = vaSplit(0) = "x" If vaSplit(0) = "x" Then lNext = lNext + 1 End If If vaSplit(lNext) Like "([A-Z])" Then Me.Priority = Mid$(vaSplit(lNext), 2, 1) lNext = lNext + 1 End If If IsDate(vaSplit(lNext)) Then If IsDate(vaSplit(lNext + 1)) Then Me.CompleteDate = DateValue(vaSplit(lNext)) Me.CreationDate = DateValue(vaSplit(lNext + 1)) lNext = lNext + 2 Else Me.CreationDate = DateValue(vaSplit(lNext)) lNext = lNext + 1 End If End If For i = lNext To UBound(vaSplit) If Left$(vaSplit(i), 1) = "+" Then Set clsProject = New CProject clsProject.Tag = Mid$(vaSplit(i), 2, Len(vaSplit(i))) Me.Projects.Add clsProject ElseIf Left$(vaSplit(i), 1) = "@" Then Set clsContext = New CContext clsContext.Tag = Mid$(vaSplit(i), 2, Len(vaSplit(i))) Me.Contexts.Add clsContext End If Next i End Property
The final special case inside the description is key/value pairs.
Sub TEST_KeyValue() Dim clsTodo As CTodo Set clsTodo = New CTodo clsTodo.Raw = "(A) 2016-04-30 Call Mom@home" Debug.Assert clsTodo.KeyValues.Count = 0 Set clsTodo = New CTodo clsTodo.Raw = "(A) 2016-05-20 2016-04-30 Call Mom @Phone +Family due:2016-05-30" Debug.Assert clsTodo.KeyValues.Count = 1 Debug.Assert clsTodo.KeyValues(1).Key = "due" Debug.Assert clsTodo.KeyValues(1).Value = "2016-05-30" Set clsTodo = New CTodo clsTodo.Raw = "(A) 2016-04-30 Call Mom @Phone @Family due:2016-05-30 key:val:ue" Debug.Assert clsTodo.KeyValues.Count = 2 Debug.Print "TEST_KeyValue" End Sub
Again I’m testing zero, one, and two instances.
Public Property Let Raw(ByVal sRaw As String) Dim vaSplit As Variant Dim lNext As Long Dim i As Long Dim clsProject As CProject Dim clsContext As CContext Dim clsKeyValue As CKeyValue Dim vaKeys As Variant vaSplit = Split(sRaw, Space(1)) Me.Complete = vaSplit(0) = "x" If vaSplit(0) = "x" Then lNext = lNext + 1 End If If vaSplit(lNext) Like "([A-Z])" Then Me.Priority = Mid$(vaSplit(lNext), 2, 1) lNext = lNext + 1 End If If IsDate(vaSplit(lNext)) Then If IsDate(vaSplit(lNext + 1)) Then Me.CompleteDate = DateValue(vaSplit(lNext)) Me.CreationDate = DateValue(vaSplit(lNext + 1)) lNext = lNext + 2 Else Me.CreationDate = DateValue(vaSplit(lNext)) lNext = lNext + 1 End If End If For i = lNext To UBound(vaSplit) If Left$(vaSplit(i), 1) = "+" Then Set clsProject = New CProject clsProject.Tag = Mid$(vaSplit(i), 2, Len(vaSplit(i))) Me.Projects.Add clsProject ElseIf Left$(vaSplit(i), 1) = "@" Then Set clsContext = New CContext clsContext.Tag = Mid$(vaSplit(i), 2, Len(vaSplit(i))) Me.Contexts.Add clsContext ElseIf InStr(1, vaSplit(i), ":") > 1 Then vaKeys = Split(vaSplit(i), ":", 2) Set clsKeyValue = New CKeyValue clsKeyValue.Key = vaKeys(0) clsKeyValue.Value = vaKeys(1) Me.KeyValues.Add clsKeyValue End If Next i End Property
Everything else is the description
Sub TEST_Description() Dim clsTodo As CTodo Set clsTodo = New CTodo clsTodo.Raw = "(A) 2016-04-30 Call Mom@home" Debug.Assert clsTodo.Desc = "Call Mom@home" Set clsTodo = New CTodo clsTodo.Raw = "(A) 2016-05-20 2016-04-30 Call Mom @Phone +Family due:2016-05-30" Debug.Assert clsTodo.Desc = "Call Mom" Set clsTodo = New CTodo clsTodo.Raw = "(A) 2016-04-30 Call Mom @Phone and Dad @Family due:2016-05-30 key:val:ue" Debug.Assert clsTodo.Desc = "Call Mom and Dad" Debug.Print "TEST_Description" End Sub
Here are the changes to the bottom of Raw
For i = lNext To UBound(vaSplit) If Left$(vaSplit(i), 1) = "+" Then Set clsProject = New CProject clsProject.Tag = Mid$(vaSplit(i), 2, Len(vaSplit(i))) Me.Projects.Add clsProject ElseIf Left$(vaSplit(i), 1) = "@" Then Set clsContext = New CContext clsContext.Tag = Mid$(vaSplit(i), 2, Len(vaSplit(i))) Me.Contexts.Add clsContext ElseIf InStr(1, vaSplit(i), ":") > 1 Then vaKeys = Split(vaSplit(i), ":", 2) Set clsKeyValue = New CKeyValue clsKeyValue.Key = vaKeys(0) clsKeyValue.Value = vaKeys(1) Me.KeyValues.Add clsKeyValue Else Me.Desc = Me.Desc & vaSplit(i) & Space(1) End If Next i 'remove the trailing space If Len(Me.Desc) > 1 Then Me.Desc = Left$(Me.Desc, Len(Me.Desc) - 1) End If
And that’s it. A properly parsed Todo.txt string ready to be used in your application. And if I make an changes to my app, I can run these tests to make sure I didn’t break anything.
You can download TodoTxt.zip
Series: