yt 0.5.4 → 0.5.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/HISTORY.md +3 -0
  4. data/README.md +2 -4
  5. data/bin/yt +0 -7
  6. data/lib/yt/actions/delete.rb +3 -3
  7. data/lib/yt/actions/insert.rb +3 -3
  8. data/lib/yt/actions/list.rb +4 -6
  9. data/lib/yt/actions/update.rb +7 -5
  10. data/lib/yt/associations/authentications.rb +114 -0
  11. data/lib/yt/associations.rb +1 -0
  12. data/lib/yt/collections/annotations.rb +2 -2
  13. data/lib/yt/collections/authentications.rb +42 -0
  14. data/lib/yt/collections/base.rb +1 -1
  15. data/lib/yt/collections/playlist_items.rb +1 -1
  16. data/lib/yt/collections/snippets.rb +1 -2
  17. data/lib/yt/collections/subscriptions.rb +1 -1
  18. data/lib/yt/collections/user_infos.rb +2 -2
  19. data/lib/yt/config.rb +0 -2
  20. data/lib/yt/errors/missing_auth.rb +50 -0
  21. data/lib/yt/errors/no_items.rb +5 -9
  22. data/lib/yt/errors/request_error.rb +52 -0
  23. data/lib/yt/models/account.rb +17 -44
  24. data/lib/yt/models/annotation.rb +117 -115
  25. data/lib/yt/models/authentication.rb +27 -0
  26. data/lib/yt/models/base.rb +9 -5
  27. data/lib/yt/models/channel.rb +6 -4
  28. data/lib/yt/models/configuration.rb +27 -35
  29. data/lib/yt/models/description.rb +61 -59
  30. data/lib/yt/models/details_set.rb +26 -24
  31. data/lib/yt/models/id.rb +3 -1
  32. data/lib/yt/models/playlist.rb +38 -36
  33. data/lib/yt/models/playlist_item.rb +29 -27
  34. data/lib/yt/models/rating.rb +18 -16
  35. data/lib/yt/models/request.rb +95 -73
  36. data/lib/yt/models/resource.rb +19 -17
  37. data/lib/yt/models/snippet.rb +39 -37
  38. data/lib/yt/models/status.rb +20 -18
  39. data/lib/yt/models/subscription.rb +24 -22
  40. data/lib/yt/models/url.rb +68 -68
  41. data/lib/yt/models/user_info.rb +51 -49
  42. data/lib/yt/models/video.rb +6 -4
  43. data/lib/yt/version.rb +1 -1
  44. data/spec/associations/device_auth/authentications_spec.rb +78 -0
  45. data/spec/associations/device_auth/channels_spec.rb +2 -4
  46. data/spec/associations/device_auth/details_sets_spec.rb +4 -5
  47. data/spec/associations/device_auth/ids_spec.rb +2 -3
  48. data/spec/associations/device_auth/playlist_items_spec.rb +3 -4
  49. data/spec/associations/device_auth/playlists_spec.rb +14 -15
  50. data/spec/associations/device_auth/ratings_spec.rb +2 -4
  51. data/spec/associations/device_auth/snippets_spec.rb +5 -7
  52. data/spec/associations/device_auth/subscriptions_spec.rb +2 -4
  53. data/spec/associations/device_auth/user_infos_spec.rb +2 -5
  54. data/spec/associations/device_auth/videos_spec.rb +3 -5
  55. data/spec/associations/server_auth/details_sets_spec.rb +1 -1
  56. data/spec/associations/server_auth/ids_spec.rb +1 -1
  57. data/spec/associations/server_auth/playlist_items_spec.rb +1 -1
  58. data/spec/associations/server_auth/playlists_spec.rb +1 -1
  59. data/spec/associations/server_auth/snippets_spec.rb +1 -1
  60. data/spec/associations/server_auth/videos_spec.rb +1 -1
  61. data/spec/collections/playlist_items_spec.rb +3 -4
  62. data/spec/collections/subscriptions_spec.rb +2 -3
  63. data/spec/errors/missing_auth_spec.rb +10 -0
  64. data/spec/errors/no_items_spec.rb +2 -1
  65. data/spec/errors/request_error_spec.rb +18 -0
  66. data/spec/models/configuration_spec.rb +0 -17
  67. data/spec/models/description_spec.rb +5 -5
  68. data/spec/models/request_spec.rb +1 -7
  69. data/spec/models/subscription_spec.rb +2 -3
  70. data/spec/models/url_spec.rb +6 -6
  71. data/spec/support/fail_matcher.rb +1 -1
  72. data/spec/support/global_hooks.rb +33 -0
  73. metadata +15 -14
  74. data/lib/yt/errors/base.rb +0 -43
  75. data/lib/yt/errors/error.rb +0 -8
  76. data/lib/yt/errors/failed.rb +0 -17
  77. data/lib/yt/errors/unauthenticated.rb +0 -34
  78. data/spec/errors/failed_spec.rb +0 -9
  79. data/spec/errors/unauthenticated_spec.rb +0 -9
  80. data/spec/support/device_app.rb +0 -15
  81. data/spec/support/server_app.rb +0 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b618d7f55a83eeba246fba2937b6fdfb13b41a49
4
- data.tar.gz: 9a40df4f2d2dd15abaa142c4ce39233f2921f89e
3
+ metadata.gz: 2c609731e55cd81b71dfbc3f53f47147dac54b51
4
+ data.tar.gz: 0993daf654fda172520af7d6d01e8620ff66fbf6
5
5
  SHA512:
6
- metadata.gz: f5fa21279a6b52775b60dacdc2b4e4ae39edecee2d04cd60b3b8461f9412b32b987f223e89bf58b72475675edb319455d77d7009340315ef60d839e86b4124f5
7
- data.tar.gz: fa8634ac7fa6c44e0269be8a180b527102742789f1c4e6d50e3d6c137e38329dbbbe7482fd0bb0038a3de0f22e07a1d1e3ded545c5234de8249ca3761d867e28
6
+ metadata.gz: 60d5bf311041934ec83bd36cfff50905d04e3ef8444f90e8be39ad3249da24618ef308bdee47688785a5bdc8d8024d91dc2a253a44d3cf204d331e6209eb66cb
7
+ data.tar.gz: fea7bc7fb323a51c21be93a0108e8d3350f808c8a4bb56cc956d2db5d4cf8c720fe6bdb8e492f39f65337d76d8843edf9898cd2464f9d354adc9ca089d0a834b
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- yt (0.5.4)
4
+ yt (0.5.5)
5
5
  activesupport
6
6
 
7
7
  GEM
data/HISTORY.md CHANGED
@@ -7,6 +7,9 @@ v0.5 - 2014/05/16
7
7
  * Add `has_one :id` to resources, to retrieve the ID of resources initialized by URL
8
8
  * Raise an error if some `has_one` associations are not found (id, snippet, details set, user info)
9
9
  * Don't check for the right :scope if Account is initialized with credentials
10
+ * Move models in Yt::Models but still auto-include them in the main namespace
11
+ * New Authentication model to separate `access_token` and `refresh_token` from Account
12
+ * New types of Errors that render more verbose errors and the failing request in cURL syntax
10
13
 
11
14
  v0.4 - 2014/05/09
12
15
  --------------------
data/README.md CHANGED
@@ -8,6 +8,7 @@ Yt helps you write apps that need to interact with the YouTube API V3.
8
8
  [![Build Status](https://travis-ci.org/Fullscreen/yt.png?branch=master)](https://travis-ci.org/Fullscreen/yt)
9
9
  [![Coverage Status](https://coveralls.io/repos/Fullscreen/yt/badge.png?)](https://coveralls.io/r/Fullscreen/yt)
10
10
  [![Code Climate](https://codeclimate.com/github/Fullscreen/yt.png)](https://codeclimate.com/github/Fullscreen/yt)
11
+ [![Inline docs](http://inch-pages.github.io/github/Fullscreen/yt.png)](http://inch-pages.github.io/github/Fullscreen/yt)
11
12
 
12
13
  After [registering your app](#registering-your-app), you can run commands like:
13
14
 
@@ -169,7 +170,6 @@ Next, add the following snippet of code to the initializer of your app:
169
170
 
170
171
  ```ruby
171
172
  Yt.configure do |config|
172
- config.scenario = :server_app
173
173
  config.api_key = '123456789012345678901234567890'
174
174
  end
175
175
  ```
@@ -246,7 +246,6 @@ refresh token, then add the following snippet of code to the initializer of your
246
246
 
247
247
  ```ruby
248
248
  Yt.configure do |config|
249
- config.scenario = :device_app
250
249
  config.client_id = '1234567890.apps.googleusercontent.com'
251
250
  config.client_secret = '1234567890'
252
251
  end
@@ -282,7 +281,6 @@ is equivalent to configuration your app with the initializer:
282
281
 
283
282
  ```ruby
284
283
  Yt.configure do |config|
285
- config.scenario = :device_app
286
284
  config.client_id = '1234567890.apps.googleusercontent.com'
287
285
  config.client_secret = '1234567890'
288
286
  config.api_key = '123456789012345678901234567890'
@@ -302,7 +300,7 @@ To install on your system, run
302
300
 
303
301
  To use inside a bundled Ruby project, add this line to the Gemfile:
304
302
 
305
- gem 'yt', '~> 0.5.4'
303
+ gem 'yt', '~> 0.5.5'
306
304
 
307
305
  Since the gem follows [Semantic Versioning](http://semver.org),
308
306
  indicating the full version in your Gemfile (~> *major*.*minor*.*patch*)
data/bin/yt CHANGED
@@ -9,13 +9,6 @@ end
9
9
 
10
10
  ############################
11
11
 
12
- Yt.configure do |config|
13
- config.scenario = :server_app
14
- config.api_key = ENV['YT_TEST_APP_SERVER_API_KEY']
15
- end
16
-
17
- ############################
18
-
19
12
  channel = Yt::Channel.new id: ARGV[0] || 'UCxO1tY8h1AhOz0T4ENwmpow'
20
13
 
21
14
  puts "Title: #{channel.title}"
@@ -7,16 +7,16 @@ module Yt
7
7
  private
8
8
 
9
9
  def do_delete(extra_delete_params = {})
10
- request = Request.new delete_params.merge(extra_delete_params)
10
+ request = Yt::Request.new delete_params.merge(extra_delete_params)
11
11
  response = request.run
12
- raise unless response.is_a? Net::HTTPNoContent
13
12
  yield response.body
14
13
  end
15
14
 
16
15
  def delete_params
17
- Request.default_params.tap do |params|
16
+ {}.tap do |params|
18
17
  params[:method] = :delete
19
18
  params[:auth] = @auth
19
+ params[:expected_response] = Net::HTTPNoContent
20
20
  end
21
21
  end
22
22
  end
@@ -7,17 +7,17 @@ module Yt
7
7
  private
8
8
 
9
9
  def do_insert(extra_insert_params = {})
10
- request = Request.new insert_params.merge(extra_insert_params)
10
+ request = Yt::Request.new insert_params.merge(extra_insert_params)
11
11
  response = request.run
12
- raise unless response.is_a? Net::HTTPOK
13
12
  @items = []
14
13
  new_item response.body
15
14
  end
16
15
 
17
16
  def insert_params
18
- Request.default_params.tap do |params|
17
+ {}.tap do |params|
19
18
  params[:method] = :post
20
19
  params[:auth] = @auth
20
+ params[:expected_response] = Net::HTTPOK
21
21
  end
22
22
  end
23
23
  end
@@ -8,9 +8,7 @@ module Yt
8
8
  alias size count
9
9
 
10
10
  def first!
11
- first.tap do |item|
12
- raise Errors::NoItems unless item
13
- end
11
+ first.tap{|item| raise Errors::NoItems unless item}
14
12
  end
15
13
 
16
14
  private
@@ -50,9 +48,8 @@ module Yt
50
48
  end
51
49
 
52
50
  def fetch_page(params = {})
53
- request = Request.new params
51
+ request = Yt::Request.new params
54
52
  response = request.run
55
- raise unless response.is_a? Net::HTTPOK
56
53
  token = response.body['nextPageToken']
57
54
  items = response.body.fetch 'items', []
58
55
  {items: items, token: token}
@@ -61,10 +58,11 @@ module Yt
61
58
  def list_params
62
59
  path = "/youtube/v3/#{self.class.to_s.demodulize.camelize :lower}"
63
60
 
64
- Request.default_params.tap do |params|
61
+ {}.tap do |params|
65
62
  params[:method] = :get
66
63
  params[:auth] = @auth
67
64
  params[:path] = path
65
+ params[:exptected_response] = Net::HTTPOK
68
66
  end
69
67
  end
70
68
  end
@@ -3,18 +3,20 @@ require 'yt/models/request'
3
3
  module Yt
4
4
  module Actions
5
5
  module Update
6
- def do_update(extra_update_params = {}, options = {})
7
- request = Request.new update_params.deep_merge(extra_update_params)
6
+
7
+ private
8
+
9
+ def do_update(extra_update_params = {})
10
+ request = Yt::Request.new update_params.deep_merge(extra_update_params)
8
11
  response = request.run
9
- expected_response = options.fetch :expect, Net::HTTPNoContent
10
- raise unless response.is_a? expected_response
11
12
  yield response.body
12
13
  end
13
14
 
14
15
  def update_params
15
- Request.default_params.tap do |params|
16
+ {}.tap do |params|
16
17
  params[:method] = :put
17
18
  params[:auth] = @auth
19
+ params[:expected_response] = Net::HTTPNoContent
18
20
  end
19
21
  end
20
22
  end
@@ -0,0 +1,114 @@
1
+ require 'yt/collections/authentications'
2
+ require 'yt/config'
3
+ require 'yt/errors/no_items'
4
+ require 'yt/errors/missing_auth'
5
+
6
+ module Yt
7
+ module Associations
8
+ # Provides the `has_one :access_token` method to YouTube resources, which
9
+ # allows to access to content detail set-specific methods like `access_token`.
10
+ # YouTube resources with access tokens are: accounts.
11
+ module Authentications
12
+ def authentication
13
+ @authentication = current_authentication
14
+ @authentication ||= new_authentication || refreshed_authentication!
15
+ end
16
+
17
+ def authentication_url
18
+ host = 'accounts.google.com'
19
+ path = '/o/oauth2/auth'
20
+ query = authentication_url_params.to_param
21
+ URI::HTTPS.build(host: host, path: path, query: query).to_s
22
+ end
23
+
24
+ private
25
+
26
+ def current_authentication
27
+ @authentication ||= Yt::Authentication.new current_data if @access_token
28
+ @authentication unless @authentication.nil? || @authentication.expired?
29
+ end
30
+
31
+ def current_data
32
+ {}.tap do |data|
33
+ data['access_token'] = @access_token
34
+ data['expires_at'] = @expires_at
35
+ data['refresh_token'] = @refresh_token
36
+ end
37
+ end
38
+
39
+ # Tries to obtain an access token using the authorization code (which
40
+ # can only be used once). On failure, does not raise an error because
41
+ # the access token might still be retrieved with a refresh token.
42
+ def new_authentication
43
+ new_authentications.first!
44
+ rescue Errors::NoItems => error
45
+ nil
46
+ end
47
+
48
+ # Tries to obtain an access token using the refresh token (which can
49
+ # be used multiple times). On failure, raise an error because there are
50
+ # no more options to obtain an access token.
51
+ def refreshed_authentication!
52
+ refreshed_authentications.first!
53
+ rescue Errors::NoItems => error
54
+ raise Errors::MissingAuth, error.to_param
55
+ end
56
+
57
+ def new_authentications
58
+ @new_authentications ||= Collections::Authentications.of(self).tap do |auth|
59
+ auth.auth_params = new_authentication_params
60
+ end
61
+ end
62
+
63
+ def refreshed_authentications
64
+ @refreshed_authentications ||= Collections::Authentications.of(self).tap do |auth|
65
+ auth.auth_params = refreshed_authentication_params
66
+ end
67
+ end
68
+
69
+ def authentication_url_params
70
+ {}.tap do |params|
71
+ params[:client_id] = client_id
72
+ params[:scope] = authentication_scope
73
+ params[:redirect_uri] = @redirect_uri
74
+ params[:response_type] = :code
75
+ params[:access_type] = :offline
76
+ # params[:include_granted_scopes] = true
77
+ end
78
+ end
79
+
80
+ def authentication_scope
81
+ @scopes.map do |scope|
82
+ "https://www.googleapis.com/auth/#{scope}"
83
+ end.join(' ') if @scopes.is_a?(Array)
84
+ end
85
+
86
+ def new_authentication_params
87
+ {}.tap do |params|
88
+ params[:client_id] = client_id
89
+ params[:client_secret] = client_secret
90
+ params[:code] = @authorization_code
91
+ params[:redirect_uri] = @redirect_uri
92
+ params[:grant_type] = :authorization_code
93
+ end
94
+ end
95
+
96
+ def refreshed_authentication_params
97
+ {}.tap do |params|
98
+ params[:client_id] = client_id
99
+ params[:client_secret] = client_secret
100
+ params[:refresh_token] = @refresh_token
101
+ params[:grant_type] = :refresh_token
102
+ end
103
+ end
104
+
105
+ def client_id
106
+ Yt.configuration.client_id
107
+ end
108
+
109
+ def client_secret
110
+ Yt.configuration.client_secret
111
+ end
112
+ end
113
+ end
114
+ end
@@ -11,6 +11,7 @@ module Yt
11
11
  extend ActiveSupport::Autoload
12
12
 
13
13
  autoload :Annotations
14
+ autoload :Authentications
14
15
  autoload :Channels
15
16
  autoload :DetailsSets
16
17
  autoload :Ids
@@ -17,13 +17,13 @@ module Yt
17
17
  params[:host] = 'www.youtube.com'
18
18
  params[:path] = '/annotations_invideo'
19
19
  params[:params] = {video_id: @parent.id}
20
+ params[:expected_response] = Net::HTTPOK
20
21
  end
21
22
  end
22
23
 
23
24
  def next_page
24
- request = Request.new list_params
25
+ request = Yt::Request.new list_params
25
26
  response = request.run
26
- raise unless response.is_a? Net::HTTPOK
27
27
  @page_token = nil
28
28
 
29
29
  document = response.body.fetch('document', {})['annotations'] || {}
@@ -0,0 +1,42 @@
1
+ require 'yt/collections/base'
2
+ require 'yt/models/authentication'
3
+
4
+ module Yt
5
+ module Collections
6
+ class Authentications < Base
7
+ attr_accessor :auth_params
8
+
9
+ private
10
+
11
+ def new_item(data)
12
+ Yt::Authentication.new data
13
+ end
14
+
15
+ def list_params
16
+ super.tap do |params|
17
+ params[:host] = 'accounts.google.com'
18
+ params[:path] = '/o/oauth2/token'
19
+ params[:body_type] = :form
20
+ params[:method] = :post
21
+ params[:auth] = nil
22
+ params[:body] = auth_params
23
+ end
24
+ end
25
+
26
+ def more_pages?
27
+ auth_params.values.all?
28
+ end
29
+
30
+ def next_page
31
+ request = Yt::Request.new list_params
32
+ Array.wrap request.run.body
33
+ rescue Yt::Error => error
34
+ expected?(error) ? [] : raise(error)
35
+ end
36
+
37
+ def expected?(error)
38
+ error.kind == 'invalid_grant'
39
+ end
40
+ end
41
+ end
42
+ end
@@ -1,7 +1,7 @@
1
1
  require 'yt/actions/delete_all'
2
2
  require 'yt/actions/insert'
3
3
  require 'yt/actions/list'
4
- require 'yt/errors/error'
4
+ require 'yt/errors/request_error'
5
5
 
6
6
  module Yt
7
7
  module Collections
@@ -11,7 +11,7 @@ module Yt
11
11
  resource["#{attrs[:kind]}Id"] = attrs[:id]
12
12
  snippet = {playlistId: @parent.id, resourceId: resource}
13
13
  do_insert body: {snippet: snippet}, params: {part: 'snippet,status'}
14
- rescue Errors::Base => error
14
+ rescue Yt::Error => error
15
15
  ignorable_errors = error.reasons & ['videoNotFound', 'forbidden']
16
16
  raise error unless options[:ignore_errors] && ignorable_errors.any?
17
17
  end
@@ -12,10 +12,9 @@ module Yt
12
12
  end
13
13
 
14
14
  def list_params
15
- parents_path = @parent.class.to_s.demodulize.underscore.pluralize
16
15
  super.tap do |params|
17
16
  params[:params] = {id: @parent.id, part: 'snippet'}
18
- params[:path] = "/youtube/v3/#{parents_path}"
17
+ params[:path] = "/youtube/v3/#{@parent.kind.pluralize}"
19
18
  end
20
19
  end
21
20
  end
@@ -8,7 +8,7 @@ module Yt
8
8
  def insert(options = {})
9
9
  throttle
10
10
  do_insert
11
- rescue Errors::Base => error
11
+ rescue Yt::Error => error
12
12
  ignorable_errors = error.reasons & ['subscriptionDuplicate']
13
13
  raise error unless options[:ignore_errors] && ignorable_errors.any?
14
14
  end
@@ -14,13 +14,13 @@ module Yt
14
14
  def list_params
15
15
  super.tap do |params|
16
16
  params[:path] = '/oauth2/v2/userinfo'
17
+ params[:expected_response] = Net::HTTPOK
17
18
  end
18
19
  end
19
20
 
20
21
  def next_page
21
- request = Request.new list_params
22
+ request = Yt::Request.new list_params
22
23
  response = request.run
23
- raise unless response.is_a? Net::HTTPOK
24
24
  @page_token = nil
25
25
 
26
26
  Array.wrap response.body
data/lib/yt/config.rb CHANGED
@@ -13,7 +13,6 @@ module Yt
13
13
  # @example A server-to-server YouTube client app
14
14
  #
15
15
  # Yt.configure do |config|
16
- # config.scenario = :server_app
17
16
  # config.api_key = 'ABCDEFGHIJ1234567890'
18
17
  # end
19
18
  #
@@ -30,7 +29,6 @@ module Yt
30
29
  #
31
30
  # @example
32
31
  # Yt.configure do |config|
33
- # config.scenario = :server_app
34
32
  # config.api_key = 'ABCDEFGHIJ1234567890'
35
33
  # end
36
34
  # @see Yt::Configuration
@@ -0,0 +1,50 @@
1
+ require 'yt/errors/request_error'
2
+ require 'yt/config'
3
+
4
+ module Yt
5
+ module Errors
6
+ class MissingAuth < RequestError
7
+ private
8
+
9
+ def explanation
10
+ 'A request to YouTube API was sent without a valid authentication'
11
+ end
12
+
13
+ def more_details
14
+ if [Yt.configuration.client_id, Yt.configuration.api_key].none?
15
+ <<-MSG.gsub(/^ {10}/, '')
16
+ In order to perform this request, you need to register your app with
17
+ Google Developers Console (https://console.developers.google.com).
18
+
19
+ Make sure your app has access to the Google+ and YouTube APIs.
20
+
21
+ If your app requires read-only access to public YouTube data, then
22
+ generate a server API key and set its value with the initializer:
23
+
24
+ Yt.configure do |config|
25
+ config.api_key = '123456789012345678901234567890'
26
+ end
27
+
28
+ or through an environment variable:
29
+
30
+ export YT_API_KEY="123456789012345678901234567890"
31
+
32
+ If your app needs to perform actions on behalf of YouTube accounts,
33
+ then generate a client ID and SECRET and set their values with the
34
+ initializer:
35
+
36
+ Yt.configure do |config|
37
+ config.client_id = '1234567890.apps.googleusercontent.com'
38
+ config.client_secret = '1234567890'
39
+ end
40
+
41
+ or through environment variables:
42
+
43
+ export YT_CLIENT_ID="1234567890.apps.googleusercontent.com"
44
+ export YT_CLIENT_SECRET="1234567890"
45
+ MSG
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -1,16 +1,12 @@
1
- require 'yt/errors/base'
1
+ require 'yt/errors/request_error'
2
2
 
3
3
  module Yt
4
4
  module Errors
5
- class NoItems < Base
6
- def message
7
- <<-MSG.gsub(/^ {6}/, '')
8
- A request to YouTube API V3 returned no items (but some were expected):
9
- #{response_body}
5
+ class NoItems < RequestError
6
+ private
10
7
 
11
- You can retry the same request manually by running:
12
- #{request_curl}
13
- MSG
8
+ def explanation
9
+ 'A request to YouTube API returned no items but some were expected'
14
10
  end
15
11
  end
16
12
  end
@@ -0,0 +1,52 @@
1
+ module Yt
2
+ module Errors
3
+ class RequestError < StandardError
4
+ def initialize(msg = nil)
5
+ @msg = msg
6
+ super msg
7
+ end
8
+
9
+ def message
10
+ <<-MSG.gsub(/^ {8}/, '')
11
+ #{explanation}:
12
+ #{response_body}
13
+
14
+ You can retry the same request manually by running:
15
+ #{request_curl}
16
+ #{more_details}
17
+ MSG
18
+ end
19
+
20
+ def kind
21
+ response_body.fetch 'error', {}
22
+ end
23
+
24
+ def reasons
25
+ kind.fetch('errors', []).map{|e| e['reason']}
26
+ end
27
+
28
+ private
29
+
30
+ def explanation
31
+ 'A request to YouTube API failed'
32
+ end
33
+
34
+ def more_details
35
+ end
36
+
37
+ def response_body
38
+ json['response_body']
39
+ end
40
+
41
+ def request_curl
42
+ json['request_curl']
43
+ end
44
+
45
+ def json
46
+ @json ||= JSON(@msg) rescue {}
47
+ end
48
+ end
49
+ end
50
+
51
+ Error = Errors::RequestError
52
+ end
@@ -1,52 +1,25 @@
1
1
  require 'yt/models/base'
2
- require 'yt/config'
3
2
 
4
3
  module Yt
5
- # Provides methods to access a YouTube account.
6
- class Account < Base
7
-
8
- has_one :channel, delegate: [:videos, :playlists, :create_playlist, :delete_playlists, :update_playlists]
9
- has_one :user_info, delegate: [:id, :email, :has_verified_email?, :gender,
10
- :name, :given_name, :family_name, :profile_url, :avatar_url, :locale, :hd]
11
-
12
- def initialize(options = {})
13
- @access_token = options[:access_token]
14
- @refresh_token = options[:refresh_token]
15
- @redirect_url = options[:redirect_url]
16
- end
17
-
18
- def access_token
19
- @access_token ||= refresh_access_token || get_access_token
20
- end
21
-
22
- def auth
23
- self
24
- end
25
-
26
- private
27
-
28
- # Obtain a new access token using the refresh token
29
- def refresh_access_token
30
- if @refresh_token
31
- body = {grant_type: 'refresh_token', refresh_token: @refresh_token}
32
- request = Request.new auth_params.deep_merge(body: body)
33
- response = request.run
34
- response.body['access_token']
4
+ module Models
5
+ # Provides methods to access a YouTube account.
6
+ class Account < Base
7
+ has_one :authentication, delegate: [:access_token, :refresh_token, :expires_at]
8
+ has_one :channel, delegate: [:videos, :playlists, :create_playlist, :delete_playlists, :update_playlists]
9
+ has_one :user_info, delegate: [:id, :email, :has_verified_email?, :gender, :name, :given_name, :family_name, :profile_url, :avatar_url, :locale, :hd]
10
+
11
+ def initialize(options = {})
12
+ @access_token = options[:access_token]
13
+ @refresh_token = options[:refresh_token]
14
+ @expires_at = options[:expires_at]
15
+ @authorization_code = options[:authorization_code]
16
+ @redirect_uri = options[:redirect_uri]
17
+ @scopes = options[:scopes]
35
18
  end
36
- end
37
19
 
38
- def auth_params
39
- {
40
- host: 'accounts.google.com',
41
- path: '/o/oauth2/token',
42
- format: :json,
43
- body_type: :form,
44
- method: :post,
45
- body: {
46
- client_id: Yt.configuration.client_id,
47
- client_secret: Yt.configuration.client_secret
48
- }
49
- }
20
+ def auth
21
+ self
22
+ end
50
23
  end
51
24
  end
52
25
  end