toodledo 1.0.2 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|