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.
- data.tar.gz.sig +0 -0
- data/CHANGELOG.md +30 -0
- data/LICENSE.md +1 -1
- data/README.md +386 -266
- data/lib/twitter.rb +4 -39
- data/lib/twitter/arguments.rb +11 -0
- data/lib/twitter/base.rb +89 -68
- data/lib/twitter/client.rb +69 -110
- data/lib/twitter/configuration.rb +7 -3
- data/lib/twitter/creatable.rb +2 -4
- data/lib/twitter/cursor.rb +50 -42
- data/lib/twitter/direct_message.rb +2 -11
- data/lib/twitter/entity/uri.rb +13 -0
- data/lib/twitter/enumerable.rb +15 -0
- data/lib/twitter/error.rb +55 -7
- data/lib/twitter/error/already_favorited.rb +1 -1
- data/lib/twitter/error/already_posted.rb +10 -0
- data/lib/twitter/error/already_retweeted.rb +1 -1
- data/lib/twitter/error/bad_gateway.rb +2 -3
- data/lib/twitter/error/bad_request.rb +2 -2
- data/lib/twitter/error/forbidden.rb +2 -2
- data/lib/twitter/error/gateway_timeout.rb +2 -3
- data/lib/twitter/error/internal_server_error.rb +2 -3
- data/lib/twitter/error/not_acceptable.rb +2 -2
- data/lib/twitter/error/not_found.rb +2 -2
- data/lib/twitter/error/service_unavailable.rb +2 -3
- data/lib/twitter/error/too_many_requests.rb +2 -2
- data/lib/twitter/error/unauthorized.rb +2 -2
- data/lib/twitter/error/unprocessable_entity.rb +2 -2
- data/lib/twitter/factory.rb +2 -8
- data/lib/twitter/geo_factory.rb +2 -2
- data/lib/twitter/geo_results.rb +36 -0
- data/lib/twitter/identity.rb +0 -22
- data/lib/twitter/list.rb +18 -4
- data/lib/twitter/media/photo.rb +3 -3
- data/lib/twitter/media_factory.rb +2 -2
- data/lib/twitter/null_object.rb +24 -0
- data/lib/twitter/oembed.rb +3 -2
- data/lib/twitter/place.rb +15 -9
- data/lib/twitter/profile_banner.rb +5 -3
- data/lib/twitter/rate_limit.rb +1 -17
- data/lib/twitter/relationship.rb +2 -10
- data/lib/twitter/rest/api/direct_messages.rb +135 -0
- data/lib/twitter/rest/api/favorites.rb +120 -0
- data/lib/twitter/rest/api/friends_and_followers.rb +290 -0
- data/lib/twitter/rest/api/help.rb +58 -0
- data/lib/twitter/rest/api/lists.rb +491 -0
- data/lib/twitter/rest/api/oauth.rb +45 -0
- data/lib/twitter/rest/api/places_and_geo.rb +104 -0
- data/lib/twitter/rest/api/saved_searches.rb +91 -0
- data/lib/twitter/rest/api/search.rb +37 -0
- data/lib/twitter/rest/api/spam_reporting.rb +29 -0
- data/lib/twitter/rest/api/suggested_users.rb +51 -0
- data/lib/twitter/rest/api/timelines.rb +202 -0
- data/lib/twitter/rest/api/trends.rb +58 -0
- data/lib/twitter/rest/api/tweets.rb +293 -0
- data/lib/twitter/rest/api/undocumented.rb +52 -0
- data/lib/twitter/rest/api/users.rb +383 -0
- data/lib/twitter/rest/api/utils.rb +219 -0
- data/lib/twitter/rest/client.rb +193 -0
- data/lib/twitter/rest/request/multipart_with_file.rb +36 -0
- data/lib/twitter/rest/response/parse_json.rb +27 -0
- data/lib/twitter/{response → rest/response}/raise_error.rb +8 -11
- data/lib/twitter/search_results.rb +33 -21
- data/lib/twitter/settings.rb +1 -6
- data/lib/twitter/size.rb +1 -1
- data/lib/twitter/streaming/client.rb +77 -0
- data/lib/twitter/streaming/connection.rb +22 -0
- data/lib/twitter/streaming/response.rb +30 -0
- data/lib/twitter/suggestion.rb +4 -2
- data/lib/twitter/token.rb +8 -0
- data/lib/twitter/trend.rb +2 -1
- data/lib/twitter/trend_results.rb +59 -0
- data/lib/twitter/tweet.rb +41 -85
- data/lib/twitter/user.rb +51 -41
- data/lib/twitter/version.rb +4 -4
- data/spec/fixtures/already_posted.json +1 -0
- data/spec/fixtures/ids_list.json +1 -1
- data/spec/fixtures/ids_list2.json +1 -1
- data/spec/fixtures/search.json +1 -1
- data/spec/fixtures/search_malformed.json +1 -1
- data/spec/fixtures/track_streaming.json +3 -0
- data/spec/helper.rb +8 -13
- data/spec/twitter/base_spec.rb +25 -99
- data/spec/twitter/configuration_spec.rb +1 -1
- data/spec/twitter/cursor_spec.rb +13 -31
- data/spec/twitter/direct_message_spec.rb +41 -8
- data/spec/twitter/entity/uri_spec.rb +74 -0
- data/spec/twitter/error_spec.rb +59 -11
- data/spec/twitter/geo/point_spec.rb +1 -1
- data/spec/twitter/geo_factory_spec.rb +3 -3
- data/spec/twitter/geo_results_spec.rb +35 -0
- data/spec/twitter/identifiable_spec.rb +0 -21
- data/spec/twitter/list_spec.rb +51 -8
- data/spec/twitter/media/photo_spec.rb +118 -3
- data/spec/twitter/media_factory_spec.rb +2 -2
- data/spec/twitter/null_object_spec.rb +26 -0
- data/spec/twitter/oembed_spec.rb +69 -45
- data/spec/twitter/place_spec.rb +68 -12
- data/spec/twitter/profile_banner_spec.rb +1 -1
- data/spec/twitter/rate_limit_spec.rb +12 -12
- data/spec/twitter/relationship_spec.rb +31 -9
- data/spec/twitter/{api → rest/api}/direct_messages_spec.rb +22 -9
- data/spec/twitter/{api → rest/api}/favorites_spec.rb +80 -7
- data/spec/twitter/{api → rest/api}/friends_and_followers_spec.rb +104 -65
- data/spec/twitter/{api → rest/api}/geo_spec.rb +10 -10
- data/spec/twitter/{api → rest/api}/help_spec.rb +6 -6
- data/spec/twitter/{api → rest/api}/lists_spec.rb +77 -56
- data/spec/twitter/{api → rest/api}/oauth_spec.rb +6 -6
- data/spec/twitter/{api → rest/api}/saved_searches_spec.rb +7 -7
- data/spec/twitter/{api → rest/api}/search_spec.rb +8 -9
- data/spec/twitter/{api → rest/api}/spam_reporting_spec.rb +3 -3
- data/spec/twitter/{api → rest/api}/suggested_users_spec.rb +5 -5
- data/spec/twitter/{api → rest/api}/timelines_spec.rb +9 -9
- data/spec/twitter/{api → rest/api}/trends_spec.rb +6 -6
- data/spec/twitter/rest/api/tweets_spec.rb +503 -0
- data/spec/twitter/{api → rest/api}/undocumented_spec.rb +19 -45
- data/spec/twitter/{api → rest/api}/users_spec.rb +60 -35
- data/spec/twitter/rest/client_spec.rb +193 -0
- data/spec/twitter/saved_search_spec.rb +11 -0
- data/spec/twitter/search_results_spec.rb +29 -42
- data/spec/twitter/settings_spec.rb +17 -6
- data/spec/twitter/streaming/client_spec.rb +75 -0
- data/spec/twitter/token_spec.rb +16 -0
- data/spec/twitter/trend_results_spec.rb +89 -0
- data/spec/twitter/trend_spec.rb +23 -0
- data/spec/twitter/tweet_spec.rb +122 -115
- data/spec/twitter/user_spec.rb +136 -77
- data/spec/twitter_spec.rb +0 -119
- data/twitter.gemspec +8 -5
- metadata +148 -141
- metadata.gz.sig +0 -0
- data/lib/twitter/action/favorite.rb +0 -19
- data/lib/twitter/action/follow.rb +0 -30
- data/lib/twitter/action/list_member_added.rb +0 -39
- data/lib/twitter/action/mention.rb +0 -46
- data/lib/twitter/action/reply.rb +0 -27
- data/lib/twitter/action/retweet.rb +0 -27
- data/lib/twitter/action/tweet.rb +0 -20
- data/lib/twitter/action_factory.rb +0 -22
- data/lib/twitter/api/arguments.rb +0 -13
- data/lib/twitter/api/direct_messages.rb +0 -148
- data/lib/twitter/api/favorites.rb +0 -126
- data/lib/twitter/api/friends_and_followers.rb +0 -334
- data/lib/twitter/api/help.rb +0 -64
- data/lib/twitter/api/lists.rb +0 -618
- data/lib/twitter/api/oauth.rb +0 -44
- data/lib/twitter/api/places_and_geo.rb +0 -121
- data/lib/twitter/api/saved_searches.rb +0 -99
- data/lib/twitter/api/search.rb +0 -37
- data/lib/twitter/api/spam_reporting.rb +0 -30
- data/lib/twitter/api/suggested_users.rb +0 -55
- data/lib/twitter/api/timelines.rb +0 -214
- data/lib/twitter/api/trends.rb +0 -63
- data/lib/twitter/api/tweets.rb +0 -304
- data/lib/twitter/api/undocumented.rb +0 -97
- data/lib/twitter/api/users.rb +0 -439
- data/lib/twitter/api/utils.rb +0 -187
- data/lib/twitter/configurable.rb +0 -96
- data/lib/twitter/default.rb +0 -102
- data/lib/twitter/entity/url.rb +0 -9
- data/lib/twitter/error/client_error.rb +0 -35
- data/lib/twitter/error/decode_error.rb +0 -9
- data/lib/twitter/error/identity_map_key_error.rb +0 -9
- data/lib/twitter/error/server_error.rb +0 -28
- data/lib/twitter/exceptable.rb +0 -36
- data/lib/twitter/identity_map.rb +0 -22
- data/lib/twitter/request/multipart_with_file.rb +0 -34
- data/lib/twitter/response/parse_json.rb +0 -25
- data/spec/fixtures/about_me.json +0 -1
- data/spec/fixtures/activity_summary.json +0 -1
- data/spec/fixtures/bad_gateway.json +0 -1
- data/spec/fixtures/bad_request.json +0 -1
- data/spec/fixtures/by_friends.json +0 -1
- data/spec/fixtures/end_session.json +0 -1
- data/spec/fixtures/forbidden.json +0 -1
- data/spec/fixtures/internal_server_error.json +0 -1
- data/spec/fixtures/not_acceptable.json +0 -1
- data/spec/fixtures/phoenix_search.phoenix +0 -1
- data/spec/fixtures/resolve.json +0 -1
- data/spec/fixtures/service_unavailable.json +0 -1
- data/spec/fixtures/totals.json +0 -1
- data/spec/fixtures/trends.json +0 -1
- data/spec/fixtures/unauthorized.json +0 -1
- data/spec/fixtures/video_facets.json +0 -1
- data/spec/twitter/action/favorite_spec.rb +0 -29
- data/spec/twitter/action/follow_spec.rb +0 -29
- data/spec/twitter/action/list_member_added_spec.rb +0 -41
- data/spec/twitter/action/mention_spec.rb +0 -52
- data/spec/twitter/action/reply_spec.rb +0 -41
- data/spec/twitter/action/retweet_spec.rb +0 -41
- data/spec/twitter/action_factory_spec.rb +0 -35
- data/spec/twitter/action_spec.rb +0 -16
- data/spec/twitter/api/tweets_spec.rb +0 -285
- data/spec/twitter/client_spec.rb +0 -223
- data/spec/twitter/error/client_error_spec.rb +0 -23
- 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
|