spotify-ruby 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/COVERAGE.md +142 -0
  4. data/README.md +60 -5
  5. data/lib/spotify.rb +7 -0
  6. data/lib/spotify/auth.rb +10 -18
  7. data/lib/spotify/sdk.rb +109 -0
  8. data/lib/spotify/sdk/base.rb +58 -0
  9. data/lib/spotify/sdk/connect.rb +26 -0
  10. data/lib/spotify/sdk/connect/device.rb +55 -0
  11. data/lib/spotify/sdk/initialization.rb +76 -0
  12. data/lib/spotify/sdk/initialization/base.rb +74 -0
  13. data/lib/spotify/sdk/initialization/oauth_access_token.rb +34 -0
  14. data/lib/spotify/sdk/initialization/plain_string.rb +26 -0
  15. data/lib/spotify/sdk/initialization/query_hash.rb +45 -0
  16. data/lib/spotify/sdk/initialization/query_string.rb +51 -0
  17. data/lib/spotify/sdk/initialization/url_string.rb +49 -0
  18. data/lib/spotify/sdk/model.rb +53 -0
  19. data/lib/spotify/version.rb +1 -1
  20. data/spotify-ruby.gemspec +5 -3
  21. metadata +45 -55
  22. data/SPEC.md +0 -18
  23. data/html/README_md.html +0 -189
  24. data/html/Spotify.html +0 -115
  25. data/html/Spotify/Auth.html +0 -281
  26. data/html/Spotify/Errors.html +0 -103
  27. data/html/Spotify/Errors/AuthClientCredentialsError.html +0 -106
  28. data/html/created.rid +0 -5
  29. data/html/css/fonts.css +0 -167
  30. data/html/css/rdoc.css +0 -590
  31. data/html/fonts/Lato-Light.ttf +0 -0
  32. data/html/fonts/Lato-LightItalic.ttf +0 -0
  33. data/html/fonts/Lato-Regular.ttf +0 -0
  34. data/html/fonts/Lato-RegularItalic.ttf +0 -0
  35. data/html/fonts/SourceCodePro-Bold.ttf +0 -0
  36. data/html/fonts/SourceCodePro-Regular.ttf +0 -0
  37. data/html/images/add.png +0 -0
  38. data/html/images/arrow_up.png +0 -0
  39. data/html/images/brick.png +0 -0
  40. data/html/images/brick_link.png +0 -0
  41. data/html/images/bug.png +0 -0
  42. data/html/images/bullet_black.png +0 -0
  43. data/html/images/bullet_toggle_minus.png +0 -0
  44. data/html/images/bullet_toggle_plus.png +0 -0
  45. data/html/images/date.png +0 -0
  46. data/html/images/delete.png +0 -0
  47. data/html/images/find.png +0 -0
  48. data/html/images/loadingAnimation.gif +0 -0
  49. data/html/images/macFFBgHack.png +0 -0
  50. data/html/images/package.png +0 -0
  51. data/html/images/page_green.png +0 -0
  52. data/html/images/page_white_text.png +0 -0
  53. data/html/images/page_white_width.png +0 -0
  54. data/html/images/plugin.png +0 -0
  55. data/html/images/ruby.png +0 -0
  56. data/html/images/tag_blue.png +0 -0
  57. data/html/images/tag_green.png +0 -0
  58. data/html/images/transparent.png +0 -0
  59. data/html/images/wrench.png +0 -0
  60. data/html/images/wrench_orange.png +0 -0
  61. data/html/images/zoom.png +0 -0
  62. data/html/index.html +0 -189
  63. data/html/js/darkfish.js +0 -161
  64. data/html/js/jquery.js +0 -4
  65. data/html/js/navigation.js +0 -142
  66. data/html/js/navigation.js.gz +0 -0
  67. data/html/js/search.js +0 -109
  68. data/html/js/search_index.js +0 -1
  69. data/html/js/search_index.js.gz +0 -0
  70. data/html/js/searcher.js +0 -229
  71. data/html/js/searcher.js.gz +0 -0
  72. data/html/table_of_contents.html +0 -83
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spotify
4
+ class SDK
5
+ ##
6
+ # For each SDK component, we have a Base class. We're using HTTParty.
7
+ #
8
+ class Base
9
+ include HTTParty
10
+ base_uri "api.spotify.com:443"
11
+
12
+ ##
13
+ # Initiate a Spotify SDK Base component.
14
+ #
15
+ # @example
16
+ # @sdk = Spotify::SDK.new("access_token")
17
+ # @auth = Spotify::SDK::Base.new(@sdk)
18
+ #
19
+ # @sdk = Spotify::SDK.new("access_token_here")
20
+ # @sdk.to_hash # => { access_token: ..., expires_at: ... }
21
+ #
22
+ # @param [Spotify::SDK] sdk An instance of Spotify::SDK as a reference point.
23
+ #
24
+ def initialize(sdk)
25
+ @sdk = sdk
26
+ @options = {
27
+ headers: {
28
+ Authorization: "Bearer %s" % sdk.access_token
29
+ }
30
+ }
31
+ end
32
+
33
+ ##
34
+ # Handle HTTParty responses.
35
+ #
36
+ # @example
37
+ # # Return the Hash from the JSON response.
38
+ # send_http_request(:get, "/v1/me/player/devices", @options)
39
+ #
40
+ # # Return the raw HTTParty::Response object.
41
+ # send_http_request(:get, "/v1/me/player/devices", @options.merge(raw: true))
42
+ #
43
+ # @param [Hash,HTTParty::Response] response The response from the HTTP request.
44
+ # @return
45
+ #
46
+ def send_http_request(method, endpoint, opts={}, &_block)
47
+ sdk_opts = opts[:_sdk_opts].presence || {}
48
+ opts_sdk = {raw: false, expect_nil: false}.merge(sdk_opts)
49
+ response = self.class.send(method, endpoint, @options.merge(opts))
50
+ response = response.parsed_response.try(:deep_symbolize_keys) if opts_sdk[:raw] == false
51
+ response = true if opts_sdk[:expect_nil] == true && response.nil?
52
+ response
53
+ end
54
+
55
+ attr_reader :sdk
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spotify
4
+ class SDK
5
+ class Connect < Base
6
+ ##
7
+ # Collect all the user's available devices.
8
+ # GET /v1/me/player/devices
9
+ #
10
+ # @example
11
+ # @sdk.connect.devices # => [#<Spotify::SDK::Connect::Device:...>, ...]
12
+ #
13
+ # @see https://developer.spotify.com/web-api/console/get-users-available-devices/
14
+ #
15
+ # @param [Hash] override_opts Custom options for HTTParty.
16
+ # @return
17
+ #
18
+ def devices(override_opts={})
19
+ response = send_http_request(:get, "/v1/me/player/devices", override_opts)
20
+ response[:devices].map do |device|
21
+ Spotify::SDK::Connect::Device.new(device, self)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spotify
4
+ class SDK
5
+ class Connect
6
+ class Device < Model
7
+ ##
8
+ # Transfer a user's playback to another device, and continue playing.
9
+ # PUT /v1/me/player
10
+ #
11
+ # @example
12
+ # device = @sdk.connect.transfer_playback!
13
+ #
14
+ # @see https://developer.spotify.com/web-api/transfer-a-users-playback/
15
+ #
16
+ # @return [Spotify::SDK::Connect::Device] self Return itself, so chained methods can be supported.
17
+ #
18
+ def transfer_playback!
19
+ transfer_playback_method(playing: true)
20
+ self
21
+ end
22
+
23
+ ##
24
+ # Transfer a user's playback to another device, and pause.
25
+ # PUT /v1/me/player
26
+ #
27
+ # @example
28
+ # device = @sdk.connect.transfer_state!
29
+ #
30
+ # @see https://developer.spotify.com/web-api/transfer-a-users-playback/
31
+ #
32
+ # @return [Spotify::SDK::Connect::Device] self Return itself, so chained methods can be supported.
33
+ #
34
+ def transfer_state!
35
+ transfer_playback_method(playing: false)
36
+ self
37
+ end
38
+
39
+ private
40
+
41
+ def transfer_playback_method(playing:)
42
+ override_opts = {
43
+ _sdk_opts: {expect_nil: true},
44
+ body: {
45
+ device_ids: [id],
46
+ play: playing
47
+ }.to_json
48
+ }
49
+
50
+ parent.send_http_request(:put, "/v1/me/player", override_opts)
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spotify
4
+ class SDK
5
+ ##
6
+ # Spotify::SDK::Initialization deals with parsing input for the
7
+ # following code block `Spotify::SDK.new(input)` and extracting
8
+ # the access token, expiry and refresh token.
9
+ #
10
+ # You'll never have to interact with this, unless you have other
11
+ # objects you want us to parse the aforementioned variables from.
12
+ #
13
+ class Initialization
14
+ ##
15
+ # This is where you mount new Initialization objects.
16
+ # Don't worry, we prefix Spotify::SDK::Initialization for you.
17
+ #
18
+ # @see /lib/spotify/sdk/initialization/url_string.rb
19
+ #
20
+ CLASSES = %i[
21
+ OAuthAccessToken
22
+ QueryString
23
+ URLString
24
+ PlainString
25
+ QueryHash
26
+ ].freeze
27
+
28
+ class << self
29
+ ##
30
+ # Initiate a new Spotify SDK Initialization object
31
+ #
32
+ # @example
33
+ # begin
34
+ # hash = Spotify::SDK::Initialization.detect("access_token")
35
+ # puts "Access Token: #{hash[:access_token]}"
36
+ # puts "Expires in: #{hash[:expires_in]}"
37
+ # puts "Refresh Token: #{hash[:refresh_token]}"
38
+ # rescue Spotify::Errors::InitializationObjectInvalidError => e
39
+ # puts "Can't recognise the input because: #{e.inspect}"
40
+ # end
41
+ #
42
+ # @param [String,Hash,OAuth2::AccessToken] subject An instance of Spotify::SDK as a reference point.
43
+ #
44
+ def detect(subject)
45
+ klasses = CLASSES.map do |klass_name|
46
+ ("Spotify::SDK::Initialization::%s" % klass_name).constantize.new(subject)
47
+ end
48
+
49
+ matches = klasses.select(&:should_perform?)
50
+
51
+ case matches.size
52
+ when 1
53
+ matches.first.perform
54
+ when 0
55
+ raise Spotify::Errors::InitializationObjectInvalidError.new
56
+ else
57
+ raise Spotify::Errors::InitializationObjectDuplicationError.new
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ class Errors
65
+ ##
66
+ # A Error class for when the initialization subject is not valid (see `initialize(subject)` for more info).
67
+ #
68
+ class InitializationObjectInvalidError < StandardError; end
69
+
70
+ ##
71
+ # A Error class for when the initialization subject matches against multiple selectors.
72
+ # When this Error occurs, this becomes an internal bug. It should be filed on the GitHub issue tracker.
73
+ #
74
+ class InitializationObjectDuplicationError < StandardError; end
75
+ end
76
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spotify
4
+ class SDK
5
+ class Initialization
6
+ ##
7
+ # For each SDK Initialization type, we have a base class to inherit from.
8
+ #
9
+ class Base
10
+ ##
11
+ # Initiate a Spotify SDK Initialization Base class.
12
+ # Note: You would not ever initiate this class, but rather inherit from it.
13
+ # See /lib/spotify/sdk/initialization/query_string.rb as an example.
14
+ #
15
+ # @example
16
+ # Spotify::SDK::Initialization::Base.new("access_token")
17
+ #
18
+ # @param [Object] subject Any object that can be used to identify an access token.
19
+ #
20
+ def initialize(subject)
21
+ @subject = subject
22
+ end
23
+
24
+ ##
25
+ # Determine whether this initialization type is valid, and should be performed.
26
+ #
27
+ # @example
28
+ # instance = Spotify::SDK::Initialization::Base.new("access_token")
29
+ # instance.should_perform?
30
+ #
31
+ # @return [Boolean] A true or false answer as to whether to perform this initialization type.
32
+ #
33
+ def should_perform?
34
+ false
35
+ end
36
+
37
+ ##
38
+ # Perform the class to extract the authentication details needed for the SDK class to run.
39
+ #
40
+ # @example
41
+ # instance = Spotify::SDK::Initialization::Base.new("access_token")
42
+ # instance.perform if instance.should_perform?
43
+ #
44
+ # @return [Hash] A hash containing only access_token, expires_in and refresh_token.
45
+ #
46
+ def perform
47
+ {
48
+ access_token: nil,
49
+ expires_in: nil,
50
+ refresh_token: nil
51
+ }
52
+ end
53
+
54
+ ##
55
+ # The subject of the class. Usually what has been sent to Spotify::SDK.new() is the subject.
56
+ #
57
+ attr_reader :subject
58
+
59
+ # TODO: Delete this when tests are written.
60
+ # def sample_inputs
61
+ # nil
62
+ # end
63
+
64
+ # SAMPLE_TOKEN = """
65
+ # AQBjjlIYyEuyK2HuzqfA2ldj0B88d63KX2pgdOC0N4Pg4Iuw232M7gEgXjQS0Zdt3Y1r2J3G
66
+ # rCOf4fs1JndDbyGY_uaPWj5hpYE_dMS0G5ouJKLaapDT50EysfV3XdW6aQlbw51dYjgZU-Ce
67
+ # NCnj7bPsq4nXhZzbUkr0aTuR8MKEOXuW7-xaz1h8et-ZFYQDa788LTS08pLu--1waspBsmqh
68
+ # SxbOl0xG5QBQ0NnTbCn1SWi-T1B7J_6twmv7GWXsR9RqeBg_U5KcT6ciz85YFrkRQ6n47PpP
69
+ # HBfTFjmJxB91plroOOIZAE3fQ37-RDqdK7YzSw6gAm0
70
+ # """.strip
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spotify
4
+ class SDK
5
+ class Initialization
6
+ ##
7
+ # This class implements accepting OAuth2::AccessToken as an initializer.
8
+ #
9
+ class OAuthAccessToken < Base
10
+ ##
11
+ # This implements the #should_perform? method from the Base class.
12
+ #
13
+ # @see /lib/spotify/sdk/authorization/base.rb
14
+ #
15
+ def should_perform?
16
+ subject.instance_of? OAuth2::AccessToken
17
+ end
18
+
19
+ ##
20
+ # This implements the #perform method from the Base class.
21
+ #
22
+ # @see /lib/spotify/sdk/authorization/base.rb
23
+ #
24
+ def perform
25
+ {
26
+ access_token: subject.token,
27
+ expires_in: subject.expires_in,
28
+ refresh_token: subject.refresh_token
29
+ }
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spotify
4
+ class SDK
5
+ class Initialization
6
+ class PlainString < Base
7
+ def should_perform?
8
+ subject.is_a?(String) && subject =~ /^[a-zA-Z0-9_-]+$/
9
+ end
10
+
11
+ def perform
12
+ {
13
+ access_token: subject,
14
+ expires_in: nil,
15
+ refresh_token: nil
16
+ }
17
+ end
18
+
19
+ # TODO: Delete this when tests are written.
20
+ # def sample_inputs
21
+ # [ SAMPLE_TOKEN ]
22
+ # end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spotify
4
+ class SDK
5
+ class Initialization
6
+ class QueryHash < Base
7
+ def subject_hash
8
+ subject.try(:with_indifferent_access) || {}
9
+ end
10
+
11
+ def should_perform?
12
+ subject_hash.has_key?(:token) || subject_hash.has_key?(:access_token)
13
+ end
14
+
15
+ def perform
16
+ subject_hash[:access_token] = subject_hash[:token] if subject_hash.has_key?(:token)
17
+ subject_hash.slice(:access_token, :expires_in, :refresh_token).symbolize_keys
18
+ end
19
+
20
+ # TODO: Delete this when tests are written.
21
+ # def sample_inputs
22
+ # [{
23
+ # token: SAMPLE_TOKEN
24
+ # }, {
25
+ # token: SAMPLE_TOKEN,
26
+ # expires_in: 1234567890
27
+ # }, {
28
+ # token: SAMPLE_TOKEN,
29
+ # expires_in: 1234567890,
30
+ # refresh_token: SAMPLE_TOKEN
31
+ # }, {
32
+ # access_token: SAMPLE_TOKEN
33
+ # }, {
34
+ # access_token: SAMPLE_TOKEN,
35
+ # expires_in: 1234567890
36
+ # }, {
37
+ # access_token: SAMPLE_TOKEN,
38
+ # expires_in: 1234567890,
39
+ # refresh_token: SAMPLE_TOKEN
40
+ # }]
41
+ # end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spotify
4
+ class SDK
5
+ class Initialization
6
+ class QueryString < Base
7
+ def params
8
+ CGI.parse(subject).with_indifferent_access
9
+ rescue NoMethodError
10
+ {}
11
+ end
12
+
13
+ def should_perform?
14
+ subject.is_a?(String) && (
15
+ params.has_key?(:token) && params.has_key?(:access_token)
16
+ )
17
+ end
18
+
19
+ def perform
20
+ {
21
+ access_token: params[access_token_key].to_a[0],
22
+ expires_in: params[:expires_in].to_a[0],
23
+ refresh_token: params[:refresh_token].to_a[0]
24
+ }
25
+ end
26
+
27
+ private
28
+
29
+ def access_token_key
30
+ if params.has_key?(:token)
31
+ :token
32
+ elsif params.has_key?(:access_token)
33
+ :access_token
34
+ end
35
+ end
36
+
37
+ # TODO: Delete this when tests are written.
38
+ # def sample_inputs
39
+ # [
40
+ # "token=#{SAMPLE_TOKEN}",
41
+ # "token=#{SAMPLE_TOKEN}&expires_in=1234567890",
42
+ # "token=#{SAMPLE_TOKEN}&expires_in=1234567890&refresh_token=#{SAMPLE_TOKEN}",
43
+ # "access_token=#{SAMPLE_TOKEN}",
44
+ # "access_token=#{SAMPLE_TOKEN}&expires_in=1234567890",
45
+ # "access_token=#{SAMPLE_TOKEN}&expires_in=1234567890&refresh_token=#{SAMPLE_TOKEN}"
46
+ # ]
47
+ # end
48
+ end
49
+ end
50
+ end
51
+ end