org_mode 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +2 -0
- data/bin/org-mode +10 -12
- data/features/cmdline_agenda.feature +5 -6
- data/features/cmdline_update.feature +42 -0
- data/features/steps.rb +4 -0
- data/lib/capture_stdout.rb +13 -0
- data/lib/org_mode/commands/agenda.rb +3 -13
- data/lib/org_mode/commands/update.rb +19 -0
- data/lib/org_mode/formatters/textual.rb +48 -0
- data/lib/org_mode/node_utils.rb +20 -0
- data/lib/org_mode/parser.rb +16 -15
- data/lib/org_mode/presenters/console.rb +51 -0
- data/lib/org_mode/processors/archive_done.rb +69 -0
- data/lib/org_mode/reporters/agenda.rb +8 -3
- data/lib/org_mode/version.rb +1 -1
- data/lib/org_mode.rb +40 -12
- data/org_mode.gemspec +1 -0
- data/spec/lib/org_mode/commands/agenda_spec.rb +6 -25
- data/spec/lib/org_mode/commands/helpers.rb +20 -0
- data/spec/lib/org_mode/commands/update_spec.rb +39 -0
- data/spec/lib/org_mode/formatters/textual_spec.rb +46 -0
- data/spec/lib/org_mode/parser_spec.rb +9 -3
- data/spec/lib/org_mode/processors/archive_done_spec.rb +44 -0
- data/spec/lib/org_mode/reporters/agenda_spec.rb +0 -29
- data/spec/lib/org_mode_spec.rb +1 -0
- data/spec/support/blueprints.rb +5 -1
- metadata +34 -4
data/Gemfile
CHANGED
@@ -6,6 +6,7 @@ gemspec
|
|
6
6
|
gem "commander"
|
7
7
|
gem "facets"
|
8
8
|
gem "mustache"
|
9
|
+
gem "colorize"
|
9
10
|
|
10
11
|
group :development do
|
11
12
|
if RUBY_VERSION =~ /^1\.9/
|
@@ -21,6 +22,7 @@ group :test do
|
|
21
22
|
gem "timecop"
|
22
23
|
gem "guard-cucumber"
|
23
24
|
gem "popen4"
|
25
|
+
gem "pry"
|
24
26
|
end
|
25
27
|
|
26
28
|
|
data/bin/org-mode
CHANGED
@@ -11,22 +11,20 @@ program :description, 'Formats and extracts information out of org-mode files'
|
|
11
11
|
program :help_formatter, :compact
|
12
12
|
|
13
13
|
command :agenda do |c|
|
14
|
-
c.syntax = 'org-mode agenda [options]'
|
15
|
-
c.summary = ''
|
16
|
-
c.description =
|
17
|
-
c.example '
|
18
|
-
c.option '--
|
14
|
+
c.syntax = 'org-mode agenda [options] *org-mode-files'
|
15
|
+
c.summary = 'Displays information on scheduled items'
|
16
|
+
c.description = nil
|
17
|
+
c.example 'extract information on scheduled items out of files', 'org-mode agenda an-org-file.org'
|
18
|
+
c.option '--no-color', 'Do not use color in output'
|
19
19
|
c.when_called OrgMode::Commands::Agenda, :execute
|
20
20
|
end
|
21
21
|
|
22
22
|
command :update do |c|
|
23
|
-
c.syntax = '
|
24
|
-
c.summary = ''
|
25
|
-
c.description = ''
|
23
|
+
c.syntax = 'org-mode update [options]'
|
24
|
+
c.summary = 'Reformats an org-file'
|
25
|
+
c.description = 'Reformat and update the org-file. Converting the org-file to a standard format'
|
26
26
|
c.example 'description', 'command example'
|
27
|
-
c.option '--
|
28
|
-
c.
|
29
|
-
# Do something or c.when_called Orgy::Commands::Update
|
30
|
-
end
|
27
|
+
c.option '--archive-done', 'Archives done items'
|
28
|
+
c.when_called OrgMode::Commands::Update, :execute
|
31
29
|
end
|
32
30
|
|
@@ -11,12 +11,11 @@ Feature: agenda
|
|
11
11
|
"""
|
12
12
|
When the script is executed on the org-file
|
13
13
|
When the script is called with "agenda"
|
14
|
-
Then the output should
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
"""
|
14
|
+
Then the output should contain "Agenda"
|
15
|
+
And the output should contain "2012-01-01"
|
16
|
+
And the output should contain "15:15"
|
17
|
+
And the output should contain "TODO"
|
18
|
+
And the output should contain "Scheduled task"
|
20
19
|
|
21
20
|
# @focus
|
22
21
|
# Scenario: should limit restults in a day view
|
@@ -0,0 +1,42 @@
|
|
1
|
+
Feature: org-mode update <orgfile>
|
2
|
+
org-mode script should reformat a file correctly
|
3
|
+
to a user can easily reformat an org-file in a
|
4
|
+
standarized way. This also enables certain
|
5
|
+
filters to be applied.
|
6
|
+
|
7
|
+
Scenario: should present meaningfull agenda information
|
8
|
+
Given we have an org-file with the following content
|
9
|
+
"""
|
10
|
+
a header
|
11
|
+
* TODO Scheduled task <1-1-2012 Wed 15:15>
|
12
|
+
content that will be indented
|
13
|
+
multiline that is
|
14
|
+
** TODO Scheduled task <1-1-2012 Wed 15:15-16:15>
|
15
|
+
some other content that will be indented
|
16
|
+
"""
|
17
|
+
When the script is called with "update"
|
18
|
+
Then the output should be
|
19
|
+
"""
|
20
|
+
a header
|
21
|
+
|
22
|
+
* TODO <2012-01-01 Sun 15:15> Scheduled task
|
23
|
+
content that will be indented
|
24
|
+
multiline that is
|
25
|
+
** TODO <2012-01-01 Sun 15:15-16:15> Scheduled task
|
26
|
+
some other content that will be indented
|
27
|
+
"""
|
28
|
+
|
29
|
+
# @focus
|
30
|
+
# Scenario: should limit restults in a day view
|
31
|
+
# Given date is "1-1-2012 15:00"
|
32
|
+
# And we have an org-file with the following content
|
33
|
+
# """
|
34
|
+
# * TODO Todays task <1-1-2012 Wed 15:15>
|
35
|
+
# * TODO Tommorrows task <2-1-2012 Wed 15:15>
|
36
|
+
# """
|
37
|
+
# When the script is called with "agenda today"
|
38
|
+
# Then the output should be
|
39
|
+
# """
|
40
|
+
# Todays activities:
|
41
|
+
# TODO Todays task
|
42
|
+
# """
|
data/features/steps.rb
CHANGED
@@ -30,6 +30,10 @@ end
|
|
30
30
|
Then /^the output should be$/ do |string|
|
31
31
|
@stdout.should == string
|
32
32
|
end
|
33
|
+
Then /^the output should contain "([^""]*)"$/ do |pattern|
|
34
|
+
@stdout.should match(pattern)
|
35
|
+
end
|
36
|
+
|
33
37
|
Then /^the error should be$/ do |string|
|
34
38
|
@script_error.stderr.chomp.should == string
|
35
39
|
end
|
@@ -1,9 +1,9 @@
|
|
1
1
|
require 'org_mode/parser'
|
2
2
|
require 'org_mode/loader'
|
3
3
|
require 'org_mode/reporters/agenda'
|
4
|
+
require 'org_mode/presenters/console'
|
4
5
|
|
5
6
|
require 'core_ext/string'
|
6
|
-
require 'mustache'
|
7
7
|
|
8
8
|
module OrgMode::Commands
|
9
9
|
class Agenda
|
@@ -18,19 +18,9 @@ module OrgMode::Commands
|
|
18
18
|
|
19
19
|
file_collection = OrgMode::Loader.load_and_parse_files(*args)
|
20
20
|
agenda_reporter = OrgMode::Reporters::Agenda.new(file_collection)
|
21
|
+
text_presenter = OrgMode::Presenters::Agenda::Console.new(agenda_reporter)
|
21
22
|
|
22
|
-
|
23
|
-
tmpl_vars[:noi_per_date] = agenda_reporter.open_nodes_grouped_by_day
|
24
|
-
|
25
|
-
puts Mustache.render <<-eos.strip_indent(8), tmpl_vars
|
26
|
-
Agenda ()
|
27
|
-
{{#noi_per_date}}
|
28
|
-
{{date}}
|
29
|
-
{{#nodes}}
|
30
|
-
{{todo_state}}{{title}}
|
31
|
-
{{/nodes}}
|
32
|
-
{{/noi_per_date}}
|
33
|
-
eos
|
23
|
+
puts text_presenter.open_items_per_day_colorized
|
34
24
|
|
35
25
|
rescue SystemCallError => e
|
36
26
|
puts "Encountered a little problem: #{e}"
|
@@ -1,4 +1,23 @@
|
|
1
|
+
require 'org_mode/parser'
|
2
|
+
require 'org_mode/loader'
|
3
|
+
require 'org_mode/formatters/textual'
|
4
|
+
require 'org_mode/processors/archive_done'
|
5
|
+
|
1
6
|
module OrgMode::Commands
|
2
7
|
class Update
|
8
|
+
def execute(args, options)
|
9
|
+
org_file = OrgMode::Loader.load_and_parse_file(args[0])
|
10
|
+
|
11
|
+
if options.archive_done
|
12
|
+
org_file = OrgMode::Processors::ArchiveDone.new(org_file).process
|
13
|
+
end
|
14
|
+
|
15
|
+
org_formatter = OrgMode::Formatters::Textual.new(org_file)
|
16
|
+
|
17
|
+
puts org_formatter.format
|
18
|
+
|
19
|
+
rescue StandardError => e
|
20
|
+
puts "Encountered a little problem: #{e}"
|
21
|
+
end
|
3
22
|
end
|
4
23
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'org_mode'
|
2
|
+
require 'facets/kernel/blank'
|
3
|
+
|
4
|
+
module OrgMode
|
5
|
+
module Formatters
|
6
|
+
class Textual
|
7
|
+
def initialize(org_file)
|
8
|
+
@org_file = org_file
|
9
|
+
end
|
10
|
+
|
11
|
+
def format
|
12
|
+
[ @org_file.header,
|
13
|
+
@org_file.nodes.map {|n| format_node(n)} * "\n",
|
14
|
+
@org_file.footer ].reject(&:blank?) * "\n\n"
|
15
|
+
end
|
16
|
+
|
17
|
+
def format_node(node)
|
18
|
+
[format_title(node), format_content(node)].reject(&:blank?) * "\n"
|
19
|
+
end
|
20
|
+
|
21
|
+
def format_title(node)
|
22
|
+
stars = "*" * node.stars
|
23
|
+
[stars, node.todo_state, format_date(node), node.title].reject(&:blank?) * ' '
|
24
|
+
end
|
25
|
+
|
26
|
+
def format_content(node)
|
27
|
+
node.content.lines.map do |l|
|
28
|
+
[" " * node.indent, l].join
|
29
|
+
end.join
|
30
|
+
end
|
31
|
+
|
32
|
+
def format_date(node)
|
33
|
+
date = if node.date_end_time
|
34
|
+
"#{node.date_start_time.strftime('%Y-%m-%d %a %H:%M')}-#{node.date_end_time.strftime('%H:%M')}"
|
35
|
+
elsif node.date_start_time
|
36
|
+
"#{node.date_start_time.strftime('%Y-%m-%d %a %H:%M')}"
|
37
|
+
elsif node.date
|
38
|
+
"#{node.date.strftime('%Y-%m-%d %a')}"
|
39
|
+
else
|
40
|
+
nil
|
41
|
+
end
|
42
|
+
|
43
|
+
date ? "<#{date}>" : nil
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module OrgMode
|
2
|
+
class NodeUtils
|
3
|
+
# Public: transforms a sequental list of
|
4
|
+
# nodes into a tree structure setting
|
5
|
+
# parents and children on the nodes
|
6
|
+
def self.convert_sequential_nodelist_into_tree(nodes)
|
7
|
+
parent_stack = []
|
8
|
+
nodes.map! do |node|
|
9
|
+
node.parent = parent_stack[node.stars - 1]
|
10
|
+
if node.parent
|
11
|
+
node.parent.children << node
|
12
|
+
end
|
13
|
+
parent_stack[node.stars] = node
|
14
|
+
end
|
15
|
+
|
16
|
+
# filter out all non root nodes
|
17
|
+
nodes.select(&:root_node?)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/org_mode/parser.rb
CHANGED
@@ -7,6 +7,7 @@
|
|
7
7
|
# Parser is decoupled from object model to make it easy to write updated
|
8
8
|
# parsers or use a database to serialize an org-mode file out of.
|
9
9
|
require 'org_mode'
|
10
|
+
require 'org_mode/node_utils'
|
10
11
|
require 'date'
|
11
12
|
|
12
13
|
module OrgMode
|
@@ -29,18 +30,18 @@ module OrgMode
|
|
29
30
|
# Returns OrgMode::File object containing all
|
30
31
|
# information of the file.
|
31
32
|
def parse(buffer)
|
32
|
-
b, nodes, e
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
33
|
+
b, nodes, e = parse_buffer(buffer)
|
34
|
+
|
35
|
+
parsed_nodes = parse_nodes(nodes)
|
36
|
+
root_nodes = NodeUtils.convert_sequential_nodelist_into_tree(parsed_nodes)
|
37
|
+
|
38
|
+
return File.new(b,root_nodes,e)
|
39
|
+
end
|
40
|
+
|
41
|
+
def parse_nodes(nodes)
|
42
|
+
nodes.map do |title,content|
|
43
|
+
NodeParser.parse(title,content)
|
42
44
|
end
|
43
|
-
return File.new(b,nodes,e)
|
44
45
|
end
|
45
46
|
|
46
47
|
def parse_buffer(buffer)
|
@@ -110,11 +111,11 @@ module OrgMode
|
|
110
111
|
def parse_extract_dates(node)
|
111
112
|
_, extracted_date, start_time, end_time = node.title.match(RxDateRegexp).to_a
|
112
113
|
node.title = node.title.gsub(RxDateRegexp, '')
|
114
|
+
node.title.strip!
|
113
115
|
|
114
|
-
if extracted_date
|
115
|
-
|
116
|
-
|
117
|
-
end
|
116
|
+
node.date = DateTime.parse(extracted_date) if extracted_date
|
117
|
+
node.date_start_time = DateTime.parse("#{extracted_date} #{start_time}") if start_time
|
118
|
+
node.date_end_time = DateTime.parse("#{extracted_date} #{end_time}") if end_time
|
118
119
|
end
|
119
120
|
|
120
121
|
RxEmptyLine = /^\s*$/
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'colorize'
|
2
|
+
require 'capture_stdout'
|
3
|
+
|
4
|
+
module OrgMode
|
5
|
+
module Presenters
|
6
|
+
module Agenda
|
7
|
+
|
8
|
+
class Console
|
9
|
+
def initialize reporter
|
10
|
+
@agenda_reporter = reporter
|
11
|
+
end
|
12
|
+
|
13
|
+
def open_items_per_day_colorized
|
14
|
+
capture_stdout do
|
15
|
+
now = DateTime.now
|
16
|
+
puts "Agenda: open items grouped by day [#{now.strftime('%Y-%m-%d %H:%M')}]".yellow.underline
|
17
|
+
puts
|
18
|
+
ongbd = @agenda_reporter.open_nodes_grouped_by_day
|
19
|
+
ongbd.each do |e|
|
20
|
+
puts "#{e.date}".blue.underline
|
21
|
+
e.nodes.each do |n|
|
22
|
+
color = :green
|
23
|
+
color = :red if n.date_start_time && n.node.date_start_time < now
|
24
|
+
color = :red if n.date && n.node.date < now
|
25
|
+
|
26
|
+
puts " " + node_line(n).send(color)
|
27
|
+
end
|
28
|
+
puts
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def node_line(n)
|
36
|
+
title = [n.todo_state, n.title].reject(&:nil?) * ' '
|
37
|
+
if n.appointment?
|
38
|
+
if n.date_end_time
|
39
|
+
"%s-%s %s" % [n.date_start_time, n.date_end_time, title]
|
40
|
+
else
|
41
|
+
"%s %s" % [n.date_start_time, title]
|
42
|
+
end
|
43
|
+
else
|
44
|
+
"ALL-DAY %s" % [title]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'org_mode'
|
2
|
+
require 'core_ext/string'
|
3
|
+
require 'delegate'
|
4
|
+
|
5
|
+
module OrgMode
|
6
|
+
module Processors
|
7
|
+
class ArchiveDoneOrgFile < SimpleDelegator
|
8
|
+
def initialize(org_file)
|
9
|
+
super(org_file)
|
10
|
+
end
|
11
|
+
|
12
|
+
def archived_root_node
|
13
|
+
select_by_title(/^Archived$/, :stars => 1).first
|
14
|
+
end
|
15
|
+
|
16
|
+
def create_archived_root_node
|
17
|
+
self.root_nodes << OrgMode::Node.new.tap do |n|
|
18
|
+
n.title = "Archived"
|
19
|
+
n.content = <<-eos.strip_indent(10)
|
20
|
+
This node contains archived items. Appended
|
21
|
+
due to calling the script with update --archive-done
|
22
|
+
eos
|
23
|
+
n.stars = 1
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def move_done_trees_to_archived_root_node
|
28
|
+
processed_nodes = []
|
29
|
+
self.nodes.each do |n|
|
30
|
+
# if not done move to processed nodes
|
31
|
+
# if done append to archived_root_node
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class ArchiveDone
|
37
|
+
def initialize(org_file)
|
38
|
+
@org_file = ArchiveDoneOrgFile.new(org_file)
|
39
|
+
end
|
40
|
+
|
41
|
+
def process
|
42
|
+
@org_file.create_archived_root_node unless @org_file.archived_root_node
|
43
|
+
|
44
|
+
to_be_archived_trees = []
|
45
|
+
walk_and_update_node_children @org_file do |children|
|
46
|
+
to_be_archived_trees << children.select(&:done?)
|
47
|
+
children.reject(&:done?)
|
48
|
+
end
|
49
|
+
|
50
|
+
@org_file.archived_root_node.children.concat( to_be_archived_trees.flatten )
|
51
|
+
|
52
|
+
@org_file
|
53
|
+
end
|
54
|
+
|
55
|
+
# Private: walks all nodes
|
56
|
+
# but updates children array with
|
57
|
+
# return value of called function.
|
58
|
+
#
|
59
|
+
# Can be used to extract nodes from
|
60
|
+
# tree
|
61
|
+
def walk_and_update_node_children(node, &block)
|
62
|
+
node.children = block.call(node.children)
|
63
|
+
node.children.map do |n|
|
64
|
+
walk_and_update_node_children(n, &block)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'facets/to_hash'
|
2
|
+
require 'facets/ostruct'
|
2
3
|
|
3
4
|
module OrgMode
|
4
5
|
module Reporters
|
@@ -26,17 +27,21 @@ module OrgMode
|
|
26
27
|
# build a nice orderd struct
|
27
28
|
noi_per_day.keys.sort.map do |date|
|
28
29
|
{ :date => date, :nodes => noi_per_day[date].map { |n| node_to_hash(n) } }
|
29
|
-
end
|
30
|
+
end.map(&:to_ostruct)
|
31
|
+
|
30
32
|
end
|
31
33
|
|
32
34
|
private
|
33
35
|
|
34
36
|
def node_to_hash(node)
|
35
|
-
rv = [:title, :content, :todo_state, :date, :stars].
|
37
|
+
rv = [:title, :content, :todo_state, :date, :date_start_time, :date_end_time, :stars, :appointment?].
|
36
38
|
map { |k| [ k, node.send(k) ] }.to_h
|
37
39
|
|
38
40
|
rv[:date] = rv[:date].strftime('%Y-%m-%d %H:%M') if rv[:date]
|
39
|
-
rv
|
41
|
+
rv[:date_start_time] = rv[:date_start_time].strftime('%H:%M') if rv[:date_start_time]
|
42
|
+
rv[:date_end_time] = rv[:date_end_time].strftime('%H:%M') if rv[:date_end_time]
|
43
|
+
rv[:node] = node
|
44
|
+
rv.to_ostruct
|
40
45
|
end
|
41
46
|
|
42
47
|
end
|
data/lib/org_mode/version.rb
CHANGED
data/lib/org_mode.rb
CHANGED
@@ -14,9 +14,8 @@ require "org_mode/version"
|
|
14
14
|
|
15
15
|
module OrgMode
|
16
16
|
|
17
|
-
|
18
17
|
class Node
|
19
|
-
attr_accessor :title, :content, :stars, :date_start_time, :date_end_time, :todo_state
|
18
|
+
attr_accessor :title, :content, :stars, :date, :date_start_time, :date_end_time, :todo_state
|
20
19
|
attr_accessor :parent, :children
|
21
20
|
|
22
21
|
def initialize
|
@@ -24,9 +23,6 @@ module OrgMode
|
|
24
23
|
@children = []
|
25
24
|
end
|
26
25
|
|
27
|
-
alias :date :date_start_time
|
28
|
-
alias :date= :date_start_time=
|
29
|
-
|
30
26
|
def indent
|
31
27
|
stars + 1
|
32
28
|
end
|
@@ -39,6 +35,10 @@ module OrgMode
|
|
39
35
|
!date.nil?
|
40
36
|
end
|
41
37
|
|
38
|
+
def appointment?
|
39
|
+
!!( date_start_time || date_end_time )
|
40
|
+
end
|
41
|
+
|
42
42
|
def open?
|
43
43
|
not done?
|
44
44
|
end
|
@@ -50,24 +50,47 @@ module OrgMode
|
|
50
50
|
end
|
51
51
|
|
52
52
|
module FileInterface
|
53
|
-
def root_nodes
|
54
|
-
nodes.select(&:root_node?)
|
55
|
-
end
|
56
|
-
|
57
53
|
def scheduled_nodes
|
58
54
|
nodes.select(&:scheduled?)
|
59
55
|
end
|
56
|
+
|
57
|
+
def select_by_title(pattern, props={})
|
58
|
+
result = nodes
|
59
|
+
props.each do |k,v|
|
60
|
+
result = result.select {|n| n.send(k) == v }
|
61
|
+
end
|
62
|
+
result.select {|n| n.title =~ pattern}
|
63
|
+
end
|
60
64
|
end
|
61
65
|
|
62
66
|
class File
|
63
|
-
attr_accessor :header, :
|
67
|
+
attr_accessor :header, :root_nodes, :footer
|
64
68
|
|
65
69
|
include FileInterface
|
66
70
|
|
67
|
-
|
71
|
+
# For universial children accessor
|
72
|
+
# file is just a node
|
73
|
+
alias :children :root_nodes
|
74
|
+
alias :children= :root_nodes=
|
75
|
+
|
76
|
+
def initialize(header, root_nodes, footer)
|
68
77
|
@header = header
|
69
78
|
@footer = footer
|
70
|
-
@
|
79
|
+
@root_nodes = root_nodes
|
80
|
+
end
|
81
|
+
|
82
|
+
def nodes
|
83
|
+
serialize_nodes(root_nodes)
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
# recusively serializes
|
89
|
+
# the nodes
|
90
|
+
def serialize_nodes(nodes)
|
91
|
+
nodes.map do |n|
|
92
|
+
[n] + serialize_nodes(n.children)
|
93
|
+
end.flatten
|
71
94
|
end
|
72
95
|
end
|
73
96
|
|
@@ -83,5 +106,10 @@ module OrgMode
|
|
83
106
|
def nodes
|
84
107
|
files.map(&:nodes).flatten
|
85
108
|
end
|
109
|
+
|
110
|
+
def root_nodes
|
111
|
+
nodes.select(&:root_node?)
|
112
|
+
end
|
113
|
+
|
86
114
|
end
|
87
115
|
end
|
data/org_mode.gemspec
CHANGED
@@ -1,23 +1,13 @@
|
|
1
1
|
require 'org_mode/commands/agenda'
|
2
|
-
require 'tempfile'
|
3
|
-
require 'core_ext/string'
|
4
|
-
require 'support/capture_stdout'
|
5
|
-
require 'support/write_into_tempfile'
|
6
2
|
require 'timecop'
|
7
3
|
|
8
|
-
|
9
|
-
def execute_and_compare_stdout_with args, options, expected_output
|
10
|
-
output = capture_stdout do
|
11
|
-
@org_mode_commands_agenda.execute(args, options)
|
12
|
-
end
|
13
|
-
output.should == expected_output
|
14
|
-
end
|
4
|
+
require 'lib/org_mode/commands/helpers'
|
15
5
|
|
16
6
|
describe OrgMode::Commands::Agenda do
|
17
7
|
before do
|
18
8
|
Timecop.freeze('2012-01-01 15:00')
|
19
9
|
|
20
|
-
@org_mode_commands_agenda = OrgMode::Commands::Agenda.new
|
10
|
+
@cmd = @org_mode_commands_agenda = OrgMode::Commands::Agenda.new
|
21
11
|
end
|
22
12
|
|
23
13
|
context '#execute' do
|
@@ -26,11 +16,8 @@ describe OrgMode::Commands::Agenda do
|
|
26
16
|
org_file = write_into_tempfile <<-eos.strip_indent(10)
|
27
17
|
* TODO Scheduled task <1-1-2012 Wed 15:15>
|
28
18
|
eos
|
29
|
-
|
30
|
-
|
31
|
-
2012-01-01
|
32
|
-
TODO Scheduled task
|
33
|
-
eos
|
19
|
+
execute_and_output_should_contain @cmd, [org_file.path], stub,
|
20
|
+
/TODO/, /2012-01-01/, /15:15/ ,/Scheduled task/
|
34
21
|
end
|
35
22
|
end
|
36
23
|
context 'when loaded with two files' do
|
@@ -41,14 +28,8 @@ describe OrgMode::Commands::Agenda do
|
|
41
28
|
org_file2 = write_into_tempfile <<-eos.strip_indent(10)
|
42
29
|
* TODO Scheduled task on the 1th <1-1-2012 Wed 15:15>
|
43
30
|
eos
|
44
|
-
|
45
|
-
|
46
|
-
Agenda ()
|
47
|
-
2012-01-01
|
48
|
-
TODO Scheduled task on the 1th
|
49
|
-
2012-01-05
|
50
|
-
TODO Scheduled task on the 5th
|
51
|
-
eos
|
31
|
+
execute_and_output_should_contain @cmd, [org_file, org_file2].map(&:path), stub,
|
32
|
+
/TODO/, /2012-01-01/, /15:15/ ,/Scheduled task/, /on the 1th/, /on the 5th/
|
52
33
|
end
|
53
34
|
end
|
54
35
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
require 'core_ext/string'
|
3
|
+
require 'support/write_into_tempfile'
|
4
|
+
require 'capture_stdout'
|
5
|
+
|
6
|
+
# Helper to execute agenda and compare the output
|
7
|
+
def execute_and_compare_stdout_with cmd, args, options, expected_output
|
8
|
+
output = capture_stdout do
|
9
|
+
cmd.execute(args, options)
|
10
|
+
end
|
11
|
+
output.should == expected_output
|
12
|
+
end
|
13
|
+
def execute_and_output_should_contain cmd, args, options, *expected_output
|
14
|
+
output = capture_stdout do
|
15
|
+
cmd.execute(args, options)
|
16
|
+
end
|
17
|
+
expected_output.each do |pattern|
|
18
|
+
output.should match( pattern )
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'org_mode/commands/update'
|
2
|
+
require 'lib/org_mode/commands/helpers'
|
3
|
+
|
4
|
+
describe OrgMode::Commands::Update do
|
5
|
+
before do
|
6
|
+
@cmd = OrgMode::Commands::Update.new
|
7
|
+
end
|
8
|
+
|
9
|
+
context '#execute' do
|
10
|
+
context 'without options' do
|
11
|
+
it 'reformats a file correctly' do
|
12
|
+
org_file = write_into_tempfile <<-eos.strip_indent(10)
|
13
|
+
* TODO Scheduled task <1-1-2012 Wed 15:15>
|
14
|
+
eos
|
15
|
+
execute_and_compare_stdout_with @cmd, [org_file.path], stub(:archive_done => false), <<-eos.strip_indent(10)
|
16
|
+
* TODO <2012-01-01 Sun 15:15> Scheduled task
|
17
|
+
eos
|
18
|
+
end
|
19
|
+
end
|
20
|
+
context 'option --archive-done' do
|
21
|
+
it 'archives the subtree as expected' do
|
22
|
+
org_file = write_into_tempfile <<-eos.strip_indent(10)
|
23
|
+
* TODO Scheduled task <1-1-2012 Wed 15:15>
|
24
|
+
** DONE Scheduled child task <1-1-2012 Wed 12:15>
|
25
|
+
*** TODO Scheduled child of child
|
26
|
+
eos
|
27
|
+
execute_and_compare_stdout_with @cmd, [org_file.path], stub(:archive_done => true), <<-eos.strip_indent(10)
|
28
|
+
* TODO <2012-01-01 Sun 15:15> Scheduled task
|
29
|
+
* Archived
|
30
|
+
This node contains archived items. Appended
|
31
|
+
due to calling the script with update --archive-done
|
32
|
+
|
33
|
+
** DONE <2012-01-01 Sun 12:15> Scheduled child task
|
34
|
+
*** TODO Scheduled child of child
|
35
|
+
eos
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'org_mode/formatters/textual'
|
2
|
+
require 'support/blueprints'
|
3
|
+
|
4
|
+
describe OrgMode::Formatters::Textual do
|
5
|
+
let(:org_file) do
|
6
|
+
nodes = []
|
7
|
+
files = []
|
8
|
+
nodes << OrgMode::Node.make(:stars => 1,
|
9
|
+
:todo_state => 'DONE',
|
10
|
+
:date => Date.parse('2012-06-01'))
|
11
|
+
|
12
|
+
# make helper for this
|
13
|
+
date = DateTime.parse('2012-02-01')
|
14
|
+
date_start = DateTime.parse('2012-02-01 15:00')
|
15
|
+
date_end = DateTime.parse('2012-02-01 16:00')
|
16
|
+
nodes << OrgMode::Node.make(:stars => 2, :date=>Date.parse('2012-02-03'))
|
17
|
+
nodes << OrgMode::Node.make(:stars => 3, :date=>date_start,
|
18
|
+
:date_end_time=>date_end,
|
19
|
+
:date_start_time=>date_start)
|
20
|
+
OrgMode::File.make(:nodes => nodes)
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
context '#format' do
|
25
|
+
before do
|
26
|
+
formatter = OrgMode::Formatters::Textual.new(org_file)
|
27
|
+
@output = formatter.format
|
28
|
+
end
|
29
|
+
|
30
|
+
it "is formatted as expected" do
|
31
|
+
@output.should == <<-eos.gsub(/^ {1,8}/,'').chomp
|
32
|
+
aheader
|
33
|
+
|
34
|
+
* DONE <2012-06-01 Fri> org-node
|
35
|
+
org-node content
|
36
|
+
** TODO <2012-02-03 Fri> org-node
|
37
|
+
org-node content
|
38
|
+
*** TODO <2012-02-01 Wed 15:00-16:00> org-node
|
39
|
+
org-node content
|
40
|
+
|
41
|
+
afooter
|
42
|
+
eos
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -180,14 +180,20 @@ describe OrgMode::NodeParser do
|
|
180
180
|
end
|
181
181
|
context "node title with date time" do
|
182
182
|
let(:node) {OrgMode::NodeParser.parse('** Date node title <2012-02-03 Wed 15:15>', nil)}
|
183
|
-
it "parses the date
|
184
|
-
node.date.strftime('%Y-%m-%d %H:%M').should == '2012-02-03
|
183
|
+
it "parses the date from the title correcty" do
|
184
|
+
node.date.strftime('%Y-%m-%d %H:%M').should == '2012-02-03 00:00'
|
185
|
+
end
|
186
|
+
it "parses the date_start_time from the title correcty" do
|
187
|
+
node.date_start_time.strftime('%Y-%m-%d %H:%M').should == '2012-02-03 15:15'
|
188
|
+
end
|
189
|
+
it "parses the date_end_time from the title correcty" do
|
190
|
+
node.date_end_time.should be_nil
|
185
191
|
end
|
186
192
|
end
|
187
193
|
context "node title with date-range" do
|
188
194
|
let(:node) {OrgMode::NodeParser.parse('** Date node title <2012-02-03 Wed 15:15-16:15>', nil)}
|
189
195
|
it "parses the date from the title correcty" do
|
190
|
-
node.date.strftime('%Y-%m-%d %H:%M').should == '2012-02-03
|
196
|
+
node.date.strftime('%Y-%m-%d %H:%M').should == '2012-02-03 00:00'
|
191
197
|
end
|
192
198
|
it "parses the start_tiem correctly" do
|
193
199
|
node.date_start_time.strftime('%Y-%m-%d %H:%M').should == '2012-02-03 15:15'
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'org_mode/processors/archive_done'
|
2
|
+
require 'support/blueprints'
|
3
|
+
require 'support/include_hash'
|
4
|
+
require 'timecop'
|
5
|
+
|
6
|
+
# When can we archive a tree
|
7
|
+
#
|
8
|
+
# we can archive a tree when all dones
|
9
|
+
# of a tree are done. Or the root-tree is done
|
10
|
+
|
11
|
+
describe OrgMode::Processors::ArchiveDone do
|
12
|
+
|
13
|
+
let(:org_file) do
|
14
|
+
nodes = []
|
15
|
+
nodes << OrgMode::Node.make(:stars => 1,
|
16
|
+
:todo_state => 'DONE',
|
17
|
+
:date => Date.parse('2012-06-01'))
|
18
|
+
nodes << OrgMode::Node.make(:stars => 2, :date=>Date.parse('2012-02-03'))
|
19
|
+
nodes << OrgMode::Node.make(:stars => 3, :date=>DateTime.parse('2012-02-01 15:00'))
|
20
|
+
OrgMode::File.make(:nodes => nodes)
|
21
|
+
end
|
22
|
+
|
23
|
+
context '#process' do
|
24
|
+
let(:processed_org_file) do
|
25
|
+
processor = OrgMode::Processors::ArchiveDone.new(org_file)
|
26
|
+
processor.process
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'archived_node' do
|
30
|
+
let(:archived_nodes) do
|
31
|
+
processed_org_file.select_by_title(/^Archived$/, :stars => 1)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should have created a node "Archived"' do
|
35
|
+
archived_nodes.length.should == 1
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should have added the DONE tree to the archived ones' do
|
39
|
+
#archived_nodes[0].children.length.should == 1
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
@@ -36,35 +36,6 @@ describe OrgMode::Reporters::Agenda do
|
|
36
36
|
nodecount_per_date = reported.map { |e| [e[:date], e[:nodes].length] }
|
37
37
|
nodecount_per_date.should == [["2012-02-01", 2], ["2012-02-03", 1], ["2012-02-05", 1]]
|
38
38
|
end
|
39
|
-
it "should result in the following subhashes" do
|
40
|
-
reported.should ==
|
41
|
-
[{:date=>"2012-02-01",
|
42
|
-
:nodes=>
|
43
|
-
[{:title=>"org-node",
|
44
|
-
:content=>"org-node content",
|
45
|
-
:todo_state=>"TODO",
|
46
|
-
:date=>"2012-02-01 00:00",
|
47
|
-
:stars=>1},
|
48
|
-
{:title=>"org-node",
|
49
|
-
:content=>"org-node content",
|
50
|
-
:todo_state=>"TODO",
|
51
|
-
:date=>"2012-02-01 15:00",
|
52
|
-
:stars=>3}]},
|
53
|
-
{:date=>"2012-02-03",
|
54
|
-
:nodes=>
|
55
|
-
[{:title=>"org-node",
|
56
|
-
:content=>"org-node content",
|
57
|
-
:todo_state=>"TODO",
|
58
|
-
:date=>"2012-02-03 00:00",
|
59
|
-
:stars=>2}]},
|
60
|
-
{:date=>"2012-02-05",
|
61
|
-
:nodes=>
|
62
|
-
[{:title=>"org-node",
|
63
|
-
:content=>"org-node content",
|
64
|
-
:todo_state=>"TODO",
|
65
|
-
:date=>"2012-02-05 00:00",
|
66
|
-
:stars=>2}]}]
|
67
|
-
end
|
68
39
|
it "it ignores the DONE task" do
|
69
40
|
dates = reported.map {|e| e[:date] }
|
70
41
|
dates.should_not include("2012-06-01")
|
data/spec/lib/org_mode_spec.rb
CHANGED
data/spec/support/blueprints.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'org_mode'
|
2
|
+
require 'org_mode/node_utils'
|
2
3
|
|
3
4
|
class OrgMode::Node
|
4
5
|
def self.make(attrs={})
|
@@ -7,6 +8,8 @@ class OrgMode::Node
|
|
7
8
|
n.stars = attrs[:stars] || rand(4)
|
8
9
|
n.content = attrs[:content] || "org-node content"
|
9
10
|
n.date = attrs[:date]
|
11
|
+
n.date_start_time = attrs[:date_start_time]
|
12
|
+
n.date_end_time = attrs[:date_end_time]
|
10
13
|
n.todo_state = attrs[:todo_state] || 'TODO'
|
11
14
|
end
|
12
15
|
end
|
@@ -15,7 +18,8 @@ end
|
|
15
18
|
class OrgMode::File
|
16
19
|
def self.make(attrs={})
|
17
20
|
nodes = attrs[:nodes] || Array.new(2) { OrgMode::Node.make }
|
18
|
-
|
21
|
+
root_nodes = OrgMode::NodeUtils.convert_sequential_nodelist_into_tree(nodes)
|
22
|
+
self.new("aheader", root_nodes , "afooter")
|
19
23
|
end
|
20
24
|
end
|
21
25
|
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: org_mode
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 23
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 4
|
10
|
+
version: 0.0.4
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Boy Maas
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-02-
|
18
|
+
date: 2012-02-12 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: rake
|
@@ -77,6 +77,21 @@ dependencies:
|
|
77
77
|
version: "0.99"
|
78
78
|
type: :runtime
|
79
79
|
version_requirements: *id004
|
80
|
+
- !ruby/object:Gem::Dependency
|
81
|
+
name: colorize
|
82
|
+
prerelease: false
|
83
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
84
|
+
none: false
|
85
|
+
requirements:
|
86
|
+
- - ~>
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
hash: 1
|
89
|
+
segments:
|
90
|
+
- 0
|
91
|
+
- 5
|
92
|
+
version: "0.5"
|
93
|
+
type: :runtime
|
94
|
+
version_requirements: *id005
|
80
95
|
description: Org-mode parser, presenter, and reformatter. Read more about it on the github pages.
|
81
96
|
email:
|
82
97
|
- boy.maas@gmail.com
|
@@ -96,22 +111,32 @@ files:
|
|
96
111
|
- features/cmdline_handles_errors_cracefully.feature
|
97
112
|
- features/cmdline_noparams.feature
|
98
113
|
- features/cmdline_todos.feature
|
114
|
+
- features/cmdline_update.feature
|
99
115
|
- features/steps.rb
|
100
116
|
- features/support/env.rb
|
101
117
|
- features/support/org_mode_script.rb
|
118
|
+
- lib/capture_stdout.rb
|
102
119
|
- lib/core_ext/string.rb
|
103
120
|
- lib/org_mode.rb
|
104
121
|
- lib/org_mode/commands.rb
|
105
122
|
- lib/org_mode/commands/agenda.rb
|
106
123
|
- lib/org_mode/commands/update.rb
|
124
|
+
- lib/org_mode/formatters/textual.rb
|
107
125
|
- lib/org_mode/loader.rb
|
126
|
+
- lib/org_mode/node_utils.rb
|
108
127
|
- lib/org_mode/parser.rb
|
128
|
+
- lib/org_mode/presenters/console.rb
|
129
|
+
- lib/org_mode/processors/archive_done.rb
|
109
130
|
- lib/org_mode/reporters/agenda.rb
|
110
131
|
- lib/org_mode/version.rb
|
111
132
|
- org_mode.gemspec
|
112
133
|
- spec/data/org-file-01-simple-node-structure.org
|
113
134
|
- spec/lib/org_mode/commands/agenda_spec.rb
|
135
|
+
- spec/lib/org_mode/commands/helpers.rb
|
136
|
+
- spec/lib/org_mode/commands/update_spec.rb
|
137
|
+
- spec/lib/org_mode/formatters/textual_spec.rb
|
114
138
|
- spec/lib/org_mode/parser_spec.rb
|
139
|
+
- spec/lib/org_mode/processors/archive_done_spec.rb
|
115
140
|
- spec/lib/org_mode/reporters/agenda_spec.rb
|
116
141
|
- spec/lib/org_mode_spec.rb
|
117
142
|
- spec/support/blueprints.rb
|
@@ -156,12 +181,17 @@ test_files:
|
|
156
181
|
- features/cmdline_handles_errors_cracefully.feature
|
157
182
|
- features/cmdline_noparams.feature
|
158
183
|
- features/cmdline_todos.feature
|
184
|
+
- features/cmdline_update.feature
|
159
185
|
- features/steps.rb
|
160
186
|
- features/support/env.rb
|
161
187
|
- features/support/org_mode_script.rb
|
162
188
|
- spec/data/org-file-01-simple-node-structure.org
|
163
189
|
- spec/lib/org_mode/commands/agenda_spec.rb
|
190
|
+
- spec/lib/org_mode/commands/helpers.rb
|
191
|
+
- spec/lib/org_mode/commands/update_spec.rb
|
192
|
+
- spec/lib/org_mode/formatters/textual_spec.rb
|
164
193
|
- spec/lib/org_mode/parser_spec.rb
|
194
|
+
- spec/lib/org_mode/processors/archive_done_spec.rb
|
165
195
|
- spec/lib/org_mode/reporters/agenda_spec.rb
|
166
196
|
- spec/lib/org_mode_spec.rb
|
167
197
|
- spec/support/blueprints.rb
|