markdo 0.1.12.alpha → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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