teambox-client 0.1.1

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 ADDED
@@ -0,0 +1,2 @@
1
+ 0.0.1 - June 19, 2010
2
+ * Initial release, inspired by @jnunemaker's Twitter gem
data/License ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Pablo Villalba, John Nunemaker
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Notes.md ADDED
@@ -0,0 +1,32 @@
1
+ Status codes
2
+ ==============================================================================
3
+
4
+ http://apiwiki.twitter.com/REST+API+Documentation
5
+
6
+ 200 OK: everything went awesome.
7
+ 304 Not Modified: there was no new data to return.
8
+ 400 Bad Request: your request is invalid, and we'll return an error message that tells you why. This is the status code returned if you've exceeded the rate limit (see below).
9
+ 401 Not Authorized: either you need to provide authentication credentials, or the credentials provided aren't valid.
10
+ 403 Forbidden: we understand your request, but are refusing to fulfill it. An accompanying error message should explain why.
11
+ 404 Not Found: either you're requesting an invalid URI or the resource in question doesn't exist (ex: no such user).
12
+ 500 Internal Server Error: we did something wrong. Please post to the group about it and the Twitter team will investigate.
13
+ 502 Bad Gateway: returned if Twitter is down or being upgraded.
14
+ 503 Service Unavailable: the Twitter servers are up, but are overloaded with requests. Try again later.
15
+
16
+ Errors
17
+ -------------------------------------------------------------------------------
18
+
19
+ An example would be:
20
+
21
+ <?xml version="1.0" encoding="UTF-8"?>
22
+ <hash>
23
+ <request>/direct_messages/destroy/456.xml</request>
24
+ <error>No direct message with that ID found.</error>
25
+ </hash>
26
+
27
+ Rate Limit Headers
28
+ -------------------------------------------------------------------------------
29
+
30
+ X-RateLimit-Limit the current limit in effect
31
+ X-RateLimit-Remaining the number of hits remaining before you are rate limited
32
+ X-RateLimit-Reset the time the current rate limiting period ends (in epoch time, number of seconds since 1970-01-01 00:00:00)
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+
2
+ This is a Ruby wrapper for Teambox API.
3
+
4
+ Largely inspired by @jnunemaker's Twitter gem.
5
+ This is currently in development, and is missing many methods.
6
+
7
+ Get started
8
+ -------------------------------------------------------------------------------
9
+
10
+ First, install the *teambox-client* gem:
11
+
12
+ gem install teambox-client
13
+
14
+ Now, run `irb -rubygems` and this snippet to get the list of activities:
15
+
16
+ require 'teambox-client'
17
+ httpauth = Teambox::HTTPAuth.new(your_username, your_password)
18
+ client = Teambox::Base.new(httpauth)
19
+ puts client.activities
20
+
21
+ Examples
22
+ -------------------------------------------------------------------------------
23
+
24
+ First, create a file on $HOME/.teambox with these values:
25
+
26
+ username: your_teambox_username_or_email
27
+ password: password
28
+
29
+ See the examples directory.
data/Rakefile ADDED
@@ -0,0 +1,46 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "teambox-client"
8
+ gem.summary = "A ruby gem wrapper for Teambox API"
9
+ gem.description = "Provides methods to read and write to Teambox for ruby apps"
10
+ gem.email = "pablo@teambox.com"
11
+ gem.homepage = "http://github.com/micho/teambox-ruby"
12
+ gem.authors = ["Pablo Villalba", "John Nunemaker"]
13
+
14
+ gem.add_dependency("hashie", "~> 0.2.0")
15
+ gem.add_dependency("httparty", "~> 0.5.0")
16
+ gem.add_dependency("yajl-ruby", "~> 0.7.0")
17
+
18
+ #gem.add_development_dependency ""
19
+ end
20
+ rescue LoadError
21
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
22
+ end
23
+
24
+ require 'rake/testtask'
25
+ Rake::TestTask.new(:test) do |test|
26
+ test.libs << 'lib' << 'test'
27
+ test.pattern = 'test/**/*_test.rb'
28
+ test.verbose = true
29
+ end
30
+
31
+ task :test => :check_dependencies
32
+ task :default => :test
33
+
34
+ require 'rake/rdoctask'
35
+ Rake::RDocTask.new do |rdoc|
36
+ if File.exist?('VERSION')
37
+ version = File.read('VERSION')
38
+ else
39
+ version = ""
40
+ end
41
+
42
+ rdoc.rdoc_dir = 'rdoc'
43
+ rdoc.title = "teambox-ruby #{version}"
44
+ rdoc.rdoc_files.include('README*')
45
+ rdoc.rdoc_files.include('lib/**/*.rb')
46
+ end
data/TODO.example.rb ADDED
@@ -0,0 +1,327 @@
1
+ # This are methods that we didn't implement on Teambox, but sound like good ideas
2
+ # or reference methods
3
+
4
+ def friendship_destroy(id)
5
+ perform_post("/friendships/destroy/#{id}.json")
6
+ end
7
+
8
+ def verify_credentials
9
+ perform_get("/account/verify_credentials.json")
10
+ end
11
+
12
+ def users(*ids_or_usernames)
13
+ ids, usernames = [], []
14
+ ids_or_usernames.each do |id_or_username|
15
+ if id_or_username.is_a?(Integer)
16
+ ids << id_or_username
17
+ elsif id_or_username.is_a?(String)
18
+ usernames << id_or_username
19
+ end
20
+ end
21
+ query = {}
22
+ query[:user_id] = ids.join(",") unless ids.empty?
23
+ query[:screen_name] = usernames.join(",") unless usernames.empty?
24
+ perform_get("/users/lookup.json", :query => query)
25
+ end
26
+
27
+ def direct_message_destroy(id)
28
+ perform_post("/direct_messages/destroy/#{id}.json")
29
+ end
30
+
31
+ # Options: id, user_id, screen_name
32
+ # Could be adapted to Teambox as "people in my projects"
33
+ def friend_ids(query={})
34
+ perform_get("/friends/ids.json", :query => query)
35
+ end
36
+
37
+ # Options: in_reply_to_status_id
38
+ def update(status, query={})
39
+ perform_post("/statuses/update.json", :body => {:status => status}.merge(query))
40
+ end
41
+
42
+ # Options: since_id, max_id, count, page
43
+ # options: count, page, ids_only
44
+ # Options: id, user_id, screen_name, page
45
+
46
+ # :per_page = max number of statues to get at once
47
+ # :page = which page of tweets you wish to get
48
+ def list_timeline(list_owner_username, slug, query = {})
49
+ perform_get("/#{list_owner_username}/lists/#{slug}/statuses.json", :query => query)
50
+ end
51
+
52
+ def list_create(list_owner_username, options)
53
+ perform_post("/#{list_owner_username}/lists.json", :body => {:user => list_owner_username}.merge(options))
54
+ end
55
+
56
+ def enable_notifications(id)
57
+ perform_post("/notifications/follow/#{id}.json")
58
+ end
59
+
60
+ def disable_notifications(id)
61
+ perform_post("/notifications/leave/#{id}.json")
62
+ end
63
+
64
+
65
+ def rate_limit_status
66
+ perform_get("/account/rate_limit_status.json")
67
+ end
68
+
69
+ # One or more of the following must be present:
70
+ # name, email, url, location, description
71
+ def update_profile(body={})
72
+ perform_post("/account/update_profile.json", :body => body)
73
+ end
74
+
75
+
76
+
77
+
78
+
79
+
80
+
81
+
82
+
83
+
84
+ require 'pp'
85
+ module Teambox
86
+ class Search
87
+ include HTTParty
88
+ include Enumerable
89
+ base_uri "search.teambox.com/search"
90
+ format :json
91
+
92
+ attr_reader :result, :query
93
+
94
+ def initialize(q=nil, options={})
95
+ @options = options
96
+ clear
97
+ containing(q) if q && q.strip != ""
98
+ endpoint_url = options[:api_endpoint]
99
+ endpoint_url = "#{endpoint_url}/search" if endpoint_url && !endpoint_url.include?("/search")
100
+ self.class.base_uri(endpoint_url) if endpoint_url
101
+ end
102
+
103
+ def user_agent
104
+ @options[:user_agent] || "Ruby Teambox Gem"
105
+ end
106
+
107
+ def from(user, exclude=false)
108
+ @query[:q] << "#{exclude ? "-" : ""}from:#{user}"
109
+ self
110
+ end
111
+
112
+ def to(user, exclude=false)
113
+ @query[:q] << "#{exclude ? "-" : ""}to:#{user}"
114
+ self
115
+ end
116
+
117
+ def referencing(user, exclude=false)
118
+ @query[:q] << "#{exclude ? "-" : ""}@#{user}"
119
+ self
120
+ end
121
+ alias :references :referencing
122
+ alias :ref :referencing
123
+
124
+ def containing(word, exclude=false)
125
+ @query[:q] << "#{exclude ? "-" : ""}#{word}"
126
+ self
127
+ end
128
+ alias :contains :containing
129
+
130
+ # adds filtering based on hash tag ie: #teambox
131
+ def hashed(tag, exclude=false)
132
+ @query[:q] << "#{exclude ? "-" : ""}\##{tag}"
133
+ self
134
+ end
135
+
136
+ # Search for a phrase instead of a group of words
137
+ def phrase(phrase)
138
+ @query[:phrase] = phrase
139
+ self
140
+ end
141
+
142
+ # lang must be ISO 639-1 code ie: en, fr, de, ja, etc.
143
+ #
144
+ # when I tried en it limited my results a lot and took
145
+ # out several tweets that were english so i'd avoid
146
+ # this unless you really want it
147
+ def lang(lang)
148
+ @query[:lang] = lang
149
+ self
150
+ end
151
+
152
+ # popular|recent
153
+ def result_type(result_type)
154
+ @query[:result_type] = result_type
155
+ self
156
+ end
157
+
158
+ # Limits the number of results per page
159
+ def per_page(num)
160
+ @query[:rpp] = num
161
+ self
162
+ end
163
+
164
+ # Which page of results to fetch
165
+ def page(num)
166
+ @query[:page] = num
167
+ self
168
+ end
169
+
170
+ # Only searches tweets since a given id.
171
+ # Recommended to use this when possible.
172
+ def since(since_id)
173
+ @query[:since_id] = since_id
174
+ self
175
+ end
176
+
177
+ # From the advanced search form, not documented in the API
178
+ # Format YYYY-MM-DD
179
+ def since_date(since_date)
180
+ @query[:since] = since_date
181
+ self
182
+ end
183
+
184
+ # From the advanced search form, not documented in the API
185
+ # Format YYYY-MM-DD
186
+ def until_date(until_date)
187
+ @query[:until] = until_date
188
+ self
189
+ end
190
+
191
+ # Ranges like 25km and 50mi work.
192
+ def geocode(lat, long, range)
193
+ @query[:geocode] = [lat, long, range].join(",")
194
+ self
195
+ end
196
+
197
+ def max(id)
198
+ @query[:max_id] = id
199
+ self
200
+ end
201
+
202
+ # Clears all the query filters to make a new search
203
+ def clear
204
+ @fetch = nil
205
+ @query = {}
206
+ @query[:q] = []
207
+ self
208
+ end
209
+
210
+ def fetch(force=false)
211
+ if @fetch.nil? || force
212
+ query = @query.dup
213
+ query[:q] = query[:q].join(" ")
214
+ perform_get(query)
215
+ end
216
+
217
+ @fetch
218
+ end
219
+
220
+ def each
221
+ results = fetch()['results']
222
+ return if results.nil?
223
+ results.each {|r| yield r}
224
+ end
225
+
226
+ def next_page?
227
+ !!fetch()["next_page"]
228
+ end
229
+
230
+ def fetch_next_page
231
+ if next_page?
232
+ s = Search.new(nil, :user_agent => user_agent)
233
+ s.perform_get(fetch()["next_page"][1..-1])
234
+ s
235
+ end
236
+ end
237
+
238
+ protected
239
+
240
+ def perform_get(query)
241
+ response = self.class.get("#{self.class.base_uri}.json", :query => query, :format => :json, :headers => {"User-Agent" => user_agent})
242
+ @fetch = Teambox.mash(response)
243
+ end
244
+
245
+ end
246
+ end
247
+
248
+
249
+
250
+
251
+
252
+
253
+
254
+
255
+
256
+
257
+
258
+
259
+
260
+
261
+
262
+
263
+
264
+ module Teambox
265
+ class OAuth
266
+ extend Forwardable
267
+
268
+ def_delegators :access_token, :get, :post, :put, :delete
269
+
270
+ attr_reader :ctoken, :csecret, :consumer_options, :api_endpoint, :signing_endpoint
271
+
272
+ # Options
273
+ # :sign_in => true to just sign in with teambox instead of doing oauth authorization
274
+ # (http://apiwiki.teambox.com/Sign-in-with-Teambox)
275
+ def initialize(ctoken, csecret, options={})
276
+ @ctoken, @csecret, @consumer_options = ctoken, csecret, {}
277
+ @api_endpoint = options[:api_endpoint] || 'http://api.teambox.com'
278
+ @signing_endpoint = options[:signing_endpoint] || 'http://api.teambox.com'
279
+ if options[:sign_in]
280
+ @consumer_options[:authorize_path] = '/oauth/authenticate'
281
+ end
282
+ end
283
+
284
+ def consumer
285
+ @consumer ||= ::OAuth::Consumer.new(@ctoken, @csecret, {:site => api_endpoint}.merge(consumer_options))
286
+ end
287
+
288
+ def signing_consumer
289
+ @signing_consumer ||= ::OAuth::Consumer.new(@ctoken, @csecret, {:site => signing_endpoint, :request_endpoint => api_endpoint }.merge(consumer_options))
290
+ end
291
+
292
+ def set_callback_url(url)
293
+ clear_request_token
294
+ request_token(:oauth_callback => url)
295
+ end
296
+
297
+ # Note: If using oauth with a web app, be sure to provide :oauth_callback.
298
+ # Options:
299
+ # :oauth_callback => String, url that teambox should redirect to
300
+ def request_token(options={})
301
+ @request_token ||= signing_consumer.get_request_token(options)
302
+ end
303
+
304
+ # For web apps use params[:oauth_verifier], for desktop apps,
305
+ # use the verifier is the pin that teambox gives users.
306
+ def authorize_from_request(rtoken, rsecret, verifier_or_pin)
307
+ request_token = ::OAuth::RequestToken.new(signing_consumer, rtoken, rsecret)
308
+ access_token = request_token.get_access_token(:oauth_verifier => verifier_or_pin)
309
+ @atoken, @asecret = access_token.token, access_token.secret
310
+ end
311
+
312
+ def access_token
313
+ @access_token ||= ::OAuth::AccessToken.new(signing_consumer, @atoken, @asecret)
314
+ end
315
+
316
+ def authorize_from_access(atoken, asecret)
317
+ @atoken, @asecret = atoken, asecret
318
+ end
319
+
320
+ private
321
+
322
+ def clear_request_token
323
+ @request_token = nil
324
+ end
325
+
326
+ end
327
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.1
@@ -0,0 +1,24 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'teambox')
2
+ require File.join(File.dirname(__FILE__), 'helpers', 'config_store')
3
+ require 'pp'
4
+
5
+ config = ConfigStore.new("#{ENV['HOME']}/.teambox")
6
+ httpauth = Teambox::HTTPAuth.new(config['username'], config['password'])
7
+
8
+ client = Teambox::Base.new(httpauth)
9
+
10
+ client.activities.each do |activity|
11
+ next unless activity["target"]
12
+ case activity["target"]["type"]
13
+ when "Comment"
14
+ pp activity["target"]["comment"]["body"]
15
+ end
16
+ end
17
+
18
+ client.activities_from_project("teambox").each do |activity|
19
+ next unless activity["target"]
20
+ case activity["target"]["type"]
21
+ when "Comment"
22
+ pp activity["target"]["comment"]["body"]
23
+ end
24
+ end
@@ -0,0 +1,38 @@
1
+ class ConfigStore
2
+ attr_reader :file
3
+
4
+ def initialize(file)
5
+ @file = file
6
+ end
7
+
8
+ def load
9
+ @config ||= YAML::load(open(file))
10
+ self
11
+ end
12
+
13
+ def [](key)
14
+ load
15
+ @config[key]
16
+ end
17
+
18
+ def []=(key, value)
19
+ @config[key] = value
20
+ end
21
+
22
+ def delete(*keys)
23
+ keys.each { |key| @config.delete(key) }
24
+ save
25
+ self
26
+ end
27
+
28
+ def update(c={})
29
+ @config.merge!(c)
30
+ save
31
+ self
32
+ end
33
+
34
+ def save
35
+ File.open(file, 'w') { |f| f.write(YAML.dump(@config)) }
36
+ self
37
+ end
38
+ end
data/examples/users.rb ADDED
@@ -0,0 +1,10 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'teambox')
2
+ require File.join(File.dirname(__FILE__), 'helpers', 'config_store')
3
+ require 'pp'
4
+
5
+ httpauth = Teambox::HTTPAuth.new(TEAMBOX_USERNAME, TEAMBOX_PASSWORD)
6
+ client = Teambox::Base.new(httpauth)
7
+
8
+ pp client.user("pablo")
9
+
10
+ # TODO: Get a non-existant user
@@ -0,0 +1,84 @@
1
+ module Teambox
2
+ class Base
3
+ extend Forwardable
4
+
5
+ def_delegators :client, :get, :post, :put, :delete
6
+
7
+ attr_reader :client
8
+
9
+ def initialize(client)
10
+ @client = client
11
+ end
12
+
13
+ # Teambox method
14
+ def activities(query={})
15
+ perform_get("/activities.json", :query => query)[:activities]
16
+ end
17
+
18
+ def activities_from_project(project_id, query={})
19
+ perform_get("/projects/#{project_id}/activities.json", :query => query)[:activities]
20
+ end
21
+
22
+ def user(id, query={})
23
+ perform_get("/users/#{id}.json", :query => query)[:user]
24
+ end
25
+
26
+ protected
27
+
28
+ def self.mime_type(file)
29
+ case
30
+ when file =~ /\.jpg/ then 'image/jpg'
31
+ when file =~ /\.gif$/ then 'image/gif'
32
+ when file =~ /\.png$/ then 'image/png'
33
+ else 'application/octet-stream'
34
+ end
35
+ end
36
+
37
+ def mime_type(f) self.class.mime_type(f) end
38
+
39
+ CRLF = "\r\n"
40
+
41
+ def self.build_multipart_bodies(parts)
42
+ boundary = Time.now.to_i.to_s(16)
43
+ body = ""
44
+ parts.each do |key, value|
45
+ esc_key = CGI.escape(key.to_s)
46
+ body << "--#{boundary}#{CRLF}"
47
+ if value.respond_to?(:read)
48
+ body << "Content-Disposition: form-data; name=\"#{esc_key}\"; filename=\"#{File.basename(value.path)}\"#{CRLF}"
49
+ body << "Content-Type: #{mime_type(value.path)}#{CRLF*2}"
50
+ body << value.read
51
+ else
52
+ body << "Content-Disposition: form-data; name=\"#{esc_key}\"#{CRLF*2}#{value}"
53
+ end
54
+ body << CRLF
55
+ end
56
+ body << "--#{boundary}--#{CRLF*2}"
57
+ {
58
+ :body => body,
59
+ :headers => {"Content-Type" => "multipart/form-data; boundary=#{boundary}"}
60
+ }
61
+ end
62
+
63
+ def build_multipart_bodies(parts) self.class.build_multipart_bodies(parts) end
64
+
65
+ private
66
+
67
+ def perform_get(path, options={})
68
+ Teambox::Request.get(self, path, options)
69
+ end
70
+
71
+ def perform_post(path, options={})
72
+ Teambox::Request.post(self, path, options)
73
+ end
74
+
75
+ def perform_put(path, options={})
76
+ Teambox::Request.put(self, path, options)
77
+ end
78
+
79
+ def perform_delete(path, options={})
80
+ Teambox::Request.delete(self, path, options)
81
+ end
82
+
83
+ end
84
+ end
@@ -0,0 +1,39 @@
1
+ module Teambox
2
+ class HTTPAuth
3
+ include HTTParty
4
+
5
+ format :plain
6
+
7
+ attr_reader :username, :password, :options
8
+
9
+ def initialize(username, password, options={})
10
+ @username, @password = username, password
11
+ @options = {:ssl => false}.merge(options)
12
+ options[:api_endpoint] ||= "teambox.com"
13
+ self.class.base_uri "http#{'s' if options[:ssl]}://#{options[:api_endpoint]}"
14
+ end
15
+
16
+ def get(uri, headers={})
17
+ self.class.get(uri, :headers => headers, :basic_auth => basic_auth)
18
+ end
19
+
20
+ def post(uri, body={}, headers={})
21
+ self.class.post(uri, :body => body, :headers => headers, :basic_auth => basic_auth)
22
+ end
23
+
24
+ def put(uri, body={}, headers={})
25
+ self.class.put(uri, :body => body, :headers => headers, :basic_auth => basic_auth)
26
+ end
27
+
28
+ def delete(uri, body={}, headers={})
29
+ self.class.delete(uri, :body => body, :headers => headers, :basic_auth => basic_auth)
30
+ end
31
+
32
+ private
33
+
34
+ def basic_auth
35
+ @basic_auth ||= {:username => @username, :password => @password}
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,71 @@
1
+ module Teambox
2
+ class Request
3
+ extend Forwardable
4
+
5
+ def self.get(client, path, options={})
6
+ new(client, :get, path, options).perform
7
+ end
8
+
9
+ def self.post(client, path, options={})
10
+ new(client, :post, path, options).perform
11
+ end
12
+
13
+ def self.put(client, path, options={})
14
+ new(client, :put, path, options).perform
15
+ end
16
+
17
+ def self.delete(client, path, options={})
18
+ new(client, :delete, path, options).perform
19
+ end
20
+
21
+ attr_reader :client, :method, :path, :options
22
+
23
+ def_delegators :client, :get, :post, :put, :delete
24
+
25
+ def initialize(client, method, path, options={})
26
+ @client, @method, @path, @options = client, method, path, options
27
+ end
28
+
29
+ def uri
30
+ @uri ||= begin
31
+ uri = URI.parse(path)
32
+
33
+ if options[:query] && options[:query] != {}
34
+ uri.query = to_query(options[:query])
35
+ end
36
+
37
+ uri.to_s
38
+ end
39
+ end
40
+
41
+ def perform
42
+ Teambox.make_friendly(send("perform_#{method}"))
43
+ end
44
+
45
+ private
46
+
47
+ def perform_get
48
+ get(uri, options[:headers])
49
+ end
50
+
51
+ def perform_post
52
+ post(uri, options[:body], options[:headers])
53
+ end
54
+
55
+ def perform_put
56
+ put(uri, options[:body], options[:headers])
57
+ end
58
+
59
+ def perform_delete
60
+ delete(uri, options[:headers])
61
+ end
62
+
63
+ def to_query(options)
64
+ options.inject([]) do |collection, opt|
65
+ collection << "#{opt[0]}=#{opt[1]}"
66
+ collection
67
+ end * '&'
68
+ end
69
+
70
+ end
71
+ end
@@ -0,0 +1,152 @@
1
+ require "forwardable"
2
+ require "hashie"
3
+ require "httparty"
4
+ require "yajl"
5
+
6
+ module Teambox
7
+ include HTTParty
8
+ API_VERSION = "1".freeze
9
+ format :json
10
+
11
+ class TeamboxError < StandardError
12
+ attr_reader :data
13
+
14
+ def initialize(data)
15
+ @data = data
16
+ super
17
+ end
18
+ end
19
+
20
+ class RateLimitExceeded < TeamboxError; end
21
+ class Unauthorized < TeamboxError; end
22
+ class General < TeamboxError; end
23
+
24
+ class Unavailable < StandardError; end
25
+ class InformTeambox < StandardError; end
26
+ class NotFound < StandardError; end
27
+
28
+ def self.api_endpoint
29
+ # @api_endpoint ||= "api.teambox.com/#{API_VERSION}"
30
+ @api_endpoint ||= "teambox.com"
31
+ end
32
+
33
+ def self.api_endpoint=(value)
34
+ @api_endpoint = value
35
+ end
36
+
37
+ def self.firehose(options = {})
38
+ perform_get("/statuses/public_timeline.json")
39
+ end
40
+
41
+ def self.user(id,options={})
42
+ perform_get("/users/show/#{id}.json")
43
+ end
44
+
45
+ def self.status(id,options={})
46
+ perform_get("/statuses/show/#{id}.json")
47
+ end
48
+
49
+ def self.friend_ids(id,options={})
50
+ perform_get("/friends/ids/#{id}.json")
51
+ end
52
+
53
+ def self.follower_ids(id,options={})
54
+ perform_get("/followers/ids/#{id}.json")
55
+ end
56
+
57
+ def self.timeline(id, options={})
58
+ perform_get("/statuses/user_timeline/#{id}.json", :query => options)
59
+ end
60
+
61
+ # :per_page = max number of statues to get at once
62
+ # :page = which page of tweets you wish to get
63
+ def self.list_timeline(list_owner_username, slug, query = {})
64
+ perform_get("/#{list_owner_username}/lists/#{slug}/statuses.json", :query => query)
65
+ end
66
+
67
+ private
68
+
69
+ def self.perform_get(uri, options = {})
70
+ base_uri self.api_endpoint
71
+ make_friendly(get(uri, options))
72
+ end
73
+
74
+ def self.make_friendly(response)
75
+ raise_errors(response)
76
+ data = parse(response)
77
+ # Don't mash arrays of integers
78
+ if data && data.is_a?(Array) && data.first.is_a?(Integer)
79
+ data
80
+ else
81
+ mash(data)
82
+ end
83
+ end
84
+
85
+ def self.raise_errors(response)
86
+ case response.code.to_i
87
+ when 400
88
+ data = parse(response)
89
+ raise RateLimitExceeded.new(data), "(#{response.code}): #{response.message} - #{data['error'] if data}"
90
+ when 401
91
+ data = parse(response)
92
+ raise Unauthorized.new(data), "(#{response.code}): #{response.message} - #{data['error'] if data}"
93
+ when 403
94
+ data = parse(response)
95
+ raise General.new(data), "(#{response.code}): #{response.message} - #{data['error'] if data}"
96
+ when 404
97
+ raise NotFound, "(#{response.code}): #{response.message}"
98
+ when 500
99
+ raise InformTeambox, "Teambox had an internal error. Please let them know in the group. (#{response.code}): #{response.message}"
100
+ when 502..503
101
+ raise Unavailable, "(#{response.code}): #{response.message}"
102
+ end
103
+ end
104
+
105
+ def self.parse(response)
106
+ Yajl::Parser.parse(response.body)
107
+ end
108
+
109
+ def self.mash(obj)
110
+ if obj.is_a?(Array)
111
+ obj.map{|item| make_mash_with_consistent_hash(item)}
112
+ elsif obj.is_a?(Hash)
113
+ make_mash_with_consistent_hash(obj)
114
+ else
115
+ obj
116
+ end
117
+ end
118
+
119
+ # Lame workaround for the fact that mash doesn't hash correctly
120
+ def self.make_mash_with_consistent_hash(obj)
121
+ m = Hashie::Mash.new(obj)
122
+ def m.hash
123
+ inspect.hash
124
+ end
125
+ return m
126
+ end
127
+
128
+ end
129
+
130
+ module Hashie
131
+ class Mash
132
+
133
+ # Converts all of the keys to strings, optionally formatting key name
134
+ def rubyify_keys!
135
+ keys.each{|k|
136
+ v = delete(k)
137
+ new_key = k.to_s.underscore
138
+ self[new_key] = v
139
+ v.rubyify_keys! if v.is_a?(Hash)
140
+ v.each{|p| p.rubyify_keys! if p.is_a?(Hash)} if v.is_a?(Array)
141
+ }
142
+ self
143
+ end
144
+
145
+ end
146
+ end
147
+
148
+ directory = File.expand_path(File.dirname(__FILE__))
149
+
150
+ require File.join(directory, "teambox-client", "httpauth")
151
+ require File.join(directory, "teambox-client", "request")
152
+ require File.join(directory, "teambox-client", "base")
@@ -0,0 +1,64 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{teambox-client}
8
+ s.version = "0.1.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Pablo Villalba", "John Nunemaker"]
12
+ s.date = %q{2010-06-19}
13
+ s.description = %q{Provides methods to read and write to Teambox for ruby apps}
14
+ s.email = %q{pablo@teambox.com}
15
+ s.extra_rdoc_files = [
16
+ "README.md"
17
+ ]
18
+ s.files = [
19
+ "History",
20
+ "License",
21
+ "Notes.md",
22
+ "README.md",
23
+ "Rakefile",
24
+ "TODO.example.rb",
25
+ "VERSION",
26
+ "examples/activities.rb",
27
+ "examples/helpers/config_store.rb",
28
+ "examples/users.rb",
29
+ "lib/teambox-client.rb",
30
+ "lib/teambox-client/base.rb",
31
+ "lib/teambox-client/httpauth.rb",
32
+ "lib/teambox-client/request.rb",
33
+ "teambox-client.gemspec"
34
+ ]
35
+ s.homepage = %q{http://github.com/micho/teambox-ruby}
36
+ s.rdoc_options = ["--charset=UTF-8"]
37
+ s.require_paths = ["lib"]
38
+ s.rubygems_version = %q{1.3.7}
39
+ s.summary = %q{A ruby gem wrapper for Teambox API}
40
+ s.test_files = [
41
+ "examples/activities.rb",
42
+ "examples/helpers/config_store.rb",
43
+ "examples/users.rb"
44
+ ]
45
+
46
+ if s.respond_to? :specification_version then
47
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
48
+ s.specification_version = 3
49
+
50
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
51
+ s.add_runtime_dependency(%q<hashie>, ["~> 0.2.0"])
52
+ s.add_runtime_dependency(%q<httparty>, ["~> 0.5.0"])
53
+ s.add_runtime_dependency(%q<yajl-ruby>, ["~> 0.7.0"])
54
+ else
55
+ s.add_dependency(%q<hashie>, ["~> 0.2.0"])
56
+ s.add_dependency(%q<httparty>, ["~> 0.5.0"])
57
+ s.add_dependency(%q<yajl-ruby>, ["~> 0.7.0"])
58
+ end
59
+ else
60
+ s.add_dependency(%q<hashie>, ["~> 0.2.0"])
61
+ s.add_dependency(%q<httparty>, ["~> 0.5.0"])
62
+ s.add_dependency(%q<yajl-ruby>, ["~> 0.7.0"])
63
+ end
64
+ end
metadata ADDED
@@ -0,0 +1,131 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: teambox-client
3
+ version: !ruby/object:Gem::Version
4
+ hash: 25
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 1
10
+ version: 0.1.1
11
+ platform: ruby
12
+ authors:
13
+ - Pablo Villalba
14
+ - John Nunemaker
15
+ autorequire:
16
+ bindir: bin
17
+ cert_chain: []
18
+
19
+ date: 2010-06-19 00:00:00 +02:00
20
+ default_executable:
21
+ dependencies:
22
+ - !ruby/object:Gem::Dependency
23
+ name: hashie
24
+ prerelease: false
25
+ requirement: &id001 !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ~>
29
+ - !ruby/object:Gem::Version
30
+ hash: 23
31
+ segments:
32
+ - 0
33
+ - 2
34
+ - 0
35
+ version: 0.2.0
36
+ type: :runtime
37
+ version_requirements: *id001
38
+ - !ruby/object:Gem::Dependency
39
+ name: httparty
40
+ prerelease: false
41
+ requirement: &id002 !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ~>
45
+ - !ruby/object:Gem::Version
46
+ hash: 11
47
+ segments:
48
+ - 0
49
+ - 5
50
+ - 0
51
+ version: 0.5.0
52
+ type: :runtime
53
+ version_requirements: *id002
54
+ - !ruby/object:Gem::Dependency
55
+ name: yajl-ruby
56
+ prerelease: false
57
+ requirement: &id003 !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ~>
61
+ - !ruby/object:Gem::Version
62
+ hash: 3
63
+ segments:
64
+ - 0
65
+ - 7
66
+ - 0
67
+ version: 0.7.0
68
+ type: :runtime
69
+ version_requirements: *id003
70
+ description: Provides methods to read and write to Teambox for ruby apps
71
+ email: pablo@teambox.com
72
+ executables: []
73
+
74
+ extensions: []
75
+
76
+ extra_rdoc_files:
77
+ - README.md
78
+ files:
79
+ - History
80
+ - License
81
+ - Notes.md
82
+ - README.md
83
+ - Rakefile
84
+ - TODO.example.rb
85
+ - VERSION
86
+ - examples/activities.rb
87
+ - examples/helpers/config_store.rb
88
+ - examples/users.rb
89
+ - lib/teambox-client.rb
90
+ - lib/teambox-client/base.rb
91
+ - lib/teambox-client/httpauth.rb
92
+ - lib/teambox-client/request.rb
93
+ - teambox-client.gemspec
94
+ has_rdoc: true
95
+ homepage: http://github.com/micho/teambox-ruby
96
+ licenses: []
97
+
98
+ post_install_message:
99
+ rdoc_options:
100
+ - --charset=UTF-8
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ none: false
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ hash: 3
109
+ segments:
110
+ - 0
111
+ version: "0"
112
+ required_rubygems_version: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ hash: 3
118
+ segments:
119
+ - 0
120
+ version: "0"
121
+ requirements: []
122
+
123
+ rubyforge_project:
124
+ rubygems_version: 1.3.7
125
+ signing_key:
126
+ specification_version: 3
127
+ summary: A ruby gem wrapper for Teambox API
128
+ test_files:
129
+ - examples/activities.rb
130
+ - examples/helpers/config_store.rb
131
+ - examples/users.rb