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
@@ -1,73 +0,0 @@
1
- require 'markdo/command'
2
- require 'date'
3
- require 'uri'
4
-
5
- module Markdo
6
- class IcsCommand < Command
7
- def run
8
- events = Dir.
9
- glob(markdown_glob).
10
- map { |path| File.readlines(path, encoding: 'UTF-8') }.
11
- flatten.
12
- grep(date_regexp).
13
- reject { |line| line.match(/[-*] \[x\]/) }.
14
- map { |line|
15
- begin
16
- raw_due_date = line.match(date_regexp)
17
- due_date = Date.parse(raw_due_date[0])
18
- Event.new(due_date, due_date, clean(line))
19
- rescue ArgumentError
20
- # invalid date, skip it
21
- end
22
- }.compact
23
-
24
- ics = template(events)
25
-
26
- @stdout.puts(ics)
27
- end
28
-
29
- protected
30
-
31
- Event = Struct.new(:date_start, :date_end, :summary) do
32
- def to_ics
33
- buf = []
34
- buf << 'BEGIN:VEVENT'
35
- buf << "DTSTART;VALUE=DATE:#{date_start.strftime('%Y%m%d')}"
36
- buf << "DTEND;VALUE=DATE:#{date_end.strftime('%Y%m%d')}"
37
- buf << "SUMMARY:#{summary}"
38
- buf << 'END:VEVENT'
39
- buf.join("\n")
40
- end
41
- end
42
-
43
- def markdown_glob
44
- "#{@env['MARKDO_ROOT']}/*.md"
45
- end
46
-
47
- def date_regexp
48
- %r(\b\d{4}-\d{2}-\d{2}\b)
49
- end
50
-
51
- def clean(line)
52
- line.
53
- sub(/\s*[-*] \[.\]\s+/, '').
54
- sub(date_regexp, '').
55
- sub('@due()', '').
56
- strip
57
- end
58
-
59
- def template(events)
60
- buf = []
61
-
62
- buf << 'BEGIN:VCALENDAR'
63
- buf << 'VERSION:2.0'
64
- buf << 'CALSCALE:GREGORIAN'
65
- buf << 'METHOD:PUBLISH'
66
- buf << 'X-WR-CALNAME:Markdo Due Dates'
67
- buf << events.map { |event| event.to_ics }
68
- buf << 'END:VCALENDAR'
69
-
70
- buf.flatten.join("\n")
71
- end
72
- end
73
- end
@@ -1,15 +0,0 @@
1
- require 'markdo/command'
2
-
3
- module Markdo
4
- class InboxCommand < Command
5
- def run
6
- @stdout.puts(File.read(inbox_path))
7
- end
8
-
9
- protected
10
-
11
- def inbox_path
12
- File.join(@env['MARKDO_ROOT'], @env['MARKDO_INBOX'])
13
- end
14
- end
15
- end
@@ -1,47 +0,0 @@
1
- require 'date'
2
- require 'markdo/query_command'
3
-
4
- module Markdo
5
- # TODO: More testing of this logic. As of 2016-01-23, I was building this
6
- # project as a proof of concept.
7
- class OverdueCommand < Command
8
- def initialize(*)
9
- @date = Date.today
10
- super
11
- end
12
-
13
- def run
14
- query_command = QueryCommand.new(@stdout, @stderr, @env)
15
-
16
- all_queries.each do |query|
17
- query_command.run(query)
18
- end
19
- end
20
-
21
- private
22
-
23
- def all_queries
24
- [previous_years, previous_months_this_year, previous_days_this_month].flatten
25
- end
26
-
27
- def previous_days_this_month
28
- (1...@date.day).to_a.map { |day|
29
- "#{@date.year}-#{justify(@date.month)}-#{justify(day)}"
30
- }
31
- end
32
-
33
- def previous_months_this_year
34
- (1...@date.month).to_a.map { |month|
35
- "#{@date.year}-#{justify(month)}"
36
- }
37
- end
38
-
39
- def previous_years
40
- (2000...@date.year).to_a.map { |year| "#{year}-" }
41
- end
42
-
43
- def justify(less_than_100)
44
- less_than_100.to_s.rjust(2, '0')
45
- end
46
- end
47
- end
@@ -1,17 +0,0 @@
1
- require 'markdo/command'
2
- require 'markdo/overdue_command'
3
- require 'markdo/star_command'
4
- require 'markdo/today_command'
5
- require 'markdo/tomorrow_command'
6
-
7
- module Markdo
8
- class OverviewCommand < Command
9
- def run
10
- commands = [OverdueCommand, StarCommand, TodayCommand, TomorrowCommand]
11
-
12
- commands.each do |command|
13
- command.new(@stdout, @stderr, @env).run
14
- end
15
- end
16
- end
17
- end
@@ -1,68 +0,0 @@
1
- require 'date'
2
- require 'markdo/command'
3
-
4
- module Markdo
5
- class ProcessCommand < Command
6
- # Built as a prototype/proof of concept to see how much I like this idea...
7
- def run
8
- lines = File.readlines(inbox_path)
9
- lines_by_filename = Hash.new { [] }
10
-
11
- index = 0
12
-
13
- while index < lines.length
14
- line = lines[index]
15
- @stdout.puts line
16
- @stdout.print 'File [hisbma]? '
17
- choice = $stdin.gets.chomp.downcase
18
-
19
- case choice
20
- when 'h'
21
- @stdout.puts 'i - inbox (keep in inbox)'
22
- @stdout.puts 's - sprint'
23
- @stdout.puts 'b - backlog'
24
- @stdout.puts 'm - maybe'
25
- @stdout.puts 'a - abort; make no changes'
26
- when 'i'
27
- lines_by_filename['Inbox.md'] <<= line
28
- index += 1
29
- when 's'
30
- lines_by_filename['Sprint.md'] <<= line
31
- index += 1
32
- when 'b'
33
- lines_by_filename['Backlog.md'] <<= line
34
- index += 1
35
- when 'm'
36
- lines_by_filename['Maybe.md'] <<= line
37
- index += 1
38
- when 'a'
39
- exit
40
- end
41
- end
42
-
43
- date = Date.today.iso8601
44
- inbox_lines = lines_by_filename.delete('Inbox.md')
45
- File.write(inbox_path, inbox_lines ? inbox_lines.join : '')
46
-
47
- lines_by_filename.each do |filename, lines|
48
- path = file_path(filename)
49
- new_content = ["\n## Processed on #{date}\n\n"] << lines
50
-
51
- File.open(path, 'a') do |file|
52
- file.puts(new_content.join)
53
- end
54
- end
55
- end
56
-
57
- private
58
-
59
- def file_path(filename)
60
- File.join(@env['MARKDO_ROOT'], filename)
61
- end
62
-
63
- def inbox_path
64
- File.join(@env['MARKDO_ROOT'], @env['MARKDO_INBOX'])
65
- end
66
- end
67
- end
68
-
@@ -1,24 +0,0 @@
1
- require 'markdo/command'
2
-
3
- module Markdo
4
- class QueryCommand < Command
5
- def run(string)
6
- regexp = Regexp.new(string, Regexp::IGNORECASE)
7
-
8
- matches = Dir.
9
- glob(markdown_glob).
10
- map { |path| File.readlines(path, encoding: 'UTF-8') }.
11
- flatten.
12
- grep(regexp).
13
- reject { |line| line.match(/[-*] \[x\]/) }
14
-
15
- @stdout.puts(matches)
16
- end
17
-
18
- protected
19
-
20
- def markdown_glob
21
- "#{@env['MARKDO_ROOT']}/*.md"
22
- end
23
- end
24
- end
@@ -1,67 +0,0 @@
1
- require 'markdo/command'
2
- require 'uri'
3
-
4
- module Markdo
5
- class RssCommand < Command
6
- def run
7
- uri_parser = URI::Parser.new
8
-
9
- items = Dir.
10
- glob(markdown_glob).
11
- map { |path| File.readlines(path, encoding: 'UTF-8') }.
12
- flatten.
13
- grep(%r(https?://)).
14
- map { |line| Item.new(title: clean(line), links: uri_parser.extract(line)) }
15
-
16
- xml = template(items)
17
-
18
- @stdout.puts(xml)
19
- end
20
-
21
- protected
22
-
23
- class Item
24
- attr_reader :title, :links
25
-
26
- def initialize(kwargs)
27
- @title = kwargs[:title]
28
- @links = kwargs[:links]
29
- end
30
-
31
- def link
32
- links && !links.empty? && links[0]
33
- end
34
- end
35
-
36
- def markdown_glob
37
- "#{@env['MARKDO_ROOT']}/*.md"
38
- end
39
-
40
- def clean(line)
41
- line.sub(/\s*[-*] \[.\]\s+/, '').chomp
42
- end
43
-
44
- def template(items)
45
- buf = []
46
-
47
- # No beginning of line whitespace allowed
48
- buf << %(<?xml version="1.0" encoding="UTF-8"?>)
49
-
50
- buf << %(<rss version="2.0">)
51
- buf << %(<channel>)
52
- buf << %(<title>Links in Markdo</title>)
53
-
54
- items.each do |item|
55
- buf << %(<item>)
56
- buf << %(<title>#{item.title}</title>)
57
- buf << %(<link>#{item.link}</link>)
58
- buf << %(</item>)
59
- end
60
-
61
- buf << %(</channel>)
62
- buf << %(</rss>)
63
-
64
- buf.join("\n")
65
- end
66
- end
67
- end
@@ -1,9 +0,0 @@
1
- require 'markdo/tag_command'
2
-
3
- module Markdo
4
- class StarCommand < TagCommand
5
- def run
6
- super('star')
7
- end
8
- end
9
- end
@@ -1,30 +0,0 @@
1
- require 'stringio'
2
-
3
- require 'markdo/command'
4
- require 'markdo/inbox_command'
5
- require 'markdo/overdue_command'
6
- require 'markdo/star_command'
7
- require 'markdo/today_command'
8
- require 'markdo/tomorrow_command'
9
- require 'markdo/week_command'
10
-
11
- module Markdo
12
- class SummaryCommand < Command
13
- def run
14
- commands = [OverdueCommand, StarCommand, TodayCommand, TomorrowCommand, WeekCommand, InboxCommand]
15
-
16
- commands.each do |command|
17
- out = StringIO.new
18
- command.new(out, @stderr, @env).run
19
-
20
- title = command.to_s.sub(/^Markdo::/, '').sub(/Command$/, '')
21
- lines = out.string.split("\n")
22
- sum = lines.length
23
-
24
- unless sum.zero?
25
- @stdout.puts("#{title}: #{sum}")
26
- end
27
- end
28
- end
29
- end
30
- end
@@ -1,9 +0,0 @@
1
- require 'markdo/query_command'
2
-
3
- module Markdo
4
- class TagCommand < QueryCommand
5
- def run(string)
6
- super("@#{string}")
7
- end
8
- end
9
- end
@@ -1,10 +0,0 @@
1
- require 'date'
2
- require 'markdo/date_command'
3
-
4
- module Markdo
5
- class TodayCommand < DateCommand
6
- def run
7
- super(Date.today)
8
- end
9
- end
10
- end
@@ -1,10 +0,0 @@
1
- require 'date'
2
- require 'markdo/date_command'
3
-
4
- module Markdo
5
- class TomorrowCommand < DateCommand
6
- def run
7
- super(Date.today + 1)
8
- end
9
- end
10
- end
@@ -1,36 +0,0 @@
1
- require 'date'
2
- require 'markdo/query_command'
3
-
4
- module Markdo
5
- # TODO: More testing of this logic. As of 2016-01-23, I was building this
6
- # project as a proof of concept.
7
- class WeekCommand < Command
8
- attr_accessor :date
9
-
10
- def initialize(*)
11
- @date = Date.today
12
- super
13
- end
14
-
15
- def run
16
- query_command = QueryCommand.new(@stdout, @stderr, @env)
17
-
18
- dates_over_the_next_week.each do |query|
19
- query_command.run(query)
20
- end
21
- end
22
-
23
- private
24
-
25
- def dates_over_the_next_week
26
- (0..7).to_a.map { |offset|
27
- adjusted_date = @date + offset
28
- "#{adjusted_date.year}-#{justify(adjusted_date.month)}-#{justify(adjusted_date.day)}"
29
- }
30
- end
31
-
32
- def justify(less_than_100)
33
- less_than_100.to_s.rjust(2, '0')
34
- end
35
- end
36
- end
@@ -1,2 +0,0 @@
1
- - [ ] Test 1
2
- - [ ] Test 2
@@ -1,4 +0,0 @@
1
- - [ ] Task 1
2
- - [ ] Task with HTTP URL http://www.example.com/
3
- - [ ] Task with HTTPS URL https://www.example.com/
4
- - [ ] Task with HTTP URL http://www.example.com/ and trailing text
@@ -1,38 +0,0 @@
1
- require 'test_helper'
2
- require 'markdo/ics_command'
3
-
4
- module Markdo
5
- describe IcsCommand do
6
- it 'outputs an iCalendar feed from the input Markdown, skipping invalid dates' do
7
- out = StringIO.new
8
- err = StringIO.new
9
- env = { 'MARKDO_ROOT' => 'test/fixtures' }
10
-
11
- IcsCommand.new(out, err, env).run
12
-
13
- out.string.must_equal <<-ICS
14
- BEGIN:VCALENDAR
15
- VERSION:2.0
16
- CALSCALE:GREGORIAN
17
- METHOD:PUBLISH
18
- X-WR-CALNAME:Markdo Due Dates
19
- BEGIN:VEVENT
20
- DTSTART;VALUE=DATE:20140401
21
- DTEND;VALUE=DATE:20140401
22
- SUMMARY:Task with long-past due date
23
- END:VEVENT
24
- BEGIN:VEVENT
25
- DTSTART;VALUE=DATE:20160401
26
- DTEND;VALUE=DATE:20160401
27
- SUMMARY:Task with due date
28
- END:VEVENT
29
- BEGIN:VEVENT
30
- DTSTART;VALUE=DATE:20160401
31
- DTEND;VALUE=DATE:20160401
32
- SUMMARY:Task with tag-style due date
33
- END:VEVENT
34
- END:VCALENDAR
35
- ICS
36
- end
37
- end
38
- end