trubl 1.4.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.yardopts +8 -0
- data/LICENSE.md +20 -0
- data/README.md +72 -0
- data/Rakefile +11 -0
- data/lib/trubl.rb +30 -0
- data/lib/trubl/api/category.rb +33 -0
- data/lib/trubl/api/channel.rb +32 -0
- data/lib/trubl/api/conversation.rb +31 -0
- data/lib/trubl/api/hashtags.rb +45 -0
- data/lib/trubl/api/me.rb +68 -0
- data/lib/trubl/api/search.rb +35 -0
- data/lib/trubl/api/streams.rb +14 -0
- data/lib/trubl/api/suggested_users.rb +16 -0
- data/lib/trubl/api/touts.rb +143 -0
- data/lib/trubl/api/users.rb +79 -0
- data/lib/trubl/authorization.rb +7 -0
- data/lib/trubl/authorizations.rb +8 -0
- data/lib/trubl/base.rb +37 -0
- data/lib/trubl/category.rb +7 -0
- data/lib/trubl/channel.rb +7 -0
- data/lib/trubl/client.rb +252 -0
- data/lib/trubl/collection.rb +43 -0
- data/lib/trubl/conversation.rb +7 -0
- data/lib/trubl/hashtag.rb +7 -0
- data/lib/trubl/hashtags.rb +8 -0
- data/lib/trubl/oauth.rb +56 -0
- data/lib/trubl/pagination.rb +17 -0
- data/lib/trubl/tout.rb +29 -0
- data/lib/trubl/touts.rb +8 -0
- data/lib/trubl/user.rb +29 -0
- data/lib/trubl/users.rb +8 -0
- data/lib/trubl/version.rb +16 -0
- data/lib/trubl/widget.rb +15 -0
- data/lib/trubl/widgets.rb +8 -0
- data/spec/fixtures/category_response.json +1 -0
- data/spec/fixtures/category_touts_response.json +689 -0
- data/spec/fixtures/category_users_response.json +1709 -0
- data/spec/fixtures/channel_response.json +1 -0
- data/spec/fixtures/channel_touts_response.json +1 -0
- data/spec/fixtures/channel_users_response.json +1 -0
- data/spec/fixtures/client1_auth_resp.json +1 -0
- data/spec/fixtures/client2_auth_resp.json +1 -0
- data/spec/fixtures/conversation_authors_response.json +37 -0
- data/spec/fixtures/conversation_response.json +41 -0
- data/spec/fixtures/conversation_touts_response.json +107 -0
- data/spec/fixtures/featured_touts_response.json +4249 -0
- data/spec/fixtures/hashtag_response.json +1 -0
- data/spec/fixtures/hashtags_touts_response.json +304 -0
- data/spec/fixtures/latest_touts_response.json +3600 -0
- data/spec/fixtures/like_tout_response.json +109 -0
- data/spec/fixtures/me_authorizations_response.json +14 -0
- data/spec/fixtures/me_fb_sharing_response.json +21 -0
- data/spec/fixtures/me_friends_response.json +121 -0
- data/spec/fixtures/me_retrieve_user_friends_response.json +174 -0
- data/spec/fixtures/me_retrieve_user_liked_touts_response.json +1255 -0
- data/spec/fixtures/me_retrieve_user_touts_response.json +479 -0
- data/spec/fixtures/retout_tout_response.json +99 -0
- data/spec/fixtures/retrieve_me_response.json +35 -0
- data/spec/fixtures/retrieve_tout.json +98 -0
- data/spec/fixtures/retrieve_tout_response.json +98 -0
- data/spec/fixtures/search_hashtags_response.json +100 -0
- data/spec/fixtures/search_touts_response.json +304 -0
- data/spec/fixtures/search_users_response.json +37 -0
- data/spec/fixtures/suggested_hashtags_response.json +137 -0
- data/spec/fixtures/suggested_users_response.json +1 -0
- data/spec/fixtures/test.mp4 +0 -0
- data/spec/fixtures/tout.json +98 -0
- data/spec/fixtures/tout_conversation_response.json +41 -0
- data/spec/fixtures/touts_liked_by_response.json +65 -0
- data/spec/fixtures/touts_liked_by_user_response.json +106 -0
- data/spec/fixtures/touts_me_updates_response.json +357 -0
- data/spec/fixtures/touts_search_results.json +304 -0
- data/spec/fixtures/trending_hashtags_response.json +149 -0
- data/spec/fixtures/unlike_tout_response.json +109 -0
- data/spec/fixtures/update_me_response.json +35 -0
- data/spec/fixtures/user.json +28 -0
- data/spec/fixtures/user_followers.json +1409 -0
- data/spec/fixtures/user_touts_response.json +479 -0
- data/spec/fixtures/user_with_utf8.json +29 -0
- data/spec/fixtures/users.json +58 -0
- data/spec/fixtures/users_search_results.json +37 -0
- data/spec/fixtures/widgets.json +142 -0
- data/spec/spec_helper.rb +61 -0
- data/spec/trubl/api/category_spec.rb +39 -0
- data/spec/trubl/api/channel_spec.rb +39 -0
- data/spec/trubl/api/conversation_spec.rb +38 -0
- data/spec/trubl/api/hashtags_spec.rb +60 -0
- data/spec/trubl/api/me_spec.rb +98 -0
- data/spec/trubl/api/search_spec.rb +40 -0
- data/spec/trubl/api/streams_spec.rb +18 -0
- data/spec/trubl/api/suggested_users_spec.rb +17 -0
- data/spec/trubl/api/touts_spec.rb +215 -0
- data/spec/trubl/api/users_spec.rb +122 -0
- data/spec/trubl/base_spec.rb +88 -0
- data/spec/trubl/category_spec.rb +15 -0
- data/spec/trubl/channel_spec.rb +16 -0
- data/spec/trubl/client_spec.rb +141 -0
- data/spec/trubl/conversation_spec.rb +13 -0
- data/spec/trubl/hashtag_spec.rb +11 -0
- data/spec/trubl/oauth_spec.rb +27 -0
- data/spec/trubl/tout_spec.rb +41 -0
- data/spec/trubl/user_spec.rb +65 -0
- data/spec/trubl_spec.rb +23 -0
- data/trubl.gemspec +41 -0
- metadata +494 -0
@@ -0,0 +1,79 @@
|
|
1
|
+
require_relative '../touts'
|
2
|
+
require_relative '../user'
|
3
|
+
require_relative '../users'
|
4
|
+
|
5
|
+
module Trubl
|
6
|
+
module API
|
7
|
+
module Users
|
8
|
+
# implements http://developer.tout.com/api-overview/users-api
|
9
|
+
# mixed in to a Client instance, the self passed to response objects is that instance
|
10
|
+
|
11
|
+
# implements http://developer.tout.com/api/users-api/apimethod/retrieve-user
|
12
|
+
# @param uid [String] a user uid
|
13
|
+
# @return [Trubl::User] or nil
|
14
|
+
def retrieve_user(uid=nil)
|
15
|
+
return nil if uid.blank?
|
16
|
+
|
17
|
+
response = get("/api/v1/users/#{uid}")
|
18
|
+
Trubl::User.new.from_response(response)
|
19
|
+
end
|
20
|
+
|
21
|
+
# implements http://developer.tout.com/api/users-api/apimethod/retrieve-users
|
22
|
+
# @param uids [Array<String>] of user uids
|
23
|
+
# @return [Array<Trubl::User>]
|
24
|
+
def retrieve_users(uids=[])
|
25
|
+
uids = (uids.is_a?(Array) ? uids : [uids]).compact.uniq.sort
|
26
|
+
return [] if uids.blank?
|
27
|
+
|
28
|
+
requests = uids.in_groups_of(100, false).collect do |uid_group|
|
29
|
+
{path: "users", query: {uids: uid_group.join(',')} }
|
30
|
+
end
|
31
|
+
|
32
|
+
multi_request(:get, requests).
|
33
|
+
collect { |response| Trubl::Users.new.from_response(response) }.
|
34
|
+
flatten.
|
35
|
+
compact
|
36
|
+
end
|
37
|
+
|
38
|
+
# implements http://developer.tout.com/api/users-api/apimethod/retrieve-list-touts-liked-user
|
39
|
+
# returns Array of Trubl::Tout instances or nil
|
40
|
+
def retrieve_user_likes(uid, order=nil, per_page=nil, page=nil)
|
41
|
+
response = get("/api/v1/users/#{uid}/likes", query: {order: order, per_page: per_page, page: page})
|
42
|
+
Trubl::Touts.new.from_response(response)
|
43
|
+
end
|
44
|
+
|
45
|
+
# implements http://developer.tout.com/api/users-api/apimethod/retrieve-users-touts
|
46
|
+
# return Array of Trubl::Tout instances or nil
|
47
|
+
def retrieve_user_touts(uid, order=nil, per_page=nil, page=nil)
|
48
|
+
response = get("/api/v1/users/#{uid}/touts", query: {order: order, per_page: per_page, page: page})
|
49
|
+
Trubl::Touts.new.from_response(response)
|
50
|
+
end
|
51
|
+
|
52
|
+
# implements http://developer.tout.com/api/users-api/apimethod/retrieve-list-users-follow-user
|
53
|
+
# returns Array of Trubl::User instances or nil
|
54
|
+
def retrieve_user_followers(uid, order=nil, per_page=nil, page=nil)
|
55
|
+
response = get("/api/v1/users/#{uid}/followers", query: {order: order, per_page: per_page, page: page})
|
56
|
+
Trubl::Users.new.from_response(response)
|
57
|
+
end
|
58
|
+
|
59
|
+
# order, per_page, page arent supported at the moment
|
60
|
+
def retrieve_user_widgets(uid, order=nil, per_page=nil, page=nil)
|
61
|
+
response = get("/api/v1/users/#{uid}/widgets")
|
62
|
+
Trubl::Widgets.new.from_response(response)
|
63
|
+
end
|
64
|
+
|
65
|
+
# implements http://developer.tout.com/api/users-api/apimethod/follow-user
|
66
|
+
# returns response object
|
67
|
+
def follow_user(uid)
|
68
|
+
post("/api/v1/users/#{uid}/follows")
|
69
|
+
end
|
70
|
+
|
71
|
+
# implements http://developer.tout.com/api/users-api/apimethod/unfollow-user
|
72
|
+
# returns response object
|
73
|
+
def unfollow_user(uid)
|
74
|
+
delete("/api/v1/users/#{uid}/follows")
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
data/lib/trubl/base.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'hashie'
|
2
|
+
|
3
|
+
module Trubl
|
4
|
+
class Base < Hashie::Mash
|
5
|
+
|
6
|
+
def from_response(response)
|
7
|
+
return nil if missing_or_exception?(response)
|
8
|
+
initialize(parse(response))
|
9
|
+
end
|
10
|
+
|
11
|
+
def parse(response)
|
12
|
+
JSON.parse(response.body)[klass_name]
|
13
|
+
end
|
14
|
+
|
15
|
+
def klass_name
|
16
|
+
self.class.name.downcase.gsub('trubl::', '')
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def missing_or_exception?(response)
|
23
|
+
code = if response.respond_to?(:code)
|
24
|
+
response.code
|
25
|
+
elsif response.respond_to?(:status)
|
26
|
+
response.status
|
27
|
+
else
|
28
|
+
nil
|
29
|
+
end
|
30
|
+
|
31
|
+
code && (400..600).include?(code)
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
|
data/lib/trubl/client.rb
ADDED
@@ -0,0 +1,252 @@
|
|
1
|
+
require_relative './api/category'
|
2
|
+
require_relative './api/channel'
|
3
|
+
require_relative './api/conversation'
|
4
|
+
require_relative './api/hashtags'
|
5
|
+
require_relative './api/me'
|
6
|
+
require_relative './api/search'
|
7
|
+
require_relative './api/streams'
|
8
|
+
require_relative './api/suggested_users'
|
9
|
+
require_relative './api/touts'
|
10
|
+
require_relative './api/users'
|
11
|
+
require_relative './widgets'
|
12
|
+
require_relative './oauth'
|
13
|
+
|
14
|
+
require 'httparty'
|
15
|
+
require 'uri'
|
16
|
+
require 'faraday'
|
17
|
+
require 'active_support/core_ext'
|
18
|
+
|
19
|
+
if RUBY_ENGINE == 'ruby'
|
20
|
+
require 'typhoeus'
|
21
|
+
require 'typhoeus/adapters/faraday'
|
22
|
+
end
|
23
|
+
|
24
|
+
# instantiate a Tout client instance
|
25
|
+
module Trubl
|
26
|
+
# Wrapper for the Tout REST API
|
27
|
+
#
|
28
|
+
# @note All methods have been separated into modules and follow the grouping used in http://developer.tout.com/apis the Tout API Documentation.
|
29
|
+
class Client
|
30
|
+
include Trubl::API::Category
|
31
|
+
include Trubl::API::Channel
|
32
|
+
include Trubl::API::Conversation
|
33
|
+
include Trubl::API::Hashtags
|
34
|
+
include Trubl::API::Me
|
35
|
+
include Trubl::API::Search
|
36
|
+
include Trubl::API::Streams
|
37
|
+
include Trubl::API::Suggested_Users
|
38
|
+
include Trubl::API::Touts
|
39
|
+
include Trubl::API::Users
|
40
|
+
include Trubl::OAuth
|
41
|
+
|
42
|
+
attr_reader :client_id, :client_secret, :access_token, :callback_url
|
43
|
+
|
44
|
+
# Initialize a new Tout client with creds and callback url
|
45
|
+
def initialize(client_id=nil, client_secret=nil, callback_url=nil, *args)
|
46
|
+
opts = (args.last.is_a?(Hash) ? args.last : {}).with_indifferent_access
|
47
|
+
|
48
|
+
opts.delete_if { |k, v| v.nil? }.reverse_merge!(default_tout_configuration)
|
49
|
+
|
50
|
+
@client_id = client_id
|
51
|
+
@client_secret = client_secret
|
52
|
+
@access_token = opts[:access_token]
|
53
|
+
@callback_url = callback_url
|
54
|
+
@uri_scheme = opts[:uri_scheme]
|
55
|
+
@uri_host = opts[:uri_host]
|
56
|
+
@uri_port = opts[:uri_port]
|
57
|
+
@uri_base_path = opts[:uri_base_path]
|
58
|
+
@uri_version = opts[:uri_version]
|
59
|
+
@auth_site = opts[:auth_site]
|
60
|
+
@authorize_url = opts[:authorize_url]
|
61
|
+
@token_url = opts[:token_url]
|
62
|
+
@email = opts[:email]
|
63
|
+
@password = opts[:password]
|
64
|
+
end
|
65
|
+
|
66
|
+
def default_tout_configuration
|
67
|
+
{
|
68
|
+
uri_scheme: 'https',
|
69
|
+
uri_host: 'api.tout.com',
|
70
|
+
uri_base_path: '/api',
|
71
|
+
uri_version: 'v1',
|
72
|
+
auth_site: 'https://www.tout.com/',
|
73
|
+
authorize_url: '/oauth/authorize',
|
74
|
+
token_url: '/oauth/token',
|
75
|
+
email: nil,
|
76
|
+
password: nil
|
77
|
+
}.with_indifferent_access
|
78
|
+
end
|
79
|
+
|
80
|
+
def credentials()
|
81
|
+
{
|
82
|
+
client_id: @client_id,
|
83
|
+
client_secret: @client_secret,
|
84
|
+
access_token: @access_token
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
88
|
+
def api_uri_root()
|
89
|
+
# Changed this from URI.join because scheme became pointless. It could not
|
90
|
+
# override the scheme set in the host and the scheme was required to be set
|
91
|
+
# in @uri_host or else URI.join throws an error
|
92
|
+
URI.parse('').tap do |uri|
|
93
|
+
uri.scheme = @uri_scheme
|
94
|
+
uri.host = @uri_host.gsub(/https?:\/\//, '') # strip the scheme if it is part of the hostname
|
95
|
+
uri.path = [@uri_base_path, @uri_version, nil].join('/')
|
96
|
+
uri.port = @uri_port unless @uri_port.blank?
|
97
|
+
end.to_s
|
98
|
+
end
|
99
|
+
|
100
|
+
# Perform an HTTP DELETE request
|
101
|
+
def delete(path, params={})
|
102
|
+
request(:delete, path, params)
|
103
|
+
end
|
104
|
+
|
105
|
+
# Perform an HTTP GET request
|
106
|
+
def get(path, params={})
|
107
|
+
request(:get, path, params)
|
108
|
+
end
|
109
|
+
|
110
|
+
# Perform an HTTP POST request
|
111
|
+
def post(path, params={})
|
112
|
+
request(:post, path, params)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Perform an HTTP Multipart Form Request
|
116
|
+
def multipart_post(path, params={})
|
117
|
+
raise ArgumentError.new("Must specify a valid file to include\nYou specified #{params[:data]}") unless File.exists?(params[:data])
|
118
|
+
uri = full_uri(path)
|
119
|
+
payload = { 'tout[data]' => Faraday::UploadIO.new(params[:data], 'video/mp4')}.merge(params)
|
120
|
+
|
121
|
+
Trubl.logger.info("Trubl::Client multipart post-ing #{uri.to_s} (content omitted)")
|
122
|
+
|
123
|
+
Faraday.new(url: uri.host) do |faraday|
|
124
|
+
faraday.headers = options
|
125
|
+
faraday.request :multipart
|
126
|
+
faraday.response :logger
|
127
|
+
faraday.adapter Faraday.default_adapter
|
128
|
+
end.post(uri.to_s, payload).tap do |response|
|
129
|
+
if !response.status =~ /20[0-9]/
|
130
|
+
Trubl.logger.fatal("Trubl::Client multipart post-ing #{uri.to_s} #{response.code} #{response.parsed_response}")
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# Perform an HTTP PUT request
|
136
|
+
def put(path, params={})
|
137
|
+
request(:put, path, params)
|
138
|
+
end
|
139
|
+
|
140
|
+
# ToDo: model response handling off of oauth2.client.request
|
141
|
+
# in fact, perhaps we swap this out for the oauth2 request method...
|
142
|
+
def request(method, path, params)
|
143
|
+
params ||= {}
|
144
|
+
uri = full_url(path)
|
145
|
+
|
146
|
+
Trubl.logger.info("Trubl::Client #{method}-ing #{uri} with params #{params.merge(headers: headers)}")
|
147
|
+
response = HTTParty.send(method, uri, params.merge(headers: headers))
|
148
|
+
|
149
|
+
if !response.code =~ /20[0-9]/
|
150
|
+
Trubl.logger.fatal("Trubl::Client #{response.code} #{method}-ing #{uri.to_s} #{response.parsed_response}")
|
151
|
+
else
|
152
|
+
Trubl.logger.debug("Trubl::Client #{uri} response: #{response.body}")
|
153
|
+
end
|
154
|
+
response.body.force_encoding("utf-8") if response.body and response.body.respond_to?(:force_encoding)
|
155
|
+
response
|
156
|
+
end
|
157
|
+
|
158
|
+
def multi_request(method, requests=[], opts={})
|
159
|
+
return [] if requests.blank? or [:get].exclude?(method.to_sym)
|
160
|
+
|
161
|
+
if requests.size == 1
|
162
|
+
request = requests.first
|
163
|
+
path = [request[:path], request[:query].try(:to_query)].compact.join('?')
|
164
|
+
return [request(method, path, request[:params])]
|
165
|
+
end
|
166
|
+
|
167
|
+
opts.reverse_merge! max_concurrency: 10
|
168
|
+
|
169
|
+
Trubl.logger.info("Trubl::Client multi-#{method}-ing #{requests.join(', ')} with headers #{headers}")
|
170
|
+
|
171
|
+
action = RUBY_ENGINE == 'ruby' ? :multi_request_typhoeus : :multi_request_threaded
|
172
|
+
|
173
|
+
self.send(action, method, requests, opts).collect do |response|
|
174
|
+
response.body.force_encoding("utf-8") if response.body and response.body.respond_to?(:force_encoding)
|
175
|
+
response
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def multi_request_threaded(method, requests=[], opts={})
|
180
|
+
responses = []
|
181
|
+
mutex = Mutex.new
|
182
|
+
requests = requests.clone
|
183
|
+
|
184
|
+
opts[:max_concurrency].times.map do
|
185
|
+
Thread.new(requests, responses) do |requests, responses|
|
186
|
+
while request = mutex.synchronize { requests.pop }
|
187
|
+
response = HTTParty.send(method, full_url(request[:path]), {headers: headers}.merge(request[:params] || {} ))
|
188
|
+
mutex.synchronize { responses << response }
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end.each(&:join)
|
192
|
+
|
193
|
+
responses
|
194
|
+
end
|
195
|
+
|
196
|
+
def multi_request_typhoeus(method, requests=[], opts={})
|
197
|
+
# https://github.com/lostisland/faraday/wiki/Parallel-requests
|
198
|
+
# https://github.com/typhoeus/typhoeus/issues/226
|
199
|
+
hydra = Typhoeus::Hydra.new(max_concurrency: opts[:max_concurrency])
|
200
|
+
|
201
|
+
conn = Faraday.new(url: api_uri_root, parallel_manager: hydra) do |builder|
|
202
|
+
builder.request :url_encoded
|
203
|
+
builder.adapter :typhoeus
|
204
|
+
end
|
205
|
+
|
206
|
+
requests = requests.collect do |request|
|
207
|
+
if request.is_a?(String)
|
208
|
+
{path: request, params: {}}
|
209
|
+
else
|
210
|
+
request.reverse_merge params: {}
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
[].tap do |responses|
|
215
|
+
conn.in_parallel do
|
216
|
+
requests.each do |request|
|
217
|
+
path = [request[:path], request[:query].try(:to_query)].compact.join('?')
|
218
|
+
responses << conn.send(method, path, request[:params], headers)
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
def set_logger(level)
|
225
|
+
Trubl.logger(level)
|
226
|
+
end
|
227
|
+
|
228
|
+
private
|
229
|
+
# Fully qualified uri
|
230
|
+
def full_uri(path)
|
231
|
+
URI.parse("#{api_uri_root}#{path}")
|
232
|
+
end
|
233
|
+
|
234
|
+
# Fully qualified url
|
235
|
+
def full_url(path)
|
236
|
+
URI.join(api_uri_root, path).to_s
|
237
|
+
end
|
238
|
+
|
239
|
+
def headers
|
240
|
+
{
|
241
|
+
"Authorization" => "Bearer #{@access_token}",
|
242
|
+
"Connection" => 'keep-alive',
|
243
|
+
"Accept" => 'application/json'
|
244
|
+
}
|
245
|
+
end
|
246
|
+
|
247
|
+
def options(params={})
|
248
|
+
headers.merge(params)
|
249
|
+
end
|
250
|
+
|
251
|
+
end
|
252
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require_relative './pagination'
|
2
|
+
|
3
|
+
module Trubl
|
4
|
+
class Collection < Array
|
5
|
+
|
6
|
+
def from_response(response, options = {})
|
7
|
+
return nil if missing_or_exception?(response)
|
8
|
+
json = JSON.parse(response.body)
|
9
|
+
self.concat (json[container_name] || []).map{|m| klass.new(m[member_name]) }
|
10
|
+
end
|
11
|
+
|
12
|
+
def klass
|
13
|
+
"Trubl::#{member_name.classify}".constantize
|
14
|
+
end
|
15
|
+
|
16
|
+
def container_name
|
17
|
+
klass_name
|
18
|
+
end
|
19
|
+
|
20
|
+
def member_name
|
21
|
+
klass_name.singularize
|
22
|
+
end
|
23
|
+
|
24
|
+
def klass_name
|
25
|
+
self.class.name.downcase.gsub('trubl::', '')
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def missing_or_exception?(response)
|
31
|
+
code = if response.respond_to?(:code)
|
32
|
+
response.code
|
33
|
+
elsif response.respond_to?(:status)
|
34
|
+
response.status
|
35
|
+
else
|
36
|
+
nil
|
37
|
+
end
|
38
|
+
|
39
|
+
code && (400..600).include?(code)
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|