yt 0.5.12 → 0.5.13

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a7a1e9d6fa998dd66d1929e0a2c39fbc0d18af93
4
- data.tar.gz: 1be8fb1e1e1b38d3d2b5f8bfc074511f2dd44b4f
3
+ metadata.gz: bc79b5682be98448f4c2ad0e47e025ece396f2ca
4
+ data.tar.gz: 3b3ba9a7646c1fe350c10bf1cc01ffc9e3c01062
5
5
  SHA512:
6
- metadata.gz: cd51e6439cd718fd612ac6433d003da818155f79b4d824c146161a7398f5c52ffa7c2c47807e66b29d08251c758fc10663ed742086b4793c5471f852e70bf109
7
- data.tar.gz: c0ebf331e22a6b59fa2f56293a01e0a85e1c729636916e0eeccf1856c9cf4715ba91f0c8a98d2325a5f103bed7331adbbc671c970f88d2cccb904b175f73b0eb
6
+ metadata.gz: e8be6da305362d7f2578157ba5cdd0d6c717bbbd8f5a677ac898af79332fc5bb098b0f777006fc7d002da696346403910d724c673e308d897b1c700fa83e295e
7
+ data.tar.gz: 90842585d61ad708d4dba3fefac8b7bc2f51ac8eeae58ca3acdd5d989bf4eae495ba9a6b984b758f4e7bc5f2b20a5d8f14604c6e821ecb935745af67fb5363c8
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- yt (0.5.12)
4
+ yt (0.5.13)
5
5
  activesupport
6
6
 
7
7
  GEM
data/HISTORY.md CHANGED
@@ -19,6 +19,7 @@ v0.5 - 2014/05/16
19
19
  * New ContentOwner subclass of Account with access to partnered channels
20
20
  * Automatically refresh the access token when it expires or becomes invalid
21
21
  * Retry once YouTube earning queries that return error 503
22
+ * Wait 3 seconds and retry *every* request that returns 500, 503 or 400 with "Invalid query"
22
23
 
23
24
  v0.4 - 2014/05/09
24
25
  --------------------
data/README.md CHANGED
@@ -365,7 +365,7 @@ To install on your system, run
365
365
 
366
366
  To use inside a bundled Ruby project, add this line to the Gemfile:
367
367
 
368
- gem 'yt', '~> 0.5.12'
368
+ gem 'yt', '~> 0.5.13'
369
369
 
370
370
  Since the gem follows [Semantic Versioning](http://semver.org),
371
371
  indicating the full version in your Gemfile (~> *major*.*minor*.*patch*)
@@ -4,17 +4,9 @@ module Yt
4
4
  module Collections
5
5
  class Earnings < Base
6
6
 
7
- def within(days_range, try_again = true)
7
+ def within(days_range)
8
8
  @days_range = days_range
9
9
  Hash[*flat_map{|daily_earning| daily_earning}]
10
- # NOTE: Once in a while, YouTube responds with 400 Error and the message
11
- # "Invalid query. Query did not conform to the expectations."; in this
12
- # case running the same query after one second fixes the issue. This is
13
- # not documented by YouTube and hardly testable, but trying again the
14
- # same query is a workaround that works and can hardly cause any damage.
15
- # Similarly, once in while YouTube responds with a random 503 error.
16
- rescue Yt::Error => e
17
- try_again && rescue?(e) ? sleep(3) && within(days_range, false) : raise
18
10
  end
19
11
 
20
12
  private
@@ -41,18 +33,6 @@ module Yt
41
33
  def items_key
42
34
  'rows'
43
35
  end
44
-
45
- def rescue?(error)
46
- bad_request?(error) || backend_error?(error)
47
- end
48
-
49
- def bad_request?(error)
50
- 'badRequest'.in?(error.reasons) && error.message =~ /did not conform/
51
- end
52
-
53
- def backend_error?(error)
54
- 'backendError'.in?(error.reasons)
55
- end
56
36
  end
57
37
  end
58
38
  end
@@ -28,25 +28,13 @@ module Yt
28
28
  def run
29
29
  if response.is_a? @expected_response
30
30
  response.tap{|response| response.body = parse_format response.body}
31
- elsif run_again_with_refreshed_authentication?
32
- run
33
31
  else
34
- raise error_for(response), request_error_message
32
+ run_again? ? run : raise(error_for(response), request_error_message)
35
33
  end
36
34
  end
37
35
 
38
36
  private
39
37
 
40
- # If a request authorized with an access token returns 401, then the
41
- # access token might have expired. If a refresh token is also present,
42
- # try to run the request one more time with a refreshed access token.
43
- def run_again_with_refreshed_authentication?
44
- if response.is_a? Net::HTTPUnauthorized
45
- @response = @http_request = @uri = nil
46
- @auth.refresh
47
- end if @auth.respond_to? :refresh
48
- end
49
-
50
38
  def response
51
39
  @response ||= Net::HTTP.start(*net_http_options) do |http|
52
40
  http.request http_request
@@ -113,6 +101,45 @@ module Yt
113
101
  end if body
114
102
  end
115
103
 
104
+ # There are two cases to run a request again: YouTube responds with a
105
+ # random error that can be fixed by waiting for some seconds and running
106
+ # the exact same query, or the access token needs to be refreshed.
107
+ def run_again?
108
+ run_again_with_refreshed_authentication? || run_again_after_a_while?
109
+ end
110
+
111
+ # Once in a while, YouTube responds with 500, or 503, or 400 Error and
112
+ # the text "Invalid query. Query did not conform to the expectations.".
113
+ # In all these cases, running the same query after some seconds fixes
114
+ # the issue. This it not documented by YouTube and hardly testable, but
115
+ # trying again is a workaround that works and hardly causes any damage.
116
+ def run_again_after_a_while?(max_retries = 1)
117
+ @retries_so_far ||= -1
118
+ @retries_so_far += 1
119
+ if (@retries_so_far < max_retries) && worth_another_try?
120
+ @response = @http_request = @uri = nil
121
+ sleep 3
122
+ end
123
+ end
124
+
125
+ def worth_another_try?
126
+ case response
127
+ when Net::HTTPServerError then true
128
+ when Net::HTTPBadRequest then response.body =~ /did not conform/
129
+ else false
130
+ end
131
+ end
132
+
133
+ # If a request authorized with an access token returns 401, then the
134
+ # access token might have expired. If a refresh token is also present,
135
+ # try to run the request one more time with a refreshed access token.
136
+ def run_again_with_refreshed_authentication?
137
+ if response.is_a? Net::HTTPUnauthorized
138
+ @response = @http_request = @uri = nil
139
+ @auth.refresh
140
+ end if @auth.respond_to? :refresh
141
+ end
142
+
116
143
  def error_for(response)
117
144
  case response
118
145
  when Net::HTTPServerError then Errors::ServerError
data/lib/yt/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Yt
2
- VERSION = '0.5.12'
2
+ VERSION = '0.5.13'
3
3
  end
@@ -7,7 +7,8 @@ describe Yt::Associations::PartneredChannels, :partner do
7
7
  context 'given a content owner with partnered channels' do
8
8
  let(:content_owner) { $content_owner }
9
9
 
10
- it { expect(partnered_channels.count).to be > 0 }
10
+ # NOTE: Uncomment once size does not runs through *all* the pages
11
+ # it { expect(partnered_channels.size).to be > 0 }
11
12
  it { expect(partnered_channels.first).to be_a Yt::Channel }
12
13
  end
13
14
  end
@@ -4,10 +4,8 @@ require 'yt/collections/earnings'
4
4
  describe Yt::Collections::Earnings do
5
5
  subject(:collection) { Yt::Collections::Earnings.new parent: channel }
6
6
  let(:channel) { Yt::Channel.new id: 'UCxO1tY8h1AhOz0T4ENwmpow' }
7
- let(:msg) { {response_body: {error: {errors: [error]}}}.to_json }
8
7
  let(:date) { 1.day.ago.to_date }
9
8
  let(:dollars) { 10 }
10
- let(:message) { 'Invalid query. Query did not conform to the expectations.' }
11
9
  before { expect(collection).to behave }
12
10
 
13
11
  describe '#within' do
@@ -16,43 +14,5 @@ describe Yt::Collections::Earnings do
16
14
 
17
15
  it { expect(collection.within(date..date)[date]).to eq dollars }
18
16
  end
19
-
20
- # NOTE: This test is just a reflection of YouTube irrational behavior
21
- # of raising 400 or 504 error once in a while when retrieving earnings.
22
- # Hopefully this will get fixed and this code (and test) removed.
23
- context 'given YouTube responds to the first request with' do
24
- let(:behave) { receive(:flat_map) do
25
- expect(collection).to receive(:flat_map).and_return [date, dollars]
26
- raise Yt::Error, msg
27
- end}
28
-
29
- context 'an Invalid Query error' do
30
- let(:error) { {reason: 'badRequest', message: message} }
31
-
32
- it { expect(collection.within(date..date)[date]).to eq dollars }
33
- end
34
-
35
- context 'a Backend error' do
36
- let(:error) { {reason: 'backendError', message: 'Backend Error'} }
37
-
38
- it { expect(collection.within(date..date)[date]).to eq dollars }
39
- end
40
- end
41
-
42
- context 'given YouTube responds to the second request with' do
43
- let(:behave) { receive(:flat_map).twice.and_raise Yt::Error, msg }
44
-
45
- context 'an Invalid Query error' do
46
- let(:error) { {reason: 'badRequest', message: message} }
47
-
48
- it { expect{collection.within date..date}.to raise_error Yt::Error }
49
- end
50
-
51
- context 'a Backend error' do
52
- let(:error) { {reason: 'backendError', message: 'Backend Error'} }
53
-
54
- it { expect{collection.within date..date}.to raise_error Yt::Error }
55
- end
56
- end
57
17
  end
58
18
  end
@@ -1,31 +1,74 @@
1
1
  require 'spec_helper'
2
2
  require 'yt/models/request'
3
3
 
4
+
4
5
  describe Yt::Request do
5
- subject(:request) { Yt::Request.new attrs }
6
- let(:attrs) { {host: 'example.com'} }
7
- before { expect(Net::HTTP).to receive(:start).and_return response }
8
- before { allow(response).to receive(:body) }
6
+ subject(:request) { Yt::Request.new host: 'example.com' }
7
+ let(:response) { response_class.new nil, nil, nil }
8
+ let(:response_body) { }
9
+ before { allow(response).to receive(:body).and_return response_body }
10
+ before { expect(Net::HTTP).to receive(:start).once.and_return response }
9
11
 
10
12
  describe '#run' do
11
- context 'given a request that returns a 500 code' do
12
- let(:response) { Net::HTTPServerError.new nil, nil, nil }
13
- it { expect{request.run}.to fail }
14
- end
13
+ context 'given a request that returns' do
14
+ context 'a success code 2XX' do
15
+ let(:response_class) { Net::HTTPOK }
15
16
 
16
- context 'given a request that returns a 401 code' do
17
- let(:response) { Net::HTTPUnauthorized.new nil, nil, nil }
18
- it { expect{request.run}.to fail }
19
- end
17
+ it { expect{request.run}.not_to fail }
18
+ end
20
19
 
21
- context 'given a request that returns a non-2XX code' do
22
- let(:response) { Net::HTTPNotFound.new nil, nil, nil }
23
- it { expect{request.run}.to fail }
24
- end
20
+ context 'an error code 5XX' do
21
+ let(:response_class) { Net::HTTPServerError }
22
+ let(:retry_response) { retry_response_class.new nil, nil, nil }
23
+ before { allow(retry_response).to receive(:body) }
24
+ before { expect(Net::HTTP).to receive(:start).at_least(:once).and_return retry_response }
25
+
26
+ context 'every time' do
27
+ let(:retry_response_class) { Net::HTTPServerError }
28
+
29
+ it { expect{request.run}.to fail }
30
+ end
31
+
32
+ context 'but returns a success code 2XX the second time' do
33
+ let(:retry_response_class) { Net::HTTPOK }
34
+
35
+ it { expect{request.run}.not_to fail }
36
+ end
37
+ end
38
+
39
+ context 'an error code 400 with "Invalid Query" message' do
40
+ let(:response_class) { Net::HTTPBadRequest }
41
+ let(:response_body) { {error: {errors: [message: message]}}.to_json }
42
+ let(:message) { 'Invalid query. Query did not conform to the expectations' }
43
+
44
+ let(:retry_response) { retry_response_class.new nil, nil, nil }
45
+ before { allow(retry_response).to receive(:body) }
46
+ before { expect(Net::HTTP).to receive(:start).at_least(:once).and_return retry_response }
47
+
48
+ context 'every time' do
49
+ let(:retry_response_class) { Net::HTTPBadRequest }
50
+
51
+ it { expect{request.run}.to fail }
52
+ end
53
+
54
+ context 'but returns a success code 2XX the second time' do
55
+ let(:retry_response_class) { Net::HTTPOK }
56
+
57
+ it { expect{request.run}.not_to fail }
58
+ end
59
+ end
60
+
61
+ context 'an error code 401' do
62
+ let(:response_class) { Net::HTTPUnauthorized }
63
+
64
+ it { expect{request.run}.to fail }
65
+ end
66
+
67
+ context 'any other non-2XX error code' do
68
+ let(:response_class) { Net::HTTPNotFound }
25
69
 
26
- context 'given a request that returns a 2XX code' do
27
- let(:response) { Net::HTTPOK.new nil, nil, nil }
28
- it { expect{request.run}.not_to fail }
70
+ it { expect{request.run}.to fail }
71
+ end
29
72
  end
30
73
  end
31
74
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.12
4
+ version: 0.5.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Claudio Baccigalupo