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
data/lib/twitter/token.rb CHANGED
@@ -2,6 +2,16 @@ require 'twitter/base'
2
2
 
3
3
  module Twitter
4
4
  class Token < Twitter::Base
5
- attr_reader :token_type, :access_token
5
+ attr_reader :access_token, :token_type
6
+ alias to_s access_token
7
+
8
+ BEARER_TYPE = "bearer"
9
+
10
+ # @return [Boolean]
11
+ def bearer?
12
+ @attrs[:token_type] == BEARER_TYPE
13
+ end
14
+ memoize :bearer?
15
+
6
16
  end
7
17
  end
data/lib/twitter/trend.rb CHANGED
@@ -1,14 +1,10 @@
1
+ require 'equalizer'
1
2
  require 'twitter/base'
2
3
 
3
4
  module Twitter
4
5
  class Trend < Twitter::Base
5
- attr_reader :events, :name, :promoted_content, :query, :url
6
-
7
- # @param other [Twitter::Trend]
8
- # @return [Boolean]
9
- def ==(other)
10
- super || attr_equal(:name, other) || attrs_equal(other)
11
- end
12
-
6
+ include Equalizer.new(:name)
7
+ attr_reader :events, :name, :promoted_content, :query
8
+ uri_attr_reader :uri
13
9
  end
14
10
  end
@@ -0,0 +1,65 @@
1
+ require 'twitter/creatable'
2
+ require 'twitter/enumerable'
3
+ require 'memoizable'
4
+ require 'twitter/null_object'
5
+
6
+ module Twitter
7
+ class TrendResults
8
+ include Twitter::Creatable
9
+ include Twitter::Enumerable
10
+ include Memoizable
11
+ attr_reader :attrs
12
+ alias to_h attrs
13
+ alias to_hash attrs
14
+ alias to_hsh attrs
15
+
16
+ class << self
17
+
18
+ # Construct a new TrendResults object from a response hash
19
+ #
20
+ # @param response [Hash]
21
+ # @return [Twitter::Base]
22
+ def from_response(response={})
23
+ new(response[:body].first)
24
+ end
25
+
26
+ end
27
+
28
+ # Initializes a new TrendResults object
29
+ #
30
+ # @param attrs [Hash]
31
+ # @return [Twitter::TrendResults]
32
+ def initialize(attrs={})
33
+ @attrs = attrs
34
+ @collection = Array(@attrs[:trends]).map do |trend|
35
+ Trend.new(trend)
36
+ end
37
+ end
38
+
39
+ # Time when the object was created on Twitter
40
+ #
41
+ # @return [Time]
42
+ def as_of
43
+ Time.parse(@attrs[:as_of]) if @attrs[:as_of]
44
+ end
45
+ memoize :as_of
46
+
47
+ def as_of?
48
+ !!@attrs[:as_of]
49
+ end
50
+ memoize :as_of?
51
+
52
+ # @return [Twitter::Place, NullObject]
53
+ def location
54
+ location? ? Place.new(@attrs[:locations].first) : NullObject.new
55
+ end
56
+ memoize :location
57
+
58
+ # @return [Boolean]
59
+ def location?
60
+ !@attrs[:locations].nil? && !@attrs[:locations].first.nil?
61
+ end
62
+ memoize :location?
63
+
64
+ end
65
+ end
data/lib/twitter/tweet.rb CHANGED
@@ -1,145 +1,96 @@
1
- require 'forwardable'
2
1
  require 'twitter/creatable'
3
- require 'twitter/exceptable'
4
- require 'twitter/identity'
5
2
 
6
3
  module Twitter
7
4
  class Tweet < Twitter::Identity
8
- extend Forwardable
9
5
  include Twitter::Creatable
10
- include Twitter::Exceptable
11
- attr_reader :favorite_count, :favorited, :favoriters, :from_user_id,
12
- :from_user_name, :in_reply_to_screen_name, :in_reply_to_attrs_id,
13
- :in_reply_to_status_id, :in_reply_to_user_id, :lang, :repliers,
14
- :retweeted, :retweeters, :source, :text, :to_user, :to_user_id,
15
- :to_user_name, :truncated
16
- alias in_reply_to_tweet_id in_reply_to_status_id
6
+ attr_reader :favorite_count, :favorited, :in_reply_to_screen_name,
7
+ :in_reply_to_attrs_id, :in_reply_to_status_id, :in_reply_to_user_id,
8
+ :lang, :retweet_count, :retweeted, :source, :text, :truncated
17
9
  alias favorites_count favorite_count
18
- alias favourite_count favorite_count
19
- alias favourites_count favorite_count
20
- alias favouriters_count favorite_count
21
- alias favourited favorited
22
- alias favourited? favorited?
23
- alias favouriters favoriters
24
- def_delegators :user, :profile_image_url, :profile_image_url_https
10
+ alias favoriters_count favorite_count
11
+ alias in_reply_to_tweet_id in_reply_to_status_id
12
+ alias retweeters_count retweet_count
13
+ object_attr_reader :GeoFactory, :geo
14
+ object_attr_reader :Metadata, :metadata
15
+ object_attr_reader :Place, :place
16
+ object_attr_reader :Tweet, :retweeted_status
17
+ alias retweet retweeted_status
18
+ alias retweeted_tweet retweeted_status
19
+ alias retweet? retweeted_status?
20
+ alias retweeted_tweet? retweeted_status?
21
+ object_attr_reader :User, :user, :status
25
22
 
26
23
  # @return [Boolean]
27
24
  def entities?
28
- !@attrs[:entities].nil?
29
- end
30
-
31
- # @return [Integer]
32
- def favoriters_count
33
- favoriters_count = @attrs[:favoriters_count]
34
- favoriters_count.to_i if favoriters_count
35
- end
36
-
37
- # @return [String]
38
- def from_user
39
- @attrs[:from_user] || user && user.screen_name
25
+ !@attrs[:entities].nil? && @attrs[:entities].any?{|_, array| !array.empty?}
40
26
  end
27
+ memoize :entities?
41
28
 
42
29
  def filter_level
43
30
  @attrs[:filter_level] || "none"
44
31
  end
32
+ memoize :filter_level
45
33
 
46
34
  # @return [String]
47
35
  # @note May be > 140 characters.
48
36
  def full_text
49
- if retweeted_status
37
+ if retweeted_status?
50
38
  prefix = text[/\A(RT @[a-z0-9_]{1,20}: )/i, 1]
51
39
  [prefix, retweeted_status.text].compact.join
52
40
  else
53
41
  text
54
42
  end
55
43
  end
56
-
57
- # @return [Twitter::Geo]
58
- def geo
59
- @geo ||= Twitter::GeoFactory.fetch_or_new(@attrs[:geo])
60
- end
44
+ memoize :full_text
61
45
 
62
46
  # @note Must include entities in your request for this method to work
63
47
  # @return [Array<Twitter::Entity::Hashtag>]
64
48
  def hashtags
65
- @hashtags ||= entities(Twitter::Entity::Hashtag, :hashtags)
49
+ entities(Entity::Hashtag, :hashtags)
66
50
  end
51
+ memoize :hashtags
67
52
 
68
53
  # @note Must include entities in your request for this method to work
69
54
  # @return [Array<Twitter::Media>]
70
55
  def media
71
- @media ||= entities(Twitter::MediaFactory, :media)
72
- end
73
-
74
- # @return [Twitter::Metadata]
75
- def metadata
76
- @metadata ||= Twitter::Metadata.fetch_or_new(@attrs[:metadata])
77
- end
78
-
79
- # @return [Twitter::Place]
80
- def place
81
- @place ||= Twitter::Place.fetch_or_new(@attrs[:place])
56
+ entities(MediaFactory, :media)
82
57
  end
83
-
84
- # @return [Integer]
85
- def repliers_count
86
- repliers_count = @attrs[:repliers_count]
87
- repliers_count.to_i if repliers_count
88
- end
89
- alias reply_count repliers_count
58
+ memoize :media
90
59
 
91
60
  # @return [Boolean]
92
61
  def reply?
93
- !!in_reply_to_status_id
94
- end
95
-
96
- # @return [Boolean]
97
- def retweet?
98
- !!retweeted_status
62
+ !!@attrs[:in_reply_to_status_id]
99
63
  end
100
-
101
- # If this Tweet is a retweet, the original Tweet is available here.
102
- #
103
- # @return [Twitter::Tweet]
104
- def retweeted_status
105
- @retweeted_status ||= self.class.fetch_or_new(@attrs[:retweeted_status])
106
- end
107
- alias retweeted_tweet retweeted_status
108
- alias retweet retweeted_status
109
-
110
- # @return [String]
111
- def retweeters_count
112
- retweeters_count = (@attrs[:retweet_count] || @attrs[:retweeters_count])
113
- retweeters_count.to_i if retweeters_count
114
- end
115
- alias retweet_count retweeters_count
64
+ memoize :reply?
116
65
 
117
66
  # @note Must include entities in your request for this method to work
118
67
  # @return [Array<Twitter::Entity::Symbol>]
119
68
  def symbols
120
- @symbols ||= entities(Twitter::Entity::Symbol, :symbols)
69
+ entities(Entity::Symbol, :symbols)
121
70
  end
71
+ memoize :symbols
122
72
 
123
- # @note Must include entities in your request for this method to work
124
- # @return [Array<Twitter::Entity::Url>]
125
- def urls
126
- @urls ||= entities(Twitter::Entity::Url, :urls)
73
+ # @return [String] The URL to the tweet.
74
+ def uri
75
+ URI.parse("https://twitter.com/#{user.screen_name}/status/#{id}")
127
76
  end
77
+ memoize :uri
78
+ alias url uri
128
79
 
129
- # @return [Twitter::User]
130
- def user
131
- @user ||= fetch_or_new_without_self(Twitter::User, @attrs, :user, :status)
80
+ # @note Must include entities in your request for this method to work
81
+ # @return [Array<Twitter::Entity::URI>]
82
+ def uris
83
+ entities(Entity::URI, :urls)
132
84
  end
85
+ memoize :uris
86
+ alias urls uris
133
87
 
134
88
  # @note Must include entities in your request for this method to work
135
89
  # @return [Array<Twitter::Entity::UserMention>]
136
90
  def user_mentions
137
- @user_mentions ||= entities(Twitter::Entity::UserMention, :user_mentions)
138
- end
139
-
140
- def user?
141
- !@attrs[:user].nil?
91
+ entities(Entity::UserMention, :user_mentions)
142
92
  end
93
+ memoize :user_mentions
143
94
 
144
95
  private
145
96
 
@@ -148,10 +99,10 @@ module Twitter
148
99
  def entities(klass, key)
149
100
  if entities?
150
101
  Array(@attrs[:entities][key.to_sym]).map do |entity|
151
- klass.fetch_or_new(entity)
102
+ klass.new(entity)
152
103
  end
153
104
  else
154
- warn "#{Kernel.caller.first}: To get #{key.to_s.tr('_', ' ')}, you must pass `:include_entities => true` when requesting the #{self.class.name}."
105
+ warn "#{Kernel.caller.first}: To get #{key.to_s.tr('_', ' ')}, you must pass `:include_entities => true` when requesting the #{self.class}."
155
106
  []
156
107
  end
157
108
  end
data/lib/twitter/user.rb CHANGED
@@ -1,12 +1,11 @@
1
1
  require 'twitter/basic_user'
2
2
  require 'twitter/creatable'
3
- require 'twitter/exceptable'
4
3
 
5
4
  module Twitter
6
5
  class User < Twitter::BasicUser
7
6
  PROFILE_IMAGE_SUFFIX_REGEX = /_normal(\.gif|\.jpe?g|\.png)$/i
7
+ PREDICATE_URI_METHOD_REGEX = /_uri\?$/
8
8
  include Twitter::Creatable
9
- include Twitter::Exceptable
10
9
  attr_reader :connections, :contributors_enabled, :default_profile,
11
10
  :default_profile_image, :description, :favourites_count,
12
11
  :follow_request_sent, :followers_count, :friends_count, :geo_enabled,
@@ -16,102 +15,128 @@ module Twitter
16
15
  :profile_link_color, :profile_sidebar_border_color,
17
16
  :profile_sidebar_fill_color, :profile_text_color,
18
17
  :profile_use_background_image, :protected, :statuses_count, :time_zone,
19
- :url, :utc_offset, :verified
20
- alias favorite_count favourites_count
21
- alias favoriters_count favourites_count
18
+ :utc_offset, :verified
22
19
  alias favorites_count favourites_count
23
- alias favourite_count favourites_count
24
- alias favouriters_count favourites_count
25
- alias follower_count followers_count
26
- alias friend_count friends_count
27
- alias status_count statuses_count
28
- alias translator is_translator
29
- alias translator? is_translator?
30
- alias tweet_count statuses_count
20
+ remove_method :favourites_count
21
+ alias profile_background_image_uri profile_background_image_url
22
+ alias profile_background_image_uri_https profile_background_image_url_https
23
+ alias translator? is_translator
31
24
  alias tweets_count statuses_count
32
- alias update_count statuses_count
33
- alias updates_count statuses_count
25
+ object_attr_reader :Tweet, :status, :user
26
+ alias tweet status
27
+ alias tweet? status?
28
+ alias tweeted? status?
34
29
 
35
- # @return [Array<Twitter::Entity::Url>]
36
- def description_urls
37
- @description_urls ||= Array(@attrs[:entities][:description][:urls]).map do |entity|
38
- Twitter::Entity::Url.fetch_or_new(entity)
30
+ class << self
31
+
32
+ private
33
+
34
+ def alias_predicate_uri_methods(method)
35
+ for replacement in %w(_url? _uri_https? _url_https?)
36
+ alias_method_sub(method, PREDICATE_URI_METHOD_REGEX, replacement)
37
+ end
38
+ end
39
+
40
+ def alias_method_sub(method, pattern, replacement)
41
+ alias_method(method.to_s.sub(pattern, replacement).to_sym, method)
39
42
  end
43
+
40
44
  end
41
45
 
46
+ # @return [Array<Twitter::Entity::URI>]
47
+ def description_uris
48
+ Array(@attrs[:entities][:description][:urls]).map do |entity|
49
+ Entity::URI.new(entity)
50
+ end
51
+ end
52
+ memoize :description_uris
53
+ alias description_urls description_uris
54
+
42
55
  # Return the URL to the user's profile banner image
43
56
  #
44
57
  # @param size [String, Symbol] The size of the image. Must be one of: 'mobile', 'mobile_retina', 'web', 'web_retina', 'ipad', or 'ipad_retina'
45
58
  # @return [String]
46
- def profile_banner_url(size=:web)
47
- insecure_url([@attrs[:profile_banner_url], size].join('/')) if profile_banner_url?
59
+ def profile_banner_uri(size=:web)
60
+ parse_encoded_uri(insecure_uri([@attrs[:profile_banner_url], size].join('/'))) if @attrs[:profile_banner_url]
48
61
  end
62
+ alias profile_banner_url profile_banner_uri
49
63
 
50
64
  # Return the secure URL to the user's profile banner image
51
65
  #
52
66
  # @param size [String, Symbol] The size of the image. Must be one of: 'mobile', 'mobile_retina', 'web', 'web_retina', 'ipad', or 'ipad_retina'
53
67
  # @return [String]
54
- def profile_banner_url_https(size=:web)
55
- [@attrs[:profile_banner_url], size].join('/') if profile_banner_url?
68
+ def profile_banner_uri_https(size=:web)
69
+ parse_encoded_uri([@attrs[:profile_banner_url], size].join('/')) if @attrs[:profile_banner_url]
56
70
  end
71
+ alias profile_banner_url_https profile_banner_uri_https
57
72
 
58
- def profile_banner_url?
59
- !@attrs[:profile_banner_url].nil?
73
+ # @return [Boolean]
74
+ def profile_banner_uri?
75
+ !!@attrs[:profile_banner_url]
60
76
  end
61
- alias profile_banner_url_https? profile_banner_url?
77
+ memoize :profile_banner_uri?
78
+ alias_predicate_uri_methods :profile_banner_uri?
62
79
 
63
80
  # Return the URL to the user's profile image
64
81
  #
65
82
  # @param size [String, Symbol] The size of the image. Must be one of: 'mini', 'normal', 'bigger' or 'original'
66
83
  # @return [String]
67
- def profile_image_url(size=:normal)
68
- insecure_url(profile_image_url_https(size)) if profile_image_url?
84
+ def profile_image_uri(size=:normal)
85
+ parse_encoded_uri(insecure_uri(profile_image_uri_https(size))) if @attrs[:profile_image_url_https]
69
86
  end
87
+ alias profile_image_url profile_image_uri
70
88
 
71
89
  # Return the secure URL to the user's profile image
72
90
  #
73
91
  # @param size [String, Symbol] The size of the image. Must be one of: 'mini', 'normal', 'bigger' or 'original'
74
92
  # @return [String]
75
- def profile_image_url_https(size=:normal)
93
+ def profile_image_uri_https(size=:normal)
76
94
  # The profile image URL comes in looking like like this:
77
95
  # https://a0.twimg.com/profile_images/1759857427/image1326743606_normal.png
78
96
  # It can be converted to any of the following sizes:
79
97
  # https://a0.twimg.com/profile_images/1759857427/image1326743606.png
80
98
  # https://a0.twimg.com/profile_images/1759857427/image1326743606_mini.png
81
99
  # https://a0.twimg.com/profile_images/1759857427/image1326743606_bigger.png
82
- resize_profile_image_url(@attrs[:profile_image_url_https], size) if profile_image_url?
100
+ parse_encoded_uri(@attrs[:profile_image_url_https].sub(PROFILE_IMAGE_SUFFIX_REGEX, profile_image_suffix(size))) if @attrs[:profile_image_url_https]
83
101
  end
102
+ alias profile_image_url_https profile_image_uri_https
84
103
 
85
- def profile_image_url?
86
- !@attrs[:profile_image_url_https].nil?
104
+ def profile_image_uri?
105
+ !!@attrs[:profile_image_url_https]
87
106
  end
88
- alias profile_image_url_https? profile_image_url?
107
+ memoize :profile_image_uri?
108
+ alias_predicate_uri_methods :profile_image_uri?
89
109
 
90
- # @return [Twitter::Tweet]
91
- def status
92
- @status ||= fetch_or_new_without_self(Twitter::Tweet, @attrs, :status, :user)
110
+ # @return [String] The URL to the user.
111
+ def uri
112
+ URI.parse("https://twitter.com/#{screen_name}")
93
113
  end
114
+ memoize :uri
115
+ alias url uri
94
116
 
95
- def status?
96
- !@attrs[:status].nil?
117
+ # @return [String] The URL to the user's website.
118
+ def website
119
+ URI.parse(@attrs[:url]) if @attrs[:url]
97
120
  end
121
+ memoize :website
122
+
123
+ def website?
124
+ !!@attrs[:url]
125
+ end
126
+ memoize :website?
98
127
 
99
128
  private
100
129
 
101
- def insecure_url(url)
102
- url.sub(/^https/i, 'http')
130
+ def parse_encoded_uri(uri)
131
+ URI.parse(URI.encode(uri))
103
132
  end
104
133
 
105
- def resize_profile_image_url(url, size)
106
- url.sub(PROFILE_IMAGE_SUFFIX_REGEX, profile_image_suffix(size))
134
+ def insecure_uri(uri)
135
+ uri.to_s.sub(/^https/i, 'http')
107
136
  end
108
137
 
109
138
  def profile_image_suffix(size)
110
- if :original == size.to_sym
111
- "\\1"
112
- else
113
- "_#{size}\\1"
114
- end
139
+ :original == size.to_sym ? "\\1" : "_#{size}\\1"
115
140
  end
116
141
 
117
142
  end
@@ -1,9 +1,9 @@
1
1
  module Twitter
2
2
  class Version
3
- MAJOR = 4 unless defined? Twitter::Version::MAJOR
4
- MINOR = 8 unless defined? Twitter::Version::MINOR
5
- PATCH = 1 unless defined? Twitter::Version::PATCH
6
- PRE = nil unless defined? Twitter::Version::PRE
3
+ MAJOR = 5
4
+ MINOR = 0
5
+ PATCH = 0
6
+ PRE = nil
7
7
 
8
8
  class << self
9
9
 
data/lib/twitter.rb CHANGED
@@ -1,14 +1,10 @@
1
- require 'twitter/action_factory'
2
- require 'twitter/client'
3
- require 'twitter/configurable'
4
1
  require 'twitter/configuration'
5
2
  require 'twitter/cursor'
6
- require 'twitter/default'
7
3
  require 'twitter/direct_message'
8
4
  require 'twitter/entity'
9
5
  require 'twitter/entity/hashtag'
10
6
  require 'twitter/entity/symbol'
11
- require 'twitter/entity/url'
7
+ require 'twitter/entity/uri'
12
8
  require 'twitter/entity/user_mention'
13
9
  require 'twitter/geo_factory'
14
10
  require 'twitter/language'
@@ -20,47 +16,16 @@ require 'twitter/place'
20
16
  require 'twitter/profile_banner'
21
17
  require 'twitter/rate_limit'
22
18
  require 'twitter/relationship'
19
+ require 'twitter/rest/client'
23
20
  require 'twitter/saved_search'
24
21
  require 'twitter/search_results'
25
22
  require 'twitter/settings'
26
23
  require 'twitter/size'
27
24
  require 'twitter/source_user'
25
+ require 'twitter/streaming/client'
28
26
  require 'twitter/suggestion'
29
27
  require 'twitter/target_user'
30
28
  require 'twitter/trend'
31
29
  require 'twitter/tweet'
32
30
  require 'twitter/user'
33
-
34
- module Twitter
35
- class << self
36
- include Twitter::Configurable
37
-
38
- # Delegate to a Twitter::Client
39
- #
40
- # @return [Twitter::Client]
41
- def client
42
- @client = Twitter::Client.new(options) unless defined?(@client) && @client.hash == options.hash
43
- @client
44
- end
45
-
46
- # Has a client been initialized on the Twitter module
47
- #
48
- # @return [Boolean]
49
- def client?
50
- !!@client
51
- end
52
-
53
- def respond_to_missing?(method_name, include_private=false); client.respond_to?(method_name, include_private); end if RUBY_VERSION >= "1.9"
54
- def respond_to?(method_name, include_private=false); client.respond_to?(method_name, include_private) || super; end if RUBY_VERSION < "1.9"
55
-
56
- private
57
-
58
- def method_missing(method_name, *args, &block)
59
- return super unless client.respond_to?(method_name)
60
- client.send(method_name, *args, &block)
61
- end
62
-
63
- end
64
- end
65
-
66
- Twitter.setup
31
+ require 'uri'
@@ -0,0 +1 @@
1
+ {"errors":"Status is a duplicate"}
@@ -1 +1 @@
1
- {"previous_cursor":0,"next_cursor_str":"1305102810874389703","ids":[14100886,22469930,351223419],"previous_cursor_str":"0","next_cursor":1305102810874389703}
1
+ {"previous_cursor":0,"next_cursor_str":"1305102810874389703","ids":[20009713,22469930,351223419],"previous_cursor_str":"0","next_cursor":1305102810874389703}
@@ -1 +1 @@
1
- {"previous_cursor":-1305101990888327757,"next_cursor_str":"0","ids":[20009713,23621851,14509199],"previous_cursor_str":"-1305101990888327757","next_cursor":0}
1
+ {"previous_cursor":-1305101990888327757,"next_cursor_str":"0","ids":[14100886,23621851,14509199],"previous_cursor_str":"-1305101990888327757","next_cursor":0}
@@ -0,0 +1,6 @@
1
+ OAuth oauth_signature="AAAAAAAAA",
2
+ oauth_timestamp="1380473364", oauth_version="1.0",
3
+ oauth_token="BBBBBBBBB",
4
+ oauth_signature_method="HMAC-SHA1",
5
+ oauth_consumer_key="CCCCCCCCCCC",
6
+ oauth_nonce="DDDDDDDDDDD"
@@ -1 +1 @@
1
- {"statuses":[{"metadata":{"result_type":"recent","iso_language_code":"en"},"created_at":"Mon Sep 17 22:41:52 +0000 2012","id":247827742178021376,"id_str":"247827742178021376","text":"Bubble Mailer #freebandnames","source":"\u003ca href=\"http:\/\/tapbots.com\" rel=\"nofollow\"\u003eTweetbot for Mac\u003c\/a\u003e","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":671293,"id_str":"671293","name":"Richard Allen","screen_name":"richrad","location":"Kansas City","url":"http:\/\/richardallen.me","description":"Give me a break, Jeffery.","protected":false,"followers_count":174,"friends_count":273,"listed_count":2,"created_at":"Sat Jan 20 16:02:45 +0000 2007","favourites_count":383,"utc_offset":-21600,"time_zone":"Central Time (US & Canada)","geo_enabled":false,"verified":false,"statuses_count":1370,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"0099B9","profile_background_image_url":"http:\/\/a0.twimg.com\/images\/themes\/theme4\/bg.gif","profile_background_image_url_https":"https:\/\/si0.twimg.com\/images\/themes\/theme4\/bg.gif","profile_background_tile":false,"profile_image_url":"http:\/\/a0.twimg.com\/profile_images\/1399177739\/notlookingup_normal.jpeg","profile_image_url_https":"https:\/\/si0.twimg.com\/profile_images\/1399177739\/notlookingup_normal.jpeg","profile_link_color":"0099B9","profile_sidebar_border_color":"5ED4DC","profile_sidebar_fill_color":"95E8EC","profile_text_color":"3C3940","profile_use_background_image":true,"show_all_inline_media":true,"default_profile":false,"default_profile_image":false,"following":null,"follow_request_sent":null,"notifications":null},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_count":0,"entities":{"hashtags":[{"text":"freebandnames","indices":[14,28]}],"urls":[],"user_mentions":[]},"favorited":false,"retweeted":false}],"search_metadata":{"completed_in":0.029,"max_id":250126199840518145,"max_id_str":"250126199840518145","next_page":"?page=2&max_id=250126199840518145&q=%23freebandnames&rpp=4&include_entities=1&result_type=mixed","page":1,"query":"%23freebandnames","refresh_url":"?since_id=250126199840518145&q=%23freebandnames&result_type=mixed&include_entities=1","results_per_page":4,"since_id":24012619984051000,"since_id_str":"24012619984051000"}}
1
+ {"statuses":[{"metadata":{"result_type":"recent","iso_language_code":"en"},"created_at":"Mon Sep 17 22:41:52 +0000 2012","id":247827742178021376,"id_str":"247827742178021376","text":"Bubble Mailer #freebandnames","source":"\u003ca href=\"http:\/\/tapbots.com\" rel=\"nofollow\"\u003eTweetbot for Mac\u003c\/a\u003e","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":671293,"id_str":"671293","name":"Richard Allen","screen_name":"richrad","location":"Kansas City","url":"http:\/\/richardallen.me","description":"Give me a break, Jeffery.","protected":false,"followers_count":174,"friends_count":273,"listed_count":2,"created_at":"Sat Jan 20 16:02:45 +0000 2007","favourites_count":383,"utc_offset":-21600,"time_zone":"Central Time (US & Canada)","geo_enabled":false,"verified":false,"statuses_count":1370,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"0099B9","profile_background_image_url":"http:\/\/a0.twimg.com\/images\/themes\/theme4\/bg.gif","profile_background_image_url_https":"https:\/\/si0.twimg.com\/images\/themes\/theme4\/bg.gif","profile_background_tile":false,"profile_image_url":"http:\/\/a0.twimg.com\/profile_images\/1399177739\/notlookingup_normal.jpeg","profile_image_url_https":"https:\/\/si0.twimg.com\/profile_images\/1399177739\/notlookingup_normal.jpeg","profile_link_color":"0099B9","profile_sidebar_border_color":"5ED4DC","profile_sidebar_fill_color":"95E8EC","profile_text_color":"3C3940","profile_use_background_image":true,"show_all_inline_media":true,"default_profile":false,"default_profile_image":false,"following":null,"follow_request_sent":null,"notifications":null},"geo":null,"coordinates":null,"place":null,"contributors":null,"retweet_count":0,"entities":{"hashtags":[{"text":"freebandnames","indices":[14,28]}],"urls":[],"user_mentions":[]},"favorited":false,"retweeted":false}],"search_metadata":{"completed_in":0.029,"max_id":250126199840518145,"max_id_str":"250126199840518145","next_page":"?page=2&max_id=250126199840518145&q=%23freebandnames&rpp=4&include_entities=1&result_type=mixed","page":1,"query":"%23freebandnames","refresh_url":"?since_id=250126199840518145&q=%23freebandnames&result_type=mixed&include_entities=1","count":4,"since_id":24012619984051000,"since_id_str":"24012619984051000"}}
@@ -1 +1 @@
1
- {"max_id":28857935752,"since_id":0,"refresh_url":"?since_id=28857935752&q=twitter","next_page":"?page=2&max_id=28857935752&q=twitter","results_per_page":15,"page":1,"completed_in":0.017349,"since_id_str":"0","max_id_str":"28857935752","query":"twitter"}
1
+ {"max_id":28857935752,"since_id":0,"refresh_url":"?since_id=28857935752&q=twitter","next_page":"?page=2&max_id=28857935752&q=twitter","count":15,"page":1,"completed_in":0.017349,"since_id_str":"0","max_id_str":"28857935752","query":"twitter"}
@@ -0,0 +1,3 @@
1
+ {"created_at":"Wed Apr 06 19:13:37 +0000 2011","id":55709764298092545,"id_str":"55709764298092545","text":"The problem with your code is that it's doing exactly what you told it to do.","source":"\u003ca href=\"http:\/\/twitter.com\/download\/iphone\" rel=\"nofollow\"\u003eTwitter for iPhone\u003c\/a\u003e","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":7505382,"id_str":"7505382","name":"Erik Michaels-Ober","screen_name":"sferik","location":"San Francisco","description":"Write code. Not too much. Mostly Ruby.","url":"https:\/\/github.com\/sferik","entities":{"url":{"urls":[{"url":"https:\/\/github.com\/sferik","expanded_url":null,"indices":[0,25]}]},"description":{"urls":[]}},"protected":false,"followers_count":2479,"friends_count":200,"listed_count":132,"created_at":"Mon Jul 16 12:59:01 +0000 2007","favourites_count":4421,"utc_offset":-28800,"time_zone":"Pacific Time (US & Canada)","geo_enabled":true,"verified":false,"statuses_count":8730,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"000000","profile_background_image_url":"http:\/\/a0.twimg.com\/profile_background_images\/677717672\/bb0b3653dcf0644e344823e0a2eb3382.png","profile_background_image_url_https":"https:\/\/si0.twimg.com\/profile_background_images\/677717672\/bb0b3653dcf0644e344823e0a2eb3382.png","profile_background_tile":false,"profile_image_url":"http:\/\/a0.twimg.com\/profile_images\/1759857427\/image1326743606_normal.png","profile_image_url_https":"https:\/\/si0.twimg.com\/profile_images\/1759857427\/image1326743606_normal.png","profile_banner_url":"https:\/\/si0.twimg.com\/profile_banners\/7505382\/1349499693","profile_link_color":"0084B4","profile_sidebar_border_color":"000000","profile_sidebar_fill_color":"DDEEF6","profile_text_color":"333333","profile_use_background_image":true,"default_profile":false,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":{"type":"Point","coordinates":[37.78349999,-122.39362884]},"coordinates":{"type":"Point","coordinates":[-122.39362884,37.78349999]},"place":{"id":"5c92ab5379de3839","url":"https:\/\/api.twitter.com\/1.1\/geo\/id\/5c92ab5379de3839.json","place_type":"neighborhood","name":"South Beach","full_name":"South Beach, San Francisco","country_code":"US","country":"United States","bounding_box":{"type":"Polygon","coordinates":[[[-122.403482,37.777529],[-122.387436,37.777529],[-122.387436,37.794486],[-122.403482,37.794486]]]},"attributes":{}},"contributors":null,"retweet_count":316,"entities":{"hashtags":[],"urls":[],"user_mentions":[]},"favorited":false,"retweeted":false}
2
+ {"created_at":"Wed Apr 06 19:13:37 +0000 2011","id":55709764298092545,"id_str":"55709764298092545","text":"The problem with your code is that it's doing exactly what you told it to do.","source":"\u003ca href=\"http:\/\/twitter.com\/download\/iphone\" rel=\"nofollow\"\u003eTwitter for iPhone\u003c\/a\u003e","truncated":false,"in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":7505382,"id_str":"7505382","name":"Erik Michaels-Ober","screen_name":"sferik","location":"San Francisco","description":"Write code. Not too much. Mostly Ruby.","url":"https:\/\/github.com\/sferik","entities":{"url":{"urls":[{"url":"https:\/\/github.com\/sferik","expanded_url":null,"indices":[0,25]}]},"description":{"urls":[]}},"protected":false,"followers_count":2479,"friends_count":200,"listed_count":132,"created_at":"Mon Jul 16 12:59:01 +0000 2007","favourites_count":4421,"utc_offset":-28800,"time_zone":"Pacific Time (US & Canada)","geo_enabled":true,"verified":false,"statuses_count":8730,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"000000","profile_background_image_url":"http:\/\/a0.twimg.com\/profile_background_images\/677717672\/bb0b3653dcf0644e344823e0a2eb3382.png","profile_background_image_url_https":"https:\/\/si0.twimg.com\/profile_background_images\/677717672\/bb0b3653dcf0644e344823e0a2eb3382.png","profile_background_tile":false,"profile_image_url":"http:\/\/a0.twimg.com\/profile_images\/1759857427\/image1326743606_normal.png","profile_image_url_https":"https:\/\/si0.twimg.com\/profile_images\/1759857427\/image1326743606_normal.png","profile_banner_url":"https:\/\/si0.twimg.com\/profile_banners\/7505382\/1349499693","profile_link_color":"0084B4","profile_sidebar_border_color":"000000","profile_sidebar_fill_color":"DDEEF6","profile_text_color":"333333","profile_use_background_image":true,"default_profile":false,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false},"geo":{"type":"Point","coordinates":[37.78349999,-122.39362884]},"coordinates":{"type":"Point","coordinates":[-122.39362884,37.78349999]},"place":{"id":"5c92ab5379de3839","url":"https:\/\/api.twitter.com\/1.1\/geo\/id\/5c92ab5379de3839.json","place_type":"neighborhood","name":"South Beach","full_name":"South Beach, San Francisco","country_code":"US","country":"United States","bounding_box":{"type":"Polygon","coordinates":[[[-122.403482,37.777529],[-122.387436,37.777529],[-122.387436,37.794486],[-122.403482,37.794486]]]},"attributes":{}},"contributors":null,"retweet_count":316,"entities":{"hashtags":[],"urls":[],"user_mentions":[]},"favorited":false,"retweeted":false}
3
+