yt 0.5.4 → 0.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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