twitter 4.8.1 → 5.0.0

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 (215) hide show
  1. data/CHANGELOG.md +36 -0
  2. data/CONTRIBUTING.md +13 -15
  3. data/LICENSE.md +1 -1
  4. data/README.md +401 -261
  5. data/Rakefile +6 -0
  6. data/lib/twitter/arguments.rb +14 -0
  7. data/lib/twitter/base.rb +109 -89
  8. data/lib/twitter/client.rb +38 -115
  9. data/lib/twitter/configuration.rb +5 -2
  10. data/lib/twitter/core_ext/kernel.rb +5 -1
  11. data/lib/twitter/creatable.rb +7 -4
  12. data/lib/twitter/cursor.rb +57 -45
  13. data/lib/twitter/direct_message.rb +2 -11
  14. data/lib/twitter/entity/uri.rb +14 -0
  15. data/lib/twitter/enumerable.rb +15 -0
  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/error.rb +65 -14
  31. data/lib/twitter/factory.rb +13 -12
  32. data/lib/twitter/geo.rb +2 -7
  33. data/lib/twitter/geo_factory.rb +11 -7
  34. data/lib/twitter/geo_results.rb +40 -0
  35. data/lib/twitter/identity.rb +4 -34
  36. data/lib/twitter/list.rb +21 -4
  37. data/lib/twitter/media/photo.rb +6 -4
  38. data/lib/twitter/media_factory.rb +11 -7
  39. data/lib/twitter/null_object.rb +25 -0
  40. data/lib/twitter/oembed.rb +3 -2
  41. data/lib/twitter/place.rb +15 -15
  42. data/lib/twitter/profile_banner.rb +3 -2
  43. data/lib/twitter/rate_limit.rb +4 -17
  44. data/lib/twitter/relationship.rb +2 -19
  45. data/lib/twitter/rest/api/direct_messages.rb +138 -0
  46. data/lib/twitter/rest/api/favorites.rb +115 -0
  47. data/lib/twitter/rest/api/friends_and_followers.rb +287 -0
  48. data/lib/twitter/rest/api/help.rb +58 -0
  49. data/lib/twitter/rest/api/lists.rb +498 -0
  50. data/lib/twitter/rest/api/oauth.rb +62 -0
  51. data/lib/twitter/rest/api/places_and_geo.rb +86 -0
  52. data/lib/twitter/rest/api/saved_searches.rb +93 -0
  53. data/lib/twitter/rest/api/search.rb +37 -0
  54. data/lib/twitter/rest/api/spam_reporting.rb +29 -0
  55. data/lib/twitter/rest/api/suggested_users.rb +51 -0
  56. data/lib/twitter/rest/api/timelines.rb +202 -0
  57. data/lib/twitter/rest/api/trends.rb +58 -0
  58. data/lib/twitter/rest/api/tweets.rb +291 -0
  59. data/lib/twitter/rest/api/undocumented.rb +52 -0
  60. data/lib/twitter/rest/api/users.rb +383 -0
  61. data/lib/twitter/rest/api/utils.rb +246 -0
  62. data/lib/twitter/rest/client.rb +175 -0
  63. data/lib/twitter/rest/request/multipart_with_file.rb +36 -0
  64. data/lib/twitter/rest/response/parse_error_json.rb +15 -0
  65. data/lib/twitter/rest/response/parse_json.rb +31 -0
  66. data/lib/twitter/{response → rest/response}/raise_error.rb +8 -11
  67. data/lib/twitter/search_results.rb +37 -21
  68. data/lib/twitter/settings.rb +1 -6
  69. data/lib/twitter/size.rb +2 -15
  70. data/lib/twitter/streaming/client.rb +89 -0
  71. data/lib/twitter/streaming/connection.rb +22 -0
  72. data/lib/twitter/streaming/event.rb +35 -0
  73. data/lib/twitter/streaming/friend_list.rb +13 -0
  74. data/lib/twitter/streaming/message_parser.rb +18 -0
  75. data/lib/twitter/streaming/response.rb +34 -0
  76. data/lib/twitter/suggestion.rb +5 -8
  77. data/lib/twitter/token.rb +11 -1
  78. data/lib/twitter/trend.rb +4 -8
  79. data/lib/twitter/trend_results.rb +65 -0
  80. data/lib/twitter/tweet.rb +43 -92
  81. data/lib/twitter/user.rb +72 -47
  82. data/lib/twitter/version.rb +4 -4
  83. data/lib/twitter.rb +4 -39
  84. data/spec/fixtures/already_posted.json +1 -0
  85. data/spec/fixtures/ids_list.json +1 -1
  86. data/spec/fixtures/ids_list2.json +1 -1
  87. data/spec/fixtures/request_token.txt +6 -0
  88. data/spec/fixtures/search.json +1 -1
  89. data/spec/fixtures/search_malformed.json +1 -1
  90. data/spec/fixtures/track_streaming.json +3 -0
  91. data/spec/fixtures/track_streaming_user.json +5 -0
  92. data/spec/helper.rb +8 -13
  93. data/spec/twitter/base_spec.rb +13 -103
  94. data/spec/twitter/basic_user_spec.rb +3 -3
  95. data/spec/twitter/configuration_spec.rb +1 -1
  96. data/spec/twitter/cursor_spec.rb +17 -35
  97. data/spec/twitter/direct_message_spec.rb +44 -11
  98. data/spec/twitter/entity/uri_spec.rb +75 -0
  99. data/spec/twitter/error_spec.rb +59 -11
  100. data/spec/twitter/geo/point_spec.rb +6 -6
  101. data/spec/twitter/geo/polygon_spec.rb +5 -5
  102. data/spec/twitter/geo_factory_spec.rb +4 -4
  103. data/spec/twitter/geo_results_spec.rb +35 -0
  104. data/spec/twitter/geo_spec.rb +6 -6
  105. data/spec/twitter/identifiable_spec.rb +5 -26
  106. data/spec/twitter/list_spec.rb +54 -11
  107. data/spec/twitter/media/photo_spec.rb +122 -6
  108. data/spec/twitter/media_factory_spec.rb +3 -3
  109. data/spec/twitter/null_object_spec.rb +27 -0
  110. data/spec/twitter/oembed_spec.rb +69 -45
  111. data/spec/twitter/place_spec.rb +84 -28
  112. data/spec/twitter/profile_banner_spec.rb +1 -1
  113. data/spec/twitter/rate_limit_spec.rb +8 -25
  114. data/spec/twitter/relationship_spec.rb +26 -12
  115. data/spec/twitter/{api → rest/api}/direct_messages_spec.rb +28 -15
  116. data/spec/twitter/{api → rest/api}/favorites_spec.rb +80 -7
  117. data/spec/twitter/{api → rest/api}/friends_and_followers_spec.rb +121 -152
  118. data/spec/twitter/{api → rest/api}/geo_spec.rb +9 -23
  119. data/spec/twitter/{api → rest/api}/help_spec.rb +6 -6
  120. data/spec/twitter/{api → rest/api}/lists_spec.rb +116 -95
  121. data/spec/twitter/{api → rest/api}/oauth_spec.rb +21 -10
  122. data/spec/twitter/{api → rest/api}/saved_searches_spec.rb +13 -13
  123. data/spec/twitter/{api → rest/api}/search_spec.rb +8 -9
  124. data/spec/twitter/{api → rest/api}/spam_reporting_spec.rb +3 -3
  125. data/spec/twitter/{api → rest/api}/suggested_users_spec.rb +5 -5
  126. data/spec/twitter/{api → rest/api}/timelines_spec.rb +9 -9
  127. data/spec/twitter/{api → rest/api}/trends_spec.rb +6 -6
  128. data/spec/twitter/rest/api/tweets_spec.rb +503 -0
  129. data/spec/twitter/{api → rest/api}/undocumented_spec.rb +19 -45
  130. data/spec/twitter/{api → rest/api}/users_spec.rb +64 -39
  131. data/spec/twitter/rest/client_spec.rb +193 -0
  132. data/spec/twitter/saved_search_spec.rb +14 -3
  133. data/spec/twitter/search_results_spec.rb +32 -45
  134. data/spec/twitter/settings_spec.rb +17 -6
  135. data/spec/twitter/size_spec.rb +5 -15
  136. data/spec/twitter/source_user_spec.rb +3 -3
  137. data/spec/twitter/streaming/client_spec.rb +92 -0
  138. data/spec/twitter/streaming/event_spec.rb +45 -0
  139. data/spec/twitter/suggestion_spec.rb +5 -15
  140. data/spec/twitter/target_user_spec.rb +3 -3
  141. data/spec/twitter/token_spec.rb +16 -0
  142. data/spec/twitter/trend_results_spec.rb +89 -0
  143. data/spec/twitter/trend_spec.rb +26 -13
  144. data/spec/twitter/tweet_spec.rb +137 -124
  145. data/spec/twitter/user_spec.rb +139 -80
  146. data/spec/twitter_spec.rb +0 -119
  147. data/twitter.gemspec +12 -6
  148. data.tar.gz.sig +0 -0
  149. metadata +197 -138
  150. metadata.gz.sig +0 -0
  151. data/lib/twitter/action/favorite.rb +0 -19
  152. data/lib/twitter/action/follow.rb +0 -30
  153. data/lib/twitter/action/list_member_added.rb +0 -39
  154. data/lib/twitter/action/mention.rb +0 -46
  155. data/lib/twitter/action/reply.rb +0 -27
  156. data/lib/twitter/action/retweet.rb +0 -27
  157. data/lib/twitter/action/tweet.rb +0 -20
  158. data/lib/twitter/action_factory.rb +0 -22
  159. data/lib/twitter/api/arguments.rb +0 -13
  160. data/lib/twitter/api/direct_messages.rb +0 -148
  161. data/lib/twitter/api/favorites.rb +0 -126
  162. data/lib/twitter/api/friends_and_followers.rb +0 -334
  163. data/lib/twitter/api/help.rb +0 -64
  164. data/lib/twitter/api/lists.rb +0 -618
  165. data/lib/twitter/api/oauth.rb +0 -44
  166. data/lib/twitter/api/places_and_geo.rb +0 -121
  167. data/lib/twitter/api/saved_searches.rb +0 -99
  168. data/lib/twitter/api/search.rb +0 -37
  169. data/lib/twitter/api/spam_reporting.rb +0 -30
  170. data/lib/twitter/api/suggested_users.rb +0 -55
  171. data/lib/twitter/api/timelines.rb +0 -214
  172. data/lib/twitter/api/trends.rb +0 -63
  173. data/lib/twitter/api/tweets.rb +0 -304
  174. data/lib/twitter/api/undocumented.rb +0 -97
  175. data/lib/twitter/api/users.rb +0 -439
  176. data/lib/twitter/api/utils.rb +0 -187
  177. data/lib/twitter/configurable.rb +0 -96
  178. data/lib/twitter/default.rb +0 -102
  179. data/lib/twitter/entity/url.rb +0 -9
  180. data/lib/twitter/error/client_error.rb +0 -35
  181. data/lib/twitter/error/decode_error.rb +0 -9
  182. data/lib/twitter/error/identity_map_key_error.rb +0 -9
  183. data/lib/twitter/error/server_error.rb +0 -28
  184. data/lib/twitter/exceptable.rb +0 -36
  185. data/lib/twitter/identity_map.rb +0 -22
  186. data/lib/twitter/request/multipart_with_file.rb +0 -34
  187. data/lib/twitter/response/parse_json.rb +0 -25
  188. data/spec/fixtures/about_me.json +0 -1
  189. data/spec/fixtures/activity_summary.json +0 -1
  190. data/spec/fixtures/bad_gateway.json +0 -1
  191. data/spec/fixtures/bad_request.json +0 -1
  192. data/spec/fixtures/by_friends.json +0 -1
  193. data/spec/fixtures/end_session.json +0 -1
  194. data/spec/fixtures/forbidden.json +0 -1
  195. data/spec/fixtures/internal_server_error.json +0 -1
  196. data/spec/fixtures/not_acceptable.json +0 -1
  197. data/spec/fixtures/phoenix_search.phoenix +0 -1
  198. data/spec/fixtures/resolve.json +0 -1
  199. data/spec/fixtures/service_unavailable.json +0 -1
  200. data/spec/fixtures/totals.json +0 -1
  201. data/spec/fixtures/trends.json +0 -1
  202. data/spec/fixtures/unauthorized.json +0 -1
  203. data/spec/fixtures/video_facets.json +0 -1
  204. data/spec/twitter/action/favorite_spec.rb +0 -29
  205. data/spec/twitter/action/follow_spec.rb +0 -29
  206. data/spec/twitter/action/list_member_added_spec.rb +0 -41
  207. data/spec/twitter/action/mention_spec.rb +0 -52
  208. data/spec/twitter/action/reply_spec.rb +0 -41
  209. data/spec/twitter/action/retweet_spec.rb +0 -41
  210. data/spec/twitter/action_factory_spec.rb +0 -35
  211. data/spec/twitter/action_spec.rb +0 -16
  212. data/spec/twitter/api/tweets_spec.rb +0 -285
  213. data/spec/twitter/client_spec.rb +0 -223
  214. data/spec/twitter/error/client_error_spec.rb +0 -23
  215. data/spec/twitter/error/server_error_spec.rb +0 -20
@@ -0,0 +1,175 @@
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
+ attr_accessor :bearer_token
52
+ attr_writer :connection_options, :middleware
53
+ ENDPOINT = 'https://api.twitter.com'
54
+
55
+ def connection_options
56
+ @connection_options ||= {
57
+ :builder => middleware,
58
+ :headers => {
59
+ :accept => 'application/json',
60
+ :user_agent => user_agent,
61
+ },
62
+ :request => {
63
+ :open_timeout => 5,
64
+ :timeout => 10,
65
+ },
66
+ }
67
+ end
68
+
69
+ # @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.
70
+ # @see https://github.com/technoweenie/faraday#advanced-middleware-usage
71
+ # @see http://mislav.uniqpath.com/2011/07/faraday-advanced-http/
72
+ # @return [Faraday::Builder]
73
+ def middleware
74
+ @middleware ||= Faraday::Builder.new do |builder|
75
+ # Convert file uploads to Faraday::UploadIO objects
76
+ builder.use Twitter::REST::Request::MultipartWithFile
77
+ # Checks for files in the payload
78
+ builder.use Faraday::Request::Multipart
79
+ # Convert request params to "www-form-urlencoded"
80
+ builder.use Faraday::Request::UrlEncoded
81
+ # Handle error responses
82
+ builder.use Twitter::REST::Response::RaiseError
83
+ # Parse JSON response bodies
84
+ builder.use Twitter::REST::Response::ParseJson
85
+ # Set Faraday's HTTP adapter
86
+ builder.adapter Faraday.default_adapter
87
+ end
88
+ end
89
+
90
+ # Perform an HTTP DELETE request
91
+ def delete(path, params={})
92
+ request(:delete, path, params)
93
+ end
94
+
95
+ # Perform an HTTP GET request
96
+ def get(path, params={})
97
+ request(:get, path, params)
98
+ end
99
+
100
+ # Perform an HTTP POST request
101
+ def post(path, params={})
102
+ signature_params = params.values.any?{|value| value.respond_to?(:to_io)} ? {} : params
103
+ request(:post, path, params, signature_params)
104
+ end
105
+
106
+ # Perform an HTTP PUT request
107
+ def put(path, params={})
108
+ request(:put, path, params)
109
+ end
110
+
111
+ # @return [Boolean]
112
+ def bearer_token?
113
+ !!bearer_token
114
+ end
115
+
116
+ # @return [Boolean]
117
+ def credentials?
118
+ super || bearer_token?
119
+ end
120
+
121
+ private
122
+
123
+ # Returns a Faraday::Connection object
124
+ #
125
+ # @return [Faraday::Connection]
126
+ def connection
127
+ @connection ||= Faraday.new(ENDPOINT, connection_options)
128
+ end
129
+
130
+ def request(method, path, params={}, signature_params=params)
131
+ response = connection.send(method.to_sym, path, params) do |request|
132
+ bearer_token_request = params.delete(:bearer_token_request)
133
+ if bearer_token_request
134
+ request.headers[:accept] = '*/*' # It is important we set this, otherwise we get an error.
135
+ request.headers[:authorization] = bearer_token_credentials_auth_header
136
+ request.headers[:content_type] = 'application/x-www-form-urlencoded; charset=UTF-8'
137
+ else
138
+ request.headers[:authorization] = auth_token(method, path, params, signature_params)
139
+ end
140
+ end
141
+ response.env
142
+ rescue Faraday::Error::ClientError, JSON::ParserError
143
+ raise Twitter::Error
144
+ end
145
+
146
+ def auth_token(method, path, params={}, signature_params=params)
147
+ if !user_token?
148
+ @bearer_token = token unless bearer_token?
149
+ bearer_auth_header
150
+ else
151
+ oauth_auth_header(method, ENDPOINT + path, signature_params).to_s
152
+ end
153
+ end
154
+
155
+ # Generates authentication header for a bearer token request
156
+ #
157
+ # @return [String]
158
+ def bearer_token_credentials_auth_header
159
+ basic_auth_token = strict_encode64("#{@consumer_key}:#{@consumer_secret}")
160
+ "Basic #{basic_auth_token}"
161
+ end
162
+
163
+ def bearer_auth_header
164
+ token = bearer_token.is_a?(Twitter::Token) && bearer_token.bearer? ? bearer_token.access_token : bearer_token
165
+ "Bearer #{token}"
166
+ end
167
+
168
+ # Base64.strict_encode64 is not available on Ruby 1.8.7
169
+ def strict_encode64(str)
170
+ Base64.encode64(str).gsub("\n", "")
171
+ end
172
+
173
+ end
174
+ end
175
+ 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,15 @@
1
+ require 'twitter/rest/response/parse_json'
2
+
3
+ module Twitter
4
+ module REST
5
+ module Response
6
+ class ParseErrorJson < Twitter::REST::Response::ParseJson
7
+
8
+ def unparsable_status_codes
9
+ super + [200]
10
+ end
11
+
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,31 @@
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 unparsable_status_codes.include?(env[:status])
21
+ end
22
+ end
23
+
24
+ def unparsable_status_codes
25
+ [204, 301, 302, 304]
26
+ end
27
+
28
+ end
29
+ end
30
+ end
31
+ end
@@ -12,20 +12,17 @@ require 'twitter/error/unauthorized'
12
12
  require 'twitter/error/unprocessable_entity'
13
13
 
14
14
  module Twitter
15
- module Response
16
- class RaiseError < Faraday::Response::Middleware
15
+ module REST
16
+ module Response
17
+ class RaiseError < Faraday::Response::Middleware
17
18
 
18
- def on_complete(env)
19
- status_code = env[:status].to_i
20
- error_class = @klass.errors[status_code]
21
- raise error_class.from_response(env) if error_class
22
- end
19
+ def on_complete(env)
20
+ status_code = env[:status].to_i
21
+ error_class = Twitter::Error.errors[status_code]
22
+ raise error_class.from_response(env) if error_class
23
+ end
23
24
 
24
- def initialize(app, klass)
25
- @klass = klass
26
- super(app)
27
25
  end
28
-
29
26
  end
30
27
  end
31
28
  end
@@ -1,55 +1,71 @@
1
- require 'twitter/base'
1
+ require 'twitter/enumerable'
2
2
 
3
3
  module Twitter
4
- class SearchResults < Twitter::Base
4
+ class SearchResults
5
+ include Twitter::Enumerable
6
+ attr_reader :attrs
7
+ alias to_h attrs
8
+ alias to_hash attrs
9
+ alias to_hsh attrs
10
+
11
+ class << self
12
+
13
+ # Construct a new SearchResults object from a response hash
14
+ #
15
+ # @param response [Hash]
16
+ # @return [Twitter::Base]
17
+ def from_response(response={})
18
+ new(response[:body])
19
+ end
20
+
21
+ end
5
22
 
6
- # @return [Array<Twitter::Tweet>]
7
- def statuses
8
- @results ||= Array(@attrs[:statuses]).map do |tweet|
9
- Twitter::Tweet.fetch_or_new(tweet)
23
+ # Initializes a new SearchResults object
24
+ #
25
+ # @param attrs [Hash]
26
+ # @return [Twitter::SearchResults]
27
+ def initialize(attrs={})
28
+ @attrs = attrs
29
+ @collection = Array(@attrs[:statuses]).map do |tweet|
30
+ Tweet.new(tweet)
10
31
  end
11
32
  end
12
- alias collection statuses
13
- alias results statuses
14
33
 
15
34
  # @return [Float]
16
35
  def completed_in
17
- @attrs[:search_metadata][:completed_in] if search_metadata?
36
+ @attrs[:search_metadata][:completed_in] if @attrs[:search_metadata]
18
37
  end
19
38
 
20
39
  # @return [Integer]
21
40
  def max_id
22
- @attrs[:search_metadata][:max_id] if search_metadata?
41
+ @attrs[:search_metadata][:max_id] if @attrs[:search_metadata]
23
42
  end
24
43
 
25
44
  # @return [Integer]
26
45
  def page
27
- @attrs[:search_metadata][:page] if search_metadata?
46
+ @attrs[:search_metadata][:page] if @attrs[:search_metadata]
28
47
  end
29
48
 
30
49
  # @return [String]
31
50
  def query
32
- @attrs[:search_metadata][:query] if search_metadata?
51
+ @attrs[:search_metadata][:query] if @attrs[:search_metadata]
33
52
  end
34
53
 
35
54
  # @return [Integer]
36
55
  def results_per_page
37
- @attrs[:search_metadata][:results_per_page] if search_metadata?
56
+ @attrs[:search_metadata][:count] if @attrs[:search_metadata]
38
57
  end
39
58
  alias rpp results_per_page
40
-
41
- def search_metadata?
42
- !@attrs[:search_metadata].nil?
43
- end
59
+ alias count results_per_page
44
60
 
45
61
  # @return [Integer]
46
62
  def since_id
47
- @attrs[:search_metadata][:since_id] if search_metadata?
63
+ @attrs[:search_metadata][:since_id] if @attrs[:search_metadata]
48
64
  end
49
65
 
50
66
  # @return [Boolean]
51
67
  def next_results?
52
- !@attrs[:search_metadata][:next_results].nil? if search_metadata?
68
+ !!(@attrs[:search_metadata] && @attrs[:search_metadata][:next_results])
53
69
  end
54
70
  alias next_page? next_results?
55
71
 
@@ -69,11 +85,11 @@ module Twitter
69
85
  #
70
86
  # @note Returned Hash can be merged into the previous search options list to easily access the refresh page.
71
87
  # @return [Hash] The parameters needed to refresh the page.
72
- def refresh_url
88
+ def refresh_results
73
89
  query_string = strip_first_character(@attrs[:search_metadata][:refresh_url])
74
90
  query_string_to_hash(query_string)
75
91
  end
76
- alias refresh_page refresh_url
92
+ alias refresh_page refresh_results
77
93
 
78
94
  private
79
95
 
@@ -5,11 +5,6 @@ module Twitter
5
5
  attr_reader :always_use_https, :discoverable_by_email, :geo_enabled,
6
6
  :language, :protected, :screen_name, :show_all_inline_media, :sleep_time,
7
7
  :time_zone
8
-
9
- # @return [Twitter::Place]
10
- def trend_location
11
- @trend_location ||= Twitter::Place.fetch_or_new(Array(@attrs[:trend_location]).first)
12
- end
13
-
8
+ object_attr_reader :Place, :trend_location
14
9
  end
15
10
  end
data/lib/twitter/size.rb CHANGED
@@ -1,24 +1,11 @@
1
+ require 'equalizer'
1
2
  require 'twitter/base'
2
3
 
3
4
  module Twitter
4
5
  class Size < Twitter::Base
6
+ include Equalizer.new(:h, :w)
5
7
  attr_reader :h, :resize, :w
6
8
  alias height h
7
9
  alias width w
8
-
9
- # @param other [Twitter::Size]
10
- # @return [Boolean]
11
- def ==(other)
12
- super || size_equal(other) || attrs_equal(other)
13
- end
14
-
15
- protected
16
-
17
- # @param other [Twitter::Size]
18
- # @return [Boolean]
19
- def size_equal(other)
20
- self.class == other.class && !other.h.nil? && h == other.h && !other.w.nil? && w == other.w
21
- end
22
-
23
10
  end
24
11
  end
@@ -0,0 +1,89 @@
1
+ require 'http/request'
2
+ require 'twitter/arguments'
3
+ require 'twitter/client'
4
+ require 'twitter/streaming/connection'
5
+ require 'twitter/streaming/response'
6
+ require 'twitter/streaming/message_parser'
7
+ require 'twitter/streaming/event'
8
+ require 'twitter/streaming/friend_list'
9
+
10
+ module Twitter
11
+ module Streaming
12
+ class Client < Twitter::Client
13
+ attr_writer :connection
14
+
15
+ # Initializes a new Client object
16
+ #
17
+ # @return [Twitter::Streaming::Client]
18
+ def initialize(options={}, &block)
19
+ super
20
+ @connection = Streaming::Connection.new
21
+ end
22
+
23
+ # @yield [Twitter::Tweet] A stream of tweets.
24
+ def filter(options={}, &block)
25
+ request(:post, 'https://stream.twitter.com:443/1.1/statuses/filter.json', options, &block)
26
+ end
27
+
28
+ # @yield [Twitter::Tweet] A stream of tweets.
29
+ def firehose(options={}, &block)
30
+ request(:get, 'https://stream.twitter.com:443/1.1/statuses/firehose.json', options, &block)
31
+ end
32
+
33
+ # @yield [Twitter::Tweet] A stream of tweets.
34
+ def sample(options={}, &block)
35
+ request(:get, 'https://stream.twitter.com:443/1.1/statuses/sample.json', options, &block)
36
+ end
37
+
38
+ # @yield [Twitter::Tweet] A stream of tweets.
39
+ def site(*args, &block)
40
+ arguments = Arguments.new(args)
41
+ request(:get, 'https://sitestream.twitter.com:443/1.1/site.json', arguments.options.merge(:follow => arguments.join(',')), &block)
42
+ end
43
+
44
+ # @yield [Twitter::Tweet] A stream of tweets.
45
+ def user(options={}, &block)
46
+ request(:get, 'https://userstream.twitter.com:443/1.1/user.json', options, &block)
47
+ end
48
+
49
+ # Set a Proc to be run when connection established.
50
+ def before_request(&block)
51
+ if block_given?
52
+ @before_request = block
53
+ self
54
+ elsif instance_variable_defined?(:@before_request)
55
+ @before_request
56
+ else
57
+ Proc.new {}
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ def request(method, uri, params, &block)
64
+ before_request.call
65
+ headers = default_headers.merge(:authorization => oauth_auth_header(method, uri, params).to_s)
66
+ request = HTTP::Request.new(method, uri + '?' + to_url_params(params), headers)
67
+ response = Streaming::Response.new do |data|
68
+ if item = Streaming::MessageParser.parse(data)
69
+ yield item
70
+ end
71
+ end
72
+ @connection.stream(request, response)
73
+ end
74
+
75
+ def to_url_params(params)
76
+ params.map do |param, value|
77
+ [param, URI.encode(value)].join("=")
78
+ end.sort.join('&')
79
+ end
80
+
81
+ def default_headers
82
+ @default_headers ||= {
83
+ :accept => '*/*',
84
+ :user_agent => user_agent,
85
+ }
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,22 @@
1
+ require 'http/parser'
2
+ require 'openssl'
3
+ require 'resolv'
4
+
5
+ module Twitter
6
+ module Streaming
7
+ class Connection
8
+
9
+ def stream(request, response)
10
+ client_context = OpenSSL::SSL::SSLContext.new
11
+ client = TCPSocket.new(Resolv.getaddress(request.uri.host), request.uri.port)
12
+ ssl_client = OpenSSL::SSL::SSLSocket.new(client, client_context)
13
+ ssl_client.connect
14
+ request.stream(ssl_client)
15
+ while body = ssl_client.readpartial(1024)
16
+ response << body
17
+ end
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,35 @@
1
+ module Twitter
2
+ module Streaming
3
+ class Event
4
+
5
+ LIST_EVENTS = [
6
+ :list_created, :list_destroyed, :list_updated, :list_member_added,
7
+ :list_member_added, :list_member_removed, :list_user_subscribed,
8
+ :list_user_subscribed, :list_user_unsubscribed, :list_user_unsubscribed
9
+ ]
10
+
11
+ TWEET_EVENTS = [
12
+ :favorite, :unfavorite
13
+ ]
14
+
15
+ attr_reader :name, :source, :target, :target_object
16
+
17
+ # @param data [Hash]
18
+ def initialize(data)
19
+ @name = data[:event].intern
20
+ @source = Twitter::User.new(data[:source])
21
+ @target = Twitter::User.new(data[:target])
22
+ @target_object = target_object_factory(@name, data[:target_object])
23
+ end
24
+
25
+ private
26
+ def target_object_factory(event_name, data)
27
+ if LIST_EVENTS.include?(event_name)
28
+ Twitter::List.new(data)
29
+ elsif TWEET_EVENTS.include?(event_name)
30
+ Twitter::Tweet.new(data)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,13 @@
1
+ module Twitter
2
+ module Streaming
3
+ class FriendList
4
+
5
+ attr_reader :friend_ids
6
+
7
+ # @param friend_ids [Array]
8
+ def initialize(friend_ids)
9
+ @friend_ids = friend_ids
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,18 @@
1
+ module Twitter
2
+ module Streaming
3
+ class MessageParser
4
+
5
+ def self.parse(data)
6
+ if data[:id]
7
+ Tweet.new(data)
8
+ elsif data[:event]
9
+ Event.new(data)
10
+ elsif data[:direct_message]
11
+ DirectMessage.new(data[:direct_message])
12
+ elsif data[:friends]
13
+ FriendList.new(data[:friends])
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,34 @@
1
+ require 'buftok'
2
+
3
+ module Twitter
4
+ module Streaming
5
+ class Response
6
+
7
+ # Initializes a new Response object
8
+ #
9
+ # @return [Twitter::Streaming::Response]
10
+ def initialize(&block)
11
+ @block = block
12
+ @parser = Http::Parser.new(self)
13
+ @tokenizer = BufferedTokenizer.new("\r\n")
14
+ end
15
+
16
+ def <<(data)
17
+ @parser << data
18
+ end
19
+
20
+ def on_headers_complete(headers)
21
+ # TODO: handle response codes
22
+ p(:status_code => @parser.status_code, :header => headers) unless @parser.status_code == 200
23
+ end
24
+
25
+ def on_body(data)
26
+ @tokenizer.extract(data).each do |line|
27
+ next if line.empty?
28
+ @block.call(JSON.parse(line, :symbolize_names => true))
29
+ end
30
+ end
31
+
32
+ end
33
+ end
34
+ end
@@ -1,21 +1,18 @@
1
+ require 'equalizer'
1
2
  require 'twitter/base'
2
3
 
3
4
  module Twitter
4
5
  class Suggestion < Twitter::Base
6
+ include Equalizer.new(:slug)
5
7
  attr_reader :name, :size, :slug
6
8
 
7
- # @param other [Twitter::Suggestion]
8
- # @return [Boolean]
9
- def ==(other)
10
- super || attr_equal(:slug, other) || attrs_equal(other)
11
- end
12
-
13
9
  # @return [Array<Twitter::User>]
14
10
  def users
15
- @users ||= Array(@attrs[:users]).map do |user|
16
- Twitter::User.fetch_or_new(user)
11
+ Array(@attrs[:users]).map do |user|
12
+ User.new(user)
17
13
  end
18
14
  end
15
+ memoize :users
19
16
 
20
17
  end
21
18
  end