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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +15 -9
- data/Dockerfile +21 -7
- data/Guardfile +11 -0
- data/README.md +12 -7
- data/Rakefile +11 -7
- data/bin/markdo +1 -1
- data/docker-compose.yml +9 -0
- data/lib/markdo/cli.rb +23 -55
- data/lib/markdo/command_support.rb +14 -0
- data/lib/markdo/commands.rb +16 -0
- data/lib/markdo/{add_command.rb → commands/add_command.rb} +2 -6
- data/lib/markdo/commands/command.rb +33 -0
- data/lib/markdo/{edit_command.rb → commands/edit_command.rb} +2 -2
- data/lib/markdo/commands/forecast_command.rb +41 -0
- data/lib/markdo/{help_command.rb → commands/help_command.rb} +7 -8
- data/lib/markdo/commands/ics_command.rb +15 -0
- data/lib/markdo/commands/inbox_command.rb +11 -0
- data/lib/markdo/commands/overdue_command.rb +11 -0
- data/lib/markdo/commands/overview_command.rb +16 -0
- data/lib/markdo/commands/process_command.rb +91 -0
- data/lib/markdo/commands/query_command.rb +14 -0
- data/lib/markdo/commands/star_command.rb +11 -0
- data/lib/markdo/commands/summary_command.rb +25 -0
- data/lib/markdo/commands/tag_command.rb +11 -0
- data/lib/markdo/commands/today_command.rb +12 -0
- data/lib/markdo/commands/tomorrow_command.rb +12 -0
- data/lib/markdo/{version_command.rb → commands/version_command.rb} +1 -1
- data/lib/markdo/commands/week_command.rb +16 -0
- data/lib/markdo/data_source.rb +27 -0
- data/lib/markdo/ics_exporter.rb +65 -0
- data/lib/markdo/models/task.rb +46 -0
- data/lib/markdo/models/task_attribute.rb +22 -0
- data/lib/markdo/models/task_collection.rb +75 -0
- data/lib/markdo/version.rb +1 -1
- data/markdo.gemspec +6 -0
- data/script/build +9 -0
- data/spec/fixtures/add_command/Inbox.md +0 -0
- data/spec/fixtures/date_commands/Inbox.md +6 -0
- data/spec/fixtures/date_commands/Sprint.md +6 -0
- data/spec/fixtures/ics_command/Inbox.md +0 -0
- data/{test/fixtures/ics_command.md → spec/fixtures/ics_command/Sprint.md} +0 -0
- data/spec/fixtures/inbox_command/Inbox.md +2 -0
- data/spec/fixtures/inbox_command/Sprint.md +2 -0
- data/spec/fixtures/process_command/Backlog.md +0 -0
- data/spec/fixtures/process_command/Inbox.md +0 -0
- data/spec/fixtures/process_command/Maybe.md +0 -0
- data/spec/fixtures/process_command/Sprint.md +0 -0
- data/spec/fixtures/query_command/Inbox.md +4 -0
- data/spec/fixtures/query_command/Sprint.md +4 -0
- data/spec/fixtures/tag_command/Inbox.md +2 -0
- data/spec/fixtures/tag_command/Sprint.md +2 -0
- data/spec/lib/cli_spec.rb +79 -0
- data/spec/lib/commands/add_command_spec.rb +70 -0
- data/spec/lib/commands/edit_command_spec.rb +32 -0
- data/spec/lib/commands/forecast_command_spec.rb +24 -0
- data/spec/lib/commands/ics_command_spec.rb +30 -0
- data/spec/lib/commands/inbox_command_spec.rb +22 -0
- data/spec/lib/commands/overdue_command_spec.rb +19 -0
- data/spec/lib/commands/overview_command_spec.rb +25 -0
- data/spec/lib/commands/process_command_spec.rb +178 -0
- data/spec/lib/commands/query_command_spec.rb +23 -0
- data/spec/lib/commands/star_command_spec.rb +19 -0
- data/spec/lib/commands/summary_command_spec.rb +23 -0
- data/spec/lib/commands/tag_command_spec.rb +21 -0
- data/spec/lib/commands/today_command_spec.rb +19 -0
- data/spec/lib/commands/tomorrow_command_spec.rb +19 -0
- data/spec/lib/commands/week_command_spec.rb +23 -0
- data/spec/lib/ics_exporter_spec.rb +59 -0
- data/spec/lib/models/task_attribute_spec.rb +85 -0
- data/spec/lib/models/task_collection_spec.rb +168 -0
- data/spec/lib/models/task_spec.rb +79 -0
- data/spec/spec_helper.rb +40 -0
- metadata +189 -42
- data/.ruby-version +0 -1
- data/lib/markdo/command.rb +0 -12
- data/lib/markdo/date_command.rb +0 -9
- data/lib/markdo/forecast_command.rb +0 -61
- data/lib/markdo/ics_command.rb +0 -73
- data/lib/markdo/inbox_command.rb +0 -15
- data/lib/markdo/overdue_command.rb +0 -47
- data/lib/markdo/overview_command.rb +0 -17
- data/lib/markdo/process_command.rb +0 -68
- data/lib/markdo/query_command.rb +0 -24
- data/lib/markdo/rss_command.rb +0 -67
- data/lib/markdo/star_command.rb +0 -9
- data/lib/markdo/summary_command.rb +0 -30
- data/lib/markdo/tag_command.rb +0 -9
- data/lib/markdo/today_command.rb +0 -10
- data/lib/markdo/tomorrow_command.rb +0 -10
- data/lib/markdo/week_command.rb +0 -36
- data/test/fixtures/inbox.md +0 -2
- data/test/fixtures/rss_command.md +0 -4
- data/test/ics_command_test.rb +0 -38
- data/test/inbox_command_test.rb +0 -19
- data/test/rss_command_test.rb +0 -39
- data/test/summary_command_test.rb +0 -19
- data/test/test_helper.rb +0 -3
data/lib/markdo/ics_command.rb
DELETED
@@ -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
|
data/lib/markdo/inbox_command.rb
DELETED
@@ -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
|
-
|
data/lib/markdo/query_command.rb
DELETED
@@ -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
|
data/lib/markdo/rss_command.rb
DELETED
@@ -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
|
data/lib/markdo/star_command.rb
DELETED
@@ -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
|
data/lib/markdo/tag_command.rb
DELETED
data/lib/markdo/today_command.rb
DELETED
data/lib/markdo/week_command.rb
DELETED
@@ -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
|
data/test/fixtures/inbox.md
DELETED
data/test/ics_command_test.rb
DELETED
@@ -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
|