twitter 4.8.1 → 5.0.0.rc.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 (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