davetron5000-rtm 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +107 -0
- data/bin/rtm +113 -0
- data/ext/hash_array.rb +22 -0
- data/ext/string_camelize.rb +10 -0
- data/lib/rtm.rb +3 -0
- data/lib/rtm/auth.rb +58 -0
- data/lib/rtm/endpoint.rb +146 -0
- data/lib/rtm/rtm.rb +145 -0
- metadata +85 -0
data/README.rdoc
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
= Ruby Client for Remember The Milk
|
2
|
+
|
3
|
+
Author:: Dave Copeland (mailto:davetron5000 at g mail dot com)
|
4
|
+
Copyright:: Copyright (c) 2009 by Dave Copeland
|
5
|
+
License:: Distributes under the Apache License, see LICENSE.txt in the source distro
|
6
|
+
|
7
|
+
== Overview
|
8
|
+
|
9
|
+
This is a very lightweight and easy-to-use client for Remember[http://www.rememberthemilk.com] The[http://www.rememberthemilk.com] Milk[http://www.rememberthemilk.com]
|
10
|
+
|
11
|
+
rtm = RTM.new(Endpoint.new(your_api_key,your_secret))
|
12
|
+
rtm.token = user_token
|
13
|
+
response = rtm.tasks.getList
|
14
|
+
response.tasks.list.as_array.each do |list|
|
15
|
+
# this is an RTM list of taskseries
|
16
|
+
end
|
17
|
+
|
18
|
+
=== Authorization
|
19
|
+
|
20
|
+
Authorization is a bit tricky, but you only need to do it one time. This example shows
|
21
|
+
command line/desktop usage
|
22
|
+
|
23
|
+
rtm = RTM.new(Endpoint.new(your_api_key,your_secret))
|
24
|
+
auth = rtm.auth
|
25
|
+
url = auth.url
|
26
|
+
frob = auth.frob
|
27
|
+
# Send user to this URL
|
28
|
+
# They will authorize your app's api_key via the RTM website.
|
29
|
+
# store the frob somewhere, you will need it
|
30
|
+
#
|
31
|
+
# When they return...
|
32
|
+
auth.frob=frob
|
33
|
+
rtm.token=auth.get_token
|
34
|
+
# store the token for all time
|
35
|
+
|
36
|
+
rtm.tasks.getList
|
37
|
+
rtm.groups.delete
|
38
|
+
rtm.tasks.notes.add
|
39
|
+
|
40
|
+
And so forth. Basically, you can call the RTM methods as if they were real methods.
|
41
|
+
|
42
|
+
=== Responses
|
43
|
+
|
44
|
+
As shown above, you can simply navigate the XML returned by method names. This uses Hash.method_missing and can be dicey, so you can also navigate a bit safer via
|
45
|
+
|
46
|
+
response = rtm.tasks.getList
|
47
|
+
response['tasks']['list'].as_array.each do |list|
|
48
|
+
# this is an RTM list of taskseries
|
49
|
+
end
|
50
|
+
|
51
|
+
Note that for any element that you want to treat as a list, you must call as_array on it, to ensure that it gets turned into a list. The underlying HTTParty code doesn't know if an XML element is supposed to be a list or just a singular.
|
52
|
+
|
53
|
+
<i>Note that you can use Ruby-style conventions instead of camel-case, e.g. <tt>rtm.tasks.get_list</tt></i>
|
54
|
+
|
55
|
+
=== Timelines
|
56
|
+
|
57
|
+
Many methods require timelines; this is the way that RTM provides for undo-type features. If you don't really care about this, or want it done for you, you can specify auto timeline creation:
|
58
|
+
|
59
|
+
|
60
|
+
rtm.auto_timeline=true
|
61
|
+
rtm.tasks.add(:name => 'My new task')
|
62
|
+
timeline = rtm.last_timeline
|
63
|
+
|
64
|
+
This will create a timeline for you, and use it to create the task. The list of methods that <b>do not</b> require timelines is used to determine if this is done, and is based on the RTM documentation at the time of this writing.
|
65
|
+
|
66
|
+
== Basic Command Line
|
67
|
+
|
68
|
+
To verify and play with the RTM API, there is a command-line tool called <tt>rtm</tt>. It takes the following commands:
|
69
|
+
|
70
|
+
[any] executes any RTM method
|
71
|
+
[auth] Prints out the auth URL to authorize your api key/ruby client. Do not do this after getting a token
|
72
|
+
[chktoken] Checks that your token is valid
|
73
|
+
[help] Shows list of commands or help for one command
|
74
|
+
[ls] Lists incomplete tasks
|
75
|
+
[token] Gets your token; do this only once and put it in your api.yaml file
|
76
|
+
|
77
|
+
The easiest way to get started is to get your API Key and Secret from RTM and then:
|
78
|
+
|
79
|
+
rtm ls
|
80
|
+
|
81
|
+
This will init your api.yaml file
|
82
|
+
|
83
|
+
rtm auth
|
84
|
+
|
85
|
+
Go to this URL and authorize your API Key. Save the frob that is printed out
|
86
|
+
|
87
|
+
rtm token <<frob>>
|
88
|
+
|
89
|
+
This will authorize your app and print out your token. Edit <tt>api.yaml</tt> and add your token.
|
90
|
+
|
91
|
+
:token: <<your token>>
|
92
|
+
|
93
|
+
To verify:
|
94
|
+
|
95
|
+
rtm ls
|
96
|
+
|
97
|
+
This should list your incomplete tasks if all is well.
|
98
|
+
|
99
|
+
rtm any contacts.getList
|
100
|
+
|
101
|
+
Will print out a hashdump of what RTM returns for this method.
|
102
|
+
|
103
|
+
== Links
|
104
|
+
|
105
|
+
* http://github.com/davetron5000/rtm - Source code
|
106
|
+
* http://davetron5000.github.com/rtm - Rubydoc
|
107
|
+
* http://www.rememberthemilk.com/services/api/methods/ - RTM API documentation
|
data/bin/rtm
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
$: << File.expand_path(File.dirname(__FILE__) + '/../lib')
|
3
|
+
$: << File.expand_path(File.dirname(__FILE__) + '/../ext')
|
4
|
+
require 'rubygems'
|
5
|
+
require 'gli'
|
6
|
+
require 'httparty'
|
7
|
+
require 'rtm'
|
8
|
+
require 'hash_array'
|
9
|
+
|
10
|
+
include GLI
|
11
|
+
|
12
|
+
arg_name 'method.call'
|
13
|
+
desc 'executes any RTM method. Do not include the "rtm." in the method name'
|
14
|
+
command :any do |c|
|
15
|
+
c.action do |global_options,options,args|
|
16
|
+
parts = args[0].split(/\./)
|
17
|
+
obj = $rtm.send(parts.shift.to_s)
|
18
|
+
response = obj
|
19
|
+
parts.each do |p|
|
20
|
+
response = response.send(p.to_sym)
|
21
|
+
end
|
22
|
+
puts response.inspect
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
desc 'Lists incomplete tasks'
|
27
|
+
arg_name 'list_name'
|
28
|
+
command :ls do |c|
|
29
|
+
c.action do |global_options,options,args|
|
30
|
+
params = { :filter => 'status:incomplete' }
|
31
|
+
params[:filter] += " AND list:#{args[0]}" if args.length > 0
|
32
|
+
response = $rtm.tasks.get_list(params)
|
33
|
+
tasks = Array.new
|
34
|
+
if response.tasks
|
35
|
+
response.tasks.list.as_array.each do |l|
|
36
|
+
l.taskseries.as_array.each do |task|
|
37
|
+
tasks << task
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
tasks.sort {|a,b| a.name.upcase <=> b.name.upcase }.each do |task|
|
42
|
+
printf "%10d - %s\n",task['id'],task.name
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
desc 'Prints out the auth URL to authorize your api key/ruby client and the frob that was used. Do not do this after getting a token'
|
48
|
+
command :auth do |c|
|
49
|
+
c.action do |global_options,options,args|
|
50
|
+
auth = $rtm.auth
|
51
|
+
puts auth.url
|
52
|
+
puts auth.frob
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
desc 'Gets your token; do this only once and put it in your api.yaml file'
|
57
|
+
arg_name "frob"
|
58
|
+
command :token do |c|
|
59
|
+
c.action do |global_options,options,args|
|
60
|
+
if args.length == 1
|
61
|
+
auth = $rtm.auth
|
62
|
+
auth.frob = args[0]
|
63
|
+
puts "Use frob '#{args[0]}'?"
|
64
|
+
a = $stdin.gets.chomp
|
65
|
+
if a == 'yes'
|
66
|
+
puts auth.get_token
|
67
|
+
else
|
68
|
+
puts "You must provide your frob as the argument"
|
69
|
+
end
|
70
|
+
else
|
71
|
+
puts "You didn't say 'yes'"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
desc 'Checks that your token is valid'
|
77
|
+
command :chktoken do |c|
|
78
|
+
c.action do |global_options,options,args|
|
79
|
+
begin
|
80
|
+
$rtm.check_token
|
81
|
+
puts "Token good"
|
82
|
+
rescue RTM::InvalidTokenException
|
83
|
+
puts "Token bad"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
pre do |global_options,command,options,args|
|
89
|
+
if !command.nil? && command.name != :help
|
90
|
+
return_val = true
|
91
|
+
if File.exists? 'api.yaml'
|
92
|
+
api_info = File.open('api.yaml') { |file| YAML::load(file) }
|
93
|
+
if !(api_info[:api_key] && api_info[:secret])
|
94
|
+
puts "api.yaml isn't complete"
|
95
|
+
return_val = false;
|
96
|
+
else
|
97
|
+
ep = RTM::Endpoint.new(api_info[:api_key],api_info[:secret])
|
98
|
+
ep.token=api_info[:token]
|
99
|
+
$rtm = RTM::RTM.new(ep)
|
100
|
+
end
|
101
|
+
else
|
102
|
+
puts "api.yaml not found; this is a YAML hash of your API key, secret key, and token"
|
103
|
+
File.open('api.yaml','w') { |file| YAML::dump({:api_key => 'your api key', :secret => 'your secret'},file); }
|
104
|
+
puts "it has been created for you with dummy values"
|
105
|
+
return_val = false
|
106
|
+
end
|
107
|
+
return_val
|
108
|
+
else
|
109
|
+
true
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
GLI.run(ARGV)
|
data/ext/hash_array.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
|
2
|
+
# These give Hash and Array a common method that returns an array.
|
3
|
+
# HTTParty doesn't know if a particular XML element is expected to be
|
4
|
+
# a list, so when it gets one of that element, it returns it as a Hash.
|
5
|
+
# So, one can simply call as_array on any part of the XML navigation
|
6
|
+
# that is expected to be an array, as such:
|
7
|
+
#
|
8
|
+
# response['rsp']['tasks']['list'].as_array.each {|a| }
|
9
|
+
#
|
10
|
+
#
|
11
|
+
|
12
|
+
class Hash
|
13
|
+
def as_array; [self]; end
|
14
|
+
|
15
|
+
def method_missing(symbol)
|
16
|
+
return self[symbol.to_s]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class Array
|
21
|
+
def as_array; self; end
|
22
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
|
2
|
+
class String
|
3
|
+
# Stolen from sequel; gives String a camelize method
|
4
|
+
def camelize(first_letter_in_uppercase = :lower)
|
5
|
+
s = gsub(/\/(.?)/){|x| "::#{x[-1..-1].upcase unless x == '/'}"}.gsub(/(^|_)(.)/){|x| x[-1..-1].upcase}
|
6
|
+
s[0...1] = s[0...1].downcase unless first_letter_in_uppercase == :upper
|
7
|
+
s
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
data/lib/rtm.rb
ADDED
data/lib/rtm/auth.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'string_camelize'
|
2
|
+
module RTM
|
3
|
+
|
4
|
+
# Implements authorization related tasks. These are to be used one-time only
|
5
|
+
# when the user first uses your application.
|
6
|
+
#
|
7
|
+
# auth = RTMAuth.new(api_key,secret)
|
8
|
+
# url = auth.get_auth_url
|
9
|
+
# # send user to url
|
10
|
+
# # When they have done so
|
11
|
+
# token = auth.get_token
|
12
|
+
# # object auth no longer needed; use token with RTM
|
13
|
+
class RTMAuth
|
14
|
+
def initialize(endpoint)
|
15
|
+
@endpoint = endpoint
|
16
|
+
end
|
17
|
+
|
18
|
+
# Get the URL to allow the user to authorize the application
|
19
|
+
# [perms] the permissions you wish to get, either :read, :write, or :delete
|
20
|
+
# [application_type] if :desktop, a frob is gotten and the URL is suitable for a desktop application. if :web, a url suitable for the web is returned.
|
21
|
+
# [callback_url] the callback URL you want the user sent to after they authorize. This will have the frob and you must call frob= before get_token with the frob that was given to you.
|
22
|
+
def url(perms = :delete, application_type=:desktop, callback_url=nil)
|
23
|
+
@frob = get_frob if application_type == :desktop
|
24
|
+
params = {'perms' => perms.to_s}
|
25
|
+
params['frob'] = @frob if @frob
|
26
|
+
params['callbackURL'] = callback_url if callback_url
|
27
|
+
@endpoint.url_for(nil,{'frob' => @frob, 'perms' => 'delete'},'auth')
|
28
|
+
end
|
29
|
+
|
30
|
+
# After the user has authorized, gets the token
|
31
|
+
def get_token
|
32
|
+
response = @endpoint.call_method('rtm.auth.getToken', { 'frob' => @frob }, false)
|
33
|
+
response['auth']['token']
|
34
|
+
end
|
35
|
+
alias :getToken :get_token
|
36
|
+
|
37
|
+
def check_token
|
38
|
+
raise "checkToken should be called on the RTM object directly";
|
39
|
+
end
|
40
|
+
alias :checkToken :check_token
|
41
|
+
|
42
|
+
def get_frob
|
43
|
+
response = @endpoint.call_method('rtm.auth.getFrob',nil,false)
|
44
|
+
@frob = response['frob']
|
45
|
+
@frob
|
46
|
+
end
|
47
|
+
alias :getFrob :get_frob
|
48
|
+
|
49
|
+
# After a call to get_frob, this returns the frob that was gotten.
|
50
|
+
def frob
|
51
|
+
@frob
|
52
|
+
end
|
53
|
+
|
54
|
+
def frob=(frob)
|
55
|
+
@frob=frob
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lib/rtm/endpoint.rb
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
|
3
|
+
module RTM
|
4
|
+
# Acts as the endopint to RTM actions, providing a means to separate the API's behavior
|
5
|
+
# with interaction with RTM.
|
6
|
+
class Endpoint
|
7
|
+
NO_TIMELINE = {
|
8
|
+
"rtm.contacts.getList" => true,
|
9
|
+
"rtm.groups.getList" => true,
|
10
|
+
"rtm.lists.getList" => true,
|
11
|
+
"rtm.reflection.getMethodInfo" => true,
|
12
|
+
"rtm.reflection.getMethods" => true,
|
13
|
+
"rtm.settings.getList" => true,
|
14
|
+
"rtm.tasks.getList" => true,
|
15
|
+
"rtm.test.echo" => true,
|
16
|
+
"rtm.test.login" => true,
|
17
|
+
"rtm.time.convert" => true,
|
18
|
+
"rtm.time.parse" => true,
|
19
|
+
"rtm.timelines.create" => true,
|
20
|
+
"rtm.timezones.getList" => true,
|
21
|
+
"rtm.transactions.undo" => true,
|
22
|
+
}
|
23
|
+
BASE_URL = 'http://www.rememberthemilk.com/services/'
|
24
|
+
|
25
|
+
# Create an endpoint to RTM, upon which methods may be called.
|
26
|
+
# [api_key] your api key
|
27
|
+
# [secret] your secret
|
28
|
+
# [http] a class that acts like HTTParty (omit; this is used for testing mostly)
|
29
|
+
def initialize(api_key,secret,http=HTTParty)
|
30
|
+
@api_key = api_key
|
31
|
+
@secret = secret
|
32
|
+
@http=http
|
33
|
+
raise "Cannot work with a secret key" if @secret.nil?
|
34
|
+
raise "Cannot work with a api_key key" if @api_key.nil?
|
35
|
+
end
|
36
|
+
|
37
|
+
def auto_timeline=(auto)
|
38
|
+
@auto_timeline = auto
|
39
|
+
end
|
40
|
+
|
41
|
+
# Update the token used to access this endpoint
|
42
|
+
def token=(token)
|
43
|
+
@token = token
|
44
|
+
end
|
45
|
+
|
46
|
+
def last_timeline
|
47
|
+
@last_timeline
|
48
|
+
end
|
49
|
+
|
50
|
+
# Calls the RTM method with the given parameters
|
51
|
+
# [method] the full RTM method, e.g. rtm.tasks.getList
|
52
|
+
# [params] the parameters to pass, can be symbols. api_key, token, and signature not required
|
53
|
+
# [token_required] if false, this method will not require a token
|
54
|
+
def call_method(method,params={},token_required=true)
|
55
|
+
@last_timeline = nil
|
56
|
+
raise NoTokenException if token_required && (!@token || @token.nil?)
|
57
|
+
params = {} if params.nil?
|
58
|
+
params[:auth_token] = @token if !@token.nil?
|
59
|
+
|
60
|
+
if (@auto_timeline && !NO_TIMELINE[method])
|
61
|
+
response = @http.get(url_for('rtm.timelines.create',{'auth_token' => @token}));
|
62
|
+
if response['timeline']
|
63
|
+
@last_timeline = response['timeline']
|
64
|
+
params[:timeline] = @last_timeline
|
65
|
+
else
|
66
|
+
raise BadResponseException, "Expected a <timeline></timeline> type response, but got: #{response.body}"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
params_no_symbols = Hash.new
|
71
|
+
params.each do |k,v|
|
72
|
+
params_no_symbols[k.to_s] = v
|
73
|
+
end
|
74
|
+
|
75
|
+
response = @http.get(url_for(method,params_no_symbols))
|
76
|
+
verify(response)
|
77
|
+
return response['rsp']
|
78
|
+
end
|
79
|
+
|
80
|
+
# Get the url for a particular call, doing the signing and all that other stuff.
|
81
|
+
#
|
82
|
+
# [method] the RTM method to call
|
83
|
+
# [params] hash of parameters. The +method+, +api_key+, and +api_sig+ parameters should _not_ be included.
|
84
|
+
# [endpoint] the endpoint relate to BASE_URL at which this request should be made.
|
85
|
+
def url_for(method,params={},endpoint='rest')
|
86
|
+
params['api_key'] = @api_key
|
87
|
+
params['method'] = method if method
|
88
|
+
signature = sign(params)
|
89
|
+
url = BASE_URL + endpoint + '/' + params_to_url(params.merge({'api_sig' => signature}))
|
90
|
+
url
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
# quick and dirty way to check the response we got back from RTM.
|
97
|
+
# Call this after every call to RTM. This will verify that you got <rsp> with a "stat='ok'"
|
98
|
+
# and, if not, make a best effort at raising the error message from RTM.
|
99
|
+
#
|
100
|
+
# [response] the response from HTTParty from RTM
|
101
|
+
def verify(response)
|
102
|
+
raise BadResponseException, "No response at all" if !response || response.nil?
|
103
|
+
raise BadResponseException, "Got response with no body" if !response.body
|
104
|
+
raise BadResponseException, "Didn't find an rsp element, response body was #{response.body}" if !response["rsp"]
|
105
|
+
raise BadResponseException, "Didn't find a stat in the <rsp> element, response body was #{response.body}" if !response["rsp"]["stat"]
|
106
|
+
if response['rsp']['stat'] != 'ok'
|
107
|
+
err = response['rsp']['err']
|
108
|
+
if err
|
109
|
+
code = err['code']
|
110
|
+
msg = err['msg']
|
111
|
+
raise VerificationException, "ERROR: Code: #{code}, Message: #{msg}"
|
112
|
+
else
|
113
|
+
raise VerificationException, "Didn't find an <err> tag in the response for stat #{response['rsp']['stat']}, response body was #{response.body}"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# Turns params into a URL
|
119
|
+
def params_to_url(params)
|
120
|
+
string = '?'
|
121
|
+
params.each do |k,v|
|
122
|
+
string += CGI::escape(k)
|
123
|
+
string += '='
|
124
|
+
string += CGI::escape(v)
|
125
|
+
string += '&'
|
126
|
+
end
|
127
|
+
string
|
128
|
+
end
|
129
|
+
|
130
|
+
# Signs the request given the params and secret key
|
131
|
+
#
|
132
|
+
# [params] hash of parameters
|
133
|
+
#
|
134
|
+
def sign(params)
|
135
|
+
raise "Something's wrong; @secret is nil" if @secret.nil?
|
136
|
+
sign_me = @secret
|
137
|
+
params.keys.sort.each do |key|
|
138
|
+
sign_me += key
|
139
|
+
raise "Omit params with nil values; key #{key} was nil" if params[key].nil?
|
140
|
+
sign_me += params[key]
|
141
|
+
end
|
142
|
+
return Digest::MD5.hexdigest(sign_me)
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
end
|
data/lib/rtm/rtm.rb
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'string_camelize'
|
3
|
+
require 'digest/md5'
|
4
|
+
require 'cgi'
|
5
|
+
require 'rtm/auth'
|
6
|
+
require 'rtm/endpoint'
|
7
|
+
|
8
|
+
module RTM
|
9
|
+
|
10
|
+
# Root access to RTM api.
|
11
|
+
# Most methods work just like the api demonstrates, however auth is a bit different:
|
12
|
+
#
|
13
|
+
# rtm = RTM.new(Endpoint.new(api_key,secret))
|
14
|
+
# auth_url = rtm.auth.url
|
15
|
+
# # Send user to url
|
16
|
+
# # When they click authorize and return to your app do:
|
17
|
+
# rtm.token=rtm.auth.get_token
|
18
|
+
#
|
19
|
+
# This is a one time thing, so cache the token somewhere and you are good to go:
|
20
|
+
#
|
21
|
+
# response = rtm.tasks.getList(:filter => 'location:Work and status:completed')
|
22
|
+
# response = rtm.groups.add(:group => 'My New Group')
|
23
|
+
#
|
24
|
+
# If you don't supply timelines, they will be created for you each time as needed.
|
25
|
+
# Also note that you can use Ruby-style method calls instead of filthy camel-case, e.g.
|
26
|
+
#
|
27
|
+
# response = rtm.tasks.get_list(:filter => 'location:Work and status:completed')
|
28
|
+
# # Same as
|
29
|
+
# response = rtm.tasks.getList(:filter => 'location:Work and status:completed')
|
30
|
+
#
|
31
|
+
class RTM
|
32
|
+
|
33
|
+
# Create access to RTM
|
34
|
+
#
|
35
|
+
# [endpoint] an Endpoint to RTM
|
36
|
+
def initialize(endpoint)
|
37
|
+
@endpoint = endpoint
|
38
|
+
end
|
39
|
+
|
40
|
+
# Set the token
|
41
|
+
def token=(token)
|
42
|
+
@endpoint.token=token
|
43
|
+
end
|
44
|
+
|
45
|
+
def auto_timeline=(a)
|
46
|
+
@endpoint.auto_timeline=a
|
47
|
+
end
|
48
|
+
|
49
|
+
# Gets the last timeline that was used if in auto-timeline mode
|
50
|
+
def last_timeline
|
51
|
+
@endpoint.last_timeline
|
52
|
+
end
|
53
|
+
|
54
|
+
# Get the auth method-space
|
55
|
+
def auth
|
56
|
+
RTMAuth.new(@endpoint)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Raises an InvalidTokenException if the token is not valid
|
60
|
+
def check_token
|
61
|
+
begin
|
62
|
+
response = @endpoint.call_method('rtm.auth.checkToken')
|
63
|
+
rescue VerificationException
|
64
|
+
raise InvalidTokenException
|
65
|
+
end
|
66
|
+
end
|
67
|
+
alias :checkToken :check_token
|
68
|
+
|
69
|
+
# Get the test method-space (Kernel defines a test method, making method_missing problematic)
|
70
|
+
def test
|
71
|
+
return RTMMethodSpace.new('test',@endpoint)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Gateway to all other method-spaces. Assumes you are making a valid call
|
75
|
+
# on RTM. Essentially, *any* method will give you an RTMMethodSpace object keyed to the
|
76
|
+
# method name, e.g. rtm.foobar will assume that RTM has a "foobar" methodspace.
|
77
|
+
# method names are converted to camelcase.
|
78
|
+
def method_missing(symbol,*args)
|
79
|
+
if !args || args.empty?
|
80
|
+
rtm_object = symbol.to_s.camelize
|
81
|
+
return RTMMethodSpace.new(rtm_object,@endpoint)
|
82
|
+
else
|
83
|
+
return super(symbol,*args)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
# Generic means of calling an RTM method. This is returned by RTM.method_missing and, in most cases, is
|
90
|
+
# the end point that calls an RTM method.
|
91
|
+
class RTMMethodSpace
|
92
|
+
|
93
|
+
# Create an RTMMethodSpace
|
94
|
+
#
|
95
|
+
# [name] the name of this method space, e.g. 'tasks'
|
96
|
+
# [endpoint] an endpoing to RTM
|
97
|
+
def initialize(name,endpoint)
|
98
|
+
@name = name
|
99
|
+
@endpoint = endpoint
|
100
|
+
raise "No endpoint" if @endpoint.nil?
|
101
|
+
end
|
102
|
+
|
103
|
+
# Calls the method on RTM in most cases. The only exception is if this RTMMethodSpace is 'tasks' and you
|
104
|
+
# call the 'notes' method on it: a new RTMMethodSpace is returned for the 'rtm.tasks.notes' method-space.
|
105
|
+
#
|
106
|
+
# This returns a response object as from HTTParty, dereferenced into <rsp>. So, for example, if you called
|
107
|
+
# the 'tasks.getList' method, you would get a hash that could be accessed via response['tasks'].
|
108
|
+
# This object is a Hash and Array structure that mimcs the XML returned by RTM. One quirk is that for methods that could return 1 or more
|
109
|
+
# of the same item (tasks.getList is a good example; it will return multilple <list> elements unless you restrict by list, in which case
|
110
|
+
# it returns only one <list> element). Because HTTParty doesn't understand this, you may find it convienient to convert such
|
111
|
+
# results to arrays. the to_array extension on Hash and Array accomplish this:
|
112
|
+
#
|
113
|
+
# response = rtm.tasks.getList(:filter => 'list:Work')
|
114
|
+
# response['tasks']['list'].as_array.eadch do |list|
|
115
|
+
# list['taskseries'].as_array.each do |task|
|
116
|
+
# puts task['name']
|
117
|
+
# end
|
118
|
+
# end
|
119
|
+
#
|
120
|
+
# So, call to_array on anything you expect to be a list.
|
121
|
+
#
|
122
|
+
# This method raises either a BadResponseException if you got a bad or garbled response from RTM or a VerificationException
|
123
|
+
# if you got a non-OK response.
|
124
|
+
def method_missing(symbol,*args)
|
125
|
+
if (@name == 'tasks' && symbol.to_s == 'notes')
|
126
|
+
return RTMMethodSpace.new("tasks.notes",@endpoint)
|
127
|
+
else
|
128
|
+
rtm_method = "rtm.#{@name}.#{symbol.to_s.camelize}"
|
129
|
+
@endpoint.call_method(rtm_method,*args)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
class VerificationException < Exception
|
135
|
+
end
|
136
|
+
|
137
|
+
class BadResponseException < Exception
|
138
|
+
end
|
139
|
+
|
140
|
+
class InvalidTokenException < Exception
|
141
|
+
end
|
142
|
+
|
143
|
+
class NoTokenException < Exception
|
144
|
+
end
|
145
|
+
end
|
metadata
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: davetron5000-rtm
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- David Copeland
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-02-25 00:00:00 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: httparty
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.3.1
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: davetron5000-gli
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.1.5
|
34
|
+
version:
|
35
|
+
description:
|
36
|
+
email: davidcopeland@naildrivin5.com
|
37
|
+
executables:
|
38
|
+
- rtm
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files:
|
42
|
+
- README.rdoc
|
43
|
+
files:
|
44
|
+
- ext/hash_array.rb
|
45
|
+
- ext/string_camelize.rb
|
46
|
+
- lib/rtm/auth.rb
|
47
|
+
- lib/rtm/endpoint.rb
|
48
|
+
- lib/rtm/rtm.rb
|
49
|
+
- lib/rtm.rb
|
50
|
+
- bin/rtm
|
51
|
+
- README.rdoc
|
52
|
+
has_rdoc: true
|
53
|
+
homepage: http://davetron5000.github.com/rtm
|
54
|
+
post_install_message:
|
55
|
+
rdoc_options:
|
56
|
+
- --title
|
57
|
+
- Remember The Milk Ruby Client
|
58
|
+
- --main
|
59
|
+
- README.rdoc
|
60
|
+
- -ri
|
61
|
+
require_paths:
|
62
|
+
- lib
|
63
|
+
- ext
|
64
|
+
- lib
|
65
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: "0"
|
70
|
+
version:
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: "0"
|
76
|
+
version:
|
77
|
+
requirements: []
|
78
|
+
|
79
|
+
rubyforge_project:
|
80
|
+
rubygems_version: 1.2.0
|
81
|
+
signing_key:
|
82
|
+
specification_version: 2
|
83
|
+
summary: client to access the Remember The Milk API
|
84
|
+
test_files: []
|
85
|
+
|