twitter 5.4.1 → 5.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -49,7 +49,7 @@ module Twitter
49
49
  # @param path [String]
50
50
  # @param args [Array]
51
51
  # @return [Array<Twitter::User>]
52
- def threaded_user_objects_from_response(request_method, path, args)
52
+ def parallel_user_objects_from_response(request_method, path, args)
53
53
  arguments = Twitter::Arguments.new(args)
54
54
  Twitter::Utils.parallel_map(arguments) do |user|
55
55
  object_from_response(Twitter::User, request_method, path, merge_user(arguments.options, user))
@@ -101,7 +101,7 @@ module Twitter
101
101
  # @param path [String]
102
102
  # @param args [Array]
103
103
  # @return [Array]
104
- def threaded_objects_from_response(klass, request_method, path, args) # rubocop:disable ParameterLists
104
+ def parallel_objects_from_response(klass, request_method, path, args) # rubocop:disable ParameterLists
105
105
  arguments = Twitter::Arguments.new(args)
106
106
  Twitter::Utils.parallel_map(arguments) do |object|
107
107
  id = extract_id(object)
@@ -139,8 +139,8 @@ module Twitter
139
139
  end
140
140
  end
141
141
  response.env
142
- rescue Faraday::Error::ClientError, JSON::ParserError
143
- raise Twitter::Error
142
+ rescue Faraday::Error::ClientError, JSON::ParserError => error
143
+ raise Twitter::Error.new(error) # rubocop:disable RaiseArgs
144
144
  end
145
145
 
146
146
  def auth_token(method, path, params = {}, signature_params = params) # rubocop:disable ParameterLists
@@ -34,7 +34,7 @@ module Twitter
34
34
  @path = path
35
35
  @options = options
36
36
  @collection = []
37
- set_attrs(attrs)
37
+ self.attrs = attrs
38
38
  end
39
39
 
40
40
  private
@@ -66,10 +66,10 @@ module Twitter
66
66
 
67
67
  def fetch_next_page
68
68
  response = @client.send(@request_method, @path, next_page)
69
- set_attrs(response[:body])
69
+ self.attrs = response[:body]
70
70
  end
71
71
 
72
- def set_attrs(attrs)
72
+ def attrs=(attrs)
73
73
  @attrs = attrs
74
74
  Array(@attrs[:statuses]).map do |tweet|
75
75
  @collection << Tweet.new(tweet)
@@ -58,6 +58,7 @@ module Twitter
58
58
  # @see https://dev.twitter.com/docs/api/1.1/get/site
59
59
  # @see https://dev.twitter.com/docs/streaming-apis/streams/site
60
60
  # @see https://dev.twitter.com/docs/streaming-apis/parameters
61
+ # @note Site Streams is currently in a limited beta. Access is restricted to whitelisted accounts.
61
62
  # @param follow [Enumerable<Integer, String, Twitter::User>] A list of user IDs, indicating the users to return statuses for in the stream.
62
63
  # @param options [Hash] A customizable set of options.
63
64
  # @option options [String] :with Specifies whether to return information for just the users specified in the follow parameter, or include messages from accounts they follow.
@@ -38,7 +38,7 @@ module Twitter
38
38
  #
39
39
  # @return [Time]
40
40
  def as_of
41
- Time.parse(@attrs[:as_of]) if @attrs[:as_of]
41
+ Time.parse(@attrs[:as_of]) unless @attrs[:as_of].nil?
42
42
  end
43
43
  memoize :as_of
44
44
 
@@ -1,15 +1,19 @@
1
1
  require 'twitter/creatable'
2
+ require 'twitter/entities'
3
+ require 'twitter/identity'
2
4
 
3
5
  module Twitter
4
6
  class Tweet < Twitter::Identity
5
7
  include Twitter::Creatable
6
- attr_reader :favorite_count, :favorited, :in_reply_to_screen_name,
7
- :in_reply_to_attrs_id, :in_reply_to_status_id,
8
- :in_reply_to_user_id, :lang, :retweet_count, :retweeted,
9
- :source, :text, :truncated
8
+ include Twitter::Entities
9
+ attr_reader :favorite_count, :favorited, :filter_level,
10
+ :in_reply_to_screen_name, :in_reply_to_attrs_id,
11
+ :in_reply_to_status_id, :in_reply_to_user_id, :lang,
12
+ :retweet_count, :retweeted, :source, :text, :truncated
10
13
  alias_method :favorites_count, :favorite_count
11
14
  alias_method :favoriters_count, :favorite_count
12
15
  alias_method :in_reply_to_tweet_id, :in_reply_to_status_id
16
+ alias_method :reply?, :in_reply_to_status_id?
13
17
  alias_method :retweeters_count, :retweet_count
14
18
  object_attr_reader :GeoFactory, :geo
15
19
  object_attr_reader :Metadata, :metadata
@@ -21,21 +25,10 @@ module Twitter
21
25
  alias_method :retweeted_tweet?, :retweeted_status?
22
26
  object_attr_reader :User, :user, :status
23
27
 
24
- # @return [Boolean]
25
- def entities?
26
- !@attrs[:entities].nil? && @attrs[:entities].any? { |_, array| !array.empty? }
27
- end
28
- memoize :entities?
29
-
30
- def filter_level
31
- @attrs[:filter_level] || 'none'
32
- end
33
- memoize :filter_level
34
-
35
- # @return [String]
36
28
  # @note May be > 140 characters.
29
+ # @return [String]
37
30
  def full_text
38
- if retweeted_status?
31
+ if retweet?
39
32
  prefix = text[/\A(RT @[a-z0-9_]{1,20}: )/i, 1]
40
33
  [prefix, retweeted_status.text].compact.join
41
34
  else
@@ -44,69 +37,12 @@ module Twitter
44
37
  end
45
38
  memoize :full_text
46
39
 
47
- # @note Must include entities in your request for this method to work
48
- # @return [Array<Twitter::Entity::Hashtag>]
49
- def hashtags
50
- entities(Entity::Hashtag, :hashtags)
51
- end
52
- memoize :hashtags
53
-
54
- # @note Must include entities in your request for this method to work
55
- # @return [Array<Twitter::Media>]
56
- def media
57
- entities(MediaFactory, :media)
58
- end
59
- memoize :media
60
-
61
- # @return [Boolean]
62
- def reply?
63
- !!@attrs[:in_reply_to_status_id]
64
- end
65
- memoize :reply?
66
-
67
- # @note Must include entities in your request for this method to work
68
- # @return [Array<Twitter::Entity::Symbol>]
69
- def symbols
70
- entities(Entity::Symbol, :symbols)
71
- end
72
- memoize :symbols
73
-
74
40
  # @return [String] The URL to the tweet.
75
41
  def uri
76
- Addressable::URI.parse("https://twitter.com/#{user.screen_name}/status/#{id}")
42
+ Addressable::URI.parse("https://twitter.com/#{user.screen_name}/status/#{id}") unless user.nil?
77
43
  end
78
44
  memoize :uri
79
45
  alias_method :url, :uri
80
-
81
- # @note Must include entities in your request for this method to work
82
- # @return [Array<Twitter::Entity::URI>]
83
- def uris
84
- entities(Entity::URI, :urls)
85
- end
86
- memoize :uris
87
- alias_method :urls, :uris
88
-
89
- # @note Must include entities in your request for this method to work
90
- # @return [Array<Twitter::Entity::UserMention>]
91
- def user_mentions
92
- entities(Entity::UserMention, :user_mentions)
93
- end
94
- memoize :user_mentions
95
-
96
- private
97
-
98
- # @param klass [Class]
99
- # @param key [Symbol]
100
- def entities(klass, key)
101
- if entities?
102
- Array(@attrs[:entities][key.to_sym]).map do |entity|
103
- klass.new(entity)
104
- end
105
- else
106
- warn "#{Kernel.caller.first}: To get #{key.to_s.tr('_', ' ')}, you must pass `:include_entities => true` when requesting the #{self.class}."
107
- []
108
- end
109
- end
110
46
  end
111
47
  Status = Tweet # rubocop:disable ConstantName
112
48
  end
@@ -1,11 +1,15 @@
1
+ require 'addressable/uri'
2
+ require 'memoizable'
1
3
  require 'twitter/basic_user'
2
4
  require 'twitter/creatable'
5
+ require 'twitter/entity/uri'
6
+ require 'twitter/profile'
3
7
 
4
8
  module Twitter
5
9
  class User < Twitter::BasicUser
6
- PROFILE_IMAGE_SUFFIX_REGEX = /_normal(\.gif|\.jpe?g|\.png)$/i
7
- PREDICATE_URI_METHOD_REGEX = /_uri\?$/
8
10
  include Twitter::Creatable
11
+ include Twitter::Profile
12
+ include Memoizable
9
13
  attr_reader :connections, :contributors_enabled, :default_profile,
10
14
  :default_profile_image, :description, :favourites_count,
11
15
  :follow_request_sent, :followers_count, :friends_count,
@@ -28,20 +32,6 @@ module Twitter
28
32
  alias_method :tweet?, :status?
29
33
  alias_method :tweeted?, :status?
30
34
 
31
- class << self
32
- private
33
-
34
- def alias_predicate_uri_methods(method)
35
- %w(_url? _uri_https? _url_https?).each do |replacement|
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)
42
- end
43
- end
44
-
45
35
  # @return [Array<Twitter::Entity::URI>]
46
36
  def description_uris
47
37
  Array(@attrs[:entities][:description][:urls]).map do |entity|
@@ -51,71 +41,16 @@ module Twitter
51
41
  memoize :description_uris
52
42
  alias_method :description_urls, :description_uris
53
43
 
54
- # Return the URL to the user's profile banner image
55
- #
56
- # @param size [String, Symbol] The size of the image. Must be one of: 'mobile', 'mobile_retina', 'web', 'web_retina', 'ipad', or 'ipad_retina'
57
- # @return [String]
58
- def profile_banner_uri(size = :web)
59
- parse_encoded_uri(insecure_uri([@attrs[:profile_banner_url], size].join('/'))) if @attrs[:profile_banner_url]
60
- end
61
- alias_method :profile_banner_url, :profile_banner_uri
62
-
63
- # Return the secure URL to the user's profile banner image
64
- #
65
- # @param size [String, Symbol] The size of the image. Must be one of: 'mobile', 'mobile_retina', 'web', 'web_retina', 'ipad', or 'ipad_retina'
66
- # @return [String]
67
- def profile_banner_uri_https(size = :web)
68
- parse_encoded_uri([@attrs[:profile_banner_url], size].join('/')) if @attrs[:profile_banner_url]
69
- end
70
- alias_method :profile_banner_url_https, :profile_banner_uri_https
71
-
72
- # @return [Boolean]
73
- def profile_banner_uri?
74
- !!@attrs[:profile_banner_url]
75
- end
76
- memoize :profile_banner_uri?
77
- alias_predicate_uri_methods :profile_banner_uri?
78
-
79
- # Return the URL to the user's profile image
80
- #
81
- # @param size [String, Symbol] The size of the image. Must be one of: 'mini', 'normal', 'bigger' or 'original'
82
- # @return [String]
83
- def profile_image_uri(size = :normal)
84
- parse_encoded_uri(insecure_uri(profile_image_uri_https(size))) if @attrs[:profile_image_url_https]
85
- end
86
- alias_method :profile_image_url, :profile_image_uri
87
-
88
- # Return the secure URL to the user's profile image
89
- #
90
- # @param size [String, Symbol] The size of the image. Must be one of: 'mini', 'normal', 'bigger' or 'original'
91
- # @return [String]
92
- def profile_image_uri_https(size = :normal)
93
- # The profile image URL comes in looking like like this:
94
- # https://a0.twimg.com/profile_images/1759857427/image1326743606_normal.png
95
- # It can be converted to any of the following sizes:
96
- # https://a0.twimg.com/profile_images/1759857427/image1326743606.png
97
- # https://a0.twimg.com/profile_images/1759857427/image1326743606_mini.png
98
- # https://a0.twimg.com/profile_images/1759857427/image1326743606_bigger.png
99
- parse_encoded_uri(@attrs[:profile_image_url_https].sub(PROFILE_IMAGE_SUFFIX_REGEX, profile_image_suffix(size))) if @attrs[:profile_image_url_https]
100
- end
101
- alias_method :profile_image_url_https, :profile_image_uri_https
102
-
103
- def profile_image_uri?
104
- !!@attrs[:profile_image_url_https]
105
- end
106
- memoize :profile_image_uri?
107
- alias_predicate_uri_methods :profile_image_uri?
108
-
109
44
  # @return [String] The URL to the user.
110
45
  def uri
111
- Addressable::URI.parse("https://twitter.com/#{screen_name}")
46
+ Addressable::URI.parse("https://twitter.com/#{screen_name}") unless screen_name.nil?
112
47
  end
113
48
  memoize :uri
114
49
  alias_method :url, :uri
115
50
 
116
51
  # @return [String] The URL to the user's website.
117
52
  def website
118
- Addressable::URI.parse(@attrs[:url]) if @attrs[:url]
53
+ Addressable::URI.parse(@attrs[:url]) unless @attrs[:url].nil?
119
54
  end
120
55
  memoize :website
121
56
 
@@ -123,19 +58,5 @@ module Twitter
123
58
  !!@attrs[:url]
124
59
  end
125
60
  memoize :website?
126
-
127
- private
128
-
129
- def parse_encoded_uri(uri)
130
- Addressable::URI.parse(URI.encode(uri))
131
- end
132
-
133
- def insecure_uri(uri)
134
- uri.to_s.sub(/^https/i, 'http')
135
- end
136
-
137
- def profile_image_suffix(size)
138
- :original == size.to_sym ? '\\1' : "_#{size}\\1"
139
- end
140
61
  end
141
62
  end
@@ -1,13 +1,12 @@
1
1
  module Twitter
2
2
  module Utils
3
- class << self
4
- def parallel_map(enumerable)
5
- # Don't bother spawning a new thread if there's only one item
6
- if enumerable.count == 1
7
- enumerable.map { |object| yield object }
8
- else
9
- enumerable.map { |object| Thread.new { yield object } }.map(&:value)
10
- end
3
+ module_function
4
+ def parallel_map(enumerable)
5
+ # Don't bother spawning a new thread if there's only one item
6
+ if enumerable.count == 1
7
+ enumerable.map { |object| yield object }
8
+ else
9
+ enumerable.map { |object| Thread.new { yield object } }.map(&:value)
11
10
  end
12
11
  end
13
12
  end
@@ -1,8 +1,8 @@
1
1
  module Twitter
2
2
  class Version
3
3
  MAJOR = 5
4
- MINOR = 4
5
- PATCH = 1
4
+ MINOR = 5
5
+ PATCH = 0
6
6
  PRE = nil
7
7
 
8
8
  class << self
@@ -1 +1 @@
1
- {"sender_screen_name":"sferik","recipient_screen_name":"pengwynn","recipient_id":14100886,"recipient":{"profile_background_tile":false,"profile_sidebar_fill_color":"dddddd","description":"Christian husband and father. Dev Experience @ HP Cloud Services. Co-host of the @changelogshow. Mashup of design & development.","friends_count":1877,"follow_request_sent":false,"notifications":false,"profile_sidebar_border_color":"cccccc","verified":false,"time_zone":"Central Time (US & Canada)","favourites_count":32,"url":"http:\/\/wynnnetherland.com","profile_background_color":"efefef","listed_count":184,"lang":"en","created_at":"Sat Mar 08 16:34:22 +0000 2008","location":"Dallas, TX","show_all_inline_media":false,"statuses_count":3928,"profile_use_background_image":true,"profile_text_color":"666666","protected":false,"profile_image_url":"http:\/\/a2.twimg.com\/profile_images\/485575482\/komikazee_normal.png","id_str":"14100886","geo_enabled":true,"name":"Wynn Netherland","contributors_enabled":false,"following":true,"profile_background_image_url":"http:\/\/a1.twimg.com\/profile_background_images\/61741268\/twitter-small.png","profile_link_color":"35abe9","screen_name":"pengwynn","id":14100886,"utc_offset":-21600,"followers_count":2767},"created_at":"Tue Oct 26 21:24:23 +0000 2010","id_str":"1825786345","sender":{"profile_background_tile":false,"profile_sidebar_fill_color":"DDEEF6","description":"Adventures in hunger and foolishness.","friends_count":88,"follow_request_sent":false,"notifications":false,"profile_sidebar_border_color":"C0DEED","verified":false,"time_zone":"Pacific Time (US & Canada)","favourites_count":729,"url":null,"profile_background_color":"000000","listed_count":28,"lang":"en","created_at":"Mon Jul 16 12:59:01 +0000 2007","location":"San Francisco","show_all_inline_media":true,"statuses_count":2970,"profile_use_background_image":true,"profile_text_color":"333333","protected":false,"profile_image_url":"http:\/\/a0.twimg.com\/profile_images\/323331048\/me_normal.jpg","id_str":"7505382","geo_enabled":true,"name":"Erik Michaels-Ober","contributors_enabled":false,"following":false,"profile_background_image_url":"http:\/\/a3.twimg.com\/profile_background_images\/162641967\/we_concept_bg2.png","profile_link_color":"0084B4","screen_name":"sferik","id":7505382,"utc_offset":-28800,"followers_count":898},"sender_id":7505382,"id":1825786345,"text":"Creating a fixture for the Twitter gem"}
1
+ {"id":418030038152642560,"id_str":"418030038152642560","text":"My #newride from @PUBLICBikes. Don't you want one? https:\/\/t.co\/7HIwCl68Y8 https:\/\/t.co\/JSSxDPr4Sf","sender":{"id":14471007,"id_str":"14471007","name":"Diana Kimball","screen_name":"dianakimball","location":"Berlin","description":"Community at @SoundCloud. Every now and then, I write a longer letter that I\u2019d love to send to you: http:\/\/t.co\/AZEzTiBwRS","url":"http:\/\/t.co\/tUTbLPD7Yc","entities":{"url":{"urls":[{"url":"http:\/\/t.co\/tUTbLPD7Yc","expanded_url":"http:\/\/blog.dianakimball.com","display_url":"blog.dianakimball.com","indices":[0,22]}]},"description":{"urls":[{"url":"http:\/\/t.co\/AZEzTiBwRS","expanded_url":"http:\/\/expertnovice.com","display_url":"expertnovice.com","indices":[100,122]}]}},"protected":false,"followers_count":4283,"friends_count":684,"listed_count":176,"created_at":"Tue Apr 22 05:14:47 +0000 2008","favourites_count":6773,"utc_offset":-18000,"time_zone":"Quito","geo_enabled":true,"verified":false,"statuses_count":5527,"lang":"en","contributors_enabled":false,"is_translator":false,"profile_background_color":"F4FFFF","profile_background_image_url":"http:\/\/abs.twimg.com\/images\/themes\/theme15\/bg.png","profile_background_image_url_https":"https:\/\/abs.twimg.com\/images\/themes\/theme15\/bg.png","profile_background_tile":false,"profile_image_url":"http:\/\/pbs.twimg.com\/profile_images\/3333204267\/51006fd104a17604e0398359ff5937ba_normal.jpeg","profile_image_url_https":"https:\/\/pbs.twimg.com\/profile_images\/3333204267\/51006fd104a17604e0398359ff5937ba_normal.jpeg","profile_banner_url":"https:\/\/pbs.twimg.com\/profile_banners\/14471007\/1359310744","profile_link_color":"8F1864","profile_sidebar_border_color":"FFFFFF","profile_sidebar_fill_color":"C0DFEC","profile_text_color":"333333","profile_use_background_image":false,"default_profile":false,"default_profile_image":false,"following":true,"follow_request_sent":false,"notifications":false},"sender_id":14471007,"sender_id_str":"14471007","sender_screen_name":"dianakimball","recipient":{"id":7505382,"id_str":"7505382","name":"Erik Michaels-Ober","screen_name":"sferik","location":"","description":"Prolonging the inevitable.","url":"https:\/\/t.co\/L2xIBaQPRf","entities":{"url":{"urls":[{"url":"https:\/\/t.co\/L2xIBaQPRf","expanded_url":"https:\/\/github.com\/sferik","display_url":"github.com\/sferik","indices":[0,23]}]},"description":{"urls":[]}},"protected":false,"followers_count":3946,"friends_count":345,"listed_count":205,"created_at":"Mon Jul 16 12:59:01 +0000 2007","favourites_count":8018,"utc_offset":3600,"time_zone":"Berlin","geo_enabled":true,"verified":false,"statuses_count":13011,"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:\/\/pbs.twimg.com\/profile_images\/378800000757402331\/d8dfba561e2e94112a737f17e7d138ca_normal.jpeg","profile_image_url_https":"https:\/\/pbs.twimg.com\/profile_images\/378800000757402331\/d8dfba561e2e94112a737f17e7d138ca_normal.jpeg","profile_banner_url":"https:\/\/pbs.twimg.com\/profile_banners\/7505382\/1385736840","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},"recipient_id":7505382,"recipient_id_str":"7505382","recipient_screen_name":"sferik","created_at":"Tue Dec 31 14:45:02 +0000 2013","entities":{"hashtags":[{"text":"newride","indices":[3,11]}],"symbols":[],"urls":[{"url":"https:\/\/t.co\/7HIwCl68Y8","expanded_url":"https:\/\/twitter.com\/PUBLICBikes\/status\/350315569708412929","display_url":"twitter.com\/PUBLICBikes\/st\u2026","indices":[51,74]},{"url":"https:\/\/t.co\/JSSxDPr4Sf","expanded_url":"https:\/\/ton.twitter.com\/1.1\/ton\/data\/dm\/418030038152642560\/418030038161047552\/0cnHqrKV.jpg","display_url":"pic.twitter.com\/JSSxDPr4Sf","indices":[75,98]}],"user_mentions":[{"screen_name":"PUBLICBikes","name":"PUBLIC Bikes","id":30035888,"id_str":"30035888","indices":[17,29]}],"media":[{"id":418030038161047552,"id_str":"418030038161047552","indices":[75,98],"media_url":"https:\/\/ton.twitter.com\/1.1\/ton\/data\/dm\/418030038152642560\/418030038161047552\/0cnHqrKV.jpg","media_url_https":"https:\/\/ton.twitter.com\/1.1\/ton\/data\/dm\/418030038152642560\/418030038161047552\/0cnHqrKV.jpg","url":"https:\/\/t.co\/JSSxDPr4Sf","display_url":"pic.twitter.com\/JSSxDPr4Sf","expanded_url":"https:\/\/ton.twitter.com\/1.1\/ton\/data\/dm\/418030038152642560\/418030038161047552\/0cnHqrKV.jpg","type":"photo","sizes":{"medium":{"w":600,"h":600,"resize":"fit"},"large":{"w":640,"h":640,"resize":"fit"},"thumb":{"w":150,"h":150,"resize":"crop"},"small":{"w":340,"h":340,"resize":"fit"}}}]}}
@@ -1,7 +1,17 @@
1
+ # coding: utf-8
1
2
  require 'helper'
2
3
 
3
4
  describe Twitter::DirectMessage do
4
5
 
6
+ before do
7
+ @old_stderr = $stderr
8
+ $stderr = StringIO.new
9
+ end
10
+
11
+ after do
12
+ $stderr = @old_stderr
13
+ end
14
+
5
15
  describe '#==' do
6
16
  it 'returns true when objects IDs are the same' do
7
17
  direct_message = Twitter::DirectMessage.new(:id => 1, :text => 'foo')
@@ -86,4 +96,136 @@ describe Twitter::DirectMessage do
86
96
  end
87
97
  end
88
98
 
99
+ describe '#hashtags' do
100
+ it 'returns an array of Entity::Hashtag when entities are set' do
101
+ hashtags_array = [
102
+ {
103
+ :text => 'twitter',
104
+ :indices => [10, 33],
105
+ }
106
+ ]
107
+ hashtags = Twitter::DirectMessage.new(:id => 28_669_546_014, :entities => {:hashtags => hashtags_array}).hashtags
108
+ expect(hashtags).to be_an Array
109
+ expect(hashtags.first).to be_a Twitter::Entity::Hashtag
110
+ expect(hashtags.first.indices).to eq([10, 33])
111
+ expect(hashtags.first.text).to eq('twitter')
112
+ end
113
+ it 'is empty when not set' do
114
+ hashtags = Twitter::DirectMessage.new(:id => 28_669_546_014).hashtags
115
+ expect(hashtags).to be_empty
116
+ end
117
+ it 'warns when not set' do
118
+ Twitter::DirectMessage.new(:id => 28_669_546_014).hashtags
119
+ expect($stderr.string).to match(/To get hashtags, you must pass `:include_entities => true` when requesting the Twitter::DirectMessage\./)
120
+ end
121
+ end
122
+
123
+ describe '#media' do
124
+ it 'returns media' do
125
+ media = Twitter::DirectMessage.new(:id => 28_669_546_014, :entities => {:media => [{:id => 1, :type => 'photo'}]}).media
126
+ expect(media).to be_an Array
127
+ expect(media.first).to be_a Twitter::Media::Photo
128
+ end
129
+ it 'is empty when not set' do
130
+ media = Twitter::DirectMessage.new(:id => 28_669_546_014).media
131
+ expect(media).to be_empty
132
+ end
133
+ it 'warns when not set' do
134
+ Twitter::DirectMessage.new(:id => 28_669_546_014).media
135
+ expect($stderr.string).to match(/To get media, you must pass `:include_entities => true` when requesting the Twitter::DirectMessage\./)
136
+ end
137
+ end
138
+
139
+ describe '#symbols' do
140
+ it 'returns an array of Entity::Symbol when symbols are set' do
141
+ symbols_array = [
142
+ {:text => 'PEP', :indices => [114, 118]},
143
+ {:text => 'COKE', :indices => [128, 133]}
144
+ ]
145
+ symbols = Twitter::DirectMessage.new(:id => 28_669_546_014, :entities => {:symbols => symbols_array}).symbols
146
+ expect(symbols).to be_an Array
147
+ expect(symbols.size).to eq(2)
148
+ expect(symbols.first).to be_a Twitter::Entity::Symbol
149
+ expect(symbols.first.indices).to eq([114, 118])
150
+ expect(symbols.first.text).to eq('PEP')
151
+ end
152
+ it 'is empty when not set' do
153
+ symbols = Twitter::DirectMessage.new(:id => 28_669_546_014).symbols
154
+ expect(symbols).to be_empty
155
+ end
156
+ it 'warns when not set' do
157
+ Twitter::DirectMessage.new(:id => 28_669_546_014).symbols
158
+ expect($stderr.string).to match(/To get symbols, you must pass `:include_entities => true` when requesting the Twitter::DirectMessage\./)
159
+ end
160
+ end
161
+
162
+ describe '#uris' do
163
+ it 'returns an array of Entity::URIs when entities are set' do
164
+ urls_array = [
165
+ {
166
+ :url => 'http://example.com/t.co',
167
+ :expanded_url => 'http://example.com/expanded',
168
+ :display_url => 'example.com/expanded…',
169
+ :indices => [10, 33],
170
+ }
171
+ ]
172
+ direct_message = Twitter::DirectMessage.new(:id => 28_669_546_014, :entities => {:urls => urls_array})
173
+ expect(direct_message.uris).to be_an Array
174
+ expect(direct_message.uris.first).to be_a Twitter::Entity::URI
175
+ expect(direct_message.uris.first.indices).to eq([10, 33])
176
+ expect(direct_message.uris.first.display_uri).to be_a String
177
+ expect(direct_message.uris.first.display_uri).to eq('example.com/expanded…')
178
+ end
179
+ it 'is empty when not set' do
180
+ direct_message = Twitter::DirectMessage.new(:id => 28_669_546_014)
181
+ expect(direct_message.uris).to be_empty
182
+ end
183
+ it 'warns when not set' do
184
+ Twitter::DirectMessage.new(:id => 28_669_546_014).urls
185
+ expect($stderr.string).to match(/To get urls, you must pass `:include_entities => true` when requesting the Twitter::DirectMessage\./)
186
+ end
187
+ it 'can handle strange urls' do
188
+ urls_array = [
189
+ {
190
+ :url => 'http://with_underscore.example.com/t.co',
191
+ :expanded_url => 'http://with_underscore.example.com/expanded',
192
+ :display_url => 'with_underscore.example.com/expanded…',
193
+ :indices => [10, 33],
194
+ }
195
+ ]
196
+ direct_message = Twitter::DirectMessage.new(:id => 28_669_546_014, :entities => {:urls => urls_array})
197
+ uri = direct_message.uris.first
198
+ expect { uri.url }.not_to raise_error
199
+ expect { uri.expanded_url }.not_to raise_error
200
+ expect { uri.display_url }.not_to raise_error
201
+ end
202
+ end
203
+
204
+ describe '#user_mentions' do
205
+ it 'returns an array of Entity::UserMention when entities are set' do
206
+ user_mentions_array = [
207
+ {
208
+ :screen_name => 'sferik',
209
+ :name => 'Erik Michaels-Ober',
210
+ :id_str => '7_505_382',
211
+ :indices => [0, 6],
212
+ :id => 7_505_382,
213
+ }
214
+ ]
215
+ user_mentions = Twitter::DirectMessage.new(:id => 28_669_546_014, :entities => {:user_mentions => user_mentions_array}).user_mentions
216
+ expect(user_mentions).to be_an Array
217
+ expect(user_mentions.first).to be_a Twitter::Entity::UserMention
218
+ expect(user_mentions.first.indices).to eq([0, 6])
219
+ expect(user_mentions.first.id).to eq(7_505_382)
220
+ end
221
+ it 'is empty when not set' do
222
+ user_mentions = Twitter::DirectMessage.new(:id => 28_669_546_014).user_mentions
223
+ expect(user_mentions).to be_empty
224
+ end
225
+ it 'warns when not set' do
226
+ Twitter::DirectMessage.new(:id => 28_669_546_014).user_mentions
227
+ expect($stderr.string).to match(/To get user mentions, you must pass `:include_entities => true` when requesting the Twitter::DirectMessage\./)
228
+ end
229
+ end
230
+
89
231
  end