twitter-multi 0.9.8.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 +290 -0
- data/License +20 -0
- data/Notes +33 -0
- data/README.rdoc +19 -0
- data/Rakefile +40 -0
- data/VERSION.yml +5 -0
- data/examples/connect.rb +30 -0
- data/examples/friendship_existence.rb +13 -0
- data/examples/helpers/config_store.rb +38 -0
- data/examples/httpauth.rb +11 -0
- data/examples/ids.rb +13 -0
- data/examples/lists.rb +11 -0
- data/examples/oauth.rb +27 -0
- data/examples/search.rb +15 -0
- data/examples/timeline.rb +19 -0
- data/examples/tumblr.rb +9 -0
- data/examples/unauthorized.rb +16 -0
- data/examples/update.rb +11 -0
- data/examples/user.rb +5 -0
- data/lib/twitter/base.rb +390 -0
- data/lib/twitter/geo.rb +25 -0
- data/lib/twitter/httpauth.rb +53 -0
- data/lib/twitter/local_trends.rb +30 -0
- data/lib/twitter/oauth.rb +64 -0
- data/lib/twitter/request.rb +71 -0
- data/lib/twitter/search.rb +163 -0
- data/lib/twitter/trends.rb +55 -0
- data/lib/twitter.rb +156 -0
- data/test/fixtures/blocking.json +1632 -0
- data/test/fixtures/firehose.json +1 -0
- data/test/fixtures/follower_ids.json +1 -0
- data/test/fixtures/followers.json +1 -0
- data/test/fixtures/friend_ids.json +1 -0
- data/test/fixtures/friends_timeline.json +1 -0
- data/test/fixtures/friendship.json +1 -0
- data/test/fixtures/friendship_exists.json +1 -0
- data/test/fixtures/geo_place.json +1 -0
- data/test/fixtures/geo_reverse_geocode.json +1 -0
- data/test/fixtures/geo_reverse_geocode_granularity.json +1 -0
- data/test/fixtures/geo_reverse_geocode_limit.json +1 -0
- data/test/fixtures/geo_search.json +1 -0
- data/test/fixtures/geo_search_ip_address.json +1 -0
- data/test/fixtures/geo_search_query.json +1 -0
- data/test/fixtures/home_timeline.json +1 -0
- data/test/fixtures/ids.json +1 -0
- data/test/fixtures/list.json +1 -0
- data/test/fixtures/list_statuses.json +1 -0
- data/test/fixtures/list_statuses_1_1.json +1 -0
- data/test/fixtures/list_statuses_2_1.json +1 -0
- data/test/fixtures/list_subscriptions.json +1 -0
- data/test/fixtures/list_users.json +1 -0
- data/test/fixtures/lists.json +1 -0
- data/test/fixtures/memberships.json +1 -0
- data/test/fixtures/mentions.json +1 -0
- data/test/fixtures/not_found.json +1 -0
- data/test/fixtures/people_search.json +39 -0
- data/test/fixtures/rate_limit_exceeded.json +1 -0
- data/test/fixtures/report_spam.json +41 -0
- data/test/fixtures/retweet.json +1 -0
- data/test/fixtures/retweeted_by_me.json +1 -0
- data/test/fixtures/retweeted_to_me.json +1 -0
- data/test/fixtures/retweeters_of_tweet.json +166 -0
- data/test/fixtures/retweets.json +1 -0
- data/test/fixtures/retweets_of_me.json +1 -0
- data/test/fixtures/sample-image.png +0 -0
- data/test/fixtures/saved_search.json +7 -0
- data/test/fixtures/saved_searches.json +16 -0
- data/test/fixtures/search.json +1 -0
- data/test/fixtures/search_from_jnunemaker.json +1 -0
- data/test/fixtures/status.json +1 -0
- data/test/fixtures/status_show.json +1 -0
- data/test/fixtures/trends_available.json +253 -0
- data/test/fixtures/trends_current.json +1 -0
- data/test/fixtures/trends_current_exclude.json +1 -0
- data/test/fixtures/trends_daily.json +1925 -0
- data/test/fixtures/trends_daily_date.json +1 -0
- data/test/fixtures/trends_daily_exclude.json +1 -0
- data/test/fixtures/trends_location.json +57 -0
- data/test/fixtures/trends_weekly.json +1 -0
- data/test/fixtures/trends_weekly_date.json +1 -0
- data/test/fixtures/trends_weekly_exclude.json +1 -0
- data/test/fixtures/unauthorized.json +1 -0
- data/test/fixtures/update_profile_background_image.json +1 -0
- data/test/fixtures/update_profile_image.json +1 -0
- data/test/fixtures/user.json +1 -0
- data/test/fixtures/user_timeline.json +710 -0
- data/test/fixtures/users.json +1 -0
- data/test/test_helper.rb +47 -0
- data/test/twitter/base_test.rb +426 -0
- data/test/twitter/geo_test.rb +79 -0
- data/test/twitter/httpauth_test.rb +86 -0
- data/test/twitter/oauth_test.rb +108 -0
- data/test/twitter/request_test.rb +217 -0
- data/test/twitter/search_test.rb +208 -0
- data/test/twitter/trends_test.rb +112 -0
- data/test/twitter_test.rb +106 -0
- metadata +329 -0
data/lib/twitter/base.rb
ADDED
@@ -0,0 +1,390 @@
|
|
1
|
+
module Twitter
|
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
|
+
# Options: since_id, max_id, count, page
|
14
|
+
def home_timeline(query={})
|
15
|
+
perform_get("/statuses/home_timeline.json", :query => query)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Options: since_id, max_id, count, page, since
|
19
|
+
def friends_timeline(query={})
|
20
|
+
perform_get("/statuses/friends_timeline.json", :query => query)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Options: id, user_id, screen_name, since_id, max_id, page, since, count
|
24
|
+
def user_timeline(query={})
|
25
|
+
perform_get("/statuses/user_timeline.json", :query => query)
|
26
|
+
end
|
27
|
+
|
28
|
+
def status(id)
|
29
|
+
perform_get("/statuses/show/#{id}.json")
|
30
|
+
end
|
31
|
+
|
32
|
+
# Options: count
|
33
|
+
def retweets(id, query={})
|
34
|
+
perform_get("/statuses/retweets/#{id}.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
|
+
# DEPRECATED: Use #mentions instead
|
43
|
+
#
|
44
|
+
# Options: since_id, max_id, since, page
|
45
|
+
def replies(query={})
|
46
|
+
warn("DEPRECATED: #replies is deprecated by Twitter; use #mentions instead")
|
47
|
+
perform_get("/statuses/replies.json", :query => query)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Options: since_id, max_id, count, page
|
51
|
+
def mentions(query={})
|
52
|
+
perform_get("/statuses/mentions.json", :query => query)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Options: since_id, max_id, count, page
|
56
|
+
def retweeted_by_me(query={})
|
57
|
+
perform_get("/statuses/retweeted_by_me.json", :query => query)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Options: since_id, max_id, count, page
|
61
|
+
def retweeted_to_me(query={})
|
62
|
+
perform_get("/statuses/retweeted_to_me.json", :query => query)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Options: since_id, max_id, count, page
|
66
|
+
def retweets_of_me(query={})
|
67
|
+
perform_get("/statuses/retweets_of_me.json", :query => query)
|
68
|
+
end
|
69
|
+
|
70
|
+
# options: count, page, ids_only
|
71
|
+
def retweeters_of(id, options={})
|
72
|
+
ids_only = !!(options.delete(:ids_only))
|
73
|
+
perform_get("/statuses/#{id}/retweeted_by#{"/ids" if ids_only}.json", :query => options)
|
74
|
+
end
|
75
|
+
|
76
|
+
def status_destroy(id)
|
77
|
+
perform_post("/statuses/destroy/#{id}.json")
|
78
|
+
end
|
79
|
+
|
80
|
+
def retweet(id)
|
81
|
+
perform_post("/statuses/retweet/#{id}.json")
|
82
|
+
end
|
83
|
+
|
84
|
+
# Options: id, user_id, screen_name, page
|
85
|
+
def friends(query={})
|
86
|
+
perform_get("/statuses/friends.json", :query => query)
|
87
|
+
end
|
88
|
+
|
89
|
+
# Options: id, user_id, screen_name, page
|
90
|
+
def followers(query={})
|
91
|
+
perform_get("/statuses/followers.json", :query => query)
|
92
|
+
end
|
93
|
+
|
94
|
+
def user(id, query={})
|
95
|
+
perform_get("/users/show/#{id}.json", :query => query)
|
96
|
+
end
|
97
|
+
|
98
|
+
def users(*ids_or_usernames)
|
99
|
+
ids, usernames = [], []
|
100
|
+
ids_or_usernames.each do |id_or_username|
|
101
|
+
if id_or_username.is_a?(Integer)
|
102
|
+
ids << id_or_username
|
103
|
+
elsif id_or_username.is_a?(String)
|
104
|
+
usernames << id_or_username
|
105
|
+
end
|
106
|
+
end
|
107
|
+
query = {}
|
108
|
+
query[:user_id] = ids.join(",") unless ids.empty?
|
109
|
+
query[:screen_name] = usernames.join(",") unless usernames.empty?
|
110
|
+
perform_get("/users/lookup.json", :query => query)
|
111
|
+
end
|
112
|
+
|
113
|
+
# Options: page, per_page
|
114
|
+
def user_search(q, query={})
|
115
|
+
q = URI.escape(q)
|
116
|
+
perform_get("/users/search.json", :query => ({:q => q}.merge(query)))
|
117
|
+
end
|
118
|
+
|
119
|
+
# Options: since, since_id, page
|
120
|
+
def direct_messages(query={})
|
121
|
+
perform_get("/direct_messages.json", :query => query)
|
122
|
+
end
|
123
|
+
|
124
|
+
# Options: since, since_id, page
|
125
|
+
def direct_messages_sent(query={})
|
126
|
+
perform_get("/direct_messages/sent.json", :query => query)
|
127
|
+
end
|
128
|
+
|
129
|
+
def direct_message_create(user, text)
|
130
|
+
perform_post("/direct_messages/new.json", :body => {:user => user, :text => text})
|
131
|
+
end
|
132
|
+
|
133
|
+
def direct_message_destroy(id)
|
134
|
+
perform_post("/direct_messages/destroy/#{id}.json")
|
135
|
+
end
|
136
|
+
|
137
|
+
def friendship_create(id, follow=false)
|
138
|
+
body = {}
|
139
|
+
body.merge!(:follow => follow) if follow
|
140
|
+
perform_post("/friendships/create/#{id}.json", :body => body)
|
141
|
+
end
|
142
|
+
|
143
|
+
def friendship_destroy(id)
|
144
|
+
perform_post("/friendships/destroy/#{id}.json")
|
145
|
+
end
|
146
|
+
|
147
|
+
def friendship_exists?(a, b)
|
148
|
+
perform_get("/friendships/exists.json", :query => {:user_a => a, :user_b => b})
|
149
|
+
end
|
150
|
+
|
151
|
+
def friendship_show(query)
|
152
|
+
perform_get("/friendships/show.json", :query => query)
|
153
|
+
end
|
154
|
+
|
155
|
+
# Options: id, user_id, screen_name
|
156
|
+
def friend_ids(query={})
|
157
|
+
perform_get("/friends/ids.json", :query => query)
|
158
|
+
end
|
159
|
+
|
160
|
+
# Options: id, user_id, screen_name
|
161
|
+
def follower_ids(query={})
|
162
|
+
perform_get("/followers/ids.json", :query => query)
|
163
|
+
end
|
164
|
+
|
165
|
+
def verify_credentials
|
166
|
+
perform_get("/account/verify_credentials.json")
|
167
|
+
end
|
168
|
+
|
169
|
+
# Device must be sms, im or none
|
170
|
+
def update_delivery_device(device)
|
171
|
+
perform_post("/account/update_delivery_device.json", :body => {:device => device})
|
172
|
+
end
|
173
|
+
|
174
|
+
# One or more of the following must be present:
|
175
|
+
# profile_background_color, profile_text_color, profile_link_color,
|
176
|
+
# profile_sidebar_fill_color, profile_sidebar_border_color
|
177
|
+
def update_profile_colors(colors={})
|
178
|
+
perform_post("/account/update_profile_colors.json", :body => colors)
|
179
|
+
end
|
180
|
+
|
181
|
+
# file should respond to #read and #path
|
182
|
+
def update_profile_image(file)
|
183
|
+
perform_post("/account/update_profile_image.json", build_multipart_bodies(:image => file))
|
184
|
+
end
|
185
|
+
|
186
|
+
# file should respond to #read and #path
|
187
|
+
def update_profile_background(file, tile = false)
|
188
|
+
perform_post("/account/update_profile_background_image.json", build_multipart_bodies(:image => file).merge(:tile => tile))
|
189
|
+
end
|
190
|
+
|
191
|
+
def rate_limit_status
|
192
|
+
perform_get("/account/rate_limit_status.json")
|
193
|
+
end
|
194
|
+
|
195
|
+
# One or more of the following must be present:
|
196
|
+
# name, email, url, location, description
|
197
|
+
def update_profile(body={})
|
198
|
+
perform_post("/account/update_profile.json", :body => body)
|
199
|
+
end
|
200
|
+
|
201
|
+
# Options: id, page
|
202
|
+
def favorites(query={})
|
203
|
+
perform_get("/favorites.json", :query => query)
|
204
|
+
end
|
205
|
+
|
206
|
+
def favorite_create(id)
|
207
|
+
perform_post("/favorites/create/#{id}.json")
|
208
|
+
end
|
209
|
+
|
210
|
+
def favorite_destroy(id)
|
211
|
+
perform_post("/favorites/destroy/#{id}.json")
|
212
|
+
end
|
213
|
+
|
214
|
+
def enable_notifications(id)
|
215
|
+
perform_post("/notifications/follow/#{id}.json")
|
216
|
+
end
|
217
|
+
|
218
|
+
def disable_notifications(id)
|
219
|
+
perform_post("/notifications/leave/#{id}.json")
|
220
|
+
end
|
221
|
+
|
222
|
+
def block(id)
|
223
|
+
perform_post("/blocks/create/#{id}.json")
|
224
|
+
end
|
225
|
+
|
226
|
+
def unblock(id)
|
227
|
+
perform_post("/blocks/destroy/#{id}.json")
|
228
|
+
end
|
229
|
+
|
230
|
+
# When reporting a user for spam, specify one or more of id, screen_name, or user_id
|
231
|
+
def report_spam(options)
|
232
|
+
perform_post("/report_spam.json", :body => options)
|
233
|
+
end
|
234
|
+
|
235
|
+
def help
|
236
|
+
perform_get("/help/test.json")
|
237
|
+
end
|
238
|
+
|
239
|
+
def list_create(list_owner_username, options)
|
240
|
+
perform_post("/#{list_owner_username}/lists.json", :body => {:user => list_owner_username}.merge(options))
|
241
|
+
end
|
242
|
+
|
243
|
+
def list_update(list_owner_username, slug, options)
|
244
|
+
perform_put("/#{list_owner_username}/lists/#{slug}.json", :body => options)
|
245
|
+
end
|
246
|
+
|
247
|
+
def list_delete(list_owner_username, slug)
|
248
|
+
perform_delete("/#{list_owner_username}/lists/#{slug}.json")
|
249
|
+
end
|
250
|
+
|
251
|
+
def lists(list_owner_username = nil, query = {})
|
252
|
+
path = case list_owner_username
|
253
|
+
when nil, Hash
|
254
|
+
query = list_owner_username
|
255
|
+
"/lists.json"
|
256
|
+
else
|
257
|
+
"/#{list_owner_username}/lists.json"
|
258
|
+
end
|
259
|
+
perform_get(path, :query => query)
|
260
|
+
end
|
261
|
+
|
262
|
+
def list(list_owner_username, slug)
|
263
|
+
perform_get("/#{list_owner_username}/lists/#{slug}.json")
|
264
|
+
end
|
265
|
+
|
266
|
+
# :per_page = max number of statues to get at once
|
267
|
+
# :page = which page of tweets you wish to get
|
268
|
+
def list_timeline(list_owner_username, slug, query = {})
|
269
|
+
perform_get("/#{list_owner_username}/lists/#{slug}/statuses.json", :query => query)
|
270
|
+
end
|
271
|
+
|
272
|
+
def memberships(list_owner_username, query={})
|
273
|
+
perform_get("/#{list_owner_username}/lists/memberships.json", :query => query)
|
274
|
+
end
|
275
|
+
|
276
|
+
def subscriptions(list_owner_username, query = {})
|
277
|
+
perform_get("/#{list_owner_username}/lists/subscriptions.json", :query => query)
|
278
|
+
end
|
279
|
+
|
280
|
+
def list_members(list_owner_username, slug, query = {})
|
281
|
+
perform_get("/#{list_owner_username}/#{slug}/members.json", :query => query)
|
282
|
+
end
|
283
|
+
|
284
|
+
def list_add_member(list_owner_username, slug, new_id)
|
285
|
+
perform_post("/#{list_owner_username}/#{slug}/members.json", :body => {:id => new_id})
|
286
|
+
end
|
287
|
+
|
288
|
+
def list_remove_member(list_owner_username, slug, id)
|
289
|
+
perform_delete("/#{list_owner_username}/#{slug}/members.json", :query => {:id => id})
|
290
|
+
end
|
291
|
+
|
292
|
+
def is_list_member?(list_owner_username, slug, id)
|
293
|
+
perform_get("/#{list_owner_username}/#{slug}/members/#{id}.json").error.nil?
|
294
|
+
end
|
295
|
+
|
296
|
+
def list_subscribers(list_owner_username, slug)
|
297
|
+
perform_get("/#{list_owner_username}/#{slug}/subscribers.json")
|
298
|
+
end
|
299
|
+
|
300
|
+
def list_subscribe(list_owner_username, slug)
|
301
|
+
perform_post("/#{list_owner_username}/#{slug}/subscribers.json")
|
302
|
+
end
|
303
|
+
|
304
|
+
def list_unsubscribe(list_owner_username, slug)
|
305
|
+
perform_delete("/#{list_owner_username}/#{slug}/subscribers.json")
|
306
|
+
end
|
307
|
+
|
308
|
+
def blocked_ids
|
309
|
+
perform_get("/blocks/blocking/ids.json", :mash => false)
|
310
|
+
end
|
311
|
+
|
312
|
+
def blocking(options={})
|
313
|
+
perform_get("/blocks/blocking.json", options)
|
314
|
+
end
|
315
|
+
|
316
|
+
def saved_searches
|
317
|
+
perform_get("/saved_searches.json")
|
318
|
+
end
|
319
|
+
|
320
|
+
def saved_search(id)
|
321
|
+
perform_get("/saved_searches/show/#{id}.json")
|
322
|
+
end
|
323
|
+
|
324
|
+
def saved_search_create(query)
|
325
|
+
perform_post("/saved_searches/create.json", :body => {:query => query})
|
326
|
+
end
|
327
|
+
|
328
|
+
def saved_search_destroy(id)
|
329
|
+
perform_delete("/saved_searches/destroy/#{id}.json")
|
330
|
+
end
|
331
|
+
|
332
|
+
protected
|
333
|
+
|
334
|
+
def self.mime_type(file)
|
335
|
+
case
|
336
|
+
when file =~ /\.jpg/ then 'image/jpg'
|
337
|
+
when file =~ /\.gif$/ then 'image/gif'
|
338
|
+
when file =~ /\.png$/ then 'image/png'
|
339
|
+
else 'application/octet-stream'
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
def mime_type(f) self.class.mime_type(f) end
|
344
|
+
|
345
|
+
CRLF = "\r\n"
|
346
|
+
|
347
|
+
def self.build_multipart_bodies(parts)
|
348
|
+
boundary = Time.now.to_i.to_s(16)
|
349
|
+
body = ""
|
350
|
+
parts.each do |key, value|
|
351
|
+
esc_key = CGI.escape(key.to_s)
|
352
|
+
body << "--#{boundary}#{CRLF}"
|
353
|
+
if value.respond_to?(:read)
|
354
|
+
body << "Content-Disposition: form-data; name=\"#{esc_key}\"; filename=\"#{File.basename(value.path)}\"#{CRLF}"
|
355
|
+
body << "Content-Type: #{mime_type(value.path)}#{CRLF*2}"
|
356
|
+
body << value.read
|
357
|
+
else
|
358
|
+
body << "Content-Disposition: form-data; name=\"#{esc_key}\"#{CRLF*2}#{value}"
|
359
|
+
end
|
360
|
+
body << CRLF
|
361
|
+
end
|
362
|
+
body << "--#{boundary}--#{CRLF*2}"
|
363
|
+
{
|
364
|
+
:body => body,
|
365
|
+
:headers => {"Content-Type" => "multipart/form-data; boundary=#{boundary}"}
|
366
|
+
}
|
367
|
+
end
|
368
|
+
|
369
|
+
def build_multipart_bodies(parts) self.class.build_multipart_bodies(parts) end
|
370
|
+
|
371
|
+
private
|
372
|
+
|
373
|
+
def perform_get(path, options={})
|
374
|
+
Twitter::Request.get(self, path, options)
|
375
|
+
end
|
376
|
+
|
377
|
+
def perform_post(path, options={})
|
378
|
+
Twitter::Request.post(self, path, options)
|
379
|
+
end
|
380
|
+
|
381
|
+
def perform_put(path, options={})
|
382
|
+
Twitter::Request.put(self, path, options)
|
383
|
+
end
|
384
|
+
|
385
|
+
def perform_delete(path, options={})
|
386
|
+
Twitter::Request.delete(self, path, options)
|
387
|
+
end
|
388
|
+
|
389
|
+
end
|
390
|
+
end
|
data/lib/twitter/geo.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
module Twitter
|
2
|
+
class Geo
|
3
|
+
include HTTParty
|
4
|
+
base_uri "api.twitter.com/#{API_VERSION}/geo"
|
5
|
+
format :json
|
6
|
+
|
7
|
+
def self.place(place_id, query={})
|
8
|
+
Twitter.mash(get("/id/#{place_id}.json", :query => query))
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.search(query={})
|
12
|
+
mashup(get("/search.json", :query => query))
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.reverse_geocode(query={})
|
16
|
+
mashup(get("/reverse_geocode.json", :query => query))
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def self.mashup(response)
|
22
|
+
response["result"].values.flatten.map{|t| Twitter.mash(t)}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Twitter
|
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
|
+
|
11
|
+
@username, @password = username, password
|
12
|
+
@options = {:ssl => false}.merge(options)
|
13
|
+
options[:api_endpoint] ||= "api.twitter.com"
|
14
|
+
|
15
|
+
if options[:api_version] == false
|
16
|
+
version_path = ''
|
17
|
+
else
|
18
|
+
options[:api_version] ||= API_VERSION
|
19
|
+
version_path = "/#{options[:api_version]}"
|
20
|
+
end
|
21
|
+
|
22
|
+
self.class.base_uri "http#{'s' if options[:ssl]}://#{options[:api_endpoint]}#{version_path}"
|
23
|
+
self.class.default_timeout options[:timeout] if options[:timeout]
|
24
|
+
end
|
25
|
+
|
26
|
+
def get(uri, headers={})
|
27
|
+
|
28
|
+
self.class.get(uri, :headers => headers, :basic_auth => basic_auth)
|
29
|
+
end
|
30
|
+
|
31
|
+
def post(uri, body={}, headers={})
|
32
|
+
|
33
|
+
self.class.post(uri, :body => body, :headers => headers, :basic_auth => basic_auth)
|
34
|
+
end
|
35
|
+
|
36
|
+
def put(uri, body={}, headers={})
|
37
|
+
|
38
|
+
self.class.put(uri, :body => body, :headers => headers, :basic_auth => basic_auth)
|
39
|
+
end
|
40
|
+
|
41
|
+
def delete(uri, body={}, headers={})
|
42
|
+
|
43
|
+
self.class.delete(uri, :body => body, :headers => headers, :basic_auth => basic_auth)
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def basic_auth
|
49
|
+
@basic_auth ||= {:username => @username, :password => @password}
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Twitter
|
2
|
+
class LocalTrends
|
3
|
+
include HTTParty
|
4
|
+
base_uri "api.twitter.com/#{API_VERSION}/trends"
|
5
|
+
format :json
|
6
|
+
|
7
|
+
def self.available(query={})
|
8
|
+
before_test(query)
|
9
|
+
query.delete(:api_endpoint)
|
10
|
+
get("/available.json", :query => query).map{|location| Twitter.mash(location)}
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.for_location(woeid,options = {})
|
14
|
+
before_test(options)
|
15
|
+
get("/#{woeid}.json").map{|location| Twitter.mash(location)}
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def self.before_test(options)
|
21
|
+
configure_base_uri(options)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.configure_base_uri(options)
|
25
|
+
new_base_url = options[:api_endpoint]
|
26
|
+
base_uri "#{new_base_url}/#{API_VERSION}/trends" if new_base_url
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Twitter
|
2
|
+
class OAuth
|
3
|
+
extend Forwardable
|
4
|
+
|
5
|
+
def_delegators :access_token, :get, :post, :put, :delete
|
6
|
+
|
7
|
+
attr_reader :ctoken, :csecret, :consumer_options, :api_endpoint, :signing_endpoint
|
8
|
+
|
9
|
+
# Options
|
10
|
+
# :sign_in => true to just sign in with twitter instead of doing oauth authorization
|
11
|
+
# (http://apiwiki.twitter.com/Sign-in-with-Twitter)
|
12
|
+
def initialize(ctoken, csecret, options={})
|
13
|
+
@ctoken, @csecret, @consumer_options = ctoken, csecret, {}
|
14
|
+
@api_endpoint = options[:api_endpoint] || 'http://api.twitter.com'
|
15
|
+
@signing_endpoint = options[:signing_endpoint] || 'http://api.twitter.com'
|
16
|
+
if options[:sign_in]
|
17
|
+
@consumer_options[:authorize_path] = '/oauth/authenticate'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def consumer
|
22
|
+
@consumer ||= ::OAuth::Consumer.new(@ctoken, @csecret, {:site => api_endpoint}.merge(consumer_options))
|
23
|
+
end
|
24
|
+
|
25
|
+
def signing_consumer
|
26
|
+
@signing_consumer ||= ::OAuth::Consumer.new(@ctoken, @csecret, {:site => signing_endpoint, :request_endpoint => api_endpoint }.merge(consumer_options))
|
27
|
+
end
|
28
|
+
|
29
|
+
def set_callback_url(url)
|
30
|
+
clear_request_token
|
31
|
+
request_token(:oauth_callback => url)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Note: If using oauth with a web app, be sure to provide :oauth_callback.
|
35
|
+
# Options:
|
36
|
+
# :oauth_callback => String, url that twitter should redirect to
|
37
|
+
def request_token(options={})
|
38
|
+
@request_token ||= signing_consumer.get_request_token(options)
|
39
|
+
end
|
40
|
+
|
41
|
+
# For web apps use params[:oauth_verifier], for desktop apps,
|
42
|
+
# use the verifier is the pin that twitter gives users.
|
43
|
+
def authorize_from_request(rtoken, rsecret, verifier_or_pin)
|
44
|
+
request_token = ::OAuth::RequestToken.new(signing_consumer, rtoken, rsecret)
|
45
|
+
access_token = request_token.get_access_token(:oauth_verifier => verifier_or_pin)
|
46
|
+
@atoken, @asecret = access_token.token, access_token.secret
|
47
|
+
end
|
48
|
+
|
49
|
+
def access_token
|
50
|
+
@access_token ||= ::OAuth::AccessToken.new(signing_consumer, @atoken, @asecret)
|
51
|
+
end
|
52
|
+
|
53
|
+
def authorize_from_access(atoken, asecret)
|
54
|
+
@atoken, @asecret = atoken, asecret
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def clear_request_token
|
60
|
+
@request_token = nil
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Twitter
|
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
|
+
Twitter.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
|