markdo 0.1.12.alpha → 0.2.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.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +15 -9
  4. data/Dockerfile +21 -7
  5. data/Guardfile +11 -0
  6. data/README.md +12 -7
  7. data/Rakefile +11 -7
  8. data/bin/markdo +1 -1
  9. data/docker-compose.yml +9 -0
  10. data/lib/markdo/cli.rb +23 -55
  11. data/lib/markdo/command_support.rb +14 -0
  12. data/lib/markdo/commands.rb +16 -0
  13. data/lib/markdo/{add_command.rb → commands/add_command.rb} +2 -6
  14. data/lib/markdo/commands/command.rb +33 -0
  15. data/lib/markdo/{edit_command.rb → commands/edit_command.rb} +2 -2
  16. data/lib/markdo/commands/forecast_command.rb +41 -0
  17. data/lib/markdo/{help_command.rb → commands/help_command.rb} +7 -8
  18. data/lib/markdo/commands/ics_command.rb +15 -0
  19. data/lib/markdo/commands/inbox_command.rb +11 -0
  20. data/lib/markdo/commands/overdue_command.rb +11 -0
  21. data/lib/markdo/commands/overview_command.rb +16 -0
  22. data/lib/markdo/commands/process_command.rb +91 -0
  23. data/lib/markdo/commands/query_command.rb +14 -0
  24. data/lib/markdo/commands/star_command.rb +11 -0
  25. data/lib/markdo/commands/summary_command.rb +25 -0
  26. data/lib/markdo/commands/tag_command.rb +11 -0
  27. data/lib/markdo/commands/today_command.rb +12 -0
  28. data/lib/markdo/commands/tomorrow_command.rb +12 -0
  29. data/lib/markdo/{version_command.rb → commands/version_command.rb} +1 -1
  30. data/lib/markdo/commands/week_command.rb +16 -0
  31. data/lib/markdo/data_source.rb +27 -0
  32. data/lib/markdo/ics_exporter.rb +65 -0
  33. data/lib/markdo/models/task.rb +46 -0
  34. data/lib/markdo/models/task_attribute.rb +22 -0
  35. data/lib/markdo/models/task_collection.rb +75 -0
  36. data/lib/markdo/version.rb +1 -1
  37. data/markdo.gemspec +6 -0
  38. data/script/build +9 -0
  39. data/spec/fixtures/add_command/Inbox.md +0 -0
  40. data/spec/fixtures/date_commands/Inbox.md +6 -0
  41. data/spec/fixtures/date_commands/Sprint.md +6 -0
  42. data/spec/fixtures/ics_command/Inbox.md +0 -0
  43. data/{test/fixtures/ics_command.md → spec/fixtures/ics_command/Sprint.md} +0 -0
  44. data/spec/fixtures/inbox_command/Inbox.md +2 -0
  45. data/spec/fixtures/inbox_command/Sprint.md +2 -0
  46. data/spec/fixtures/process_command/Backlog.md +0 -0
  47. data/spec/fixtures/process_command/Inbox.md +0 -0
  48. data/spec/fixtures/process_command/Maybe.md +0 -0
  49. data/spec/fixtures/process_command/Sprint.md +0 -0
  50. data/spec/fixtures/query_command/Inbox.md +4 -0
  51. data/spec/fixtures/query_command/Sprint.md +4 -0
  52. data/spec/fixtures/tag_command/Inbox.md +2 -0
  53. data/spec/fixtures/tag_command/Sprint.md +2 -0
  54. data/spec/lib/cli_spec.rb +79 -0
  55. data/spec/lib/commands/add_command_spec.rb +70 -0
  56. data/spec/lib/commands/edit_command_spec.rb +32 -0
  57. data/spec/lib/commands/forecast_command_spec.rb +24 -0
  58. data/spec/lib/commands/ics_command_spec.rb +30 -0
  59. data/spec/lib/commands/inbox_command_spec.rb +22 -0
  60. data/spec/lib/commands/overdue_command_spec.rb +19 -0
  61. data/spec/lib/commands/overview_command_spec.rb +25 -0
  62. data/spec/lib/commands/process_command_spec.rb +178 -0
  63. data/spec/lib/commands/query_command_spec.rb +23 -0
  64. data/spec/lib/commands/star_command_spec.rb +19 -0
  65. data/spec/lib/commands/summary_command_spec.rb +23 -0
  66. data/spec/lib/commands/tag_command_spec.rb +21 -0
  67. data/spec/lib/commands/today_command_spec.rb +19 -0
  68. data/spec/lib/commands/tomorrow_command_spec.rb +19 -0
  69. data/spec/lib/commands/week_command_spec.rb +23 -0
  70. data/spec/lib/ics_exporter_spec.rb +59 -0
  71. data/spec/lib/models/task_attribute_spec.rb +85 -0
  72. data/spec/lib/models/task_collection_spec.rb +168 -0
  73. data/spec/lib/models/task_spec.rb +79 -0
  74. data/spec/spec_helper.rb +40 -0
  75. metadata +189 -42
  76. data/.ruby-version +0 -1
  77. data/lib/markdo/command.rb +0 -12
  78. data/lib/markdo/date_command.rb +0 -9
  79. data/lib/markdo/forecast_command.rb +0 -61
  80. data/lib/markdo/ics_command.rb +0 -73
  81. data/lib/markdo/inbox_command.rb +0 -15
  82. data/lib/markdo/overdue_command.rb +0 -47
  83. data/lib/markdo/overview_command.rb +0 -17
  84. data/lib/markdo/process_command.rb +0 -68
  85. data/lib/markdo/query_command.rb +0 -24
  86. data/lib/markdo/rss_command.rb +0 -67
  87. data/lib/markdo/star_command.rb +0 -9
  88. data/lib/markdo/summary_command.rb +0 -30
  89. data/lib/markdo/tag_command.rb +0 -9
  90. data/lib/markdo/today_command.rb +0 -10
  91. data/lib/markdo/tomorrow_command.rb +0 -10
  92. data/lib/markdo/week_command.rb +0 -36
  93. data/test/fixtures/inbox.md +0 -2
  94. data/test/fixtures/rss_command.md +0 -4
  95. data/test/ics_command_test.rb +0 -38
  96. data/test/inbox_command_test.rb +0 -19
  97. data/test/rss_command_test.rb +0 -39
  98. data/test/summary_command_test.rb +0 -19
  99. data/test/test_helper.rb +0 -3
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+ require 'markdo/commands/star_command'
3
+
4
+ module Markdo
5
+ describe QueryCommand do
6
+ it 'outputs tasks that match the given string, case insensitive' do
7
+ skip 'Dir.glob not supported' unless Dir.respond_to?(:glob)
8
+
9
+ command_support = build_command_support({
10
+ 'MARKDO_ROOT' => 'spec/fixtures/query_command'
11
+ })
12
+
13
+ QueryCommand.new(command_support).run('asdf')
14
+
15
+ expect(command_support.stdout.string).to eq(<<-EOF)
16
+ - [ ] ASDF in inbox
17
+ - [ ] asdf in inbox
18
+ - [ ] ASDF
19
+ - [ ] asdf
20
+ EOF
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+ require 'markdo/commands/star_command'
3
+
4
+ module Markdo
5
+ describe StarCommand do
6
+ it 'outputs tasks with the @star tag' do
7
+ skip 'Dir.glob not supported' unless Dir.respond_to?(:glob)
8
+
9
+ command_support = build_command_support_for_date_commands
10
+
11
+ StarCommand.new(command_support).run
12
+
13
+ expect(command_support.stdout.string).to eq(<<-EOF)
14
+ - [ ] @star Starred in inbox
15
+ - [ ] @star Starred
16
+ EOF
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+ require 'markdo/commands/summary_command'
3
+
4
+ module Markdo
5
+ describe SummaryCommand do
6
+ it 'outputs summary counts' do
7
+ skip 'Dir.glob not supported' unless Dir.respond_to?(:glob)
8
+
9
+ command_support = build_command_support_for_date_commands
10
+
11
+ SummaryCommand.new(command_support).run
12
+
13
+ expect(command_support.stdout.string).to eq(<<-XML)
14
+ Overdue: 2
15
+ Starred: 2
16
+ Today: 2
17
+ Tomorrow: 2
18
+ Soon: 2
19
+ Inbox: 6
20
+ XML
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+ require 'markdo/commands/tag_command'
3
+
4
+ module Markdo
5
+ describe TagCommand do
6
+ it 'outputs tasks with the given tag' do
7
+ skip 'Dir.glob not supported' unless Dir.respond_to?(:glob)
8
+
9
+ command_support = build_command_support({
10
+ 'MARKDO_ROOT' => 'spec/fixtures/tag_command'
11
+ })
12
+
13
+ TagCommand.new(command_support).run('foo')
14
+
15
+ expect(command_support.stdout.string).to eq(<<-EOF)
16
+ - [ ] Tagged @foo in inbox
17
+ - [ ] Tagged @foo
18
+ EOF
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+ require 'markdo/commands/today_command'
3
+
4
+ module Markdo
5
+ describe TodayCommand do
6
+ it 'outputs tasks due today' do
7
+ skip 'Dir.glob not supported' unless Dir.respond_to?(:glob)
8
+
9
+ command_support = build_command_support_for_date_commands
10
+
11
+ TodayCommand.new(command_support).run
12
+
13
+ expect(command_support.stdout.string).to eq(<<-EOF)
14
+ - [ ] @due(2016-02-28) Due today in inbox
15
+ - [ ] @due(2016-02-28) Due today
16
+ EOF
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+ require 'markdo/commands/tomorrow_command'
3
+
4
+ module Markdo
5
+ describe TomorrowCommand do
6
+ it 'outputs tasks due tomorrow' do
7
+ skip 'Dir.glob not supported' unless Dir.respond_to?(:glob)
8
+
9
+ command_support = build_command_support_for_date_commands
10
+
11
+ TomorrowCommand.new(command_support).run
12
+
13
+ expect(command_support.stdout.string).to eq(<<-EOF)
14
+ - [ ] @due(2016-02-29) Due tomorrow in inbox
15
+ - [ ] @due(2016-02-29) Due tomorrow
16
+ EOF
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+ require 'markdo/commands/week_command'
3
+
4
+ module Markdo
5
+ describe WeekCommand do
6
+ it 'outputs tasks due over the next week, including today and tomorrow' do
7
+ skip 'Dir.glob not supported' unless Dir.respond_to?(:glob)
8
+
9
+ command_support = build_command_support_for_date_commands
10
+
11
+ WeekCommand.new(command_support).run
12
+
13
+ expect(command_support.stdout.string).to eq(<<-EOF)
14
+ - [ ] @due(2016-02-28) Due today in inbox
15
+ - [ ] @due(2016-02-28) Due today
16
+ - [ ] @due(2016-02-29) Due tomorrow in inbox
17
+ - [ ] @due(2016-02-29) Due tomorrow
18
+ - [ ] @due(2016-03-06) Due soon in inbox
19
+ - [ ] @due(2016-03-06) Due soon
20
+ EOF
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,59 @@
1
+ require 'spec_helper'
2
+ require 'markdo/models/task_collection'
3
+ require 'markdo/ics_exporter'
4
+
5
+ module Markdo
6
+ describe IcsExporter do
7
+ it 'produces the body of an iCalendar (.ics) file for incomplete tasks with a valid due date' do
8
+ task_collection = TaskCollection.new([
9
+ '- [ ] Task with no tags',
10
+ '- [ ] @due(2016-04-01) Task with tag-style due date',
11
+ '- [ ] @due(2016-06-31) Task with invalid date',
12
+ '- [x] @due(2016-04-01) Completed task with tag-style due date',
13
+ ])
14
+ ics_exporter = IcsExporter.new(task_collection)
15
+
16
+ begin
17
+ Date.parse('2016-06-31')
18
+ rescue
19
+ # MRI raises an error, Opal pushes the date to the next valid one.
20
+ invalid_dates_raise_error = true
21
+ end
22
+
23
+ if invalid_dates_raise_error
24
+ expect(ics_exporter.to_ics).to eq(<<-ICS)
25
+ BEGIN:VCALENDAR
26
+ VERSION:2.0
27
+ CALSCALE:GREGORIAN
28
+ METHOD:PUBLISH
29
+ X-WR-CALNAME:Markdo Due Dates
30
+ BEGIN:VEVENT
31
+ DTSTART;VALUE=DATE:20160401
32
+ DTEND;VALUE=DATE:20160401
33
+ SUMMARY:Task with tag-style due date
34
+ END:VEVENT
35
+ END:VCALENDAR
36
+ ICS
37
+ else
38
+ expect(ics_exporter.to_ics).to eq(<<-ICS)
39
+ BEGIN:VCALENDAR
40
+ VERSION:2.0
41
+ CALSCALE:GREGORIAN
42
+ METHOD:PUBLISH
43
+ X-WR-CALNAME:Markdo Due Dates
44
+ BEGIN:VEVENT
45
+ DTSTART;VALUE=DATE:20160401
46
+ DTEND;VALUE=DATE:20160401
47
+ SUMMARY:Task with tag-style due date
48
+ END:VEVENT
49
+ BEGIN:VEVENT
50
+ DTSTART;VALUE=DATE:20160701
51
+ DTEND;VALUE=DATE:20160701
52
+ SUMMARY:Task with invalid date
53
+ END:VEVENT
54
+ END:VCALENDAR
55
+ ICS
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,85 @@
1
+ require 'spec_helper'
2
+ require 'markdo/models/task_attribute'
3
+
4
+ module Markdo
5
+ describe TaskAttribute do
6
+ describe 'given no value' do
7
+ describe '#value' do
8
+ it 'is nil' do
9
+ expect(TaskAttribute.new('foo', nil).value).to be_nil
10
+ end
11
+ end
12
+
13
+ describe '#date_value' do
14
+ it 'is nil' do
15
+ expect(TaskAttribute.new('foo', nil).date_value).to be_nil
16
+ end
17
+ end
18
+
19
+ describe '#==' do
20
+ it 'is true when key and value are the same' do
21
+ assert_equality(TaskAttribute.new('foo', nil),
22
+ TaskAttribute.new('foo', nil))
23
+ end
24
+
25
+ it 'is false when key is different' do
26
+ assert_inequality(TaskAttribute.new('foo', nil),
27
+ TaskAttribute.new('bar', nil))
28
+ end
29
+ end
30
+ end
31
+
32
+ describe 'given a word value' do
33
+ describe '#value' do
34
+ it 'is that word' do
35
+ expect(TaskAttribute.new('foo', 'bar').value).to eq('bar')
36
+ end
37
+ end
38
+
39
+ describe '#date_value' do
40
+ it 'is nil' do
41
+ expect(TaskAttribute.new('foo', 'bar').date_value).to be_nil
42
+ end
43
+ end
44
+
45
+ describe '#==' do
46
+ it 'is true when key and value are the same' do
47
+ assert_equality(TaskAttribute.new('foo', 'bar'),
48
+ TaskAttribute.new('foo', 'bar'))
49
+ end
50
+
51
+ it 'is false when key is different' do
52
+ assert_inequality(TaskAttribute.new('foo', 'bar'),
53
+ TaskAttribute.new('bar', 'bar'))
54
+ end
55
+ end
56
+ end
57
+
58
+ describe 'given an ISO-8601-formatted date value' do
59
+ describe '#value' do
60
+ it 'is the string' do
61
+ expect(TaskAttribute.new('due', '2016-01-01').value).to eq('2016-01-01')
62
+ end
63
+ end
64
+
65
+ describe '#date_value' do
66
+ it 'is the date' do
67
+ expect(TaskAttribute.new('due', '2016-01-01').date_value).
68
+ to eq(Date.new(2016, 1, 1))
69
+ end
70
+ end
71
+
72
+ describe '#==' do
73
+ it 'is true when key and value are the same' do
74
+ assert_equality(TaskAttribute.new('foo', '2016-01-01'),
75
+ TaskAttribute.new('foo', '2016-01-01'))
76
+ end
77
+
78
+ it 'is false when key is different' do
79
+ assert_inequality(TaskAttribute.new('foo', '2016-01-01'),
80
+ TaskAttribute.new('bar', '2016-01-01'))
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,168 @@
1
+ require 'spec_helper'
2
+ require 'markdo/models/task_collection'
3
+
4
+ module Markdo
5
+ describe TaskCollection do
6
+ describe '#all' do
7
+ it 'returns all tasks' do
8
+ task_collection = TaskCollection.new([
9
+ '- [ ] Example 1',
10
+ '- [ ] Example 2',
11
+ ])
12
+
13
+ expect(task_collection.all).to eq([
14
+ Task.new('- [ ] Example 1'),
15
+ Task.new('- [ ] Example 2'),
16
+ ])
17
+ end
18
+ end
19
+
20
+ describe '#with_match' do
21
+ it 'returns tasks matching a regular expression' do
22
+ task_collection = TaskCollection.new([
23
+ '- [ ] Foo',
24
+ '- [ ] Bar',
25
+ ])
26
+
27
+ expect(task_collection.with_match(/foo/i)).to eq([
28
+ Task.new('- [ ] Foo'),
29
+ ])
30
+ end
31
+
32
+ it 'returns tasks matching a string' do
33
+ task_collection = TaskCollection.new([
34
+ '- [ ] Foo',
35
+ '- [ ] Bar',
36
+ ])
37
+
38
+ expect(task_collection.with_match(/Bar/)).to eq([
39
+ Task.new('- [ ] Bar'),
40
+ ])
41
+ end
42
+ end
43
+
44
+ describe '#with_tag' do
45
+ it 'returns tasks with the given tag' do
46
+ expect(build_task_collection.with_tag('tag')).to eq([
47
+ Task.new('- [ ] Example @tag'),
48
+ ])
49
+ end
50
+ end
51
+
52
+ describe '#starred' do
53
+ it 'returns tasks with the @star tag' do
54
+ expect(build_task_collection.starred).to eq([
55
+ Task.new('- [ ] Example @star'),
56
+ ])
57
+ end
58
+ end
59
+
60
+ describe '#with_attribute' do
61
+ it 'returns tasks with the given tag, with or without a value' do
62
+ expect(build_task_collection.with_attribute('priority')).to eq([
63
+ Task.new('- [ ] Example @priority'),
64
+ Task.new('- [ ] Example @priority(1)'),
65
+ Task.new('- [ ] Example @priority(2)'),
66
+ ])
67
+ end
68
+ end
69
+
70
+ describe '#due_on' do
71
+ it 'returns tasks due on the given date' do
72
+ expect(build_task_collection.due_on(Date.new(2016, 3, 3))).to eq([
73
+ Task.new('- [ ] Example @due(2016-03-03)'),
74
+ ])
75
+ end
76
+ end
77
+
78
+ describe '#due_between' do
79
+ it 'returns tasks due between the given begin date and end date' do
80
+ begin_date = Date.new(2016, 3, 3)
81
+ end_date = Date.new(2016, 3, 5)
82
+ tasks = build_task_collection.due_between(begin_date, end_date)
83
+
84
+ expect(tasks).to eq([
85
+ Task.new('- [ ] Example @due(2016-03-03)'),
86
+ Task.new('- [ ] Example @due(2016-03-04)'),
87
+ Task.new('- [ ] Example @due(2016-03-05)'),
88
+ ])
89
+ end
90
+ end
91
+
92
+ describe '#overdue' do
93
+ it 'returns tasks due before today' do
94
+ expect(build_task_collection.overdue).to eq([
95
+ Task.new('- [ ] Example @due(2016-02-27)'),
96
+ ])
97
+ end
98
+ end
99
+
100
+ describe '#due_today' do
101
+ it 'returns tasks due today' do
102
+ expect(build_task_collection.due_today).to eq([
103
+ Task.new('- [ ] Example @due(2016-02-28)'),
104
+ ])
105
+ end
106
+ end
107
+
108
+ describe '#due_tomorrow' do
109
+ it 'returns tasks due the day after today' do
110
+ expect(build_task_collection.due_tomorrow).to eq([
111
+ Task.new('- [ ] Example @due(2016-02-29)'),
112
+ ])
113
+ end
114
+ end
115
+
116
+ describe '#due_soon' do
117
+ it 'returns tasks due within a week of the day after today' do
118
+ expect(build_task_collection.due_soon).to eq([
119
+ Task.new('- [ ] Example @due(2016-03-01)'),
120
+ Task.new('- [ ] Example @due(2016-03-02)'),
121
+ Task.new('- [ ] Example @due(2016-03-03)'),
122
+ Task.new('- [ ] Example @due(2016-03-04)'),
123
+ Task.new('- [ ] Example @due(2016-03-05)'),
124
+ Task.new('- [ ] Example @due(2016-03-06)'),
125
+ ])
126
+ end
127
+ end
128
+
129
+ describe '#deferred_until_today' do
130
+ it 'returns tasks deferred up until and including today' do
131
+ expect(build_task_collection.deferred_until_today).to eq([
132
+ Task.new('- [ ] Example @defer(1996-01-01)'),
133
+ Task.new('- [ ] Example @defer(2016-02-27)'),
134
+ Task.new('- [ ] Example @defer(2016-02-28)'),
135
+ ])
136
+ end
137
+ end
138
+
139
+ def build_task_collection
140
+ today = Date.new(2016, 2, 28)
141
+
142
+ lines = [
143
+ '- [ ] No tags',
144
+ '- [ ] Example @tag',
145
+ '- [ ] Example @star',
146
+ '- [ ] Example @priority',
147
+ '- [ ] Example @priority(1)',
148
+ '- [ ] Example @priority(2)',
149
+ '- [ ] Example @due(2016-02-27)',
150
+ '- [ ] Example @due(2016-02-28)',
151
+ '- [ ] Example @due(2016-02-29)',
152
+ '- [ ] Example @due(2016-03-01)',
153
+ '- [ ] Example @due(2016-03-02)',
154
+ '- [ ] Example @due(2016-03-03)',
155
+ '- [ ] Example @due(2016-03-04)',
156
+ '- [ ] Example @due(2016-03-05)',
157
+ '- [ ] Example @due(2016-03-06)',
158
+ '- [ ] Example @due(2016-03-07)',
159
+ '- [ ] Example @defer(1996-01-01)',
160
+ '- [ ] Example @defer(2016-02-27)',
161
+ '- [ ] Example @defer(2016-02-28)',
162
+ '- [ ] Example @defer(2016-02-29)',
163
+ ]
164
+
165
+ TaskCollection.new(lines, today)
166
+ end
167
+ end
168
+ end