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/lib/toodledo/context.rb
CHANGED
data/lib/toodledo/folder.rb
CHANGED
@@ -27,11 +27,8 @@ module Toodledo
|
|
27
27
|
def archived?
|
28
28
|
return @archived == 1
|
29
29
|
end
|
30
|
-
|
31
|
-
|
32
|
-
return "*[#{name}]"
|
33
|
-
end
|
34
|
-
|
30
|
+
|
31
|
+
# Creates a session object from an XML element.
|
35
32
|
def self.parse(session, el)
|
36
33
|
id = el.attributes['id']
|
37
34
|
is_private = el.attributes['private']
|
@@ -46,5 +43,4 @@ module Toodledo
|
|
46
43
|
end
|
47
44
|
end
|
48
45
|
|
49
|
-
|
50
46
|
end
|
data/lib/toodledo/goal.rb
CHANGED
@@ -26,6 +26,7 @@ module Toodledo
|
|
26
26
|
@id = id
|
27
27
|
@level = level
|
28
28
|
@contributes_id = contributes_id
|
29
|
+
@contributes = nil
|
29
30
|
@name = name
|
30
31
|
end
|
31
32
|
|
@@ -59,16 +60,9 @@ module Toodledo
|
|
59
60
|
end
|
60
61
|
|
61
62
|
def to_xml()
|
62
|
-
return "<goal id=\"#{@id}\" level=\"#{@level}\" contributes=\"#{@
|
63
|
+
return "<goal id=\"#{@id}\" level=\"#{@level}\" contributes=\"#{@contributes_id}\" name=\"#{@name}\">"
|
63
64
|
end
|
64
65
|
|
65
|
-
def to_s()
|
66
|
-
msg = "$[#{name}]"
|
67
|
-
#if (contributes != NO_GOAL)
|
68
|
-
# msg += " (Contributes to: #{contributes.name})"
|
69
|
-
#end
|
70
|
-
return msg
|
71
|
-
end
|
72
66
|
end
|
73
67
|
|
74
68
|
end
|
data/lib/toodledo/priority.rb
CHANGED
@@ -30,22 +30,5 @@ module Toodledo
|
|
30
30
|
return false
|
31
31
|
end
|
32
32
|
|
33
|
-
def self.convert(input)
|
34
|
-
case input
|
35
|
-
when 'negative'
|
36
|
-
return Priority::NEGATIVE
|
37
|
-
when 'low'
|
38
|
-
return Priority::LOW
|
39
|
-
when 'medium'
|
40
|
-
return Priority::MEDIUM
|
41
|
-
when 'high'
|
42
|
-
return Priority::HIGH
|
43
|
-
when 'top'
|
44
|
-
return Priority::TOP
|
45
|
-
else
|
46
|
-
raise ArgumentError.new("Unknown priority: #{input}")
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
33
|
end
|
51
34
|
end
|
data/lib/toodledo/session.rb
CHANGED
@@ -21,7 +21,9 @@ module Toodledo
|
|
21
21
|
USER_AGENT = "Ruby/#{Toodledo::VERSION} (#{RUBY_PLATFORM})"
|
22
22
|
|
23
23
|
HEADERS = {
|
24
|
-
'User-Agent' => USER_AGENT
|
24
|
+
'User-Agent' => USER_AGENT,
|
25
|
+
'Connection' => 'keep-alive',
|
26
|
+
'Keep-Alive' => '300'
|
25
27
|
}
|
26
28
|
|
27
29
|
EXPIRATION_TIME_IN_SECS = 60 * 60
|
@@ -37,10 +39,10 @@ module Toodledo
|
|
37
39
|
attr_reader :base_url, :user_id, :proxy
|
38
40
|
|
39
41
|
# Creates a new session, using the given user name and password.
|
40
|
-
# throws
|
42
|
+
# throws InvalidConfigurationError if user_id or password are nil.
|
41
43
|
def initialize(user_id, password, logger = nil)
|
42
|
-
raise "Nil user_id" if (user_id == nil)
|
43
|
-
raise "Nil password" if (password == nil)
|
44
|
+
raise InvalidConfigurationError.new("Nil user_id") if (user_id == nil)
|
45
|
+
raise InvalidConfigurationError.new("Nil password") if (password == nil)
|
44
46
|
|
45
47
|
@user_id = user_id
|
46
48
|
@password = password
|
@@ -72,11 +74,11 @@ module Toodledo
|
|
72
74
|
# logger.debug("user_id = #{@user_id}, #{@email} #{@password}")
|
73
75
|
|
74
76
|
if (@user_id == '1')
|
75
|
-
raise "
|
77
|
+
raise InvalidConfigurationError.new("Invalid user_id")
|
76
78
|
end
|
77
79
|
|
78
80
|
if (@user_id == '0')
|
79
|
-
raise "
|
81
|
+
raise InvalidConfigurationError.new("Invalid password")
|
80
82
|
end
|
81
83
|
|
82
84
|
# Set the base URL.
|
@@ -140,14 +142,6 @@ module Toodledo
|
|
140
142
|
raise 'Must call connect() before this method'
|
141
143
|
end
|
142
144
|
|
143
|
-
# Break all the parameters down into key=value seperated by semi colons
|
144
|
-
stringified_params = (key != nil) ? ';key=' + key : ''
|
145
|
-
|
146
|
-
params.each { |k, v|
|
147
|
-
stringified_params += ';' + k.to_s + '=' + escape_text(v)
|
148
|
-
}
|
149
|
-
url = make_uri(method, stringified_params)
|
150
|
-
|
151
145
|
# If it's been more than an hour, then ask for a new key.
|
152
146
|
if (@key != nil && expired?)
|
153
147
|
logger.debug("call(#{method}) connection expired, reconnecting...") if logger
|
@@ -157,8 +151,21 @@ module Toodledo
|
|
157
151
|
proxy = @proxy
|
158
152
|
disconnect() # ensures that key == nil, which is crucial to avoid an endless loop...
|
159
153
|
connect(base_url, proxy)
|
154
|
+
|
155
|
+
# swap out the key (if any) before we start assembling the request
|
156
|
+
if (key != nil)
|
157
|
+
key = @key
|
158
|
+
end
|
160
159
|
end
|
161
160
|
|
161
|
+
# Break all the parameters down into key=value seperated by semi colons
|
162
|
+
stringified_params = (key != nil) ? ';key=' + key : ''
|
163
|
+
|
164
|
+
params.each { |k, v|
|
165
|
+
stringified_params += ';' + k.to_s + '=' + escape_text(v)
|
166
|
+
}
|
167
|
+
url = make_uri(method, stringified_params)
|
168
|
+
|
162
169
|
# Establish the proxy
|
163
170
|
if (@proxy != nil)
|
164
171
|
logger.debug("call(#{method}) establishing proxy...") if logger
|
@@ -215,7 +222,7 @@ module Toodledo
|
|
215
222
|
|
216
223
|
# Gets the token method, given the id.
|
217
224
|
def get_token(user_id)
|
218
|
-
raise "Nil user_id" if (user_id == nil)
|
225
|
+
raise "Nil user_id" if (user_id == nil || user_id.empty?)
|
219
226
|
|
220
227
|
params = { :userid => user_id }
|
221
228
|
result = call('getToken', params)
|
data/lib/toodledo/task.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
|
2
2
|
require 'toodledo/repeat'
|
3
3
|
require 'toodledo/priority'
|
4
|
+
require 'date'
|
4
5
|
|
5
6
|
module Toodledo
|
6
7
|
|
@@ -83,88 +84,6 @@ module Toodledo
|
|
83
84
|
return ! (@children == nil || @children == 0)
|
84
85
|
end
|
85
86
|
|
86
|
-
def to_s()
|
87
|
-
if (priority == Priority::NEGATIVE)
|
88
|
-
fancyp = 'v'
|
89
|
-
elsif (priority == Priority::LOW)
|
90
|
-
fancyp = '~'
|
91
|
-
else
|
92
|
-
fancyp = '!' * priority
|
93
|
-
end
|
94
|
-
|
95
|
-
msg = "#{fancyp}"
|
96
|
-
|
97
|
-
if (folder != Folder::NO_FOLDER)
|
98
|
-
msg += " *[#{folder.name}]"
|
99
|
-
end
|
100
|
-
|
101
|
-
if (context != Context::NO_CONTEXT)
|
102
|
-
msg += " @[#{context.name}]"
|
103
|
-
end
|
104
|
-
|
105
|
-
if (goal != Goal::NO_GOAL)
|
106
|
-
msg += " $[#{goal.name}]"
|
107
|
-
end
|
108
|
-
|
109
|
-
if (duedate != nil)
|
110
|
-
fmt = Session::DATE_FORMAT + ' ' + Session::TIME_FORMAT
|
111
|
-
msg += " \#[#{duedatemodifier}#{duedate.strftime(fmt)}]"
|
112
|
-
end
|
113
|
-
|
114
|
-
if (repeat != Repeat::NONE)
|
115
|
-
msg += " repeat[#{readable_repeat()}]"
|
116
|
-
end
|
117
|
-
|
118
|
-
if (tag != nil)
|
119
|
-
msg += " tag[#{tag}]"
|
120
|
-
end
|
121
|
-
|
122
|
-
if (parent_id != nil)
|
123
|
-
msg += " parent[#{parent_id}]"
|
124
|
-
end
|
125
|
-
|
126
|
-
if (length != nil)
|
127
|
-
msg += " length[#{length}]"
|
128
|
-
end
|
129
|
-
|
130
|
-
if (timer != nil)
|
131
|
-
msg += " timer[#{timer}]"
|
132
|
-
end
|
133
|
-
|
134
|
-
msg += " #{title}"
|
135
|
-
|
136
|
-
if (note != nil)
|
137
|
-
msg += "\n #{note}"
|
138
|
-
end
|
139
|
-
|
140
|
-
return msg
|
141
|
-
end
|
142
|
-
|
143
|
-
def readable_repeat()
|
144
|
-
case repeat
|
145
|
-
when Repeat::NONE
|
146
|
-
''
|
147
|
-
when Repeat::WEEKLY
|
148
|
-
"weekly"
|
149
|
-
when Repeat::MONTHLY
|
150
|
-
"monthly"
|
151
|
-
when Repeat::YEARLY
|
152
|
-
"yearly"
|
153
|
-
when Repeat::DAILY
|
154
|
-
"daily"
|
155
|
-
when Repeat::BIWEEKLY
|
156
|
-
"biweekly"
|
157
|
-
when Repeat::BIMONTHLY
|
158
|
-
"bimonthly"
|
159
|
-
when Repeat::SEMIANNUALLY
|
160
|
-
"semiannually"
|
161
|
-
when Repeat::QUARTERLY
|
162
|
-
"quarterly"
|
163
|
-
else
|
164
|
-
''
|
165
|
-
end
|
166
|
-
end
|
167
|
-
|
168
87
|
# Parses a task element and returns a new Task.
|
169
88
|
def self.parse(session, el)
|
170
89
|
|
data/test/client_test.rb
ADDED
@@ -0,0 +1,192 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__),'..','lib')
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
require 'toodledo/command_line/client'
|
5
|
+
require 'flexmock/test_unit'
|
6
|
+
|
7
|
+
module Toodledo
|
8
|
+
module CommandLine
|
9
|
+
class ClientTest < Test::Unit::TestCase
|
10
|
+
|
11
|
+
def setup()
|
12
|
+
client = Client.new()
|
13
|
+
|
14
|
+
# Set up a partial mock so we can override :print
|
15
|
+
@client = flexmock(client)
|
16
|
+
@session = flexmock('session')
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_set_filter()
|
20
|
+
# We don't want an error message printed out here.
|
21
|
+
@client.should_receive(:print).never
|
22
|
+
@client.set_filter(@session, '!top')
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_add_task_with_no_args()
|
26
|
+
|
27
|
+
input = 'This is a test'
|
28
|
+
args = {}
|
29
|
+
|
30
|
+
@session.should_receive(:add_task).with(input, args).and_return 1
|
31
|
+
@client.should_receive(:print).with('Task 1 added.')
|
32
|
+
@client.add_task(@session, input)
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_add_task_with_folder()
|
36
|
+
|
37
|
+
input = '*Inbasket This is a test'
|
38
|
+
|
39
|
+
args = { :folder => "Inbasket" }
|
40
|
+
|
41
|
+
@session.should_receive(:add_task).with('This is a test', args).and_return(1)
|
42
|
+
@client.should_receive(:print).with('Task 1 added.')
|
43
|
+
@client.add_task(@session, input)
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_add_task_with_context()
|
47
|
+
|
48
|
+
input = '@Home This is a test'
|
49
|
+
|
50
|
+
args = { :context => "Home" }
|
51
|
+
|
52
|
+
@session.should_receive(:add_task).with('This is a test', args).and_return(1)
|
53
|
+
@client.should_receive(:print).with('Task 1 added.')
|
54
|
+
|
55
|
+
@client.add_task(@session, input)
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_add_task_with_goal()
|
59
|
+
|
60
|
+
input = '^Goal This is a test'
|
61
|
+
|
62
|
+
args = { :goal => "Goal" }
|
63
|
+
|
64
|
+
@session.should_receive(:add_task).with('This is a test', args).and_return(1)
|
65
|
+
@client.should_receive(:print).with('Task 1 added.')
|
66
|
+
|
67
|
+
@client.add_task(@session, input)
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_add_task_with_priority()
|
71
|
+
|
72
|
+
input = '!top This is a test'
|
73
|
+
|
74
|
+
args = { :priority => Priority::TOP }
|
75
|
+
|
76
|
+
@session.should_receive(:add_task).with('This is a test', args).and_return(1)
|
77
|
+
@client.should_receive(:print).with('Task 1 added.')
|
78
|
+
|
79
|
+
@client.add_task(@session, input)
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_add_folder()
|
83
|
+
input = 'name'
|
84
|
+
|
85
|
+
id = '1234'
|
86
|
+
@session.should_receive(:add_folder).with(input).and_return(id)
|
87
|
+
@client.should_receive(:print).with('Folder 1234 added.')
|
88
|
+
|
89
|
+
@client.add_folder(@session, input)
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_add_context()
|
93
|
+
input = 'name'
|
94
|
+
|
95
|
+
id = '1234'
|
96
|
+
@session.should_receive(:add_context).with(input).and_return(id)
|
97
|
+
@client.should_receive(:print).with('Context 1234 added.')
|
98
|
+
|
99
|
+
@client.add_context(@session, input)
|
100
|
+
end
|
101
|
+
|
102
|
+
def test_add_goal()
|
103
|
+
input = 'name'
|
104
|
+
|
105
|
+
id = '1234'
|
106
|
+
level = Goal::SHORT_LEVEL
|
107
|
+
@session.should_receive(:add_goal).with(input, level).and_return(id)
|
108
|
+
@client.should_receive(:print).with('Goal 1234 added.')
|
109
|
+
|
110
|
+
@client.add_goal(@session, input)
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_list_tasks_with_nothing()
|
114
|
+
|
115
|
+
params = {
|
116
|
+
:priority => Priority::LOW,
|
117
|
+
:title => 'foo',
|
118
|
+
:folder => Folder::NO_FOLDER,
|
119
|
+
:context => Context::NO_CONTEXT,
|
120
|
+
:goal => Goal::NO_GOAL,
|
121
|
+
:repeat => Repeat::NONE
|
122
|
+
}
|
123
|
+
task = Task.new(1234, params)
|
124
|
+
tasks = [ task ]
|
125
|
+
@session.should_receive(:get_tasks).and_return(tasks)
|
126
|
+
@client.should_receive(:print).with('<1234> -- !low foo')
|
127
|
+
|
128
|
+
input = ''
|
129
|
+
@client.list_tasks(@session, input)
|
130
|
+
end
|
131
|
+
|
132
|
+
|
133
|
+
def test_list_tasks_with_everything()
|
134
|
+
|
135
|
+
params = {
|
136
|
+
:priority => Priority::LOW,
|
137
|
+
:title => 'foo',
|
138
|
+
:folder => Folder.new(1234, 0, 0, 'test folder'),
|
139
|
+
:context => Context.new(345, 'test context'),
|
140
|
+
:goal => Goal.new(342341, 0, 0, 'test goal'),
|
141
|
+
:repeat => Repeat::BIWEEKLY,
|
142
|
+
:tag => 'some tag'
|
143
|
+
}
|
144
|
+
task = Task.new(1234, params)
|
145
|
+
tasks = [ task ]
|
146
|
+
@session.should_receive(:get_tasks).and_return(tasks)
|
147
|
+
@client.should_receive(:print).with('<1234> -- !low *[test folder] @[test context] ^[test goal] repeat[biweekly] tag[some tag] foo')
|
148
|
+
|
149
|
+
input = ''
|
150
|
+
@client.list_tasks(@session, input)
|
151
|
+
end
|
152
|
+
|
153
|
+
def test_list_contexts()
|
154
|
+
context = Context.new(1234, 'Context')
|
155
|
+
contexts = [ context ]
|
156
|
+
@session.should_receive(:get_contexts).and_return(contexts)
|
157
|
+
@client.should_receive(:print).with('<1234> -- @[Context]')
|
158
|
+
|
159
|
+
input = ''
|
160
|
+
@client.list_contexts(@session, input)
|
161
|
+
end
|
162
|
+
|
163
|
+
def test_list_goals()
|
164
|
+
goals = [ Goal.new(1234, Goal::LIFE_LEVEL, 0, 'Name') ]
|
165
|
+
@session.should_receive(:get_goals).and_return(goals)
|
166
|
+
@client.should_receive(:print).with('<1234> -- life ^[Name]')
|
167
|
+
|
168
|
+
input = ''
|
169
|
+
@client.list_goals(@session, input)
|
170
|
+
end
|
171
|
+
|
172
|
+
def test_list_folders()
|
173
|
+
folders = [ Folder.new(1234, 0, 0, 'Name') ]
|
174
|
+
@session.should_receive(:get_folders).and_return(folders)
|
175
|
+
@client.should_receive(:print).with('<1234> -- *[Name]')
|
176
|
+
|
177
|
+
input = ''
|
178
|
+
@client.list_folders(@session, input)
|
179
|
+
end
|
180
|
+
|
181
|
+
def test_archive_folder()
|
182
|
+
|
183
|
+
@session.should_receive(:edit_folder).and_return(true)
|
184
|
+
@client.should_receive(:print).with('Folder 234 archived.')
|
185
|
+
|
186
|
+
input = '234'
|
187
|
+
@client.archive_folder(@session, input)
|
188
|
+
end
|
189
|
+
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|