taskmeister 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +15 -9
- data/features/add.feature +6 -5
- data/features/done.feature +9 -8
- data/features/edit.feature +13 -11
- data/features/list.feature +6 -5
- data/features/project_dir.feature +6 -5
- data/features/replace.feature +6 -5
- data/features/show.feature +9 -8
- data/lib/taskmeister.rb +1 -0
- data/lib/taskmeister/cli/commands.rb +70 -0
- data/lib/taskmeister/cli/main.rb +7 -42
- data/lib/taskmeister/cli/options.rb +8 -17
- data/lib/taskmeister/task.rb +6 -13
- data/lib/taskmeister/task_list.rb +12 -12
- data/lib/taskmeister/task_list_reader.rb +9 -6
- data/lib/taskmeister/version.rb +1 -1
- data/spec/lib/taskmeister/task_list_reader_spec.rb +31 -20
- data/spec/lib/taskmeister/task_list_spec.rb +8 -8
- data/spec/lib/taskmeister/task_spec.rb +43 -20
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c3ed72f2ffd7c5abeff6d683b75dc8e0d8cb8fa1
|
4
|
+
data.tar.gz: 868ad94b5ddd220439631d2997897ca5f8aafcac
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e9a06654fba462da119efeb967e71c96ef0791f3faa05108013edc9fdac5f39875aab18bb27ebd2bd51af2aec077e84fd51324bb9acd7e143fd60da451b3faa6
|
7
|
+
data.tar.gz: 89d3baf2c913ba4c40639cb183bc2895ed3ff5f82282e724bbcd9cf573c0d8db14708f73d2975afe5e6113edc32dfa30bd8807558ac8cf1b6f6a67c95a9c47fd
|
data/README.md
CHANGED
@@ -4,9 +4,10 @@ Simple command line task management modelled after [t](http://stevelosh.com/proj
|
|
4
4
|
|
5
5
|
I like the simplicity of t but there are a couple of changes I wanted.
|
6
6
|
|
7
|
-
First: I like to have separate task lists per project
|
8
|
-
|
9
|
-
|
7
|
+
First: I like to have separate task lists per project but I want a single shell
|
8
|
+
alias for all of them. So I infer the task list name from the current project
|
9
|
+
directory. It finds a project directory by walking up from the current directory
|
10
|
+
until it finds a `.git` or `.hg` directory in it.
|
10
11
|
|
11
12
|
Second: I like to have a short list of notes accompanying my tasks. You can add
|
12
13
|
notes beneath your task. They can be edited via Vim with `taskmeister -e` or
|
@@ -16,14 +17,19 @@ Third: I format my task files in Markdown so that when I open them on my phone
|
|
16
17
|
via a Markdown-enabled editor they look nice. The format is still simple:
|
17
18
|
|
18
19
|
```markdown
|
19
|
-
|
20
|
+
# Update gems [∞](#aaf83a9b-02f7-4cc0-8ee1-4d98b98903b8)
|
20
21
|
|
21
|
-
|
22
|
-
|
23
|
-
|
22
|
+
# Refactor the widget model [∞](#ae0cce15-456d-48c0-a2e2-69d5f567e092)
|
23
|
+
|
24
|
+
# Update the README [∞](#a5d4d3a9-2b9a-427a-9047-b47c6aec8f93)
|
25
|
+
|
26
|
+
Some notes to go with the task above.
|
27
|
+
|
28
|
+
- item 1
|
29
|
+
- item 2
|
30
|
+
|
31
|
+
> Or a quote
|
24
32
|
|
25
|
-
Refactor the task model - [id](ae0cce15-456d-48c0-a2e2-69d5f567e092)
|
26
|
-
Update the README - [id](a5d4d3a9-2b9a-427a-9047-b47c6aec8f93)
|
27
33
|
```
|
28
34
|
|
29
35
|
## Installation
|
data/features/add.feature
CHANGED
@@ -3,13 +3,14 @@ Feature: taskmeister adds a task to the list
|
|
3
3
|
Scenario: Add a task to the list
|
4
4
|
Given a file named "mylist.md" with:
|
5
5
|
"""
|
6
|
-
Task one
|
6
|
+
# Task one [∞](#e8fd82de-c379-496d-a77b-2873192e8ea8)
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
Notes line one
|
9
|
+
Notes line two
|
10
10
|
|
11
|
-
Task two
|
12
|
-
|
11
|
+
# Task two [∞](#ae0cce15-456d-48c0-a2e2-69d5f567e092)
|
12
|
+
|
13
|
+
# Task three [∞](#a5d4d3a9-2b9a-427a-9047-b47c6aec8f93)
|
13
14
|
"""
|
14
15
|
When I successfully run `taskmeister --list mylist.md A new task`
|
15
16
|
And I successfully run `taskmeister --list mylist.md`
|
data/features/done.feature
CHANGED
@@ -3,13 +3,14 @@ Feature: taskmeister removes finished tasks from the list
|
|
3
3
|
Scenario: Complete a task in the list
|
4
4
|
Given a file named "mylist.md" with:
|
5
5
|
"""
|
6
|
-
Task one
|
6
|
+
# Task one [∞](#a8fd82de-c379-496d-a77b-2873192e8ea8)
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
Notes line one
|
9
|
+
Notes line two
|
10
10
|
|
11
|
-
Task two
|
12
|
-
|
11
|
+
# Task two [∞](#ae0cce15-456d-48c0-a2e2-69d5f567e092)
|
12
|
+
|
13
|
+
# Task three [∞](#a5d4d3a9-2b9a-427a-9047-b47c6aec8f93)
|
13
14
|
"""
|
14
15
|
When I successfully run `taskmeister --list mylist.md --done a`
|
15
16
|
And I successfully run `taskmeister --list mylist.md`
|
@@ -18,10 +19,10 @@ Task three - [id](a5d4d3a9-2b9a-427a-9047-b47c6aec8f93)
|
|
18
19
|
Scenario: Delete the task list when you remove the last task
|
19
20
|
Given a file named "mylist.md" with:
|
20
21
|
"""
|
21
|
-
Task one
|
22
|
+
# Task one [∞](#a8fd82de-c379-496d-a77b-2873192e8ea8)
|
22
23
|
|
23
|
-
|
24
|
-
|
24
|
+
Notes line one
|
25
|
+
Notes line two
|
25
26
|
"""
|
26
27
|
When I successfully run `taskmeister --list mylist.md --done a`
|
27
28
|
Then the file "mylist.md" should not exist
|
data/features/edit.feature
CHANGED
@@ -3,13 +3,14 @@ Feature: taskmeister opens vim to edit your task list
|
|
3
3
|
Scenario: Edit entire task list
|
4
4
|
Given a file named "mylist.md" with:
|
5
5
|
"""
|
6
|
-
Task one
|
6
|
+
# Task one [∞](#a8fd82de-c379-496d-a77b-2873192e8ea8)
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
Notes line one
|
9
|
+
Notes line two
|
10
10
|
|
11
|
-
Task two
|
12
|
-
|
11
|
+
# Task two [∞](#ae0cce15-456d-48c0-a2e2-69d5f567e092)
|
12
|
+
|
13
|
+
# Task three [∞](#a5d4d3a9-2b9a-427a-9047-b47c6aec8f93)
|
13
14
|
"""
|
14
15
|
And I double `vim`
|
15
16
|
When I run `taskmeister --list mylist.md --edit`
|
@@ -19,16 +20,17 @@ Task three - [id](a5d4d3a9-2b9a-427a-9047-b47c6aec8f93)
|
|
19
20
|
Scenario: Edit specific task in the list
|
20
21
|
Given a file named "mylist.md" with:
|
21
22
|
"""
|
22
|
-
Task one
|
23
|
+
# Task one [∞](#a8fd82de-c379-496d-a77b-2873192e8ea8)
|
24
|
+
|
25
|
+
Notes line one
|
26
|
+
Notes line two
|
23
27
|
|
24
|
-
|
25
|
-
> Notes line two
|
28
|
+
# Task two [∞](#ae0cce15-456d-48c0-a2e2-69d5f567e092)
|
26
29
|
|
27
|
-
Task
|
28
|
-
Task three - [id](a5d4d3a9-2b9a-427a-9047-b47c6aec8f93)
|
30
|
+
# Task three [∞](#a5d4d3a9-2b9a-427a-9047-b47c6aec8f93)
|
29
31
|
"""
|
30
32
|
And I double `vim`
|
31
33
|
When I run `taskmeister --list mylist.md --edit a`
|
32
|
-
Then the double `vim` should have been run with "+/
|
34
|
+
Then the double `vim` should have been run with "+/a8fd82de-c379-496d-a77b-2873192e8ea8" and file "mylist.md"
|
33
35
|
And the exit status should be 0
|
34
36
|
|
data/features/list.feature
CHANGED
@@ -3,13 +3,14 @@ Feature: taskmeister lists the contents of a task list
|
|
3
3
|
Scenario: List tasks in list
|
4
4
|
Given a file named "mylist.md" with:
|
5
5
|
"""
|
6
|
-
Task one
|
6
|
+
# Task one [∞](#a8fd82de-c379-496d-a77b-2873192e8ea8)
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
Notes line one
|
9
|
+
Notes line two
|
10
10
|
|
11
|
-
Task two
|
12
|
-
|
11
|
+
# Task two [∞](#ae0cce15-456d-48c0-a2e2-69d5f567e092)
|
12
|
+
|
13
|
+
# Task three [∞](#a5d4d3a9-2b9a-427a-9047-b47c6aec8f93)
|
13
14
|
"""
|
14
15
|
When I successfully run `taskmeister --list mylist.md`
|
15
16
|
Then the output should contain "a - Task one »"
|
@@ -6,13 +6,14 @@ Feature: taskmeister infers the name of your task list from your current project
|
|
6
6
|
And a directory named "project/child"
|
7
7
|
And a file named "project.md" with:
|
8
8
|
"""
|
9
|
-
Task one
|
9
|
+
# Task one [∞](#a8fd82de-c379-496d-a77b-2873192e8ea8)
|
10
10
|
|
11
|
-
|
12
|
-
|
11
|
+
Notes line one
|
12
|
+
Notes line two
|
13
13
|
|
14
|
-
Task two
|
15
|
-
|
14
|
+
# Task two [∞](#ae0cce15-456d-48c0-a2e2-69d5f567e092)
|
15
|
+
|
16
|
+
# Task three [∞](#a5d4d3a9-2b9a-427a-9047-b47c6aec8f93)
|
16
17
|
"""
|
17
18
|
And I cd to "project/child"
|
18
19
|
When I successfully run `taskmeister --task-dir ../../`
|
data/features/replace.feature
CHANGED
@@ -3,13 +3,14 @@ Feature: taskmeister replaces a task in the list
|
|
3
3
|
Scenario: Replace text of task in list
|
4
4
|
Given a file named "mylist.md" with:
|
5
5
|
"""
|
6
|
-
Task one
|
6
|
+
# Task one [∞](#e8fd82de-c379-496d-a77b-2873192e8ea8)
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
Notes line one
|
9
|
+
Notes line two
|
10
10
|
|
11
|
-
Task two
|
12
|
-
|
11
|
+
# Task two [∞](#ae0cce15-456d-48c0-a2e2-69d5f567e092)
|
12
|
+
|
13
|
+
# Task three [∞](#a5d4d3a9-2b9a-427a-9047-b47c6aec8f93)
|
13
14
|
"""
|
14
15
|
When I successfully run `taskmeister --list mylist.md -r a A new task`
|
15
16
|
And I successfully run `taskmeister --list mylist.md`
|
data/features/show.feature
CHANGED
@@ -3,16 +3,17 @@ Feature: taskmeister shows the details of a task in the list
|
|
3
3
|
Scenario: Show task in list
|
4
4
|
Given a file named "mylist.md" with:
|
5
5
|
"""
|
6
|
-
Task one
|
6
|
+
# Task one [∞](#a8fd82de-c379-496d-a77b-2873192e8ea8)
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
Notes line one
|
9
|
+
Notes line two
|
10
10
|
|
11
|
-
Task two
|
12
|
-
|
11
|
+
# Task two [∞](#ae0cce15-456d-48c0-a2e2-69d5f567e092)
|
12
|
+
|
13
|
+
# Task three [∞](#a5d4d3a9-2b9a-427a-9047-b47c6aec8f93)
|
13
14
|
"""
|
14
15
|
When I successfully run `taskmeister --list mylist.md --show a`
|
15
|
-
Then the output should contain "Task one
|
16
|
-
And the output should contain "
|
17
|
-
And the output should contain "
|
16
|
+
Then the output should contain "# Task one [∞](#a8fd82de-c379-496d-a77b-2873192e8ea8)"
|
17
|
+
And the output should contain "Notes line one"
|
18
|
+
And the output should contain "Notes line two"
|
18
19
|
|
data/lib/taskmeister.rb
CHANGED
@@ -0,0 +1,70 @@
|
|
1
|
+
require "ostruct"
|
2
|
+
|
3
|
+
module Taskmeister
|
4
|
+
module Cli
|
5
|
+
module Commands
|
6
|
+
|
7
|
+
class Command
|
8
|
+
def initialize(task_list, options=OpenStruct.new, stdout=STDOUT, kernel=Kernel)
|
9
|
+
@task_list = task_list
|
10
|
+
@options = options
|
11
|
+
@stdout = stdout
|
12
|
+
@kernel = kernel
|
13
|
+
end
|
14
|
+
|
15
|
+
def execute!
|
16
|
+
# Override this in derived class
|
17
|
+
end
|
18
|
+
|
19
|
+
protected
|
20
|
+
|
21
|
+
def write
|
22
|
+
Taskmeister::TaskListWriter.to_markdown_file(@task_list)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class List < Command
|
27
|
+
def execute!
|
28
|
+
@stdout.puts @task_list.to_short_list
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class Show < Command
|
33
|
+
def execute!
|
34
|
+
@stdout.puts @task_list.markdown_for(@options.task_id)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class Add < Command
|
39
|
+
def execute!
|
40
|
+
@task_list.add(Taskmeister::Task.create(@options.task_text))
|
41
|
+
write
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class Replace < Command
|
46
|
+
def execute!
|
47
|
+
@task_list.replace(@options.task_id, @options.task_text)
|
48
|
+
write
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class Done < Command
|
53
|
+
def execute!
|
54
|
+
@task_list.complete(@options.task_id)
|
55
|
+
write
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class Edit < Command
|
60
|
+
def execute!
|
61
|
+
task = @task_list[@options.task_id]
|
62
|
+
|
63
|
+
search_path = task ? "+/#{task.id} " : ""
|
64
|
+
|
65
|
+
system "vim #{search_path}#{@task_list.file_path}"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/lib/taskmeister/cli/main.rb
CHANGED
@@ -10,49 +10,18 @@ module Taskmeister
|
|
10
10
|
def execute!
|
11
11
|
options = Options.new(@stdout, @kernel).parse(@argv)
|
12
12
|
|
13
|
-
|
13
|
+
file_path = task_list_path(options)
|
14
|
+
task_list = Taskmeister::TaskListReader.from_markdown_file(file_path)
|
14
15
|
|
15
|
-
|
16
|
-
end
|
17
|
-
|
18
|
-
private
|
19
|
-
|
20
|
-
def run_command(options, task_list)
|
21
|
-
case options.command
|
22
|
-
when Commands::LIST
|
23
|
-
@stdout.puts task_list.to_short_list
|
24
|
-
|
25
|
-
when Commands::SHOW
|
26
|
-
|
27
|
-
@stdout.puts task_list.markdown_for(options.task_id)
|
28
|
-
|
29
|
-
when Commands::EDIT
|
30
|
-
|
31
|
-
task = task_list[options.task_id]
|
32
|
-
|
33
|
-
search_path = task ? "+/#{task.id} " : ""
|
34
|
-
system "vim #{search_path}#{task_list.file_path}"
|
35
|
-
|
36
|
-
when Commands::ADD
|
16
|
+
command = options.command.new(task_list, options, @stdout, @kernel)
|
37
17
|
|
38
|
-
|
39
|
-
update task_list
|
40
|
-
|
41
|
-
when Commands::DONE
|
42
|
-
|
43
|
-
task_list.complete(options.task_id)
|
44
|
-
update task_list
|
45
|
-
|
46
|
-
when Commands::REPLACE
|
47
|
-
|
48
|
-
task_list.replace(options.task_id, options.task_text)
|
49
|
-
update task_list
|
50
|
-
|
51
|
-
end
|
18
|
+
command.execute!
|
52
19
|
|
53
20
|
@kernel.exit 0
|
54
21
|
end
|
55
22
|
|
23
|
+
private
|
24
|
+
|
56
25
|
def task_list_path(options)
|
57
26
|
Pathname.new(options.task_dir) + task_list_name(options)
|
58
27
|
end
|
@@ -61,16 +30,12 @@ module Taskmeister
|
|
61
30
|
task_list_name = options.list || TaskListName.from_project_dir(Pathname.getwd)
|
62
31
|
|
63
32
|
unless task_list_name
|
64
|
-
@stderr.puts "Could not find a project directory. Please specify a task list."
|
33
|
+
@stderr.puts "Could not find a project directory. Please specify a task list instead."
|
65
34
|
@kernel.exit 1
|
66
35
|
end
|
67
36
|
|
68
37
|
task_list_name
|
69
38
|
end
|
70
|
-
|
71
|
-
def update(task_list)
|
72
|
-
Taskmeister::TaskListWriter.to_markdown_file(task_list)
|
73
|
-
end
|
74
39
|
end
|
75
40
|
end
|
76
41
|
end
|
@@ -4,17 +4,8 @@ require "pathname"
|
|
4
4
|
|
5
5
|
module Taskmeister
|
6
6
|
module Cli
|
7
|
-
module Commands
|
8
|
-
ADD = "add"
|
9
|
-
LIST = "list"
|
10
|
-
REPLACE = "replace"
|
11
|
-
EDIT = "edit"
|
12
|
-
SHOW = "show"
|
13
|
-
DONE = "done"
|
14
|
-
end
|
15
|
-
|
16
7
|
class Options
|
17
|
-
def initialize(stdout
|
8
|
+
def initialize(stdout=STDOUT, kernel=Kernel)
|
18
9
|
@stdout = stdout
|
19
10
|
@kernel = kernel
|
20
11
|
end
|
@@ -45,26 +36,26 @@ module Taskmeister
|
|
45
36
|
|
46
37
|
opts.on("-d", "--done TASK_ID",
|
47
38
|
"Finish a task") do |task_id|
|
48
|
-
options.command = Commands::
|
39
|
+
options.command = Commands::Done
|
49
40
|
options.task_id = task_id
|
50
41
|
end
|
51
42
|
|
52
43
|
opts.on("-s", "--show TASK_ID",
|
53
44
|
"Show a task list item and its notes") do |task_id|
|
54
|
-
options.command = Commands::
|
45
|
+
options.command = Commands::Show
|
55
46
|
options.task_id = task_id
|
56
47
|
end
|
57
48
|
|
58
49
|
opts.on("-e", "--edit [TASK_ID]",
|
59
50
|
"Edit task list in Vim",
|
60
51
|
" Will search for a specific task if TASK_ID is provided") do |task_id|
|
61
|
-
options.command = Commands::
|
52
|
+
options.command = Commands::Edit
|
62
53
|
options.task_id = task_id
|
63
54
|
end
|
64
55
|
|
65
56
|
opts.on("-r", "--replace TASK_ID",
|
66
57
|
"Replace a task description") do |task_id|
|
67
|
-
options.command = Commands::
|
58
|
+
options.command = Commands::Replace
|
68
59
|
options.task_id = task_id
|
69
60
|
end
|
70
61
|
|
@@ -88,8 +79,8 @@ module Taskmeister
|
|
88
79
|
|
89
80
|
# If there is TASK TEXT and the default command hasn't been overwritten
|
90
81
|
# by the user, set the command to ADD
|
91
|
-
if !task_text.empty? and options.command == Commands::
|
92
|
-
options.command = Commands::
|
82
|
+
if !task_text.empty? and options.command == Commands::List
|
83
|
+
options.command = Commands::Add
|
93
84
|
end
|
94
85
|
|
95
86
|
options
|
@@ -99,7 +90,7 @@ module Taskmeister
|
|
99
90
|
|
100
91
|
def default_options
|
101
92
|
OpenStruct.new.tap do |o|
|
102
|
-
o.command = Commands::
|
93
|
+
o.command = Commands::List
|
103
94
|
o.task_dir = Pathname.getwd
|
104
95
|
end
|
105
96
|
end
|
data/lib/taskmeister/task.rb
CHANGED
@@ -5,26 +5,21 @@ module Taskmeister
|
|
5
5
|
attr_reader :text, :notes, :id
|
6
6
|
|
7
7
|
def initialize(text, id, notes)
|
8
|
-
@text, @id, @notes = text, id, notes
|
8
|
+
@text, @id, @notes = text, id, Array(notes)
|
9
9
|
end
|
10
10
|
|
11
11
|
def notes?
|
12
|
-
notes
|
12
|
+
notes.any? { |ns| !ns.empty? }
|
13
13
|
end
|
14
14
|
|
15
15
|
def to_markdown
|
16
|
-
[ "#{text}
|
17
|
-
|
18
|
-
a << ""
|
19
|
-
a.concat notes.split("\n").map { |n|
|
20
|
-
n.size > 0 ? "> #{n}" : ">"
|
21
|
-
}
|
22
|
-
a << ""
|
16
|
+
[ "# #{text} [∞](##{id})" ].tap do |a|
|
17
|
+
a.concat notes
|
23
18
|
end
|
24
19
|
end
|
25
20
|
|
26
21
|
def self.create(text)
|
27
|
-
self.new(text, SecureRandom.uuid, "")
|
22
|
+
self.new(text, SecureRandom.uuid, [""])
|
28
23
|
end
|
29
24
|
|
30
25
|
def self.from_markdown(lines)
|
@@ -32,13 +27,11 @@ module Taskmeister
|
|
32
27
|
|
33
28
|
text, id = task_attributes(task)
|
34
29
|
|
35
|
-
notes = notes.map { |l| l.gsub(/\A> ?/, "") }.join("\n")
|
36
|
-
|
37
30
|
self.new(text, id, notes)
|
38
31
|
end
|
39
32
|
|
40
33
|
def self.task_attributes(line)
|
41
|
-
matches = line.match(
|
34
|
+
matches = line.match(/#\s(.+)\s\[∞\]\(#([\w-]+)\)/)
|
42
35
|
|
43
36
|
fail "Invalid task: #{line}" unless matches
|
44
37
|
|
@@ -3,9 +3,9 @@ module Taskmeister
|
|
3
3
|
attr_reader :file_path
|
4
4
|
|
5
5
|
def initialize(tasks, file_path)
|
6
|
-
@file_path = file_path
|
6
|
+
@file_path = Pathname.new file_path
|
7
7
|
|
8
|
-
@
|
8
|
+
@shortIdToTask = {}
|
9
9
|
|
10
10
|
tasks.each do |t|
|
11
11
|
add(t)
|
@@ -15,23 +15,23 @@ module Taskmeister
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def to_short_list
|
18
|
-
longest_id = @
|
19
|
-
@
|
18
|
+
longest_id = @shortIdToTask.keys.max_by(&:length)
|
19
|
+
@shortIdToTask.map { |id, task|
|
20
20
|
marker = task.notes? ? " »" : ""
|
21
21
|
"%-#{longest_id.length}s - %s%s" % [id, task.text, marker]
|
22
22
|
}
|
23
23
|
end
|
24
24
|
|
25
25
|
def tasks
|
26
|
-
@
|
26
|
+
@shortIdToTask.values
|
27
27
|
end
|
28
28
|
|
29
29
|
def [](key)
|
30
|
-
@
|
30
|
+
@shortIdToTask[key]
|
31
31
|
end
|
32
32
|
|
33
33
|
def empty?
|
34
|
-
@
|
34
|
+
@shortIdToTask.empty?
|
35
35
|
end
|
36
36
|
|
37
37
|
def dirty?
|
@@ -40,12 +40,12 @@ module Taskmeister
|
|
40
40
|
|
41
41
|
def add(task)
|
42
42
|
prefix = assign_short_code_to_task(task)
|
43
|
-
@
|
43
|
+
@shortIdToTask[prefix] = task
|
44
44
|
@dirty = true
|
45
45
|
end
|
46
46
|
|
47
47
|
def complete(short_id)
|
48
|
-
removed_val = @
|
48
|
+
removed_val = @shortIdToTask.delete(short_id)
|
49
49
|
@dirty = true if removed_val
|
50
50
|
end
|
51
51
|
|
@@ -53,12 +53,12 @@ module Taskmeister
|
|
53
53
|
task = self[short_id]
|
54
54
|
return unless task
|
55
55
|
|
56
|
-
@
|
56
|
+
@shortIdToTask[short_id] = Task.new(new_text, task.id, task.notes)
|
57
57
|
@dirty = true
|
58
58
|
end
|
59
59
|
|
60
60
|
def markdown_for(short_id)
|
61
|
-
return [] unless @
|
61
|
+
return [] unless @shortIdToTask.has_key?(short_id)
|
62
62
|
|
63
63
|
self[short_id].to_markdown
|
64
64
|
end
|
@@ -68,7 +68,7 @@ module Taskmeister
|
|
68
68
|
def assign_short_code_to_task(task)
|
69
69
|
task.id.length.times do |i|
|
70
70
|
prefix = task.id.slice(0, i + 1)
|
71
|
-
return prefix unless @
|
71
|
+
return prefix unless @shortIdToTask.has_key?(prefix)
|
72
72
|
end
|
73
73
|
end
|
74
74
|
end
|
@@ -1,16 +1,19 @@
|
|
1
1
|
module Taskmeister
|
2
2
|
class TaskListReader
|
3
3
|
def self.from_markdown(file_lines, file_path)
|
4
|
-
|
4
|
+
lines_grouped_by_task = \
|
5
5
|
file_lines.map(&:chomp)
|
6
|
-
.reject(&:empty?)
|
7
6
|
.reduce([]) do |acc, l|
|
8
|
-
acc
|
9
|
-
|
10
|
-
|
7
|
+
acc.tap do |a|
|
8
|
+
if l.match(/#\s.+\s\[∞\]\(#[\w-]+\)/)
|
9
|
+
acc << [l] # A new task
|
10
|
+
else
|
11
|
+
acc.last << l # A line of note for the latest task
|
12
|
+
end
|
13
|
+
end
|
11
14
|
end
|
12
15
|
|
13
|
-
tasks =
|
16
|
+
tasks = lines_grouped_by_task.map do |ls|
|
14
17
|
Task.from_markdown ls
|
15
18
|
end
|
16
19
|
|
data/lib/taskmeister/version.rb
CHANGED
@@ -25,48 +25,59 @@ module Taskmeister
|
|
25
25
|
|
26
26
|
describe "passed a list of simple tasks" do
|
27
27
|
let(:lines) { [
|
28
|
-
"Task number 1\n",
|
29
|
-
"
|
28
|
+
"# Task number 1 [∞](#e8fd82de-c379-496d-a77b-2873192e8ea8)\n",
|
29
|
+
"\n",
|
30
|
+
"# Task number 2 [∞](#e9fd82de-c379-496d-a77b-2873192e8ea8)\n",
|
31
|
+
"\n"
|
30
32
|
]
|
31
33
|
}
|
32
34
|
|
33
35
|
it "creates a task for each line, stripping new lines" do
|
34
|
-
expect(Task).to receive(:from_markdown).with([ "Task number 1" ])
|
35
|
-
expect(Task).to receive(:from_markdown).with([ "Task number 2" ])
|
36
|
+
expect(Task).to receive(:from_markdown).with([ "# Task number 1 [∞](#e8fd82de-c379-496d-a77b-2873192e8ea8)", ""])
|
37
|
+
expect(Task).to receive(:from_markdown).with([ "# Task number 2 [∞](#e9fd82de-c379-496d-a77b-2873192e8ea8)", ""])
|
36
38
|
it
|
37
39
|
end
|
38
40
|
end
|
39
41
|
|
40
42
|
describe "passed a list of tasks with notes" do
|
41
43
|
let(:lines) { [
|
42
|
-
"Task number 1\n",
|
44
|
+
"# Task number 1 [∞](#e8fd82de-c379-496d-a77b-2873192e8ea8)\n",
|
45
|
+
"\n",
|
46
|
+
"note line 1\n",
|
47
|
+
"\n",
|
48
|
+
"\n",
|
49
|
+
"note line 2\n",
|
43
50
|
"\n",
|
44
|
-
"
|
45
|
-
"> note line 2\n",
|
51
|
+
"# Task number 2 [∞](#18fd82de-c379-496d-a77b-2873192e8ea8)\n",
|
46
52
|
"\n",
|
47
|
-
"Task number
|
48
|
-
"Task number 3\n",
|
53
|
+
"# Task number 3 [∞](#e9fd82de-c379-496d-a77b-2873192e8ea8)\n",
|
49
54
|
"\n",
|
50
|
-
"
|
51
|
-
"
|
52
|
-
""
|
53
|
-
"",
|
55
|
+
"note line 3\n",
|
56
|
+
"note line 4\n",
|
57
|
+
"\n"
|
54
58
|
]
|
55
59
|
}
|
56
60
|
|
57
61
|
it "creates a task for each line with their associated notes, stripping newlines" do
|
58
62
|
expect(Task).to receive(:from_markdown).with([
|
59
|
-
"Task number 1",
|
60
|
-
"
|
61
|
-
"
|
63
|
+
"# Task number 1 [∞](#e8fd82de-c379-496d-a77b-2873192e8ea8)",
|
64
|
+
"",
|
65
|
+
"note line 1",
|
66
|
+
"",
|
67
|
+
"",
|
68
|
+
"note line 2",
|
69
|
+
"",
|
62
70
|
])
|
63
71
|
expect(Task).to receive(:from_markdown).with([
|
64
|
-
"Task number 2"
|
72
|
+
"# Task number 2 [∞](#18fd82de-c379-496d-a77b-2873192e8ea8)",
|
73
|
+
"",
|
65
74
|
])
|
66
75
|
expect(Task).to receive(:from_markdown).with([
|
67
|
-
"Task number 3",
|
68
|
-
"
|
69
|
-
"
|
76
|
+
"# Task number 3 [∞](#e9fd82de-c379-496d-a77b-2873192e8ea8)",
|
77
|
+
"",
|
78
|
+
"note line 3",
|
79
|
+
"note line 4",
|
80
|
+
""
|
70
81
|
])
|
71
82
|
|
72
83
|
it
|
@@ -4,13 +4,13 @@ require "taskmeister/task"
|
|
4
4
|
module Taskmeister
|
5
5
|
RSpec.describe TaskList do
|
6
6
|
|
7
|
-
let(:task1) { Task.new("task 1", "78dc2561-8c9a-4780-a560-56e1e14f2ed3",
|
8
|
-
let(:task2) { Task.new("task 2", "b2110029-ffa0-4e54-985a-2aa6d1bed53b",
|
7
|
+
let(:task1) { Task.new("task 1", "78dc2561-8c9a-4780-a560-56e1e14f2ed3", []) }
|
8
|
+
let(:task2) { Task.new("task 2", "b2110029-ffa0-4e54-985a-2aa6d1bed53b", []) }
|
9
9
|
let(:task3) { Task.new("task 3", "1c890df0-97a5-4310-9b01-374983416af7", "a note") }
|
10
|
-
let(:task4) { Task.new("task 4", "1c891df0-97a5-4310-9b01-374983416af7",
|
11
|
-
let(:task5) { Task.new("task 5", "ef0915ee-3d11-4358-b12c-15a4ab0d1d26",
|
12
|
-
let(:task6) { Task.new("task 6", "f2a0d281-f323-4909-8547-d4af5315a295",
|
13
|
-
let(:task7) { Task.new("task 7", "f2a0d281-4323-4909-8547-d4af5315a295",
|
10
|
+
let(:task4) { Task.new("task 4", "1c891df0-97a5-4310-9b01-374983416af7", []) }
|
11
|
+
let(:task5) { Task.new("task 5", "ef0915ee-3d11-4358-b12c-15a4ab0d1d26", []) }
|
12
|
+
let(:task6) { Task.new("task 6", "f2a0d281-f323-4909-8547-d4af5315a295", []) }
|
13
|
+
let(:task7) { Task.new("task 7", "f2a0d281-4323-4909-8547-d4af5315a295", []) }
|
14
14
|
|
15
15
|
let(:tasks) { [
|
16
16
|
task1, task2, task3, task4, task5, task6, task7
|
@@ -18,8 +18,8 @@ module Taskmeister
|
|
18
18
|
|
19
19
|
let(:list) { described_class.new(tasks, "fake file") }
|
20
20
|
|
21
|
-
it "saves its file path" do
|
22
|
-
expect(list.file_path).to eq "fake file"
|
21
|
+
it "initially saves its file path" do
|
22
|
+
expect(list.file_path).to eq Pathname.new("fake file")
|
23
23
|
end
|
24
24
|
|
25
25
|
it "is initially clean" do
|
@@ -5,24 +5,28 @@ module Taskmeister
|
|
5
5
|
describe ".from_markdown" do
|
6
6
|
subject { described_class.from_markdown(lines) }
|
7
7
|
|
8
|
-
describe "passed a
|
8
|
+
describe "passed a task with an empty note" do
|
9
9
|
let(:lines) { [
|
10
|
-
"A task name
|
10
|
+
"# A task name [∞](#78dc2561-8c9a-4780-a560-56e1e14f2ed3)",
|
11
|
+
"",
|
11
12
|
]}
|
12
13
|
|
13
14
|
it "sets the name and id of the task" do
|
14
15
|
expect(subject.id).to eq "78dc2561-8c9a-4780-a560-56e1e14f2ed3"
|
15
16
|
expect(subject.text).to eq "A task name"
|
16
|
-
expect(subject.notes).to eq ""
|
17
|
+
expect(subject.notes).to eq [""]
|
17
18
|
end
|
18
19
|
end
|
19
20
|
|
20
21
|
describe "passed a single valid line and notes" do
|
21
22
|
let(:lines) { [
|
22
|
-
"A task name
|
23
|
-
"
|
24
|
-
"
|
25
|
-
"
|
23
|
+
"# A task name [∞](#78dc2561-8c9a-4780-a560-56e1e14f2ed3)",
|
24
|
+
"",
|
25
|
+
"note line 1",
|
26
|
+
"",
|
27
|
+
"",
|
28
|
+
"note line 2",
|
29
|
+
""
|
26
30
|
]}
|
27
31
|
|
28
32
|
it "sets the name and id of the task" do
|
@@ -30,8 +34,15 @@ module Taskmeister
|
|
30
34
|
expect(subject.text).to eq "A task name"
|
31
35
|
end
|
32
36
|
|
33
|
-
it "
|
34
|
-
expect(subject.notes).to eq
|
37
|
+
it "sets the notes of the task" do
|
38
|
+
expect(subject.notes).to eq [
|
39
|
+
"",
|
40
|
+
"note line 1",
|
41
|
+
"",
|
42
|
+
"",
|
43
|
+
"note line 2",
|
44
|
+
""
|
45
|
+
]
|
35
46
|
end
|
36
47
|
end
|
37
48
|
|
@@ -47,33 +58,45 @@ module Taskmeister
|
|
47
58
|
end
|
48
59
|
|
49
60
|
describe ".create" do
|
50
|
-
it "returns a task with the specified text
|
61
|
+
it "returns a task with the specified text, a new id, and empty notes" do
|
51
62
|
task = described_class.create("My task text")
|
52
63
|
expect(task.text).to eq "My task text"
|
53
64
|
expect(task.id).to match(/[\w-]+/)
|
54
|
-
expect(task.notes).to eq ""
|
65
|
+
expect(task.notes).to eq [""]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "#notes?" do
|
70
|
+
it "returns true if there is a non-empty string in the notes list" do
|
71
|
+
task = Task.new("My task text", "f23891df0-97a5-4310-9b01-374983416af7", ["", "a note", ""])
|
72
|
+
expect(task.notes?).to be_truthy
|
73
|
+
end
|
74
|
+
|
75
|
+
it "returns false if there is are only empty strings in the notes list" do
|
76
|
+
task = Task.new("My task text", "f23891df0-97a5-4310-9b01-374983416af7", ["", ""])
|
77
|
+
expect(task.notes?).to be_falsy
|
55
78
|
end
|
56
79
|
end
|
57
80
|
|
58
81
|
describe "#to_markdown" do
|
59
82
|
it "returns a list of markdown-formatted lines" do
|
60
|
-
task = described_class.new("task 8", "f23891df0-97a5-4310-9b01-374983416af7", "
|
83
|
+
task = described_class.new("task 8", "f23891df0-97a5-4310-9b01-374983416af7", [""])
|
61
84
|
expect(task.to_markdown).to eq [
|
62
|
-
"task 8
|
85
|
+
"# task 8 [∞](#f23891df0-97a5-4310-9b01-374983416af7)",
|
86
|
+
""
|
63
87
|
]
|
64
88
|
end
|
65
89
|
|
66
90
|
it "returns a list of markdown-formatted lines including notes" do
|
67
91
|
task = described_class.new("task 8", "f23891df0-97a5-4310-9b01-374983416af7",
|
68
|
-
"First line of a note
|
92
|
+
["", "First line of a note", "second line", "", "third line"])
|
69
93
|
expect(task.to_markdown).to eq [
|
70
|
-
"task 8
|
94
|
+
"# task 8 [∞](#f23891df0-97a5-4310-9b01-374983416af7)",
|
71
95
|
"",
|
72
|
-
"
|
73
|
-
"
|
74
|
-
"
|
75
|
-
"
|
76
|
-
""
|
96
|
+
"First line of a note",
|
97
|
+
"second line",
|
98
|
+
"",
|
99
|
+
"third line"
|
77
100
|
]
|
78
101
|
end
|
79
102
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: taskmeister
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ray Grasso
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-08-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -120,6 +120,7 @@ files:
|
|
120
120
|
- features/step_definitions/command_double_steps.rb
|
121
121
|
- features/support/env.rb
|
122
122
|
- lib/taskmeister.rb
|
123
|
+
- lib/taskmeister/cli/commands.rb
|
123
124
|
- lib/taskmeister/cli/main.rb
|
124
125
|
- lib/taskmeister/cli/options.rb
|
125
126
|
- lib/taskmeister/cli/task_list_name.rb
|
@@ -154,7 +155,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
154
155
|
version: '0'
|
155
156
|
requirements: []
|
156
157
|
rubyforge_project:
|
157
|
-
rubygems_version: 2.
|
158
|
+
rubygems_version: 2.4.1
|
158
159
|
signing_key:
|
159
160
|
specification_version: 4
|
160
161
|
summary: Another command line task manager.
|