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.
Files changed (97) hide show
  1. data/History +290 -0
  2. data/License +20 -0
  3. data/Notes +33 -0
  4. data/README.rdoc +19 -0
  5. data/Rakefile +40 -0
  6. data/VERSION.yml +5 -0
  7. data/examples/connect.rb +30 -0
  8. data/examples/friendship_existence.rb +13 -0
  9. data/examples/helpers/config_store.rb +38 -0
  10. data/examples/httpauth.rb +11 -0
  11. data/examples/ids.rb +13 -0
  12. data/examples/lists.rb +11 -0
  13. data/examples/oauth.rb +27 -0
  14. data/examples/search.rb +15 -0
  15. data/examples/timeline.rb +19 -0
  16. data/examples/tumblr.rb +9 -0
  17. data/examples/unauthorized.rb +16 -0
  18. data/examples/update.rb +11 -0
  19. data/examples/user.rb +5 -0
  20. data/lib/twitter/base.rb +390 -0
  21. data/lib/twitter/geo.rb +25 -0
  22. data/lib/twitter/httpauth.rb +53 -0
  23. data/lib/twitter/local_trends.rb +30 -0
  24. data/lib/twitter/oauth.rb +64 -0
  25. data/lib/twitter/request.rb +71 -0
  26. data/lib/twitter/search.rb +163 -0
  27. data/lib/twitter/trends.rb +55 -0
  28. data/lib/twitter.rb +156 -0
  29. data/test/fixtures/blocking.json +1632 -0
  30. data/test/fixtures/firehose.json +1 -0
  31. data/test/fixtures/follower_ids.json +1 -0
  32. data/test/fixtures/followers.json +1 -0
  33. data/test/fixtures/friend_ids.json +1 -0
  34. data/test/fixtures/friends_timeline.json +1 -0
  35. data/test/fixtures/friendship.json +1 -0
  36. data/test/fixtures/friendship_exists.json +1 -0
  37. data/test/fixtures/geo_place.json +1 -0
  38. data/test/fixtures/geo_reverse_geocode.json +1 -0
  39. data/test/fixtures/geo_reverse_geocode_granularity.json +1 -0
  40. data/test/fixtures/geo_reverse_geocode_limit.json +1 -0
  41. data/test/fixtures/geo_search.json +1 -0
  42. data/test/fixtures/geo_search_ip_address.json +1 -0
  43. data/test/fixtures/geo_search_query.json +1 -0
  44. data/test/fixtures/home_timeline.json +1 -0
  45. data/test/fixtures/ids.json +1 -0
  46. data/test/fixtures/list.json +1 -0
  47. data/test/fixtures/list_statuses.json +1 -0
  48. data/test/fixtures/list_statuses_1_1.json +1 -0
  49. data/test/fixtures/list_statuses_2_1.json +1 -0
  50. data/test/fixtures/list_subscriptions.json +1 -0
  51. data/test/fixtures/list_users.json +1 -0
  52. data/test/fixtures/lists.json +1 -0
  53. data/test/fixtures/memberships.json +1 -0
  54. data/test/fixtures/mentions.json +1 -0
  55. data/test/fixtures/not_found.json +1 -0
  56. data/test/fixtures/people_search.json +39 -0
  57. data/test/fixtures/rate_limit_exceeded.json +1 -0
  58. data/test/fixtures/report_spam.json +41 -0
  59. data/test/fixtures/retweet.json +1 -0
  60. data/test/fixtures/retweeted_by_me.json +1 -0
  61. data/test/fixtures/retweeted_to_me.json +1 -0
  62. data/test/fixtures/retweeters_of_tweet.json +166 -0
  63. data/test/fixtures/retweets.json +1 -0
  64. data/test/fixtures/retweets_of_me.json +1 -0
  65. data/test/fixtures/sample-image.png +0 -0
  66. data/test/fixtures/saved_search.json +7 -0
  67. data/test/fixtures/saved_searches.json +16 -0
  68. data/test/fixtures/search.json +1 -0
  69. data/test/fixtures/search_from_jnunemaker.json +1 -0
  70. data/test/fixtures/status.json +1 -0
  71. data/test/fixtures/status_show.json +1 -0
  72. data/test/fixtures/trends_available.json +253 -0
  73. data/test/fixtures/trends_current.json +1 -0
  74. data/test/fixtures/trends_current_exclude.json +1 -0
  75. data/test/fixtures/trends_daily.json +1925 -0
  76. data/test/fixtures/trends_daily_date.json +1 -0
  77. data/test/fixtures/trends_daily_exclude.json +1 -0
  78. data/test/fixtures/trends_location.json +57 -0
  79. data/test/fixtures/trends_weekly.json +1 -0
  80. data/test/fixtures/trends_weekly_date.json +1 -0
  81. data/test/fixtures/trends_weekly_exclude.json +1 -0
  82. data/test/fixtures/unauthorized.json +1 -0
  83. data/test/fixtures/update_profile_background_image.json +1 -0
  84. data/test/fixtures/update_profile_image.json +1 -0
  85. data/test/fixtures/user.json +1 -0
  86. data/test/fixtures/user_timeline.json +710 -0
  87. data/test/fixtures/users.json +1 -0
  88. data/test/test_helper.rb +47 -0
  89. data/test/twitter/base_test.rb +426 -0
  90. data/test/twitter/geo_test.rb +79 -0
  91. data/test/twitter/httpauth_test.rb +86 -0
  92. data/test/twitter/oauth_test.rb +108 -0
  93. data/test/twitter/request_test.rb +217 -0
  94. data/test/twitter/search_test.rb +208 -0
  95. data/test/twitter/trends_test.rb +112 -0
  96. data/test/twitter_test.rb +106 -0
  97. metadata +329 -0
@@ -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
@@ -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