twitter 4.3.0 → 4.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. data/.yardopts +10 -0
  2. data/CHANGELOG.md +123 -110
  3. data/CONTRIBUTING.md +51 -0
  4. data/README.md +17 -2
  5. data/lib/twitter/api/direct_messages.rb +3 -6
  6. data/lib/twitter/api/favorites.rb +6 -13
  7. data/lib/twitter/api/friends_and_followers.rb +76 -8
  8. data/lib/twitter/api/lists.rb +65 -36
  9. data/lib/twitter/api/places_and_geo.rb +3 -3
  10. data/lib/twitter/api/saved_searches.rb +2 -2
  11. data/lib/twitter/api/spam_reporting.rb +2 -2
  12. data/lib/twitter/api/suggested_users.rb +1 -1
  13. data/lib/twitter/api/timelines.rb +12 -8
  14. data/lib/twitter/api/tweets.rb +8 -12
  15. data/lib/twitter/api/undocumented.rb +3 -3
  16. data/lib/twitter/api/users.rb +16 -10
  17. data/lib/twitter/api/utils.rb +109 -30
  18. data/lib/twitter/base.rb +15 -5
  19. data/lib/twitter/basic_user.rb +0 -1
  20. data/lib/twitter/client.rb +16 -37
  21. data/lib/twitter/core_ext/enumerable.rb +2 -2
  22. data/lib/twitter/default.rb +5 -8
  23. data/lib/twitter/exceptable.rb +36 -0
  24. data/lib/twitter/factory.rb +4 -4
  25. data/lib/twitter/list.rb +0 -1
  26. data/lib/twitter/request/multipart_with_file.rb +5 -7
  27. data/lib/twitter/search_results.rb +10 -6
  28. data/lib/twitter/settings.rb +0 -1
  29. data/lib/twitter/source_user.rb +0 -7
  30. data/lib/twitter/target_user.rb +0 -1
  31. data/lib/twitter/tweet.rb +15 -17
  32. data/lib/twitter/user.rb +4 -14
  33. data/lib/twitter/version.rb +4 -4
  34. data/spec/fixtures/followers_list.json +1 -0
  35. data/spec/fixtures/friends_list.json +1 -0
  36. data/spec/fixtures/ids_list.json +1 -1
  37. data/spec/fixtures/ids_list2.json +1 -1
  38. data/spec/helper.rb +9 -8
  39. data/spec/twitter/action_factory_spec.rb +1 -1
  40. data/spec/twitter/api/favorites_spec.rb +2 -2
  41. data/spec/twitter/api/friends_and_followers_spec.rb +102 -2
  42. data/spec/twitter/api/spam_reporting_spec.rb +2 -2
  43. data/spec/twitter/api/tweets_spec.rb +2 -2
  44. data/spec/twitter/api/users_spec.rb +107 -49
  45. data/spec/twitter/base_spec.rb +1 -1
  46. data/spec/twitter/client_spec.rb +4 -4
  47. data/spec/twitter/cursor_spec.rb +2 -2
  48. data/spec/twitter/error/client_error_spec.rb +16 -5
  49. data/spec/twitter/error/server_error_spec.rb +1 -1
  50. data/spec/twitter/error_spec.rb +2 -2
  51. data/spec/twitter/geo_factory_spec.rb +1 -1
  52. data/spec/twitter/identifiable_spec.rb +2 -2
  53. data/spec/twitter/media_factory_spec.rb +1 -1
  54. data/spec/twitter/search_results_spec.rb +11 -0
  55. data/spec/twitter/tweet_spec.rb +11 -0
  56. data/twitter.gemspec +3 -2
  57. metadata +190 -173
  58. data/lib/twitter/core_ext/array.rb +0 -7
  59. data/lib/twitter/core_ext/hash.rb +0 -100
  60. data/lib/twitter/core_ext/string.rb +0 -10
@@ -3,7 +3,6 @@ require 'twitter/identity'
3
3
  module Twitter
4
4
  class BasicUser < Twitter::Identity
5
5
  attr_reader :following, :screen_name
6
- alias following? following
7
6
  alias handle screen_name
8
7
  alias username screen_name
9
8
  alias user_name screen_name
@@ -27,7 +27,6 @@ module Twitter
27
27
  # @note All methods have been separated into modules and follow the same grouping used in {http://dev.twitter.com/doc the Twitter API Documentation}.
28
28
  # @see http://dev.twitter.com/pages/every_developer
29
29
  class Client
30
- include Twitter::API
31
30
  include Twitter::API::DirectMessages
32
31
  include Twitter::API::Favorites
33
32
  include Twitter::API::FriendsAndFollowers
@@ -67,57 +66,37 @@ module Twitter
67
66
 
68
67
  # Perform an HTTP POST request
69
68
  def post(path, params={})
70
- request(:post, path, params)
69
+ signature_params = params.values.any?{|value| value.respond_to?(:to_io)} ? {} : params
70
+ request(:post, path, params, signature_params)
71
71
  end
72
72
 
73
- # Perform an HTTP UPDATE request
73
+ # Perform an HTTP PUT request
74
74
  def put(path, params={})
75
75
  request(:put, path, params)
76
76
  end
77
77
 
78
78
  private
79
79
 
80
- # Returns a Faraday::Connection object
81
- #
82
- # @return [Faraday::Connection]
83
- def connection
84
- @connection ||= Faraday.new(@endpoint, @connection_options.merge(:builder => @middleware))
85
- end
86
-
87
- # Perform an HTTP request
88
- #
89
- # @raise [Twitter::Error::ClientError, Twitter::Error::DecodeError]
90
- def request(method, path, params={})
91
- uri = URI(@endpoint) unless uri.respond_to?(:host)
92
- uri += path
93
- request_headers = {}
94
- if credentials?
95
- authorization = auth_header(method, uri, params)
96
- request_headers[:authorization] = authorization.to_s
97
- end
98
- connection.url_prefix = @endpoint
99
- response = connection.run_request(method.to_sym, path, nil, request_headers) do |request|
100
- unless params.empty?
101
- case request.method
102
- when :post, :put
103
- request.body = params
104
- else
105
- request.params.update(params)
106
- end
107
- end
108
- yield request if block_given?
80
+ def request(method, path, params={}, signature_params=params)
81
+ connection.send(method.to_sym, path, params) do |request|
82
+ request.headers[:authorization] = auth_header(method.to_sym, path, signature_params).to_s
109
83
  end.env
110
- response
111
84
  rescue Faraday::Error::ClientError
112
85
  raise Twitter::Error::ClientError
113
86
  rescue MultiJson::DecodeError
114
87
  raise Twitter::Error::DecodeError
115
88
  end
116
89
 
117
- def auth_header(method, uri, params={})
118
- # When posting a file, don't sign any params
119
- signature_params = [:post, :put].include?(method.to_sym) && params.values.any?{|value| value.respond_to?(:to_io)} ? {} : params
120
- SimpleOAuth::Header.new(method, uri, signature_params, credentials)
90
+ # Returns a Faraday::Connection object
91
+ #
92
+ # @return [Faraday::Connection]
93
+ def connection
94
+ @connection ||= Faraday.new(@endpoint, @connection_options.merge(:builder => @middleware))
95
+ end
96
+
97
+ def auth_header(method, path, params={})
98
+ uri = URI(@endpoint + path)
99
+ SimpleOAuth::Header.new(method, uri, params, credentials)
121
100
  end
122
101
 
123
102
  end
@@ -15,9 +15,9 @@ private
15
15
  def abort_on_exception
16
16
  initial_abort_on_exception = Thread.abort_on_exception
17
17
  Thread.abort_on_exception ||= true
18
- value = yield
18
+ yield
19
+ ensure
19
20
  Thread.abort_on_exception = initial_abort_on_exception
20
- value
21
21
  end
22
22
 
23
23
  end
@@ -2,7 +2,6 @@ require 'faraday'
2
2
  require 'twitter/configurable'
3
3
  require 'twitter/error/client_error'
4
4
  require 'twitter/error/server_error'
5
- require 'twitter/identity_map'
6
5
  require 'twitter/request/multipart_with_file'
7
6
  require 'twitter/response/parse_json'
8
7
  require 'twitter/response/raise_error'
@@ -10,7 +9,7 @@ require 'twitter/version'
10
9
 
11
10
  module Twitter
12
11
  module Default
13
- ENDPOINT = 'https://api.twitter.com' unless defined? ENDPOINT
12
+ ENDPOINT = 'https://api.twitter.com' unless defined? Twitter::Default::ENDPOINT
14
13
  CONNECTION_OPTIONS = {
15
14
  :headers => {
16
15
  :accept => 'application/json',
@@ -20,8 +19,8 @@ module Twitter
20
19
  :raw => true,
21
20
  :ssl => {:verify => false},
22
21
  :timeout => 10,
23
- } unless defined? CONNECTION_OPTIONS
24
- IDENTITY_MAP = false unless defined? IDENTITY_MAP
22
+ } unless defined? Twitter::Default::CONNECTION_OPTIONS
23
+ IDENTITY_MAP = false unless defined? Twitter::Default::IDENTITY_MAP
25
24
  MIDDLEWARE = Faraday::Builder.new do |builder|
26
25
  # Convert file uploads to Faraday::UploadIO objects
27
26
  builder.use Twitter::Request::MultipartWithFile
@@ -37,7 +36,7 @@ module Twitter
37
36
  builder.use Twitter::Response::RaiseError, Twitter::Error::ServerError
38
37
  # Set Faraday's HTTP adapter
39
38
  builder.adapter Faraday.default_adapter
40
- end
39
+ end unless defined? Twitter::Default::MIDDLEWARE
41
40
 
42
41
  class << self
43
42
 
@@ -66,7 +65,7 @@ module Twitter
66
65
  ENV['TWITTER_OAUTH_TOKEN_SECRET']
67
66
  end
68
67
 
69
- # @note This is configurable in case you want to use HTTP instead of HTTPS or use a Twitter-compatible endpoint.
68
+ # @note This is configurable in case you want to use a Twitter-compatible endpoint.
70
69
  # @see http://status.net/wiki/Twitter-compatible_API
71
70
  # @see http://en.blog.wordpress.com/2009/12/12/twitter-api/
72
71
  # @see http://staff.tumblr.com/post/287703110/api
@@ -76,12 +75,10 @@ module Twitter
76
75
  ENDPOINT
77
76
  end
78
77
 
79
- # @return [Hash]
80
78
  def connection_options
81
79
  CONNECTION_OPTIONS
82
80
  end
83
81
 
84
- # @return [Twitter::IdentityMap]
85
82
  def identity_map
86
83
  IDENTITY_MAP
87
84
  end
@@ -0,0 +1,36 @@
1
+ module Twitter
2
+ module Exceptable
3
+
4
+ private
5
+
6
+ # Return a hash that includes everything but the given keys.
7
+ #
8
+ # @param klass [Class]
9
+ # @param hash [Hash]
10
+ # @param key1 [Symbol]
11
+ # @param key2 [Symbol]
12
+ def fetch_or_new_without_self(klass, hash, key1, key2)
13
+ klass.fetch_or_new(hash.dup[key1].merge(key2 => except(hash, key1))) unless hash[key1].nil?
14
+ end
15
+
16
+ # Return a hash that includes everything but the given keys.
17
+ #
18
+ # @param hash [Hash]
19
+ # @param key [Symbol]
20
+ # @return [Hash]
21
+ def except(hash, key)
22
+ except!(hash.dup, key)
23
+ end
24
+
25
+ # Replaces the hash without the given keys.
26
+ #
27
+ # @param hash [Hash]
28
+ # @param key [Symbol]
29
+ # @return [Hash]
30
+ def except!(hash, key)
31
+ hash.delete(key)
32
+ hash
33
+ end
34
+
35
+ end
36
+ end
@@ -1,5 +1,3 @@
1
- require 'twitter/core_ext/string'
2
-
3
1
  module Twitter
4
2
  class Factory
5
3
 
@@ -10,8 +8,10 @@ module Twitter
10
8
  # @return [Twitter::Action::Favorite, Twitter::Action::Follow, Twitter::Action::ListMemberAdded, Twitter::Action::Mention, Twitter::Action::Reply, Twitter::Action::Retweet]
11
9
  def self.fetch_or_new(method, klass, attrs={})
12
10
  return unless attrs
13
- if type = attrs.delete(method.to_sym)
14
- klass.const_get(type.camelize.to_sym).fetch_or_new(attrs)
11
+ type = attrs.delete(method.to_sym)
12
+ if type
13
+ const_name = type.gsub(/\/(.?)/){"::#{$1.upcase}"}.gsub(/(?:^|_)(.)/){$1.upcase}
14
+ klass.const_get(const_name.to_sym).fetch_or_new(attrs)
15
15
  else
16
16
  raise ArgumentError, "argument must have :#{method} key"
17
17
  end
@@ -7,7 +7,6 @@ module Twitter
7
7
  include Twitter::Creatable
8
8
  attr_reader :description, :following, :full_name, :member_count,
9
9
  :mode, :name, :slug, :subscriber_count, :uri
10
- alias following? following
11
10
 
12
11
  # @return [Twitter::User]
13
12
  def user
@@ -10,14 +10,12 @@ module Twitter
10
10
  end
11
11
 
12
12
  def call(env)
13
- if env[:body].is_a?(Hash)
14
- env[:body].each do |key, value|
15
- if value.respond_to?(:to_io)
16
- env[:body][key] = Faraday::UploadIO.new(value, mime_type(value.path), value.path)
17
- env[:request_headers][CONTENT_TYPE] = self.class.mime_type
18
- end
13
+ env[:body].each do |key, value|
14
+ if value.respond_to?(:to_io)
15
+ env[:body][key] = Faraday::UploadIO.new(value, mime_type(value.path), value.path)
16
+ env[:request_headers][CONTENT_TYPE] = self.class.mime_type
19
17
  end
20
- end
18
+ end if env[:body].is_a?(::Hash)
21
19
  @app.call(env)
22
20
  end
23
21
 
@@ -15,33 +15,37 @@ module Twitter
15
15
 
16
16
  # @return [Float]
17
17
  def completed_in
18
- @attrs[:search_metadata][:completed_in] unless @attrs[:search_metadata].nil?
18
+ @attrs[:search_metadata][:completed_in] if search_metadata?
19
19
  end
20
20
 
21
21
  # @return [Integer]
22
22
  def max_id
23
- @attrs[:search_metadata][:max_id] unless @attrs[:search_metadata].nil?
23
+ @attrs[:search_metadata][:max_id] if search_metadata?
24
24
  end
25
25
 
26
26
  # @return [Integer]
27
27
  def page
28
- @attrs[:search_metadata][:page] unless @attrs[:search_metadata].nil?
28
+ @attrs[:search_metadata][:page] if search_metadata?
29
29
  end
30
30
 
31
31
  # @return [String]
32
32
  def query
33
- @attrs[:search_metadata][:query] unless @attrs[:search_metadata].nil?
33
+ @attrs[:search_metadata][:query] if search_metadata?
34
34
  end
35
35
 
36
36
  # @return [Integer]
37
37
  def results_per_page
38
- @attrs[:search_metadata][:results_per_page] unless @attrs[:search_metadata].nil?
38
+ @attrs[:search_metadata][:results_per_page] if search_metadata?
39
39
  end
40
40
  alias rpp results_per_page
41
41
 
42
+ def search_metadata?
43
+ !@attrs[:search_metadata].nil?
44
+ end
45
+
42
46
  # @return [Integer]
43
47
  def since_id
44
- @attrs[:search_metadata][:since_id] unless @attrs[:search_metadata].nil?
48
+ @attrs[:search_metadata][:since_id] if search_metadata?
45
49
  end
46
50
 
47
51
  end
@@ -6,7 +6,6 @@ module Twitter
6
6
  attr_reader :always_use_https, :discoverable_by_email, :geo_enabled,
7
7
  :language, :protected, :screen_name, :show_all_inline_media, :sleep_time,
8
8
  :time_zone
9
- alias protected? protected
10
9
 
11
10
  # @return [Twitter::Place]
12
11
  def trend_location
@@ -4,12 +4,5 @@ module Twitter
4
4
  class SourceUser < Twitter::BasicUser
5
5
  attr_reader :all_replies, :blocking, :can_dm, :followed_by, :marked_spam,
6
6
  :notifications_enabled, :want_retweets
7
- alias all_replies? all_replies
8
- alias blocking? blocking
9
- alias can_dm? can_dm
10
- alias followed_by? followed_by
11
- alias marked_spam? marked_spam
12
- alias notifications_enabled? notifications_enabled
13
- alias want_retweets? want_retweets
14
7
  end
15
8
  end
@@ -3,6 +3,5 @@ require 'twitter/basic_user'
3
3
  module Twitter
4
4
  class TargetUser < Twitter::BasicUser
5
5
  attr_reader :followed_by
6
- alias followed_by? followed_by
7
6
  end
8
7
  end
@@ -1,8 +1,8 @@
1
- require 'twitter/core_ext/hash'
2
1
  require 'twitter/creatable'
3
2
  require 'twitter/entity/hashtag'
4
3
  require 'twitter/entity/url'
5
4
  require 'twitter/entity/user_mention'
5
+ require 'twitter/exceptable'
6
6
  require 'twitter/geo_factory'
7
7
  require 'twitter/identity'
8
8
  require 'twitter/media_factory'
@@ -13,18 +13,21 @@ require 'twitter/user'
13
13
  module Twitter
14
14
  class Tweet < Twitter::Identity
15
15
  include Twitter::Creatable
16
+ include Twitter::Exceptable
16
17
  attr_reader :favorited, :favoriters, :from_user_id, :from_user_name,
17
18
  :in_reply_to_screen_name, :in_reply_to_attrs_id, :in_reply_to_status_id,
18
19
  :in_reply_to_user_id, :iso_language_code, :profile_image_url,
19
20
  :profile_image_url_https, :repliers, :retweeted, :retweeters, :source,
20
21
  :text, :to_user, :to_user_id, :to_user_name, :truncated
21
22
  alias in_reply_to_tweet_id in_reply_to_status_id
22
- alias favorited? favorited
23
23
  alias favourited favorited
24
- alias favourited? favorited
24
+ alias favourited? favorited?
25
25
  alias favouriters favoriters
26
- alias retweeted? retweeted
27
- alias truncated? truncated
26
+
27
+ # @return [Boolean]
28
+ def entities?
29
+ !@attrs[:entities].nil?
30
+ end
28
31
 
29
32
  # @return [Integer]
30
33
  def favoriters_count
@@ -121,7 +124,7 @@ module Twitter
121
124
 
122
125
  # @return [Twitter::User]
123
126
  def user
124
- @user ||= Twitter::User.fetch_or_new(@attrs.dup[:user].merge(:status => @attrs.except(:user))) if user?
127
+ @user ||= fetch_or_new_without_self(Twitter::User, @attrs, :user, :status)
125
128
  end
126
129
 
127
130
  # @note Must include entities in your request for this method to work
@@ -130,30 +133,25 @@ module Twitter
130
133
  @user_mentions ||= entities(Twitter::Entity::UserMention, :user_mentions)
131
134
  end
132
135
 
133
- # @return [Boolean]
134
- def entities?
135
- !@attrs[:entities].nil?
136
+ def user?
137
+ !@attrs[:user].nil?
136
138
  end
137
139
 
138
140
  private
139
141
 
140
142
  # @param klass [Class]
141
- # @param method [Symbol]
142
- def entities(klass, method)
143
+ # @param key [Symbol]
144
+ def entities(klass, key)
143
145
  if entities?
144
- Array(@attrs[:entities][method.to_sym]).map do |user_mention|
146
+ Array(@attrs[:entities][key.to_sym]).map do |user_mention|
145
147
  klass.fetch_or_new(user_mention)
146
148
  end
147
149
  else
148
- warn "#{Kernel.caller.first}: To get #{method.to_s.tr('_', ' ')}, you must pass `:include_entities => true` when requesting the #{self.class.name}."
150
+ warn "#{Kernel.caller.first}: To get #{key.to_s.tr('_', ' ')}, you must pass `:include_entities => true` when requesting the #{self.class.name}."
149
151
  []
150
152
  end
151
153
  end
152
154
 
153
- def user?
154
- !@attrs[:user].nil?
155
- end
156
-
157
155
  end
158
156
 
159
157
  Status = Tweet
@@ -1,12 +1,13 @@
1
1
  require 'twitter/basic_user'
2
- require 'twitter/core_ext/hash'
3
2
  require 'twitter/creatable'
3
+ require 'twitter/exceptable'
4
4
  require 'twitter/tweet'
5
5
 
6
6
  module Twitter
7
7
  class User < Twitter::BasicUser
8
8
  PROFILE_IMAGE_SUFFIX_REGEX = /_normal(\.gif|\.jpe?g|\.png)$/
9
9
  include Twitter::Creatable
10
+ include Twitter::Exceptable
10
11
  attr_reader :connections, :contributors_enabled, :default_profile,
11
12
  :default_profile_image, :description, :favourites_count,
12
13
  :follow_request_sent, :followers_count, :friends_count, :geo_enabled,
@@ -17,10 +18,6 @@ module Twitter
17
18
  :profile_sidebar_fill_color, :profile_text_color,
18
19
  :profile_use_background_image, :protected, :statuses_count, :time_zone,
19
20
  :url, :utc_offset, :verified
20
- alias contributors_enabled? contributors_enabled
21
- alias default_profile? default_profile
22
- alias default_profile_image? default_profile_image
23
- alias follow_request_sent? follow_request_sent
24
21
  alias favorite_count favourites_count
25
22
  alias favoriters_count favourites_count
26
23
  alias favorites_count favourites_count
@@ -28,20 +25,13 @@ module Twitter
28
25
  alias favouriters_count favourites_count
29
26
  alias follower_count followers_count
30
27
  alias friend_count friends_count
31
- alias geo_enabled? geo_enabled
32
- alias is_translator? is_translator
33
- alias notifications? notifications
34
- alias profile_background_tile? profile_background_tile
35
- alias profile_use_background_image? profile_use_background_image
36
- alias protected? protected
37
28
  alias status_count statuses_count
38
29
  alias translator is_translator
39
- alias translator? is_translator
30
+ alias translator? is_translator?
40
31
  alias tweet_count statuses_count
41
32
  alias tweets_count statuses_count
42
33
  alias update_count statuses_count
43
34
  alias updates_count statuses_count
44
- alias verified? verified
45
35
 
46
36
  # Return the URL to the user's profile banner image
47
37
  #
@@ -93,7 +83,7 @@ module Twitter
93
83
 
94
84
  # @return [Twitter::Tweet]
95
85
  def status
96
- @status ||= Twitter::Tweet.fetch_or_new(@attrs.dup[:status].merge(:user => @attrs.except(:status))) if status?
86
+ @status ||= fetch_or_new_without_self(Twitter::Tweet, @attrs, :status, :user)
97
87
  end
98
88
 
99
89
  def status?