moocow 0.1.3

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/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
+