toodledo 1.0.2 → 1.1.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.
- data/History.txt +16 -0
- data/Manifest.txt +12 -3
- data/README.txt +55 -26
- data/Rakefile +1 -0
- data/lib/toodledo.rb +2 -1
- data/lib/toodledo/command_line/add_command.rb +102 -1
- data/lib/toodledo/command_line/client.rb +405 -149
- data/lib/toodledo/command_line/context_formatter.rb +9 -0
- data/lib/toodledo/command_line/delete_command.rb +75 -1
- data/lib/toodledo/command_line/folder_formatter.rb +9 -0
- data/lib/toodledo/command_line/goal_formatter.rb +26 -0
- data/lib/toodledo/command_line/interactive_command.rb +43 -0
- data/lib/toodledo/command_line/list_folders_command.rb +32 -0
- data/lib/toodledo/command_line/list_goals_command.rb +31 -0
- data/lib/toodledo/command_line/{list_command.rb → list_tasks_command.rb} +9 -2
- data/lib/toodledo/command_line/parser_helper.rb +65 -8
- data/lib/toodledo/command_line/sTdin_command.rb +31 -0
- data/lib/toodledo/command_line/task_formatter.rb +102 -0
- data/lib/toodledo/context.rb +1 -2
- data/lib/toodledo/folder.rb +2 -6
- data/lib/toodledo/goal.rb +2 -8
- data/lib/toodledo/invalid_configuration_error.rb +14 -0
- data/lib/toodledo/priority.rb +0 -17
- data/lib/toodledo/session.rb +22 -15
- data/lib/toodledo/task.rb +1 -82
- data/test/client_test.rb +192 -0
- data/test/parser_helper_test.rb +74 -6
- data/test/session_test.rb +2 -2
- data/test/toodledo_functional_test.rb +2 -2
- metadata +19 -8
- data/CHANGELOG +0 -1
- data/lib/toodledo/command_line/main_command.rb +0 -153
data/History.txt
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
== 1.1.0 / 2008-02-24
|
2
|
+
|
3
|
+
* Add functionality to add, delete goals, contexts, and folders from client.
|
4
|
+
* Add functionality to archive folders.
|
5
|
+
* Add stdin command to read from pipe or redirected files
|
6
|
+
* Add formatters for the client so it's easier to tweak line output.
|
7
|
+
* Add proper priority support to the client (in the format !top, !high, etc.)
|
8
|
+
* Make filter able to take symbols.
|
9
|
+
* Change symbol for goals from $ to ^ (was confusing when entering 'I owe $5')
|
10
|
+
* Add unit tests for client.
|
11
|
+
* Better error messages and explanation of toodledo setup.
|
12
|
+
* Fix the debug command so it doesn't crash the interactive client.
|
13
|
+
* Fixed some bugs in date processing.
|
14
|
+
* Fixed bugs in priority handling.
|
15
|
+
* Fixed session invalid bug on reconnect.
|
16
|
+
|
1
17
|
== 1.0.2 / 2008-02-14
|
2
18
|
|
3
19
|
* Fix a silly bug where whitespace wasn't stripped from edit, complete and delete.
|
data/Manifest.txt
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
CHANGELOG
|
2
1
|
History.txt
|
3
2
|
Manifest.txt
|
4
3
|
README.txt
|
@@ -9,22 +8,32 @@ lib/toodledo/command_line/add_command.rb
|
|
9
8
|
lib/toodledo/command_line/base_command.rb
|
10
9
|
lib/toodledo/command_line/client.rb
|
11
10
|
lib/toodledo/command_line/complete_command.rb
|
11
|
+
lib/toodledo/command_line/context_formatter.rb
|
12
12
|
lib/toodledo/command_line/delete_command.rb
|
13
13
|
lib/toodledo/command_line/edit_command.rb
|
14
|
+
lib/toodledo/command_line/folder_formatter.rb
|
15
|
+
lib/toodledo/command_line/goal_formatter.rb
|
14
16
|
lib/toodledo/command_line/hotlist_command.rb
|
15
|
-
lib/toodledo/command_line/
|
16
|
-
lib/toodledo/command_line/
|
17
|
+
lib/toodledo/command_line/interactive_command.rb
|
18
|
+
lib/toodledo/command_line/list_tasks_command.rb
|
19
|
+
lib/toodledo/command_line/list_folders_command.rb
|
20
|
+
lib/toodledo/command_line/list_goals_command.rb
|
21
|
+
lib/toodledo/command_line/list_tasks_command.rb
|
17
22
|
lib/toodledo/command_line/parser_helper.rb
|
18
23
|
lib/toodledo/command_line/setup_command.rb
|
24
|
+
lib/toodledo/command_line/sTdin_command.rb
|
25
|
+
lib/toodledo/command_line/task_formatter.rb
|
19
26
|
lib/toodledo/context.rb
|
20
27
|
lib/toodledo/folder.rb
|
21
28
|
lib/toodledo/goal.rb
|
29
|
+
lib/toodledo/invalid_configuration_error.rb
|
22
30
|
lib/toodledo/item_not_found_error.rb
|
23
31
|
lib/toodledo/priority.rb
|
24
32
|
lib/toodledo/repeat.rb
|
25
33
|
lib/toodledo/server_error.rb
|
26
34
|
lib/toodledo/session.rb
|
27
35
|
lib/toodledo/task.rb
|
36
|
+
test/client_test.rb
|
28
37
|
test/parser_helper_test.rb
|
29
38
|
test/session_test.rb
|
30
39
|
test/toodledo_functional_test.rb
|
data/README.txt
CHANGED
@@ -13,8 +13,9 @@ The client allows you to work with Toodledo from the command line. It will
|
|
13
13
|
work in either interactive or command line mode.
|
14
14
|
|
15
15
|
You can also use the client in your shell scripts, or use the API directly
|
16
|
-
as part of a web application.
|
17
|
-
out your top priority?
|
16
|
+
as part of a web application. Custom private RSS feed? Want to have the Mac
|
17
|
+
read out your top priority? Input tasks through Quicksilver? Print out
|
18
|
+
tasks with a BetaBrite? It can all happen.
|
18
19
|
|
19
20
|
== FEATURES/PROBLEMS:
|
20
21
|
|
@@ -25,43 +26,50 @@ out your top priority? It can all happen.
|
|
25
26
|
* Easy configuration and automation (Quicksilver / Scripts / Automator)
|
26
27
|
|
27
28
|
== SYNOPSIS:
|
29
|
+
|
30
|
+
=== SETUP:
|
31
|
+
|
32
|
+
The first thing you should do is open up your browser and go to:
|
33
|
+
|
34
|
+
http://www.toodledo.com/info/api_doc.php
|
35
|
+
|
36
|
+
and retrieve your userid. You will need this for setup.
|
37
|
+
|
38
|
+
Then, install toodledo. This is either 'gem install toodledo' or
|
39
|
+
'sudo gem install toodledo' depending on your platform.
|
40
|
+
|
41
|
+
Then, type 'toodledo setup' and enter your userid and password in
|
42
|
+
the spaces provided. Then save the file, and you're good to go.
|
43
|
+
|
44
|
+
=== COMMAND LINE:
|
28
45
|
|
29
|
-
|
30
|
-
|
31
|
-
is
|
46
|
+
You can add tasks. The simplest form is here:
|
47
|
+
|
48
|
+
toodledo add 'This is a test'
|
49
|
+
|
50
|
+
But tasks don't have to be simple. Toodledo has a particularly rich model of
|
51
|
+
a task, and allows full GTD type state to be attached to them. The syntax
|
52
|
+
for the client is as follows:
|
32
53
|
|
33
54
|
*Folder
|
34
55
|
@Context
|
35
|
-
|
56
|
+
^Goal
|
57
|
+
!Priority
|
36
58
|
|
37
|
-
You can encase the symbol with square brackets if there is a space
|
38
|
-
involved:
|
59
|
+
You can encase the symbol with square brackets if there is a space involved:
|
39
60
|
|
40
61
|
*[Blue Sky]
|
41
62
|
@[Someday / Maybe]
|
42
|
-
|
63
|
+
^[Write Toodledo Ruby API]
|
64
|
+
!top
|
43
65
|
|
44
66
|
Let's use the command line client to list only the tasks you have in the office:
|
45
67
|
|
46
68
|
toodledo list '@Office *Action'
|
47
69
|
|
48
|
-
In interactive mode, the client will also allow you to set up filters. If you
|
49
|
-
type 'folder', 'context' or 'goal' with an argument, then only tasks that match
|
50
|
-
those criteria will be displayed. That is, if you do this:
|
51
|
-
|
52
|
-
context Office
|
53
|
-
folder Action
|
54
|
-
list
|
55
|
-
|
56
|
-
Then it produces the same results as the previous list command.
|
57
|
-
|
58
|
-
You can add tasks. The simplest form is here:
|
59
|
-
|
60
|
-
toodledo add 'This is a test'
|
61
|
-
|
62
70
|
Now let's add a task with several symbols:
|
63
71
|
|
64
|
-
toodledo add '*Action @Programming
|
72
|
+
toodledo add '*Action @Programming ^[Write Toodledo Ruby API] Write docs'
|
65
73
|
|
66
74
|
You can also edit tasks, using the task id. This sets the folder to Someday:
|
67
75
|
|
@@ -72,7 +80,28 @@ And finally you can complete or delete tasks, again using the task id.
|
|
72
80
|
toodledo complete 15934131
|
73
81
|
toodledo delete 15934131
|
74
82
|
|
75
|
-
|
83
|
+
=== INTERACTIVE MODE:
|
84
|
+
|
85
|
+
Toodledo also comes with an interactive mode that is used if no arguments are
|
86
|
+
found:
|
87
|
+
|
88
|
+
toodledo
|
89
|
+
> add This is a test
|
90
|
+
|
91
|
+
You can type help at the prompt for a complete list of commands. The client
|
92
|
+
makes for a nice way to enter in tasks as you think of them.
|
93
|
+
|
94
|
+
The client will also allow you to set up filters. Filters are added with
|
95
|
+
the symbols, so in interactive mode
|
96
|
+
|
97
|
+
filter @Office *Action
|
98
|
+
list
|
99
|
+
|
100
|
+
Then it produces the same results as:
|
101
|
+
|
102
|
+
toodledo list '@Office *Action'
|
103
|
+
|
104
|
+
Finally, if you want to write your own scripts, working with Toodledo is very
|
76
105
|
simple, since it will use the YAML config file:
|
77
106
|
|
78
107
|
require 'rubygems'
|
@@ -102,7 +131,7 @@ this instead:
|
|
102
131
|
|
103
132
|
* sudo gem install toodledo
|
104
133
|
* toodledo setup (sets up the YAML file with your credentials)
|
105
|
-
* toodledo
|
134
|
+
* toodledo
|
106
135
|
|
107
136
|
== LICENSE:
|
108
137
|
GNU LESSER GENERAL PUBLIC LICENSE
|
data/Rakefile
CHANGED
@@ -14,6 +14,7 @@ Hoe.new('toodledo', Toodledo::VERSION) do |p|
|
|
14
14
|
p.url = 'http://rubyforge.org/projects/toodledo'
|
15
15
|
p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
|
16
16
|
p.rsync_args << ' --exclude=statsvn/'
|
17
|
+
p.test_globs = ["test/**/*_test.rb"]
|
17
18
|
p.extra_deps = ['cmdparse', 'highline']
|
18
19
|
end
|
19
20
|
|
data/lib/toodledo.rb
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
module Toodledo
|
6
6
|
|
7
7
|
# Required for gem
|
8
|
-
VERSION = '1.0
|
8
|
+
VERSION = '1.1.0'
|
9
9
|
|
10
10
|
# Returns the configuration object.
|
11
11
|
def self.get_config()
|
@@ -55,6 +55,7 @@ end
|
|
55
55
|
|
56
56
|
require 'toodledo/server_error'
|
57
57
|
require 'toodledo/item_not_found_error'
|
58
|
+
require 'toodledo/invalid_configuration_error'
|
58
59
|
require 'toodledo/task'
|
59
60
|
require 'toodledo/context'
|
60
61
|
require 'toodledo/goal'
|
@@ -1,10 +1,111 @@
|
|
1
1
|
|
2
2
|
module Toodledo
|
3
3
|
module CommandLine
|
4
|
-
|
4
|
+
|
5
|
+
#
|
6
|
+
# Adds commands
|
7
|
+
#
|
8
|
+
class AddCommand < CmdParse::Command
|
9
|
+
def initialize(client)
|
10
|
+
super('add', true)
|
11
|
+
self.short_desc = 'Adds an object'
|
12
|
+
|
13
|
+
self.add_command(AddTaskCommand.new(client), true)
|
14
|
+
#self.add_command(AddFolderCommand.new(client))
|
15
|
+
#self.add_command(AddGoalCommand.new(client))
|
16
|
+
#self.add_command(AddContextCommand.new(client))
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
def execute(args)
|
21
|
+
puts "hello world!"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
#
|
26
|
+
# Adds a folder from the command
|
27
|
+
#
|
28
|
+
class AddFolderCommand < BaseCommand
|
29
|
+
|
30
|
+
include Toodledo::CommandLine::ParserHelper
|
31
|
+
|
32
|
+
def initialize(client)
|
33
|
+
super(client, 'folder', false)
|
34
|
+
self.short_desc = "Add a folder"
|
35
|
+
self.description = "Adds a folder to Toodledo"
|
36
|
+
end
|
37
|
+
|
38
|
+
def execute(args)
|
39
|
+
return if (args == nil)
|
40
|
+
Toodledo.begin(client.logger) do |session|
|
41
|
+
line = args.join(' ')
|
42
|
+
client.add_folder(session, line)
|
43
|
+
end
|
44
|
+
|
45
|
+
return 0
|
46
|
+
rescue ItemNotFoundError => e
|
47
|
+
puts e.message
|
48
|
+
return -1
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
#
|
53
|
+
# Adds a goal from the command line
|
54
|
+
#
|
55
|
+
class AddGoalCommand < BaseCommand
|
5
56
|
|
6
57
|
include Toodledo::CommandLine::ParserHelper
|
7
58
|
|
59
|
+
def initialize(client)
|
60
|
+
super(client, 'goal', false)
|
61
|
+
self.short_desc = "Add a goal"
|
62
|
+
self.description = "Adds a goal to Toodledo"
|
63
|
+
end
|
64
|
+
|
65
|
+
def execute(args)
|
66
|
+
return if (args == nil)
|
67
|
+
Toodledo.begin(client.logger) do |session|
|
68
|
+
line = args.join(' ')
|
69
|
+
client.add_goal(session, line)
|
70
|
+
end
|
71
|
+
|
72
|
+
return 0
|
73
|
+
rescue ItemNotFoundError => e
|
74
|
+
puts e.message
|
75
|
+
return -1
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
#
|
80
|
+
# Adds a context from the command line
|
81
|
+
#
|
82
|
+
class AddContextCommand < BaseCommand
|
83
|
+
|
84
|
+
def initialize(client)
|
85
|
+
super(client, 'context', false)
|
86
|
+
self.short_desc = "Adds context"
|
87
|
+
self.description = "Adds a context to Toodledo"
|
88
|
+
end
|
89
|
+
|
90
|
+
def execute(args)
|
91
|
+
return if (args == nil)
|
92
|
+
Toodledo.begin(client.logger) do |session|
|
93
|
+
line = args.join(' ')
|
94
|
+
client.add_context(session, line)
|
95
|
+
end
|
96
|
+
|
97
|
+
return 0
|
98
|
+
rescue ItemNotFoundError => e
|
99
|
+
puts e.message
|
100
|
+
return -1
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
#
|
105
|
+
# Adds a task from the command line.
|
106
|
+
#
|
107
|
+
class AddTaskCommand < BaseCommand
|
108
|
+
|
8
109
|
def initialize(client)
|
9
110
|
super(client, 'add', false)
|
10
111
|
self.short_desc = "Add a task"
|
@@ -8,14 +8,32 @@ require 'yaml'
|
|
8
8
|
require 'toodledo'
|
9
9
|
require 'toodledo/command_line/parser_helper'
|
10
10
|
require 'toodledo/command_line/base_command'
|
11
|
-
require 'toodledo/command_line/
|
11
|
+
require 'toodledo/command_line/interactive_command'
|
12
|
+
require 'toodledo/command_line/stdin_command'
|
13
|
+
require 'toodledo/command_line/setup_command'
|
14
|
+
|
15
|
+
# CREATE
|
12
16
|
require 'toodledo/command_line/add_command'
|
13
|
-
|
14
|
-
|
17
|
+
|
18
|
+
# READ
|
15
19
|
require 'toodledo/command_line/hotlist_command'
|
20
|
+
require 'toodledo/command_line/list_tasks_command'
|
21
|
+
require 'toodledo/command_line/list_folders_command'
|
22
|
+
require 'toodledo/command_line/list_contexts_command'
|
23
|
+
require 'toodledo/command_line/list_goals_command'
|
24
|
+
|
25
|
+
# UPDATE
|
26
|
+
require 'toodledo/command_line/edit_command'
|
16
27
|
require 'toodledo/command_line/complete_command'
|
28
|
+
|
29
|
+
# DELETE
|
17
30
|
require 'toodledo/command_line/delete_command'
|
18
|
-
|
31
|
+
|
32
|
+
# FORMATTERS
|
33
|
+
require 'toodledo/command_line/task_formatter'
|
34
|
+
require 'toodledo/command_line/context_formatter'
|
35
|
+
require 'toodledo/command_line/folder_formatter'
|
36
|
+
require 'toodledo/command_line/goal_formatter'
|
19
37
|
|
20
38
|
module Toodledo
|
21
39
|
module CommandLine
|
@@ -50,6 +68,12 @@ module Toodledo
|
|
50
68
|
|
51
69
|
@userconfig = test(?e, userconfig) ? IO::read(userconfig) : CONFIG
|
52
70
|
@userconfig = YAML.load(@userconfig).merge(opts)
|
71
|
+
@formatters = {
|
72
|
+
:task => TaskFormatter.new,
|
73
|
+
:goal => GoalFormatter.new,
|
74
|
+
:context => ContextFormatter.new,
|
75
|
+
:folder => FolderFormatter.new
|
76
|
+
}
|
53
77
|
end
|
54
78
|
|
55
79
|
def debug?
|
@@ -91,113 +115,74 @@ module Toodledo
|
|
91
115
|
user_id = session.user_id
|
92
116
|
proxy = session.proxy
|
93
117
|
|
94
|
-
|
95
|
-
|
96
|
-
|
118
|
+
print "base_url = #{base_url}"
|
119
|
+
print "user_id = #{user_id}"
|
120
|
+
print "proxy = #{proxy.inspect}"
|
97
121
|
end
|
98
122
|
|
99
123
|
# Sets the context filter. Subsequent calls to show tasks
|
100
124
|
# will only show tasks that have this context.
|
101
125
|
#
|
102
|
-
def
|
103
|
-
|
104
|
-
input = ask("Selected context? > ") { |q| q.readline = true }
|
105
|
-
end
|
126
|
+
def set_filter(session, input)
|
127
|
+
logger.debug("set_filter(#{input})")
|
106
128
|
|
107
129
|
input.strip!
|
108
130
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
end
|
116
|
-
|
117
|
-
@filters[:context] = input
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
#
|
122
|
-
# Sets the folder filter. Subsequent calls to show tasks
|
123
|
-
# will only show tasks that are in this folder.
|
124
|
-
#
|
125
|
-
def set_folder_filter(session, input)
|
126
|
-
if (input == nil)
|
127
|
-
input = ask("Selected folder? > ") { |q| q.readline = true }
|
131
|
+
context = parse_context(input)
|
132
|
+
if (context != nil)
|
133
|
+
c = session.get_context_by_name(context)
|
134
|
+
if (c == nil)
|
135
|
+
print "No such context: #{context}"
|
136
|
+
return
|
137
|
+
end
|
138
|
+
@filters[:context] = c
|
128
139
|
end
|
129
140
|
|
130
|
-
input
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
end
|
139
|
-
else
|
140
|
-
@filters[:folder] = input
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
# Sets the goal filter. Subsequent calls to show tasks
|
145
|
-
# will only show tasks that have this goal.
|
146
|
-
#
|
147
|
-
def set_goal_filter(session, input)
|
148
|
-
if (input == nil)
|
149
|
-
input = ask("Selected goal? > ") { |q| q.readline = true }
|
141
|
+
goal = parse_goal(input)
|
142
|
+
if (goal != nil)
|
143
|
+
g = session.get_goal_by_name(goal)
|
144
|
+
if (g == nil)
|
145
|
+
print "No such goal: #{goal}"
|
146
|
+
return
|
147
|
+
end
|
148
|
+
@filters[:goal] = g
|
150
149
|
end
|
151
|
-
|
152
|
-
input.strip!
|
153
150
|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
else
|
162
|
-
@filters[:goal] = input
|
163
|
-
end
|
164
|
-
end
|
165
|
-
|
166
|
-
# Sets the context filter. Subsequent calls to show tasks
|
167
|
-
# will only show tasks that have this context.
|
168
|
-
#
|
169
|
-
def set_priority_filter(session, input)
|
170
|
-
if (input == nil)
|
171
|
-
input = ask("Selected priority? > ") { |q| q.readline = true }
|
151
|
+
folder = parse_folder(input)
|
152
|
+
if (folder != nil)
|
153
|
+
f = session.get_folder_by_name(folder)
|
154
|
+
if (f == nil)
|
155
|
+
print "No such folder: #{folder}"
|
156
|
+
end
|
157
|
+
@filters[:folder] = f
|
172
158
|
end
|
173
159
|
|
174
|
-
input
|
175
|
-
|
176
|
-
|
177
|
-
input = input.to_i
|
178
|
-
else
|
179
|
-
input = input.downcase
|
180
|
-
input = Priority.convert(input)
|
160
|
+
priority = parse_priority(input)
|
161
|
+
if (priority != nil)
|
162
|
+
@filters[:priority] = priority
|
181
163
|
end
|
182
164
|
|
183
|
-
if (
|
184
|
-
|
165
|
+
if (logger)
|
166
|
+
logger.debug("@filters = #{@filters.inspect}")
|
185
167
|
end
|
186
|
-
|
187
|
-
@filters[:priority] = input
|
188
|
-
end
|
168
|
+
end
|
189
169
|
|
190
170
|
#
|
191
171
|
# Shows all the filters.
|
192
172
|
#
|
193
173
|
def list_filters()
|
194
|
-
if (@filters.empty?)
|
195
|
-
|
174
|
+
if (@filters == nil || @filters.empty?)
|
175
|
+
print "No filters."
|
196
176
|
return
|
197
177
|
end
|
198
178
|
|
199
179
|
@filters.each do |k, v|
|
200
|
-
|
180
|
+
if (v.respond_to? :name)
|
181
|
+
name = v.name
|
182
|
+
else
|
183
|
+
name = v
|
184
|
+
end
|
185
|
+
print "#{k}: #{name}\n"
|
201
186
|
end
|
202
187
|
end
|
203
188
|
|
@@ -206,54 +191,9 @@ module Toodledo
|
|
206
191
|
#
|
207
192
|
def unfilter()
|
208
193
|
@filters = {}
|
209
|
-
|
210
|
-
end
|
211
|
-
|
212
|
-
#
|
213
|
-
# Shows all the folders for this user.
|
214
|
-
#
|
215
|
-
def folders(session)
|
216
|
-
my_folders = session.get_folders()
|
217
|
-
|
218
|
-
my_folders.sort! do |a, b|
|
219
|
-
a.name <=> b.name
|
220
|
-
end
|
221
|
-
|
222
|
-
for folder in my_folders
|
223
|
-
puts "<#{folder.server_id}> -- #{folder}\n"
|
224
|
-
end
|
225
|
-
end
|
226
|
-
|
227
|
-
#
|
228
|
-
# Shows all the contexts for this user.
|
229
|
-
#
|
230
|
-
def contexts(session)
|
231
|
-
my_contexts = session.get_contexts()
|
232
|
-
|
233
|
-
my_contexts.sort! do |a, b|
|
234
|
-
a.name <=> b.name
|
235
|
-
end
|
236
|
-
|
237
|
-
for context in my_contexts
|
238
|
-
puts "<#{context.server_id}> -- #{context}\n"
|
239
|
-
end
|
240
|
-
end
|
241
|
-
|
242
|
-
#
|
243
|
-
# Shows all the goals for this user.
|
244
|
-
#
|
245
|
-
def goals(session)
|
246
|
-
my_goals = session.get_goals()
|
247
|
-
|
248
|
-
my_goals.sort! do |a, b|
|
249
|
-
a.level <=> b.level
|
250
|
-
end
|
251
|
-
|
252
|
-
for goal in my_goals
|
253
|
-
puts "<#{goal.server_id}> -- #{goal}\n"
|
254
|
-
end
|
194
|
+
print "Filters cleared.\n"
|
255
195
|
end
|
256
|
-
|
196
|
+
|
257
197
|
#
|
258
198
|
# Displays the 'hotlist' of tasks. This shows all the uncompleted items with
|
259
199
|
# priority set to 3 or 2. There's no facility in the API for this, so we have
|
@@ -269,6 +209,7 @@ module Toodledo
|
|
269
209
|
context = parse_context(input)
|
270
210
|
folder = parse_folder(input)
|
271
211
|
goal = parse_goal(input)
|
212
|
+
priority = parse_priority(input)
|
272
213
|
|
273
214
|
params = { :notcomp => true }
|
274
215
|
|
@@ -285,6 +226,10 @@ module Toodledo
|
|
285
226
|
params.merge!({ :goal => goal })
|
286
227
|
end
|
287
228
|
|
229
|
+
if (priority != nil)
|
230
|
+
params.merge!({ :priority => priority })
|
231
|
+
end
|
232
|
+
|
288
233
|
tasks = session.get_tasks(params)
|
289
234
|
|
290
235
|
# Highest priority first
|
@@ -298,7 +243,7 @@ module Toodledo
|
|
298
243
|
|
299
244
|
for task in tasks
|
300
245
|
if (task.priority > not_important)
|
301
|
-
|
246
|
+
print @formatters[:task].format(task)
|
302
247
|
end
|
303
248
|
end
|
304
249
|
end
|
@@ -307,6 +252,8 @@ module Toodledo
|
|
307
252
|
# Lists tasks (subject to any filters that may be present).
|
308
253
|
#
|
309
254
|
def list_tasks(session, input)
|
255
|
+
logger.debug("list_tasks(#{input})")
|
256
|
+
|
310
257
|
params = { :notcomp => true }
|
311
258
|
|
312
259
|
params.merge!(@filters)
|
@@ -315,6 +262,7 @@ module Toodledo
|
|
315
262
|
context = parse_context(input)
|
316
263
|
folder = parse_folder(input)
|
317
264
|
goal = parse_goal(input)
|
265
|
+
priority = parse_priority(input)
|
318
266
|
|
319
267
|
# If there are, they override what we have set.
|
320
268
|
if (folder != nil)
|
@@ -329,6 +277,10 @@ module Toodledo
|
|
329
277
|
params.merge!({ :goal => goal })
|
330
278
|
end
|
331
279
|
|
280
|
+
if (priority != nil)
|
281
|
+
params.merge!({ :priority => priority })
|
282
|
+
end
|
283
|
+
|
332
284
|
tasks = session.get_tasks(params)
|
333
285
|
|
334
286
|
# Highest priority first
|
@@ -337,7 +289,66 @@ module Toodledo
|
|
337
289
|
end
|
338
290
|
|
339
291
|
for task in tasks
|
340
|
-
|
292
|
+
print @formatters[:task].format(task)
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
#
|
297
|
+
# Lists the goals. Takes an optional argument of
|
298
|
+
# 'short', 'medium' or 'life'.
|
299
|
+
#
|
300
|
+
def list_goals(session, input)
|
301
|
+
|
302
|
+
input.strip!
|
303
|
+
input.downcase!
|
304
|
+
|
305
|
+
goals = session.get_goals()
|
306
|
+
|
307
|
+
goals.sort! do |a, b|
|
308
|
+
a.level <=> b.level
|
309
|
+
end
|
310
|
+
|
311
|
+
level_filter = nil
|
312
|
+
case input
|
313
|
+
when 'short'
|
314
|
+
level_filter = Goal::SHORT_LEVEL
|
315
|
+
when 'medium'
|
316
|
+
level_filter = Goal::MEDIUM_LEVEL
|
317
|
+
when 'life'
|
318
|
+
level_filter = Goal::LIFE_LEVEL
|
319
|
+
end
|
320
|
+
|
321
|
+
for goal in goals
|
322
|
+
if (level_filter != nil && goal.level != level_filter)
|
323
|
+
next # skip this goal if it doesn't meet the filter
|
324
|
+
end
|
325
|
+
print @formatters[:goal].format(goal)
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
#
|
330
|
+
# Lists the contexts.
|
331
|
+
#
|
332
|
+
def list_contexts(session, input)
|
333
|
+
params = { }
|
334
|
+
|
335
|
+
contexts = session.get_contexts()
|
336
|
+
|
337
|
+
for context in contexts
|
338
|
+
print @formatters[:context].format(context)
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
#
|
343
|
+
# Lists the folders.
|
344
|
+
#
|
345
|
+
def list_folders(session, input)
|
346
|
+
params = { }
|
347
|
+
|
348
|
+
folders = session.get_folders()
|
349
|
+
|
350
|
+
for folder in folders
|
351
|
+
print @formatters[:folder].format(folder)
|
341
352
|
end
|
342
353
|
end
|
343
354
|
|
@@ -348,7 +359,7 @@ module Toodledo
|
|
348
359
|
# The order of symbols does not matter, but the title must be the last thing
|
349
360
|
# on the line.
|
350
361
|
#
|
351
|
-
# add @[Deep Space] *Action
|
362
|
+
# add @[Deep Space] *Action ^[For Great Justice] Take off every Zig
|
352
363
|
#
|
353
364
|
# There is no priority handling in this method. It may be added if there is
|
354
365
|
# demand for it.
|
@@ -356,9 +367,13 @@ module Toodledo
|
|
356
367
|
context = parse_context(line)
|
357
368
|
folder = parse_folder(line)
|
358
369
|
goal = parse_goal(line)
|
370
|
+
priority = parse_priority(line)
|
359
371
|
title = parse_remainder(line)
|
360
372
|
|
361
|
-
params = {
|
373
|
+
params = {}
|
374
|
+
if (priority != nil)
|
375
|
+
params.merge!({ :priority => priority })
|
376
|
+
end
|
362
377
|
|
363
378
|
if (folder != nil)
|
364
379
|
params.merge!({ :folder => folder })
|
@@ -379,7 +394,61 @@ module Toodledo
|
|
379
394
|
|
380
395
|
task_id = session.add_task(title, params)
|
381
396
|
|
382
|
-
|
397
|
+
print "Task #{task_id} added."
|
398
|
+
end
|
399
|
+
|
400
|
+
def add_context(session, input)
|
401
|
+
|
402
|
+
title = input.strip
|
403
|
+
|
404
|
+
context_id = session.add_context(title)
|
405
|
+
|
406
|
+
print "Context #{context_id} added."
|
407
|
+
end
|
408
|
+
|
409
|
+
def add_goal(session, input)
|
410
|
+
input.strip!
|
411
|
+
|
412
|
+
# Assume that a goal is short, medium or life, and
|
413
|
+
# don't stick a symbol on it.
|
414
|
+
level = parse_level(input)
|
415
|
+
if (level == nil)
|
416
|
+
level = Toodledo::Goal::SHORT_LEVEL
|
417
|
+
else
|
418
|
+
input = clean(LEVEL_REGEXP, input)
|
419
|
+
input.strip!
|
420
|
+
end
|
421
|
+
|
422
|
+
goal_id = session.add_goal(input, level)
|
423
|
+
|
424
|
+
print "Goal #{goal_id} added."
|
425
|
+
end
|
426
|
+
|
427
|
+
def add_folder(session, input)
|
428
|
+
|
429
|
+
title = input.strip
|
430
|
+
|
431
|
+
folder_id = session.add_folder(title)
|
432
|
+
|
433
|
+
print "Folder #{folder_id} added."
|
434
|
+
end
|
435
|
+
|
436
|
+
#
|
437
|
+
# Archives a folder.
|
438
|
+
#
|
439
|
+
def archive_folder(session, line)
|
440
|
+
|
441
|
+
line.strip!
|
442
|
+
|
443
|
+
folder_id = line
|
444
|
+
params = { :archived => 1 }
|
445
|
+
session.edit_folder(folder_id, params)
|
446
|
+
|
447
|
+
print "Folder #{folder_id} archived."
|
448
|
+
end
|
449
|
+
|
450
|
+
def archive_goal(session, line)
|
451
|
+
# Not implemented! No way to edit a goal.
|
383
452
|
end
|
384
453
|
|
385
454
|
#
|
@@ -393,6 +462,7 @@ module Toodledo
|
|
393
462
|
context = parse_context(input)
|
394
463
|
folder = parse_folder(input)
|
395
464
|
goal = parse_goal(input)
|
465
|
+
priority = parse_priority(input)
|
396
466
|
task_id = parse_remainder(input)
|
397
467
|
|
398
468
|
logger.debug("edit_task: task_id = #{task_id}")
|
@@ -417,9 +487,13 @@ module Toodledo
|
|
417
487
|
params.merge!({ :goal => goal })
|
418
488
|
end
|
419
489
|
|
490
|
+
if (priority != nil)
|
491
|
+
params.merge!({ :priority => priority })
|
492
|
+
end
|
493
|
+
|
420
494
|
session.edit_task(task_id, params)
|
421
495
|
|
422
|
-
|
496
|
+
print "Task #{task_id} edited."
|
423
497
|
end
|
424
498
|
|
425
499
|
# Masks the task as completed. Uses a task id as argument.
|
@@ -437,12 +511,13 @@ module Toodledo
|
|
437
511
|
|
438
512
|
params = { :completed => 1 }
|
439
513
|
if (session.edit_task(task_id, params))
|
440
|
-
|
514
|
+
print "Task #{task_id} completed."
|
441
515
|
else
|
442
|
-
|
516
|
+
print "Task #{task_id} could not be completed!"
|
443
517
|
end
|
444
518
|
end
|
445
519
|
|
520
|
+
|
446
521
|
# Deletes a task, using the task id.
|
447
522
|
#
|
448
523
|
# delete 123
|
@@ -457,12 +532,178 @@ module Toodledo
|
|
457
532
|
task_id.strip!
|
458
533
|
|
459
534
|
if (session.delete_task(task_id))
|
460
|
-
|
535
|
+
print "Task #{task_id} deleted."
|
536
|
+
else
|
537
|
+
print "Task #{task_id} could not be deleted!"
|
538
|
+
end
|
539
|
+
end
|
540
|
+
|
541
|
+
def delete_context(session, line)
|
542
|
+
id = line
|
543
|
+
|
544
|
+
id.strip!
|
545
|
+
|
546
|
+
if (session.delete_context(id))
|
547
|
+
print "Context #{id} deleted."
|
548
|
+
else
|
549
|
+
print "Context #{id} could not be deleted!"
|
550
|
+
end
|
551
|
+
end
|
552
|
+
|
553
|
+
def delete_goal(session, line)
|
554
|
+
id = line
|
555
|
+
|
556
|
+
id.strip!
|
557
|
+
|
558
|
+
if (session.delete_goal(id))
|
559
|
+
print "Goal #{id} deleted."
|
560
|
+
else
|
561
|
+
print "Goal #{id} could not be deleted!"
|
562
|
+
end
|
563
|
+
end
|
564
|
+
|
565
|
+
def delete_folder(session, line)
|
566
|
+
id = line
|
567
|
+
|
568
|
+
id.strip!
|
569
|
+
|
570
|
+
if (session.delete_folder(id))
|
571
|
+
print "Folder #{id} deleted."
|
572
|
+
else
|
573
|
+
print "Folder #{id} could not be deleted!"
|
574
|
+
end
|
575
|
+
end
|
576
|
+
|
577
|
+
# Prints out a single line.
|
578
|
+
def print(line = nil)
|
579
|
+
if (line == nil)
|
580
|
+
puts
|
461
581
|
else
|
462
|
-
puts
|
582
|
+
puts line
|
463
583
|
end
|
464
584
|
end
|
465
585
|
|
586
|
+
#
|
587
|
+
# Displays the help message.
|
588
|
+
#
|
589
|
+
def help()
|
590
|
+
print "hotlist Shows the hotlist"
|
591
|
+
print "folders Shows all folders"
|
592
|
+
print "goals Shows all goals"
|
593
|
+
print "contexts Shows all contexts"
|
594
|
+
print "tasks Shows tasks ('tasks *Action @Home')"
|
595
|
+
print
|
596
|
+
print "add Adds task ('add *Action @Home Eat breakfast')"
|
597
|
+
print " folder Adds a folder ('add folder MyFolder')"
|
598
|
+
print " context Adds a context ('add context MyContext')"
|
599
|
+
print " goal Adds a goal ('add goal MyGoal')"
|
600
|
+
print "edit Edits a task ('edit *Action 1134')"
|
601
|
+
print "complete Completes a task ('complete 1234')"
|
602
|
+
print "delete Deletes a task ('delete 1134')"
|
603
|
+
print " folder Deletes a folder ('delete folder 1')"
|
604
|
+
print " context Deletes a context ('delete context 2')"
|
605
|
+
print " goal Deletes a goal ('delete goal 3')"
|
606
|
+
print
|
607
|
+
print "archive Archives a folder ('archive 1234')"
|
608
|
+
print "filter Defines filters ('filter *Action @Someday')"
|
609
|
+
print "unfilter Removes all filters"
|
610
|
+
print "filters Displays the list of filters"
|
611
|
+
print
|
612
|
+
print "help or ? Displays this help message"
|
613
|
+
print "quit or exit Leaves the application"
|
614
|
+
end
|
615
|
+
|
616
|
+
def clean(regexp, input)
|
617
|
+
return input.sub(regexp, '')
|
618
|
+
end
|
619
|
+
|
620
|
+
def execute_command(session, input)
|
621
|
+
case input
|
622
|
+
when /^help/, /^\s*\?/
|
623
|
+
help()
|
624
|
+
|
625
|
+
when /^add/
|
626
|
+
line = clean(/^add/, input)
|
627
|
+
line.strip!
|
628
|
+
case line
|
629
|
+
when /folder/
|
630
|
+
add_folder(session, clean(/folder/, line))
|
631
|
+
when /context/
|
632
|
+
add_context(session, clean(/context/, line))
|
633
|
+
when /goal/
|
634
|
+
add_goal(session, clean(/goal/, line))
|
635
|
+
else
|
636
|
+
add_task(session, line)
|
637
|
+
end
|
638
|
+
|
639
|
+
when /^edit/
|
640
|
+
line = clean(/^edit/, input)
|
641
|
+
edit_task(session, line)
|
642
|
+
|
643
|
+
when /^delete/
|
644
|
+
line = clean(/^delete/, input)
|
645
|
+
line.strip!
|
646
|
+
case line
|
647
|
+
when /folder/
|
648
|
+
delete_folder(session, clean(/folder/, line))
|
649
|
+
when /context/
|
650
|
+
delete_context(session, clean(/context/, line))
|
651
|
+
when /goal/
|
652
|
+
delete_goal(session, clean(/goal/, line))
|
653
|
+
else
|
654
|
+
delete_task(session, line)
|
655
|
+
end
|
656
|
+
|
657
|
+
when /^archive/
|
658
|
+
archive_folder(session, clean(/^archive/, input))
|
659
|
+
|
660
|
+
when /^hotlist/
|
661
|
+
line = clean(/^hotlist/, input)
|
662
|
+
hotlist(session, line)
|
663
|
+
|
664
|
+
when /^complete/
|
665
|
+
line = clean(/^complete/, input)
|
666
|
+
complete_task(session, line)
|
667
|
+
|
668
|
+
when /^tasks/
|
669
|
+
line = clean(/^(tasks)/, input)
|
670
|
+
list_tasks(session, line)
|
671
|
+
|
672
|
+
when /^folders/
|
673
|
+
line = clean(/^folders/, input)
|
674
|
+
list_folders(session,line)
|
675
|
+
|
676
|
+
when /^goals/
|
677
|
+
line = clean(/^goals/, input)
|
678
|
+
list_goals(session,line)
|
679
|
+
|
680
|
+
when /^contexts/
|
681
|
+
line = clean(/^contexts/, input)
|
682
|
+
list_contexts(session,line)
|
683
|
+
|
684
|
+
when /^filters/
|
685
|
+
list_filters()
|
686
|
+
|
687
|
+
when /^filter/
|
688
|
+
line = clean(/^filter/, input)
|
689
|
+
set_filter(session, line)
|
690
|
+
|
691
|
+
when /^config/
|
692
|
+
show_config(session)
|
693
|
+
|
694
|
+
when /^unfilter/
|
695
|
+
unfilter()
|
696
|
+
|
697
|
+
when /debug/
|
698
|
+
self.debug = ! self.debug?
|
699
|
+
|
700
|
+
when /^quit/, /^exit/
|
701
|
+
exit 0
|
702
|
+
else
|
703
|
+
print "'#{input}' is not a command: type help for a list"
|
704
|
+
end
|
705
|
+
end
|
706
|
+
|
466
707
|
#
|
467
708
|
# Runs the client main command. This is what gets run from 'toodledo'.
|
468
709
|
# Ironically doesn't do much except for set up the commands and parse
|
@@ -485,25 +726,40 @@ module Toodledo
|
|
485
726
|
opt.separator "Global options:"
|
486
727
|
opt.on("--debug", "Print debugging information") {|t| self.debug = true }
|
487
728
|
end
|
729
|
+
|
730
|
+
# this is the default command if we don't receive any options.
|
731
|
+
cmd.add_command(InteractiveCommand.new(self), true)
|
488
732
|
|
489
|
-
cmd.add_command(
|
733
|
+
cmd.add_command(StdinCommand.new(self))
|
734
|
+
|
735
|
+
cmd.add_command(AddTaskCommand.new(self))
|
736
|
+
|
737
|
+
cmd.add_command(ListTasksCommand.new(self))
|
738
|
+
cmd.add_command(ListFoldersCommand.new(self))
|
739
|
+
cmd.add_command(ListGoalsCommand.new(self))
|
740
|
+
cmd.add_command(ListContextsCommand.new(self))
|
490
741
|
|
491
|
-
cmd.add_command(AddCommand.new(self))
|
492
|
-
cmd.add_command(ListCommand.new(self))
|
493
742
|
cmd.add_command(EditCommand.new(self))
|
494
743
|
cmd.add_command(CompleteCommand.new(self))
|
495
|
-
cmd.add_command(
|
744
|
+
cmd.add_command(DeleteTaskCommand.new(self))
|
496
745
|
cmd.add_command(HotlistCommand.new(self))
|
497
746
|
cmd.add_command(SetupCommand.new(self))
|
498
|
-
|
499
|
-
|
500
|
-
cmd.add_command(CmdParse::HelpCommand.new, true)
|
747
|
+
|
748
|
+
cmd.add_command(CmdParse::HelpCommand.new)
|
501
749
|
cmd.add_command(CmdParse::VersionCommand.new)
|
502
750
|
|
503
751
|
cmd.parse
|
504
752
|
|
505
753
|
# Return a good exit status.
|
506
754
|
return 0
|
755
|
+
rescue InvalidConfigurationError => e
|
756
|
+
logger.debug(e)
|
757
|
+
print "The client is missing (or cannot use) the user id or password it needs to connect."
|
758
|
+
print "Run 'toodledo setup' and save the file to fix this."
|
759
|
+
return -1
|
760
|
+
rescue ServerError => e
|
761
|
+
print "The server returned a fatal error: #{e.message}"
|
762
|
+
return -1
|
507
763
|
end
|
508
764
|
end #class
|
509
765
|
end
|