org_mode 0.0.3 → 0.0.4
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.
- 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
|