hallon 0.14.0 → 0.15.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,62 +1,34 @@
1
1
  # coding: utf-8
2
- #
3
- # DISCLAIMER:
4
- # This file was written without extensive testing, and is merely an
5
- # example. Before using this yourself, I advice you to look through
6
- # the code carefully.
7
- #
8
2
 
9
- $LOAD_PATH.unshift(File.expand_path('../lib', File.dirname(__FILE__)))
10
-
11
- require 'hallon'
12
- require './spec/support/config'
13
-
14
- # Utility
15
- def prompt(str)
16
- print str
17
- gets.chomp
18
- end
19
-
20
- session = Hallon::Session.initialize IO.read(ENV['HALLON_APPKEY']) do
21
- on(:log_message) do |message|
22
- puts "[LOG] #{message}"
23
- end
24
-
25
- on(:connection_error) do |error|
26
- Hallon::Error.maybe_raise(error)
27
- end
28
-
29
- on(:logged_out) do
30
- abort "[FAIL] Logged out!"
31
- end
32
- end
33
-
34
- session.login!(ENV['HALLON_USERNAME'], ENV['HALLON_PASSWORD'])
35
-
36
- puts "Successfully logged in!"
3
+ # Require support code, used by all the examples.
4
+ require_relative 'example_support'
5
+ session = Hallon::Session.instance
37
6
 
38
7
  while username = prompt("Enter a Spotify username: ")
39
8
  begin
40
- puts "Fetching container for #{username}..."
41
- published = Hallon::User.new(username).published
42
- session.wait_for { published.loaded? }
9
+ puts "Loading #{username}."
10
+ user = Hallon::User.new(username)
43
11
 
44
- puts "Listing #{published.size} playlists."
45
- published.contents.each do |playlist|
46
- next if playlist.nil? # folder or somesuch
12
+ puts "Fetching published playlists for #{username}..."
13
+ published = user.published.load
47
14
 
48
- session.wait_for { playlist.loaded? }
15
+ puts "Loading #{published.size} playlists."
16
+ all_playlists = published.contents.find_all do |playlist|
17
+ playlist.is_a?(Hallon::Playlist) # ignore folders
18
+ end
49
19
 
50
- puts
51
- puts playlist.name << ": "
20
+ all_playlists.each(&:load)
52
21
 
53
- playlist.tracks.each_with_index do |track, i|
54
- session.wait_for { track.loaded? }
22
+ all_playlists.each do |playlist|
23
+ puts
24
+ puts "Listing tracks for #{playlist.name} (#{playlist.to_str}):"
55
25
 
26
+ tracks = playlist.tracks.to_a.map(&:load)
27
+ tracks.each_with_index do |track, i|
56
28
  puts "\t (#{i+1}/#{playlist.size}) #{track.name}"
57
29
  end
58
30
  end
59
31
  rescue Interrupt
60
- # do nothing, continue with loop
32
+ puts "Interrupted!"
61
33
  end
62
34
  end
data/hallon.gemspec CHANGED
@@ -22,9 +22,10 @@ Gem::Specification.new do |gem|
22
22
  gem.required_ruby_version = '>= 1.9'
23
23
 
24
24
  gem.add_dependency 'ref', '~> 1.0'
25
- gem.add_dependency 'spotify', '~> 10.3.0'
25
+ gem.add_dependency 'spotify', '~> 11.0.2'
26
26
  gem.add_development_dependency 'rake', '~> 0.8'
27
27
  gem.add_development_dependency 'rspec', '~> 2'
28
28
  gem.add_development_dependency 'yard'
29
+ gem.add_development_dependency 'bundler'
29
30
  gem.add_development_dependency 'rdiscount'
30
31
  end
data/lib/hallon.rb CHANGED
@@ -59,14 +59,15 @@ module Hallon
59
59
  # @example
60
60
  # Hallon::URI === "spotify:user:burgestrand" # => true
61
61
  URI = /(spotify:(?:
62
- (?:artist|album|track|user:[^:]+:playlist):[a-fA-F0-9]+
63
- |user:[^:]+
62
+ (?:artist|album|user:[^:]+:playlist):[a-zA-Z0-9]{22}
63
+ |track:[a-zA-Z0-9]{22}(?:\#\d{1,2}:\d{1,2})?
64
+ |user:[^:]+(?::starred)?
64
65
  |search:(?:[-\w$\.+!*'(),]+|%[a-fA-F0-9]{2})+
65
66
  |image:[a-fA-F0-9]{40}
66
67
  ))
67
68
  /x
68
69
 
69
- # Thrown by {Loadable#load} on failure.
70
+ # Thrown by {Loadable#load} and {Playlist#update} on failure.
70
71
  TimeoutError = Class.new(Hallon::Error)
71
72
 
72
73
  class << self
@@ -83,5 +84,5 @@ module Hallon
83
84
  end
84
85
  end
85
86
 
86
- self.load_timeout = 5 # seconds
87
+ self.load_timeout = 31.4159 # seconds
87
88
  end
data/lib/hallon/album.rb CHANGED
@@ -23,7 +23,7 @@ module Hallon
23
23
  Spotify.enum_type(:albumtype).symbols
24
24
  end
25
25
 
26
- extend Linkable
26
+ include Linkable
27
27
  include Loadable
28
28
 
29
29
  to_link :from_album
data/lib/hallon/artist.rb CHANGED
@@ -11,7 +11,7 @@ module Hallon
11
11
  #
12
12
  # @see http://developer.spotify.com/en/libspotify/docs/group__artist.html
13
13
  class Artist < Base
14
- extend Linkable
14
+ include Linkable
15
15
  include Loadable
16
16
 
17
17
  from_link :as_artist
data/lib/hallon/base.rb CHANGED
@@ -1,6 +1,4 @@
1
1
  # coding: utf-8
2
- require 'timeout'
3
-
4
2
  module Hallon
5
3
  # All objects in Hallon are mere representations of Spotify objects.
6
4
  # Hallon::Base covers basic functionality shared by all of these.
@@ -41,6 +39,7 @@ module Hallon
41
39
  end
42
40
 
43
41
  private
42
+
44
43
  # @macro [attach] to_link
45
44
  # @method to_link
46
45
  # @scope instance
@@ -72,7 +71,8 @@ module Hallon
72
71
  #
73
72
  # @param [Spotify::Pointer, String, Link] resource
74
73
  # @return [Spotify::Pointer]
75
- # @raise [TypeError] when pointer could not be created, or null
74
+ # @raise [TypeError] when the pointer is of the wrong type
75
+ # @raise [ArgumentError] when pointer could not be created, or null
76
76
  def to_pointer(resource, type, *args)
77
77
  if resource.is_a?(FFI::Pointer) and not resource.is_a?(Spotify::Pointer)
78
78
  raise TypeError, "Hallon does not support raw FFI::Pointers, wrap it in a Spotify::Pointer"
@@ -23,6 +23,7 @@ module Spotify
23
23
  # @raise [NoMethodError] if `function` is not defined
24
24
  # @see Spotify::Pointer
25
25
  def self.wrap_function(function, return_type)
26
+ method(function)
26
27
  define_singleton_method("#{function}!") do |*args|
27
28
  pointer = public_send(function, *args)
28
29
  Spotify::Pointer.new(pointer, return_type, function !~ /create/)
@@ -81,7 +82,6 @@ module Spotify
81
82
  wrap_function :link_create_from_user, :link
82
83
 
83
84
  wrap_function :search_create, :search
84
- wrap_function :radio_search_create, :search
85
85
  wrap_function :search_track, :track
86
86
  wrap_function :search_album, :album
87
87
  wrap_function :search_artist, :artist
data/lib/hallon/image.rb CHANGED
@@ -4,7 +4,7 @@ module Hallon
4
4
  #
5
5
  # @see http://developer.spotify.com/en/libspotify/docs/group__image.html
6
6
  class Image < Base
7
- extend Linkable
7
+ include Linkable
8
8
 
9
9
  from_link :as_image do |link|
10
10
  Spotify.image_create_from_link!(session.pointer, link)
data/lib/hallon/link.rb CHANGED
@@ -11,6 +11,8 @@ module Hallon
11
11
  def self.valid?(spotify_uri)
12
12
  if spotify_uri.is_a?(Link)
13
13
  return true
14
+ elsif spotify_uri.to_s["\x00"] # image ids
15
+ return false
14
16
  end
15
17
 
16
18
  link = Spotify.link_create_from_string!(spotify_uri.to_s)
@@ -54,7 +56,7 @@ module Hallon
54
56
  def to_str(length = length)
55
57
  FFI::Buffer.alloc_out(length + 1) do |b|
56
58
  Spotify.link_as_string(pointer, b, b.size)
57
- return b.get_string(0)
59
+ return b.get_string(0).force_encoding("UTF-8")
58
60
  end
59
61
  end
60
62
 
@@ -8,123 +8,120 @@ module Hallon
8
8
  #
9
9
  # @private
10
10
  module Linkable
11
- # Defines `#from_link`, used in converting a link to a pointer. You
12
- # can either pass it a `method_name`, or a `type` and a block.
13
- #
14
- # @overload from_link(method_name)
15
- # Define `#from_link` simply by giving the name of the method,
16
- # minus the `link_` prefix.
17
- #
18
- # @example
19
- # class Album
20
- # extend Linkable
21
- #
22
- # from_link :as_album # => Spotify.link_as_album(pointer, *args)
23
- # # ^ is roughly equivalent to:
24
- # def from_link(link, *args)
25
- # unless Spotify::Pointer.typechecks?(link, :link)
26
- # link = Link.new(link).pointer(:album)
27
- # end
28
- #
29
- # Spotify.link_as_album!(link)
30
- # end
31
- # end
32
- #
33
- # @param [Symbol] method_name
34
- #
35
- # @overload from_link(type) { |*args| … }
36
- # Define `#from_link` to use the given block to convert an object
37
- # from a link. The link is converted to a pointer and typechecked
38
- # to be of the same type as `type` before given to the block.
39
- #
40
- # @example
41
- # class User
42
- # extend Linkable
43
- #
44
- # from_link :profile do |pointer|
45
- # Spotify.link_as_user!(pointer)
46
- # end
47
- # # ^ is roughly equivalent to:
48
- # def from_link(link, *args)
49
- # unless Spotify::Pointer.typechecks?(link, :link)
50
- # link = Link.new(link).pointer(:profile)
51
- # end
52
- #
53
- # Spotify.link_as_user!(link)
54
- # end
55
- # end
56
- #
57
- # @param [#to_s] type link type
58
- # @yield [link, *args] called when conversion is needed from Link pointer
59
- # @yieldparam [Spotify::Pointer] link
60
- # @yieldparam *args any extra arguments given to `#from_link`
61
- #
62
- # @note Private API. You probably do not need to care about this method.
63
- def from_link(as_object, &block)
64
- block ||= Spotify.method(:"link_#{as_object}!")
65
- type = as_object.to_s[/^(as_)?([^_]+)/, 2].to_sym
11
+ module ClassMethods
12
+ # Defines `#from_link`, used in converting a link to a pointer. You
13
+ # can either pass it a `method_name`, or a `type` and a block.
14
+ #
15
+ # @overload from_link(method_name)
16
+ # Define `#from_link` simply by giving the name of the method,
17
+ # minus the `link_` prefix.
18
+ #
19
+ # @example
20
+ # class Album
21
+ # include Linkable
22
+ #
23
+ # from_link :as_album # => Spotify.link_as_album(pointer, *args)
24
+ # # ^ is roughly equivalent to:
25
+ # def from_link(link, *args)
26
+ # unless Spotify::Pointer.typechecks?(link, :link)
27
+ # link = Link.new(link).pointer(:album)
28
+ # end
29
+ #
30
+ # Spotify.link_as_album!(link)
31
+ # end
32
+ # end
33
+ #
34
+ # @param [Symbol] method_name
35
+ #
36
+ # @overload from_link(type) { |*args| }
37
+ # Define `#from_link` to use the given block to convert an object
38
+ # from a link. The link is converted to a pointer and typechecked
39
+ # to be of the same type as `type` before given to the block.
40
+ #
41
+ # @example
42
+ # class User
43
+ # include Linkable
44
+ #
45
+ # from_link :profile do |pointer|
46
+ # Spotify.link_as_user!(pointer)
47
+ # end
48
+ # # ^ is roughly equivalent to:
49
+ # def from_link(link, *args)
50
+ # unless Spotify::Pointer.typechecks?(link, :link)
51
+ # link = Link.new(link).pointer(:profile)
52
+ # end
53
+ #
54
+ # Spotify.link_as_user!(link)
55
+ # end
56
+ # end
57
+ #
58
+ # @param [#to_s] type link type
59
+ # @yield [link, *args] called when conversion is needed from Link pointer
60
+ # @yieldparam [Spotify::Pointer] link
61
+ # @yieldparam *args any extra arguments given to `#from_link`
62
+ #
63
+ # @note Private API. You probably do not need to care about this method.
64
+ def from_link(as_object, &block)
65
+ block ||= Spotify.method(:"link_#{as_object}!")
66
+ type = as_object.to_s[/^(as_)?([^_]+)/, 2].to_sym
67
+
68
+ define_method(:from_link) do |link, *args|
69
+ unless Spotify::Pointer.typechecks?(link, :link)
70
+ link = Link.new(link).pointer(type)
71
+ end
66
72
 
67
- define_method(:from_link) do |link, *args|
68
- unless Spotify::Pointer.typechecks?(link, :link)
69
- link = Link.new(link).pointer(type)
73
+ instance_exec(link, *args, &block)
70
74
  end
71
75
 
72
- instance_exec(link, *args, &block)
76
+ private :from_link
73
77
  end
74
78
 
75
- private :from_link
76
- end
77
-
78
- # Defines `#to_link` method, used in converting the object to a {Link}.
79
- #
80
- # @example
81
- # class Artist
82
- # extend Linkable
83
- #
84
- # to_link :from_artist
85
- # # ^ is the same as:
86
- # def to_link(*args)
87
- # link = Spotify.link_create_from_artist!(pointer, *args)
88
- # Link.new(link)
89
- # end
90
- # end
91
- #
92
- # @param [Symbol] cmethod name of the C method, say `from_artist` in `Spotify.link_create_from_artist`.
93
- # @return [Link, nil]
94
- def to_link(cmethod)
95
- define_method(:to_link) do |*args|
96
- link = Spotify.__send__(:"link_create_#{cmethod}!", pointer, *args)
97
- Link.from(link)
79
+ # Defines `#to_link` method, used in converting the object to a {Link}.
80
+ #
81
+ # @example
82
+ # class Artist
83
+ # include Linkable
84
+ #
85
+ # to_link :from_artist
86
+ # # ^ is the same as:
87
+ # def to_link(*args)
88
+ # link = Spotify.link_create_from_artist!(pointer, *args)
89
+ # Link.new(link)
90
+ # end
91
+ # end
92
+ #
93
+ # @param [Symbol] cmethod name of the C method, say `from_artist` in `Spotify.link_create_from_artist`.
94
+ # @return [Link, nil]
95
+ def to_link(cmethod)
96
+ define_method(:to_link) do |*args|
97
+ link = Spotify.__send__(:"link_create_#{cmethod}!", pointer, *args)
98
+ Link.from(link)
99
+ end
98
100
  end
99
101
  end
100
102
 
101
- private :from_link
102
- private :to_link
103
-
104
- def self.extended(other)
105
- other.send(:include, InstanceMethods)
103
+ def self.included(other)
104
+ other.extend ClassMethods
106
105
  end
107
106
 
108
- module InstanceMethods
109
- # Converts the Linkable first to a Link, and then that link to a String.
110
- #
111
- # @note Returns an empty string if the #to_link call fails.
112
- # @return [String]
113
- def to_str
114
- link = to_link
115
- link &&= link.to_str
116
- link.to_s
117
- end
107
+ # Converts the Linkable first to a Link, and then that link to a String.
108
+ #
109
+ # @note Returns an empty string if the #to_link call fails.
110
+ # @return [String]
111
+ def to_str
112
+ link = to_link
113
+ link &&= link.to_str
114
+ link.to_s
115
+ end
118
116
 
119
- # Compare the Linkable to other. If other is a Linkable, also
120
- # compare their `to_link` if necessary.
121
- #
122
- # @param [Object] other
123
- # @return [Boolean]
124
- def ===(other)
125
- super or if other.respond_to?(:to_link)
126
- to_link == other.to_link
127
- end
117
+ # Compare the Linkable to other. If other is a Linkable, also
118
+ # compare their `to_link` if necessary.
119
+ #
120
+ # @param [Object] other
121
+ # @return [Boolean]
122
+ def ===(other)
123
+ super or if other.respond_to?(:to_link)
124
+ to_link == other.to_link
128
125
  end
129
126
  end
130
127
  end
@@ -1,4 +1,5 @@
1
1
  # coding: utf-8
2
+ require 'timeout'
2
3
 
3
4
  module Hallon
4
5
  module Loadable
@@ -101,12 +101,14 @@ module Hallon
101
101
  # Defines a handler for the given event.
102
102
  #
103
103
  # @param [#to_s] event name of event to handle
104
- # @return [Proc] the given block
104
+ # @return [Proc] the previous handler
105
105
  # @yield (*args) event handler block
106
106
  def on(event, &block)
107
107
  raise ArgumentError, "no block given" unless block
108
108
  raise NameError, "no such callback: #{event}" unless has_callback?(event)
109
- handlers[event.to_s] = block
109
+ handlers[event.to_s].tap do
110
+ handlers[event.to_s] = block
111
+ end
110
112
  end
111
113
 
112
114
  # @param [#to_s] name