rememberthemilk 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
@@ -0,0 +1,35 @@
1
+ NAME
2
+ rememberthemilk.rb
3
+
4
+
5
+ DESCRIPTION
6
+ simple (162 loc), json only, interface to the excellent RememberTheMilk API
7
+
8
+ http://www.rememberthemilk.com/services/api/methods/
9
+
10
+ supports all methods including the complicated frob/auth get token logic
11
+
12
+
13
+ SYNOPSIS
14
+ config = {
15
+ :api_key => api_key,
16
+ :shared_secret => shared_secret,
17
+ :username => username,
18
+ :password => password
19
+ }
20
+
21
+ api = RememberTheMilk.new(config)
22
+
23
+ token = api.get_token!
24
+
25
+ config[:token] = token
26
+
27
+ api = RememberTheMilk.new(config)
28
+
29
+ jj api.ping
30
+
31
+ jj api.call('rtm.tasks.getList')
32
+
33
+ jj api.call('rtm.contacts.getList')
34
+
35
+ # etc
data/Rakefile ADDED
@@ -0,0 +1,371 @@
1
+ This.rubyforge_project = 'codeforpeople'
2
+ This.author = "Ara T. Howard"
3
+ This.email = "ara.t.howard@gmail.com"
4
+ This.homepage = "http://github.com/ahoward/#{ This.lib }/tree/master"
5
+
6
+
7
+ task :default do
8
+ puts((Rake::Task.tasks.map{|task| task.name.gsub(/::/,':')} - ['default']).sort)
9
+ end
10
+
11
+ task :test do
12
+ run_tests!
13
+ end
14
+
15
+ namespace :test do
16
+ task(:unit){ run_tests!(:unit) }
17
+ task(:functional){ run_tests!(:functional) }
18
+ task(:integration){ run_tests!(:integration) }
19
+ end
20
+
21
+ def run_tests!(which = nil)
22
+ which ||= '**'
23
+ test_dir = File.join(This.dir, "test")
24
+ test_glob ||= File.join(test_dir, "#{ which }/**_test.rb")
25
+ test_rbs = Dir.glob(test_glob).sort
26
+
27
+ div = ('=' * 119)
28
+ line = ('-' * 119)
29
+ helper = "-r ./test/helper.rb" if test(?e, "./test/helper.rb")
30
+
31
+ test_rbs.each_with_index do |test_rb, index|
32
+ testno = index + 1
33
+ command = "#{ This.ruby } -I ./lib -I ./test/lib #{ helper } #{ test_rb }"
34
+
35
+ puts
36
+ say(div, :color => :cyan, :bold => true)
37
+ say("@#{ testno } => ", :bold => true, :method => :print)
38
+ say(command, :color => :cyan, :bold => true)
39
+ say(line, :color => :cyan, :bold => true)
40
+
41
+ system(command)
42
+
43
+ say(line, :color => :cyan, :bold => true)
44
+
45
+ status = $?.exitstatus
46
+
47
+ if status.zero?
48
+ say("@#{ testno } <= ", :bold => true, :color => :white, :method => :print)
49
+ say("SUCCESS", :color => :green, :bold => true)
50
+ else
51
+ say("@#{ testno } <= ", :bold => true, :color => :white, :method => :print)
52
+ say("FAILURE", :color => :red, :bold => true)
53
+ end
54
+ say(line, :color => :cyan, :bold => true)
55
+
56
+ exit(status) unless status.zero?
57
+ end
58
+ end
59
+
60
+
61
+ task :gemspec do
62
+ ignore_extensions = 'git', 'svn', 'tmp', /sw./, 'bak', 'gem'
63
+ ignore_directories = 'pkg'
64
+ ignore_files = 'test/log'
65
+
66
+ shiteless =
67
+ lambda do |list|
68
+ list.delete_if do |entry|
69
+ next unless test(?e, entry)
70
+ extension = File.basename(entry).split(%r/[.]/).last
71
+ ignore_extensions.any?{|ext| ext === extension}
72
+ end
73
+ list.delete_if do |entry|
74
+ next unless test(?d, entry)
75
+ dirname = File.expand_path(entry)
76
+ ignore_directories.any?{|dir| File.expand_path(dir) == dirname}
77
+ end
78
+ list.delete_if do |entry|
79
+ next unless test(?f, entry)
80
+ filename = File.expand_path(entry)
81
+ ignore_files.any?{|file| File.expand_path(file) == filename}
82
+ end
83
+ end
84
+
85
+ lib = This.lib
86
+ object = This.object
87
+ version = This.version
88
+ files = shiteless[Dir::glob("**/**")]
89
+ executables = shiteless[Dir::glob("bin/*")].map{|exe| File.basename(exe)}
90
+ has_rdoc = true #File.exist?('doc')
91
+ test_files = "test/#{ lib }.rb" if File.file?("test/#{ lib }.rb")
92
+ summary = object.respond_to?(:summary) ? object.summary : "summary: #{ lib } kicks the ass"
93
+ description = object.respond_to?(:description) ? object.description : "description: #{ lib } kicks the ass"
94
+
95
+ if This.extensions.nil?
96
+ This.extensions = []
97
+ extensions = This.extensions
98
+ %w( Makefile configure extconf.rb ).each do |ext|
99
+ extensions << ext if File.exists?(ext)
100
+ end
101
+ end
102
+ extensions = [extensions].flatten.compact
103
+
104
+ template =
105
+ if test(?e, 'gemspec.erb')
106
+ Template{ IO.read('gemspec.erb') }
107
+ else
108
+ Template {
109
+ <<-__
110
+ ## #{ lib }.gemspec
111
+ #
112
+
113
+ Gem::Specification::new do |spec|
114
+ spec.name = #{ lib.inspect }
115
+ spec.version = #{ version.inspect }
116
+ spec.platform = Gem::Platform::RUBY
117
+ spec.summary = #{ lib.inspect }
118
+ spec.description = #{ description.inspect }
119
+
120
+ spec.files = #{ files.inspect }
121
+ spec.executables = #{ executables.inspect }
122
+
123
+ spec.require_path = "lib"
124
+
125
+ spec.has_rdoc = #{ has_rdoc.inspect }
126
+ spec.test_files = #{ test_files.inspect }
127
+ #spec.add_dependency 'lib', '>= version'
128
+ spec.add_dependency 'fattr'
129
+
130
+ spec.extensions.push(*#{ extensions.inspect })
131
+
132
+ spec.rubyforge_project = #{ This.rubyforge_project.inspect }
133
+ spec.author = #{ This.author.inspect }
134
+ spec.email = #{ This.email.inspect }
135
+ spec.homepage = #{ This.homepage.inspect }
136
+ end
137
+ __
138
+ }
139
+ end
140
+
141
+ Fu.mkdir_p(This.pkgdir)
142
+ gemspec = File.join(This.pkgdir, "#{ lib }.gemspec")
143
+ open(gemspec, "w"){|fd| fd.puts(template)}
144
+ This.gemspec = gemspec
145
+ end
146
+
147
+ task :gem => [:clean, :gemspec] do
148
+ Fu.mkdir_p(This.pkgdir)
149
+ before = Dir['*.gem']
150
+ cmd = "gem build #{ This.gemspec }"
151
+ `#{ cmd }`
152
+ after = Dir['*.gem']
153
+ gem = ((after - before).first || after.first) or abort('no gem!')
154
+ Fu.mv(gem, This.pkgdir)
155
+ This.gem = File.join(This.pkgdir, File.basename(gem))
156
+ end
157
+
158
+ task :readme do
159
+ samples = ''
160
+ prompt = '~ > '
161
+ lib = This.lib
162
+ version = This.version
163
+
164
+ Dir['sample*/*'].sort.each do |sample|
165
+ samples << "\n" << " <========< #{ sample } >========>" << "\n\n"
166
+
167
+ cmd = "cat #{ sample }"
168
+ samples << Util.indent(prompt + cmd, 2) << "\n\n"
169
+ samples << Util.indent(`#{ cmd }`, 4) << "\n"
170
+
171
+ cmd = "ruby #{ sample }"
172
+ samples << Util.indent(prompt + cmd, 2) << "\n\n"
173
+
174
+ cmd = "ruby -e'STDOUT.sync=true; exec %(ruby -I ./lib #{ sample })'"
175
+ samples << Util.indent(`#{ cmd } 2>&1`, 4) << "\n"
176
+ end
177
+
178
+ template =
179
+ if test(?e, 'readme.erb')
180
+ Template{ IO.read('readme.erb') }
181
+ else
182
+ Template {
183
+ <<-__
184
+ NAME
185
+ #{ lib }
186
+
187
+ DESCRIPTION
188
+
189
+ INSTALL
190
+ gem install #{ lib }
191
+
192
+ SAMPLES
193
+ #{ samples }
194
+ __
195
+ }
196
+ end
197
+
198
+ open("README", "w"){|fd| fd.puts template}
199
+ end
200
+
201
+
202
+ task :clean do
203
+ Dir[File.join(This.pkgdir, '**/**')].each{|entry| Fu.rm_rf(entry)}
204
+ end
205
+
206
+
207
+ task :release => [:clean, :gemspec, :gem] do
208
+ gems = Dir[File.join(This.pkgdir, '*.gem')].flatten
209
+ raise "which one? : #{ gems.inspect }" if gems.size > 1
210
+ raise "no gems?" if gems.size < 1
211
+
212
+ cmd = "gem push #{ This.gem }"
213
+ puts cmd
214
+ puts
215
+ system(cmd)
216
+ abort("cmd(#{ cmd }) failed with (#{ $?.inspect })") unless $?.exitstatus.zero?
217
+
218
+ cmd = "rubyforge login && rubyforge add_release #{ This.rubyforge_project } #{ This.lib } #{ This.version } #{ This.gem }"
219
+ puts cmd
220
+ puts
221
+ system(cmd)
222
+ abort("cmd(#{ cmd }) failed with (#{ $?.inspect })") unless $?.exitstatus.zero?
223
+ end
224
+
225
+
226
+
227
+
228
+
229
+ BEGIN {
230
+ # support for this rakefile
231
+ #
232
+ $VERBOSE = nil
233
+
234
+ require 'ostruct'
235
+ require 'erb'
236
+ require 'fileutils'
237
+ require 'rbconfig'
238
+
239
+ # fu shortcut
240
+ #
241
+ Fu = FileUtils
242
+
243
+ # cache a bunch of stuff about this rakefile/environment
244
+ #
245
+ This = OpenStruct.new
246
+
247
+ This.file = File.expand_path(__FILE__)
248
+ This.dir = File.dirname(This.file)
249
+ This.pkgdir = File.join(This.dir, 'pkg')
250
+
251
+ # grok lib
252
+ #
253
+ lib = ENV['LIB']
254
+ unless lib
255
+ lib = File.basename(Dir.pwd).sub(/[-].*$/, '')
256
+ end
257
+ This.lib = lib
258
+
259
+ # grok version
260
+ #
261
+ version = ENV['VERSION']
262
+ unless version
263
+ require "./lib/#{ This.lib }"
264
+ This.name = lib.capitalize
265
+ This.object = eval(This.name)
266
+ version = This.object.send(:version)
267
+ end
268
+ This.version = version
269
+
270
+ # we need to know the name of the lib an it's version
271
+ #
272
+ abort('no lib') unless This.lib
273
+ abort('no version') unless This.version
274
+
275
+ # discover full path to this ruby executable
276
+ #
277
+ c = Config::CONFIG
278
+ bindir = c["bindir"] || c['BINDIR']
279
+ ruby_install_name = c['ruby_install_name'] || c['RUBY_INSTALL_NAME'] || 'ruby'
280
+ ruby_ext = c['EXEEXT'] || ''
281
+ ruby = File.join(bindir, (ruby_install_name + ruby_ext))
282
+ This.ruby = ruby
283
+
284
+ # some utils
285
+ #
286
+ module Util
287
+ def indent(s, n = 2)
288
+ s = unindent(s)
289
+ ws = ' ' * n
290
+ s.gsub(%r/^/, ws)
291
+ end
292
+
293
+ def unindent(s)
294
+ indent = nil
295
+ s.each do |line|
296
+ next if line =~ %r/^\s*$/
297
+ indent = line[%r/^\s*/] and break
298
+ end
299
+ indent ? s.gsub(%r/^#{ indent }/, "") : s
300
+ end
301
+ extend self
302
+ end
303
+
304
+ # template support
305
+ #
306
+ class Template
307
+ def initialize(&block)
308
+ @block = block
309
+ @template = block.call.to_s
310
+ end
311
+ def expand(b=nil)
312
+ ERB.new(Util.unindent(@template)).result(b||@block)
313
+ end
314
+ alias_method 'to_s', 'expand'
315
+ end
316
+ def Template(*args, &block) Template.new(*args, &block) end
317
+
318
+ # colored console output support
319
+ #
320
+ This.ansi = {
321
+ :clear => "\e[0m",
322
+ :reset => "\e[0m",
323
+ :erase_line => "\e[K",
324
+ :erase_char => "\e[P",
325
+ :bold => "\e[1m",
326
+ :dark => "\e[2m",
327
+ :underline => "\e[4m",
328
+ :underscore => "\e[4m",
329
+ :blink => "\e[5m",
330
+ :reverse => "\e[7m",
331
+ :concealed => "\e[8m",
332
+ :black => "\e[30m",
333
+ :red => "\e[31m",
334
+ :green => "\e[32m",
335
+ :yellow => "\e[33m",
336
+ :blue => "\e[34m",
337
+ :magenta => "\e[35m",
338
+ :cyan => "\e[36m",
339
+ :white => "\e[37m",
340
+ :on_black => "\e[40m",
341
+ :on_red => "\e[41m",
342
+ :on_green => "\e[42m",
343
+ :on_yellow => "\e[43m",
344
+ :on_blue => "\e[44m",
345
+ :on_magenta => "\e[45m",
346
+ :on_cyan => "\e[46m",
347
+ :on_white => "\e[47m"
348
+ }
349
+ def say(phrase, *args)
350
+ options = args.last.is_a?(Hash) ? args.pop : {}
351
+ options[:color] = args.shift.to_s.to_sym unless args.empty?
352
+ keys = options.keys
353
+ keys.each{|key| options[key.to_s.to_sym] = options.delete(key)}
354
+
355
+ color = options[:color]
356
+ bold = options.has_key?(:bold)
357
+
358
+ parts = [phrase]
359
+ parts.unshift(This.ansi[color]) if color
360
+ parts.unshift(This.ansi[:bold]) if bold
361
+ parts.push(This.ansi[:clear]) if parts.size > 1
362
+
363
+ method = options[:method] || :puts
364
+
365
+ Kernel.send(method, parts.join)
366
+ end
367
+
368
+ # always run out of the project dir
369
+ #
370
+ Dir.chdir(This.dir)
371
+ }
@@ -0,0 +1,67 @@
1
+ module RememberTheMilk
2
+ Version = '0.0.1' unless defined?(Version)
3
+ Load = Kernel.method(:load) unless defined?(Load)
4
+
5
+ def version
6
+ RememberTheMilk::Version
7
+ end
8
+
9
+ def libdir(*args, &block)
10
+ @libdir ||= File.expand_path(__FILE__).sub(/\.rb$/,'')
11
+ libdir = args.empty? ? @libdir : File.join(@libdir, *args.map{|arg| arg.to_s})
12
+ ensure
13
+ if block
14
+ begin
15
+ $LOAD_PATH.unshift(libdir)
16
+ RememberTheMilk.send(:module_eval, &block)
17
+ ensure
18
+ $LOAD_PATH.shift()
19
+ end
20
+ end
21
+ end
22
+
23
+ def load(*args, &block)
24
+ Load.call(*args, &block)
25
+ end
26
+
27
+ def new(*args, &block)
28
+ Api.new(*args, &block)
29
+ end
30
+
31
+ extend self
32
+ end
33
+
34
+ RTM = RememberTheMilk unless defined?(RTM)
35
+ Rememberthemilk = RememberTheMilk unless defined?(Rememberthemilk)
36
+
37
+ require 'md5'
38
+ require 'cgi'
39
+ require 'net/http'
40
+
41
+ begin
42
+ require 'rubygems'
43
+ rescue LoadError
44
+ nil
45
+ end
46
+
47
+ require 'json' unless defined?(JSON)
48
+ require 'options' unless defined?(Options)
49
+ require 'orderedhash' unless defined?(OrderedHash)
50
+ require 'tzinfo' unless defined?(TZInfo)
51
+
52
+
53
+ RememberTheMilk.libdir do
54
+ load 'error.rb'
55
+ load 'api.rb'
56
+ end
57
+
58
+
59
+ if __FILE__ == $0
60
+ options = YAML.load(IO.read(File.expand_path('~/.rtm.yml')))
61
+ api = RememberTheMilk::Api.new(options)
62
+ #p api.ping
63
+ #p api.call('rtm.tasks.getList')
64
+ #p api.get_token!
65
+ #p api.call('rtm.settings.getList')
66
+ #p api.localtime(utc)
67
+ end
@@ -0,0 +1,133 @@
1
+ module RememberTheMilk
2
+ class Api
3
+ Host = 'www.rememberthemilk.com' unless defined?(Host)
4
+ Url = "http://#{ Host }" unless defined?(Url)
5
+
6
+ class << Api
7
+ def url_for(*args)
8
+ options = args.options.pop
9
+
10
+ service = options[:service] || :rest
11
+ url = "/services/#{ service }"
12
+ url += args.flatten.compact.join('/') unless args.empty?
13
+ url.squeeze('/')
14
+ url.chomp!('/')
15
+ url += '/'
16
+
17
+ query = options[:query] || {}
18
+ unless query.empty?
19
+ url = url + '?' + query_string_for(query)
20
+ end
21
+
22
+ if options[:absolute]
23
+ url = Url + url
24
+ end
25
+
26
+ url
27
+ end
28
+
29
+ def query_string_for(query)
30
+ query.to_a.map{|k,v| [escape(k), escape(v)].join('=')}.join('&')
31
+ end
32
+
33
+ def escape(val)
34
+ CGI::escape(val.to_s).gsub(/ /, '+')
35
+ end
36
+ end
37
+
38
+ attr_accessor :api_key
39
+ attr_accessor :shared_secret
40
+ attr_accessor :frob
41
+ attr_accessor :token
42
+ attr_accessor :username
43
+ attr_accessor :password
44
+
45
+ def initialize(*args, &block)
46
+ options = args.options
47
+ @api_key = options[:api_key]
48
+ @shared_secret = options[:shared_secret]
49
+ @frob = options[:frob]
50
+ @token = options[:token]
51
+ @username = options[:username]
52
+ @password = options[:password]
53
+ end
54
+
55
+ def apply_for_an_api_key
56
+ url = 'http://www.rememberthemilk.com/services/api/keys.rtm'
57
+ puts(url)
58
+ open(url)
59
+ end
60
+
61
+ def api_sig_for(query)
62
+ kvs = query.to_a.map{|k,v| [k.to_s, v.to_s]}.sort
63
+ MD5.md5(@shared_secret + kvs.join).to_s
64
+ end
65
+
66
+ def ping
67
+ call('rtm.test.echo')
68
+ end
69
+
70
+ def call(method, *args, &block)
71
+ options = args.options
72
+
73
+ query = Hash.new
74
+ query['method'] = method unless method.to_s.empty?
75
+ query['api_key'] = @api_key
76
+ query['auth_token'] = @token
77
+ query['format'] = 'json'
78
+ options.each do |key, val|
79
+ query[key] = val
80
+ end
81
+ query['api_sig'] = api_sig_for(query)
82
+
83
+ url = Api.url_for(:query => query, :service => 'rest')
84
+ response = Net::HTTP.get_response(Host, url)
85
+
86
+ json = JSON.parse(response.body)
87
+
88
+ rsp = json['rsp']
89
+
90
+ if rsp['stat'] != 'ok'
91
+ raise(Error, "#{ method }(#{ args.inspect }) @ #{ url } #=> #{ rsp.inspect }")
92
+ end
93
+
94
+ rsp
95
+ end
96
+
97
+ def localtime(time)
98
+ time = Time.parse(time.to_s) unless time.is_a?(Time)
99
+ @settings ||= call('rtm.settings.getList')
100
+ @timezone = TZInfo::Timezone.get(@settings['settings']['timezone'])
101
+ @timezone.utc_to_local(time.utc)
102
+ end
103
+
104
+ # see: http://www.rememberthemilk.com/services/api/authentication.rtm
105
+ #
106
+ def get_token!
107
+ require 'mechanize'
108
+ require 'pp'
109
+ frob = get_frob
110
+ query = Hash.new
111
+ query[:frob] = frob
112
+ query[:api_key] = @api_key
113
+ query[:perms] = 'delete'
114
+ query['api_sig'] = api_sig_for(query)
115
+ url = Api.url_for(:query => query, :service => :auth, :absolute => true)
116
+ response = Net::HTTP.get_response(Host, url)
117
+ location = response.header['Location']
118
+ m = Mechanize.new
119
+ page = m.get(url)
120
+ form = page.forms.first
121
+ form['username'] = @username
122
+ form['password'] = @password
123
+ page = form.submit
124
+ rsp = call('rtm.auth.getToken', :frob => frob)
125
+ token = rsp['auth']['token']
126
+ end
127
+
128
+ def get_frob
129
+ rsp = call('rtm.auth.getFrob')
130
+ rsp['frob']
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,4 @@
1
+ module RememberTheMilk
2
+ class Error < ::StandardError
3
+ end
4
+ end
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rememberthemilk
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - Ara T. Howard
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-09-13 00:00:00 -06:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: fattr
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ version: "0"
31
+ type: :runtime
32
+ version_requirements: *id001
33
+ description: "description: rememberthemilk kicks the ass"
34
+ email: ara.t.howard@gmail.com
35
+ executables: []
36
+
37
+ extensions: []
38
+
39
+ extra_rdoc_files: []
40
+
41
+ files:
42
+ - lib/rememberthemilk/api.rb
43
+ - lib/rememberthemilk/error.rb
44
+ - lib/rememberthemilk.rb
45
+ - Rakefile
46
+ - README
47
+ has_rdoc: true
48
+ homepage: http://github.com/ahoward/rememberthemilk/tree/master
49
+ licenses: []
50
+
51
+ post_install_message:
52
+ rdoc_options: []
53
+
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ segments:
62
+ - 0
63
+ version: "0"
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ segments:
70
+ - 0
71
+ version: "0"
72
+ requirements: []
73
+
74
+ rubyforge_project: codeforpeople
75
+ rubygems_version: 1.3.7
76
+ signing_key:
77
+ specification_version: 3
78
+ summary: rememberthemilk
79
+ test_files: []
80
+