moocow 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
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
+
@@ -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
@@ -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
@@ -0,0 +1,145 @@
1
+ require 'rubygems'
2
+ require 'string_camelize'
3
+ require 'digest/md5'
4
+ require 'cgi'
5
+ require 'moocow/auth'
6
+ require 'moocow/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.each 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
data/lib/moocow.rb ADDED
@@ -0,0 +1,5 @@
1
+ require 'moocow/moocow'
2
+ require 'moocow/auth'
3
+ require 'moocow/endpoint'
4
+ require 'hash_array'
5
+ require 'string_camelize'
metadata ADDED
@@ -0,0 +1,85 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: moocow
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.3
5
+ platform: ruby
6
+ authors:
7
+ - David Copeland
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-04-22 00:00:00 -04: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: 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/moocow/auth.rb
47
+ - lib/moocow/endpoint.rb
48
+ - lib/moocow/moocow.rb
49
+ - lib/moocow.rb
50
+ - bin/rtm
51
+ - README.rdoc
52
+ has_rdoc: true
53
+ homepage: http://davetron5000.github.com/moocow
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: moocow
80
+ rubygems_version: 1.3.1
81
+ signing_key:
82
+ specification_version: 2
83
+ summary: Ruby Client for Remember The Milk
84
+ test_files: []
85
+