twitter 4.8.1 → 5.0.0.rc.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (197) hide show
  1. data.tar.gz.sig +0 -0
  2. data/CHANGELOG.md +30 -0
  3. data/LICENSE.md +1 -1
  4. data/README.md +386 -266
  5. data/lib/twitter.rb +4 -39
  6. data/lib/twitter/arguments.rb +11 -0
  7. data/lib/twitter/base.rb +89 -68
  8. data/lib/twitter/client.rb +69 -110
  9. data/lib/twitter/configuration.rb +7 -3
  10. data/lib/twitter/creatable.rb +2 -4
  11. data/lib/twitter/cursor.rb +50 -42
  12. data/lib/twitter/direct_message.rb +2 -11
  13. data/lib/twitter/entity/uri.rb +13 -0
  14. data/lib/twitter/enumerable.rb +15 -0
  15. data/lib/twitter/error.rb +55 -7
  16. data/lib/twitter/error/already_favorited.rb +1 -1
  17. data/lib/twitter/error/already_posted.rb +10 -0
  18. data/lib/twitter/error/already_retweeted.rb +1 -1
  19. data/lib/twitter/error/bad_gateway.rb +2 -3
  20. data/lib/twitter/error/bad_request.rb +2 -2
  21. data/lib/twitter/error/forbidden.rb +2 -2
  22. data/lib/twitter/error/gateway_timeout.rb +2 -3
  23. data/lib/twitter/error/internal_server_error.rb +2 -3
  24. data/lib/twitter/error/not_acceptable.rb +2 -2
  25. data/lib/twitter/error/not_found.rb +2 -2
  26. data/lib/twitter/error/service_unavailable.rb +2 -3
  27. data/lib/twitter/error/too_many_requests.rb +2 -2
  28. data/lib/twitter/error/unauthorized.rb +2 -2
  29. data/lib/twitter/error/unprocessable_entity.rb +2 -2
  30. data/lib/twitter/factory.rb +2 -8
  31. data/lib/twitter/geo_factory.rb +2 -2
  32. data/lib/twitter/geo_results.rb +36 -0
  33. data/lib/twitter/identity.rb +0 -22
  34. data/lib/twitter/list.rb +18 -4
  35. data/lib/twitter/media/photo.rb +3 -3
  36. data/lib/twitter/media_factory.rb +2 -2
  37. data/lib/twitter/null_object.rb +24 -0
  38. data/lib/twitter/oembed.rb +3 -2
  39. data/lib/twitter/place.rb +15 -9
  40. data/lib/twitter/profile_banner.rb +5 -3
  41. data/lib/twitter/rate_limit.rb +1 -17
  42. data/lib/twitter/relationship.rb +2 -10
  43. data/lib/twitter/rest/api/direct_messages.rb +135 -0
  44. data/lib/twitter/rest/api/favorites.rb +120 -0
  45. data/lib/twitter/rest/api/friends_and_followers.rb +290 -0
  46. data/lib/twitter/rest/api/help.rb +58 -0
  47. data/lib/twitter/rest/api/lists.rb +491 -0
  48. data/lib/twitter/rest/api/oauth.rb +45 -0
  49. data/lib/twitter/rest/api/places_and_geo.rb +104 -0
  50. data/lib/twitter/rest/api/saved_searches.rb +91 -0
  51. data/lib/twitter/rest/api/search.rb +37 -0
  52. data/lib/twitter/rest/api/spam_reporting.rb +29 -0
  53. data/lib/twitter/rest/api/suggested_users.rb +51 -0
  54. data/lib/twitter/rest/api/timelines.rb +202 -0
  55. data/lib/twitter/rest/api/trends.rb +58 -0
  56. data/lib/twitter/rest/api/tweets.rb +293 -0
  57. data/lib/twitter/rest/api/undocumented.rb +52 -0
  58. data/lib/twitter/rest/api/users.rb +383 -0
  59. data/lib/twitter/rest/api/utils.rb +219 -0
  60. data/lib/twitter/rest/client.rb +193 -0
  61. data/lib/twitter/rest/request/multipart_with_file.rb +36 -0
  62. data/lib/twitter/rest/response/parse_json.rb +27 -0
  63. data/lib/twitter/{response → rest/response}/raise_error.rb +8 -11
  64. data/lib/twitter/search_results.rb +33 -21
  65. data/lib/twitter/settings.rb +1 -6
  66. data/lib/twitter/size.rb +1 -1
  67. data/lib/twitter/streaming/client.rb +77 -0
  68. data/lib/twitter/streaming/connection.rb +22 -0
  69. data/lib/twitter/streaming/response.rb +30 -0
  70. data/lib/twitter/suggestion.rb +4 -2
  71. data/lib/twitter/token.rb +8 -0
  72. data/lib/twitter/trend.rb +2 -1
  73. data/lib/twitter/trend_results.rb +59 -0
  74. data/lib/twitter/tweet.rb +41 -85
  75. data/lib/twitter/user.rb +51 -41
  76. data/lib/twitter/version.rb +4 -4
  77. data/spec/fixtures/already_posted.json +1 -0
  78. data/spec/fixtures/ids_list.json +1 -1
  79. data/spec/fixtures/ids_list2.json +1 -1
  80. data/spec/fixtures/search.json +1 -1
  81. data/spec/fixtures/search_malformed.json +1 -1
  82. data/spec/fixtures/track_streaming.json +3 -0
  83. data/spec/helper.rb +8 -13
  84. data/spec/twitter/base_spec.rb +25 -99
  85. data/spec/twitter/configuration_spec.rb +1 -1
  86. data/spec/twitter/cursor_spec.rb +13 -31
  87. data/spec/twitter/direct_message_spec.rb +41 -8
  88. data/spec/twitter/entity/uri_spec.rb +74 -0
  89. data/spec/twitter/error_spec.rb +59 -11
  90. data/spec/twitter/geo/point_spec.rb +1 -1
  91. data/spec/twitter/geo_factory_spec.rb +3 -3
  92. data/spec/twitter/geo_results_spec.rb +35 -0
  93. data/spec/twitter/identifiable_spec.rb +0 -21
  94. data/spec/twitter/list_spec.rb +51 -8
  95. data/spec/twitter/media/photo_spec.rb +118 -3
  96. data/spec/twitter/media_factory_spec.rb +2 -2
  97. data/spec/twitter/null_object_spec.rb +26 -0
  98. data/spec/twitter/oembed_spec.rb +69 -45
  99. data/spec/twitter/place_spec.rb +68 -12
  100. data/spec/twitter/profile_banner_spec.rb +1 -1
  101. data/spec/twitter/rate_limit_spec.rb +12 -12
  102. data/spec/twitter/relationship_spec.rb +31 -9
  103. data/spec/twitter/{api → rest/api}/direct_messages_spec.rb +22 -9
  104. data/spec/twitter/{api → rest/api}/favorites_spec.rb +80 -7
  105. data/spec/twitter/{api → rest/api}/friends_and_followers_spec.rb +104 -65
  106. data/spec/twitter/{api → rest/api}/geo_spec.rb +10 -10
  107. data/spec/twitter/{api → rest/api}/help_spec.rb +6 -6
  108. data/spec/twitter/{api → rest/api}/lists_spec.rb +77 -56
  109. data/spec/twitter/{api → rest/api}/oauth_spec.rb +6 -6
  110. data/spec/twitter/{api → rest/api}/saved_searches_spec.rb +7 -7
  111. data/spec/twitter/{api → rest/api}/search_spec.rb +8 -9
  112. data/spec/twitter/{api → rest/api}/spam_reporting_spec.rb +3 -3
  113. data/spec/twitter/{api → rest/api}/suggested_users_spec.rb +5 -5
  114. data/spec/twitter/{api → rest/api}/timelines_spec.rb +9 -9
  115. data/spec/twitter/{api → rest/api}/trends_spec.rb +6 -6
  116. data/spec/twitter/rest/api/tweets_spec.rb +503 -0
  117. data/spec/twitter/{api → rest/api}/undocumented_spec.rb +19 -45
  118. data/spec/twitter/{api → rest/api}/users_spec.rb +60 -35
  119. data/spec/twitter/rest/client_spec.rb +193 -0
  120. data/spec/twitter/saved_search_spec.rb +11 -0
  121. data/spec/twitter/search_results_spec.rb +29 -42
  122. data/spec/twitter/settings_spec.rb +17 -6
  123. data/spec/twitter/streaming/client_spec.rb +75 -0
  124. data/spec/twitter/token_spec.rb +16 -0
  125. data/spec/twitter/trend_results_spec.rb +89 -0
  126. data/spec/twitter/trend_spec.rb +23 -0
  127. data/spec/twitter/tweet_spec.rb +122 -115
  128. data/spec/twitter/user_spec.rb +136 -77
  129. data/spec/twitter_spec.rb +0 -119
  130. data/twitter.gemspec +8 -5
  131. metadata +148 -141
  132. metadata.gz.sig +0 -0
  133. data/lib/twitter/action/favorite.rb +0 -19
  134. data/lib/twitter/action/follow.rb +0 -30
  135. data/lib/twitter/action/list_member_added.rb +0 -39
  136. data/lib/twitter/action/mention.rb +0 -46
  137. data/lib/twitter/action/reply.rb +0 -27
  138. data/lib/twitter/action/retweet.rb +0 -27
  139. data/lib/twitter/action/tweet.rb +0 -20
  140. data/lib/twitter/action_factory.rb +0 -22
  141. data/lib/twitter/api/arguments.rb +0 -13
  142. data/lib/twitter/api/direct_messages.rb +0 -148
  143. data/lib/twitter/api/favorites.rb +0 -126
  144. data/lib/twitter/api/friends_and_followers.rb +0 -334
  145. data/lib/twitter/api/help.rb +0 -64
  146. data/lib/twitter/api/lists.rb +0 -618
  147. data/lib/twitter/api/oauth.rb +0 -44
  148. data/lib/twitter/api/places_and_geo.rb +0 -121
  149. data/lib/twitter/api/saved_searches.rb +0 -99
  150. data/lib/twitter/api/search.rb +0 -37
  151. data/lib/twitter/api/spam_reporting.rb +0 -30
  152. data/lib/twitter/api/suggested_users.rb +0 -55
  153. data/lib/twitter/api/timelines.rb +0 -214
  154. data/lib/twitter/api/trends.rb +0 -63
  155. data/lib/twitter/api/tweets.rb +0 -304
  156. data/lib/twitter/api/undocumented.rb +0 -97
  157. data/lib/twitter/api/users.rb +0 -439
  158. data/lib/twitter/api/utils.rb +0 -187
  159. data/lib/twitter/configurable.rb +0 -96
  160. data/lib/twitter/default.rb +0 -102
  161. data/lib/twitter/entity/url.rb +0 -9
  162. data/lib/twitter/error/client_error.rb +0 -35
  163. data/lib/twitter/error/decode_error.rb +0 -9
  164. data/lib/twitter/error/identity_map_key_error.rb +0 -9
  165. data/lib/twitter/error/server_error.rb +0 -28
  166. data/lib/twitter/exceptable.rb +0 -36
  167. data/lib/twitter/identity_map.rb +0 -22
  168. data/lib/twitter/request/multipart_with_file.rb +0 -34
  169. data/lib/twitter/response/parse_json.rb +0 -25
  170. data/spec/fixtures/about_me.json +0 -1
  171. data/spec/fixtures/activity_summary.json +0 -1
  172. data/spec/fixtures/bad_gateway.json +0 -1
  173. data/spec/fixtures/bad_request.json +0 -1
  174. data/spec/fixtures/by_friends.json +0 -1
  175. data/spec/fixtures/end_session.json +0 -1
  176. data/spec/fixtures/forbidden.json +0 -1
  177. data/spec/fixtures/internal_server_error.json +0 -1
  178. data/spec/fixtures/not_acceptable.json +0 -1
  179. data/spec/fixtures/phoenix_search.phoenix +0 -1
  180. data/spec/fixtures/resolve.json +0 -1
  181. data/spec/fixtures/service_unavailable.json +0 -1
  182. data/spec/fixtures/totals.json +0 -1
  183. data/spec/fixtures/trends.json +0 -1
  184. data/spec/fixtures/unauthorized.json +0 -1
  185. data/spec/fixtures/video_facets.json +0 -1
  186. data/spec/twitter/action/favorite_spec.rb +0 -29
  187. data/spec/twitter/action/follow_spec.rb +0 -29
  188. data/spec/twitter/action/list_member_added_spec.rb +0 -41
  189. data/spec/twitter/action/mention_spec.rb +0 -52
  190. data/spec/twitter/action/reply_spec.rb +0 -41
  191. data/spec/twitter/action/retweet_spec.rb +0 -41
  192. data/spec/twitter/action_factory_spec.rb +0 -35
  193. data/spec/twitter/action_spec.rb +0 -16
  194. data/spec/twitter/api/tweets_spec.rb +0 -285
  195. data/spec/twitter/client_spec.rb +0 -223
  196. data/spec/twitter/error/client_error_spec.rb +0 -23
  197. data/spec/twitter/error/server_error_spec.rb +0 -20
@@ -0,0 +1,219 @@
1
+ require 'twitter/arguments'
2
+ require 'twitter/cursor'
3
+ require 'twitter/user'
4
+ require 'uri'
5
+
6
+ module Twitter
7
+ module REST
8
+ module API
9
+ module Utils
10
+
11
+ DEFAULT_CURSOR = -1
12
+ URI_SUBSTRING = "://"
13
+
14
+ private
15
+
16
+ # Take a URI string or Twitter::Identity object and return its ID
17
+ #
18
+ # @param object [Integer, String, URI, Twitter::Identity] An ID, URI, or object.
19
+ # @return [Integer]
20
+ def extract_id(object)
21
+ case object
22
+ when ::Integer
23
+ object
24
+ when ::String
25
+ object.split("/").last.to_i
26
+ when ::URI
27
+ object.path.split("/").last.to_i
28
+ when Twitter::Identity
29
+ object.id
30
+ end
31
+ end
32
+
33
+ # @param request_method [Symbol]
34
+ # @param path [String]
35
+ # @param args [Array]
36
+ # @return [Array<Twitter::User>]
37
+ def threaded_user_objects_from_response(request_method, path, args)
38
+ arguments = Twitter::Arguments.new(args)
39
+ arguments.flatten.threaded_map do |user|
40
+ object_from_response(Twitter::User, request_method, path, merge_user(arguments.options, user))
41
+ end
42
+ end
43
+
44
+ # @param request_method [Symbol]
45
+ # @param path [String]
46
+ # @param args [Array]
47
+ # @return [Array<Twitter::User>]
48
+ def user_objects_from_response(request_method, path, args)
49
+ arguments = Twitter::Arguments.new(args)
50
+ merge_user!(arguments.options, arguments.pop || screen_name) unless arguments.options[:user_id] || arguments.options[:screen_name]
51
+ objects_from_response(Twitter::User, request_method, path, arguments.options)
52
+ end
53
+
54
+ # @param klass [Class]
55
+ # @param request_method [Symbol]
56
+ # @param path [String]
57
+ # @param args [Array]
58
+ # @return [Array]
59
+ def objects_from_response_with_user(klass, request_method, path, args)
60
+ arguments = Twitter::Arguments.new(args)
61
+ merge_user!(arguments.options, arguments.pop)
62
+ objects_from_response(klass, request_method, path, arguments.options)
63
+ end
64
+
65
+ # @param klass [Class]
66
+ # @param request_method [Symbol]
67
+ # @param path [String]
68
+ # @param options [Hash]
69
+ # @return [Array]
70
+ def objects_from_response(klass, request_method, path, options={})
71
+ response = send(request_method.to_sym, path, options)[:body]
72
+ objects_from_array(klass, response)
73
+ end
74
+
75
+ # @param klass [Class]
76
+ # @param array [Array]
77
+ # @return [Array]
78
+ def objects_from_array(klass, array)
79
+ array.map do |element|
80
+ klass.new(element)
81
+ end
82
+ end
83
+
84
+ # @param klass [Class]
85
+ # @param request_method [Symbol]
86
+ # @param path [String]
87
+ # @param args [Array]
88
+ # @return [Array]
89
+ def threaded_objects_from_response(klass, request_method, path, args)
90
+ arguments = Twitter::Arguments.new(args)
91
+ arguments.flatten.threaded_map do |object|
92
+ id = extract_id(object)
93
+ object_from_response(klass, request_method, path, arguments.options.merge(:id => id))
94
+ end
95
+ end
96
+
97
+ # @param klass [Class]
98
+ # @param request_method [Symbol]
99
+ # @param path [String]
100
+ # @param options [Hash]
101
+ # @return [Object]
102
+ def object_from_response(klass, request_method, path, options={})
103
+ response = send(request_method.to_sym, path, options)
104
+ klass.from_response(response)
105
+ end
106
+
107
+ # @param collection_name [Symbol]
108
+ # @param klass [Class]
109
+ # @param request_method [Symbol]
110
+ # @param path [String]
111
+ # @param args [Array]
112
+ # @return [Twitter::Cursor]
113
+ def cursor_from_response_with_user(collection_name, klass, request_method, path, args)
114
+ arguments = Twitter::Arguments.new(args)
115
+ merge_user!(arguments.options, arguments.pop || screen_name) unless arguments.options[:user_id] || arguments.options[:screen_name]
116
+ cursor_from_response(collection_name, klass, request_method, path, arguments.options)
117
+ end
118
+
119
+ # @param collection_name [Symbol]
120
+ # @param klass [Class]
121
+ # @param request_method [Symbol]
122
+ # @param path [String]
123
+ # @param options [Hash]
124
+ # @return [Twitter::Cursor]
125
+ def cursor_from_response(collection_name, klass, request_method, path, options)
126
+ merge_default_cursor!(options)
127
+ response = send(request_method.to_sym, path, options)
128
+ Twitter::Cursor.from_response(response, collection_name.to_sym, klass, self, request_method, path, options)
129
+ end
130
+
131
+ def handle_forbidden_error(klass, error)
132
+ if error.message == klass::MESSAGE
133
+ raise klass.new
134
+ else
135
+ raise error
136
+ end
137
+ end
138
+
139
+ def merge_default_cursor!(options)
140
+ options[:cursor] = DEFAULT_CURSOR unless options[:cursor]
141
+ end
142
+
143
+ def screen_name
144
+ @screen_name ||= verify_credentials.screen_name
145
+ end
146
+
147
+ # Take a user and merge it into the hash with the correct key
148
+ #
149
+ # @param hash [Hash]
150
+ # @param user [Integer, String, Twitter::User] A Twitter user ID, screen name, URI, or object.
151
+ # @return [Hash]
152
+ def merge_user(hash, user, prefix=nil)
153
+ merge_user!(hash.dup, user, prefix)
154
+ end
155
+
156
+ # Take a user and merge it into the hash with the correct key
157
+ #
158
+ # @param hash [Hash]
159
+ # @param user [Integer, String, URI, Twitter::User] A Twitter user ID, screen name, URI, or object.
160
+ # @return [Hash]
161
+ def merge_user!(hash, user, prefix=nil)
162
+ case user
163
+ when Integer
164
+ hash[[prefix, "user_id"].compact.join("_").to_sym] = user
165
+ when String
166
+ if user[URI_SUBSTRING]
167
+ hash[[prefix, "screen_name"].compact.join("_").to_sym] = user.split("/").last
168
+ else
169
+ hash[[prefix, "screen_name"].compact.join("_").to_sym] = user
170
+ end
171
+ when ::URI
172
+ hash[[prefix, "screen_name"].compact.join("_").to_sym] = user.path.split("/").last
173
+ when Twitter::User
174
+ hash[[prefix, "user_id"].compact.join("_").to_sym] = user.id
175
+ end
176
+ hash
177
+ end
178
+
179
+ # Take a multiple users and merge them into the hash with the correct keys
180
+ #
181
+ # @param hash [Hash]
182
+ # @param users [Enumerable<Integer, String, Twitter::User>] A collection of Twitter user IDs, screen_names, or objects.
183
+ # @return [Hash]
184
+ def merge_users(hash, users)
185
+ merge_users!(hash.dup, users)
186
+ end
187
+
188
+ # Take a multiple users and merge them into the hash with the correct keys
189
+ #
190
+ # @param hash [Hash]
191
+ # @param users [Enumerable<Integer, String, URI, Twitter::User>] A collection of Twitter user IDs, screen_names, URIs, or objects.
192
+ # @return [Hash]
193
+ def merge_users!(hash, users)
194
+ user_ids, screen_names = [], []
195
+ users.flatten.each do |user|
196
+ case user
197
+ when Integer
198
+ user_ids << user
199
+ when String
200
+ if user[URI_SUBSTRING]
201
+ screen_names << user.split("/").last
202
+ else
203
+ screen_names << user
204
+ end
205
+ when ::URI
206
+ screen_names << user.path.split("/").last
207
+ when Twitter::User
208
+ user_ids << user.id
209
+ end
210
+ end
211
+ hash[:user_id] = user_ids.join(',') unless user_ids.empty?
212
+ hash[:screen_name] = screen_names.join(',') unless screen_names.empty?
213
+ hash
214
+ end
215
+
216
+ end
217
+ end
218
+ end
219
+ end
@@ -0,0 +1,193 @@
1
+ require 'base64'
2
+ require 'faraday'
3
+ require 'faraday/request/multipart'
4
+ require 'json'
5
+ require 'twitter/client'
6
+ require 'twitter/error'
7
+ require 'twitter/error/configuration_error'
8
+ require 'twitter/rest/api/direct_messages'
9
+ require 'twitter/rest/api/favorites'
10
+ require 'twitter/rest/api/friends_and_followers'
11
+ require 'twitter/rest/api/help'
12
+ require 'twitter/rest/api/lists'
13
+ require 'twitter/rest/api/oauth'
14
+ require 'twitter/rest/api/places_and_geo'
15
+ require 'twitter/rest/api/saved_searches'
16
+ require 'twitter/rest/api/search'
17
+ require 'twitter/rest/api/spam_reporting'
18
+ require 'twitter/rest/api/suggested_users'
19
+ require 'twitter/rest/api/timelines'
20
+ require 'twitter/rest/api/trends'
21
+ require 'twitter/rest/api/tweets'
22
+ require 'twitter/rest/api/undocumented'
23
+ require 'twitter/rest/api/users'
24
+ require 'twitter/rest/request/multipart_with_file'
25
+ require 'twitter/rest/response/parse_json'
26
+ require 'twitter/rest/response/raise_error'
27
+
28
+ module Twitter
29
+ module REST
30
+ # Wrapper for the Twitter REST API
31
+ #
32
+ # @note All methods have been separated into modules and follow the same grouping used in {http://dev.twitter.com/doc the Twitter API Documentation}.
33
+ # @see http://dev.twitter.com/pages/every_developer
34
+ class Client < Twitter::Client
35
+ include Twitter::REST::API::DirectMessages
36
+ include Twitter::REST::API::Favorites
37
+ include Twitter::REST::API::FriendsAndFollowers
38
+ include Twitter::REST::API::Help
39
+ include Twitter::REST::API::Lists
40
+ include Twitter::REST::API::OAuth
41
+ include Twitter::REST::API::PlacesAndGeo
42
+ include Twitter::REST::API::SavedSearches
43
+ include Twitter::REST::API::Search
44
+ include Twitter::REST::API::SpamReporting
45
+ include Twitter::REST::API::SuggestedUsers
46
+ include Twitter::REST::API::Timelines
47
+ include Twitter::REST::API::Trends
48
+ include Twitter::REST::API::Tweets
49
+ include Twitter::REST::API::Undocumented
50
+ include Twitter::REST::API::Users
51
+
52
+ attr_writer :bearer_token, :connection_options, :middleware
53
+
54
+ ENDPOINT = 'https://api.twitter.com'
55
+
56
+ # @return [String]
57
+ def bearer_token
58
+ if instance_variable_defined?(:@bearer_token)
59
+ @bearer_token
60
+ else
61
+ ENV['TWITTER_BEARER_TOKEN']
62
+ end
63
+ end
64
+
65
+ def connection_options
66
+ {
67
+ :builder => middleware,
68
+ :headers => {
69
+ :accept => 'application/json',
70
+ :user_agent => user_agent,
71
+ },
72
+ :request => {
73
+ :open_timeout => 5,
74
+ :timeout => 10,
75
+ },
76
+ }
77
+ end
78
+
79
+ # @note Faraday's middleware stack implementation is comparable to that of Rack middleware. The order of middleware is important: the first middleware on the list wraps all others, while the last middleware is the innermost one.
80
+ # @see https://github.com/technoweenie/faraday#advanced-middleware-usage
81
+ # @see http://mislav.uniqpath.com/2011/07/faraday-advanced-http/
82
+ # @return [Faraday::Builder]
83
+ def middleware
84
+ @middleware ||= Faraday::Builder.new do |builder|
85
+ # Convert file uploads to Faraday::UploadIO objects
86
+ builder.use Twitter::REST::Request::MultipartWithFile
87
+ # Checks for files in the payload
88
+ builder.use Faraday::Request::Multipart
89
+ # Convert request params to "www-form-urlencoded"
90
+ builder.use Faraday::Request::UrlEncoded
91
+ # Handle error responses
92
+ builder.use Twitter::REST::Response::RaiseError
93
+ # Parse JSON response bodies
94
+ builder.use Twitter::REST::Response::ParseJson
95
+ # Set Faraday's HTTP adapter
96
+ builder.adapter Faraday.default_adapter
97
+ end
98
+ end
99
+
100
+ # Perform an HTTP DELETE request
101
+ def delete(path, params={})
102
+ request(:delete, path, params)
103
+ end
104
+
105
+ # Perform an HTTP GET request
106
+ def get(path, params={})
107
+ request(:get, path, params)
108
+ end
109
+
110
+ # Perform an HTTP POST request
111
+ def post(path, params={})
112
+ signature_params = params.values.any?{|value| value.respond_to?(:to_io)} ? {} : params
113
+ request(:post, path, params, signature_params)
114
+ end
115
+
116
+ # Perform an HTTP PUT request
117
+ def put(path, params={})
118
+ request(:put, path, params)
119
+ end
120
+
121
+ # @return [Boolean]
122
+ def bearer_token?
123
+ !!bearer_token
124
+ end
125
+
126
+ # @return [Boolean]
127
+ def credentials?
128
+ super || bearer_token?
129
+ end
130
+
131
+ private
132
+
133
+ # Returns a proc that can be used to setup the Faraday::Request headers
134
+ #
135
+ # @param method [Symbol]
136
+ # @param path [String]
137
+ # @param params [Hash]
138
+ # @return [Proc]
139
+ def request_setup(method, path, params, signature_params)
140
+ Proc.new do |request|
141
+ if params.delete(:bearer_token_request)
142
+ request.headers[:authorization] = bearer_token_credentials_auth_header
143
+ request.headers[:content_type] = 'application/x-www-form-urlencoded; charset=UTF-8'
144
+ request.headers[:accept] = '*/*' # It is important we set this, otherwise we get an error.
145
+ elsif params.delete(:app_auth) || !user_token?
146
+ @bearer_token = token unless bearer_token?
147
+ request.headers[:authorization] = bearer_auth_header
148
+ else
149
+ request.headers[:authorization] = oauth_auth_header(method, ENDPOINT + path, signature_params).to_s
150
+ end
151
+ end
152
+ end
153
+
154
+ def request(method, path, params={}, signature_params=params)
155
+ request_setup = request_setup(method, path, params, signature_params)
156
+ connection.send(method.to_sym, path, params, &request_setup).env
157
+ rescue Faraday::Error::ClientError, JSON::ParserError
158
+ raise Twitter::Error
159
+ end
160
+
161
+ # Returns a Faraday::Connection object
162
+ #
163
+ # @return [Faraday::Connection]
164
+ def connection
165
+ @connection ||= Faraday.new(ENDPOINT, connection_options)
166
+ end
167
+
168
+ # Generates authentication header for a bearer token request
169
+ #
170
+ # @return [String]
171
+ def bearer_token_credentials_auth_header
172
+ basic_auth_token = strict_encode64("#{@consumer_key}:#{@consumer_secret}")
173
+ "Basic #{basic_auth_token}"
174
+ end
175
+
176
+ def bearer_auth_header
177
+ if bearer_token.is_a?(Twitter::Token) && bearer_token.bearer?
178
+ "Bearer #{bearer_token.access_token}"
179
+ else
180
+ "Bearer #{bearer_token}"
181
+ end
182
+ end
183
+
184
+ private
185
+
186
+ # Base64.strict_encode64 is not available on Ruby 1.8.7
187
+ def strict_encode64(str)
188
+ Base64.encode64(str).gsub("\n", "")
189
+ end
190
+
191
+ end
192
+ end
193
+ end
@@ -0,0 +1,36 @@
1
+ require 'faraday'
2
+
3
+ module Twitter
4
+ module REST
5
+ module Request
6
+ class MultipartWithFile < Faraday::Middleware
7
+ CONTENT_TYPE = 'Content-Type'
8
+
9
+ def call(env)
10
+ env[:body].each do |key, value|
11
+ if value.respond_to?(:to_io)
12
+ env[:body][key] = Faraday::UploadIO.new(value, mime_type(value.path), value.path)
13
+ end
14
+ end if env[:body].is_a?(::Hash)
15
+ @app.call(env)
16
+ end
17
+
18
+ private
19
+
20
+ def mime_type(path)
21
+ case path
22
+ when /\.jpe?g/i
23
+ 'image/jpeg'
24
+ when /\.gif$/i
25
+ 'image/gif'
26
+ when /\.png$/i
27
+ 'image/png'
28
+ else
29
+ 'application/octet-stream'
30
+ end
31
+ end
32
+
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,27 @@
1
+ require 'faraday'
2
+ require 'json'
3
+
4
+ module Twitter
5
+ module REST
6
+ module Response
7
+ class ParseJson < Faraday::Response::Middleware
8
+
9
+ def parse(body)
10
+ case body
11
+ when /\A^\s*$\z/, nil
12
+ nil
13
+ else
14
+ JSON.parse(body, :symbolize_names => true)
15
+ end
16
+ end
17
+
18
+ def on_complete(env)
19
+ if respond_to?(:parse)
20
+ env[:body] = parse(env[:body]) unless [204, 301, 302, 304].include?(env[:status])
21
+ end
22
+ end
23
+
24
+ end
25
+ end
26
+ end
27
+ end