hallon 0.0.0 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.autotest +6 -0
- data/.gemtest +0 -0
- data/.gitignore +29 -0
- data/.rspec +7 -0
- data/.yardopts +8 -0
- data/CHANGELOG +20 -0
- data/Gemfile +2 -0
- data/LICENSE.txt +21 -0
- data/QUIRKS +11 -0
- data/README.markdown +58 -0
- data/Rakefile +75 -0
- data/Termfile +7 -0
- data/examples/logging_in.rb +26 -0
- data/examples/printing_link_information.rb +27 -0
- data/hallon.gemspec +31 -0
- data/lib/hallon.rb +34 -0
- data/lib/hallon/error.rb +54 -0
- data/lib/hallon/ext/ffi.rb +26 -0
- data/lib/hallon/ext/spotify.rb +101 -0
- data/lib/hallon/image.rb +70 -0
- data/lib/hallon/link.rb +101 -0
- data/lib/hallon/linkable.rb +50 -0
- data/lib/hallon/observable.rb +91 -0
- data/lib/hallon/session.rb +189 -0
- data/lib/hallon/synchronizable.rb +32 -0
- data/lib/hallon/user.rb +69 -0
- data/lib/hallon/version.rb +7 -0
- data/spec/fixtures/example_uris.rb +11 -0
- data/spec/fixtures/pink_cover.jpg +0 -0
- data/spec/hallon/error_spec.rb +30 -0
- data/spec/hallon/ffi_spec.rb +5 -0
- data/spec/hallon/hallon_spec.rb +16 -0
- data/spec/hallon/image_spec.rb +41 -0
- data/spec/hallon/link_spec.rb +84 -0
- data/spec/hallon/linkable_spec.rb +43 -0
- data/spec/hallon/observable_spec.rb +103 -0
- data/spec/hallon/session_spec.rb +61 -0
- data/spec/hallon/synchronizable_spec.rb +19 -0
- data/spec/hallon/user_spec.rb +73 -0
- data/spec/spec_helper.rb +71 -0
- data/spec/support/.gitkeep +0 -0
- data/spec/support/context_initialized_session.rb +3 -0
- data/spec/support/context_logged_in.rb +16 -0
- data/spec/support/cover_me.rb +5 -0
- data/spec/support/shared_for_loadable_objects.rb +7 -0
- metadata +271 -96
data/.autotest
ADDED
data/.gemtest
ADDED
File without changes
|
data/.gitignore
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# Private files
|
2
|
+
*.key
|
3
|
+
spec/support/config.rb
|
4
|
+
tmp/
|
5
|
+
libspotify-*
|
6
|
+
core.*
|
7
|
+
Gemfile.lock
|
8
|
+
|
9
|
+
# Extension files
|
10
|
+
*.bundle
|
11
|
+
|
12
|
+
# Generated by:
|
13
|
+
## rcov/cover_me
|
14
|
+
coverage/
|
15
|
+
coverage.data
|
16
|
+
|
17
|
+
## yardoc
|
18
|
+
doc/
|
19
|
+
.yardoc
|
20
|
+
|
21
|
+
## bundler
|
22
|
+
.bundle
|
23
|
+
|
24
|
+
## jeweler
|
25
|
+
pkg
|
26
|
+
*.gem
|
27
|
+
|
28
|
+
## Mac OS
|
29
|
+
.DS_Store
|
data/.rspec
ADDED
data/.yardopts
ADDED
data/CHANGELOG
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Hallon’s Changelog
|
2
|
+
==================
|
3
|
+
|
4
|
+
v0.1.0
|
5
|
+
------------------
|
6
|
+
Initial, first, release! This version is merely made to
|
7
|
+
have a starting point, a point of reference, for future
|
8
|
+
releases soon to come.
|
9
|
+
|
10
|
+
- Error subsystem is covered (`sp_error_message(error_code)`)
|
11
|
+
- Image subsystem is complete, however you can only create images
|
12
|
+
from links at this moment.
|
13
|
+
- Session API is partial. Currently you can login, logout, retrieve
|
14
|
+
the logged in user and query user relations.
|
15
|
+
- User API is complete, but you can only create users from your
|
16
|
+
currently logged in user (through Session) or from links.
|
17
|
+
|
18
|
+
The API is still very young, and I expect a lot of changes to
|
19
|
+
happen to it, to make the asynchronous nature of libspotify
|
20
|
+
easier to handle.
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Copyright 2011 Kim Burgestrand. All rights reserved.
|
2
|
+
|
3
|
+
Redistribution and use in source and binary forms, with or without modification, are
|
4
|
+
permitted provided that the following conditions are met:
|
5
|
+
|
6
|
+
1. Redistributions of source code must retain the above copyright notice, this list of
|
7
|
+
conditions and the following disclaimer.
|
8
|
+
|
9
|
+
2. Redistributions in binary form must reproduce the above copyright notice, this list
|
10
|
+
of conditions and the following disclaimer in the documentation and/or other materials
|
11
|
+
provided with the distribution.
|
12
|
+
|
13
|
+
THIS SOFTWARE IS PROVIDED BY KIM BURGESTRAND ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
14
|
+
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KIM BURGESTRAND OR
|
16
|
+
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
17
|
+
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
18
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
19
|
+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
20
|
+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
21
|
+
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/QUIRKS
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
# Quirks, oddities and general notes about libspotify
|
2
|
+
|
3
|
+
## Session
|
4
|
+
- `sp_session_release` will segfault (rare)
|
5
|
+
- `login` callback fires even if the login fails; instead,
|
6
|
+
`connection_error` will fire a few moments later
|
7
|
+
- creating a session, releasing it, and then creating one again
|
8
|
+
will segfault
|
9
|
+
|
10
|
+
## Link
|
11
|
+
- segfaults if created before a session
|
data/README.markdown
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
What is Hallon?
|
2
|
+
===============
|
3
|
+
We rubyists have this awesome [spotify gem][] allowing us to use [libspotify][] from within Ruby, but it has a significant drawback: the `libspotify` API is very hard to use. Now, we can’t have that, so what do we do? We make Hallon!
|
4
|
+
|
5
|
+
Hallon is Swedish for “Raspberry”, and has been written to satisfy my needs for API simplicity. It provides you with a wrapper around the spotify gem, making the experience of using `libspotify` from Ruby much more enjoyable.
|
6
|
+
|
7
|
+
Hallon would not have been possible if not for these people:
|
8
|
+
|
9
|
+
- Per Reimers, cracking synchronization bugs with me in the deep night (4 AM) and correcting me when I didn’t know better
|
10
|
+
- [Spotify](http://www.spotify.com/), providing a service worth attention (and my money!)
|
11
|
+
- [Linus Oleander](https://github.com/oleander), involving me with the `radiofy.se` project, ultimately spawning the necessity of Hallon
|
12
|
+
- [Jesper Särnesjö][], creator of [Greenstripes][], making me think of Hallon as an achievable goal
|
13
|
+
|
14
|
+
Code samples can be found under `examples/` directory.
|
15
|
+
|
16
|
+
This is awesome! I want to help!
|
17
|
+
--------------------------------
|
18
|
+
Sweet! You contribute in more than one way!
|
19
|
+
|
20
|
+
### Write code!
|
21
|
+
[Fork](http://help.github.com/forking/) Hallon, [write tests for everything](http://relishapp.com/rspec) you do (so I don’t break your stuff during my own development) and send a pull request. If you modify existing files, please adhere to the coding standard surrounding your code!
|
22
|
+
|
23
|
+
### [Send me feedback and requests](http://github.com/Burgestrand/Hallon/issues)
|
24
|
+
Really, I ❤ feedback! Suggestions on how to improve the API, tell me what is delicious about Hallon, tell me what is yucky about Hallon… anything! All feedback is useful in one way or another.
|
25
|
+
|
26
|
+
You have any questions?
|
27
|
+
-----------------------
|
28
|
+
If you need to discuss issues or feature requests you can use [Hallons issue tracker](http://github.com/Burgestrand/Hallon/issues). For *anything* else you have to say or ask I can also be reached via [email (found on GitHub profile)](http://github.com/Burgestrand) or [@burgestrand on twitter](http://twitter.com/Burgestrand).
|
29
|
+
|
30
|
+
In fact, you can contact me via email or twitter even if it’s about features or issues. I’ll probably put them in the issue tracker myself after the discussion ;)
|
31
|
+
|
32
|
+
What’s the catch?
|
33
|
+
-----------------
|
34
|
+
There are several!
|
35
|
+
|
36
|
+
### Hallon is unstable
|
37
|
+
The API is unstable, my code is likely unstable. Everything should be considered unstable!
|
38
|
+
|
39
|
+
### Hallon only supports one session per process
|
40
|
+
You can only keep one session with Spotify alive at a time in the same process, due to a limitation of `libspotify`.
|
41
|
+
|
42
|
+
### You still have to worry about threads
|
43
|
+
I have been doing my best at hiding the complexity in `libspotify`, but it’s still a work in progress. Despite my efforts, you’ll need to be familiar with concurrent programming to use Hallon properly.
|
44
|
+
|
45
|
+
Versioning policy
|
46
|
+
-----------------
|
47
|
+
Hallon uses [semantic versioning](http://semver.org) as of v0.0.0. As long
|
48
|
+
as Hallon stays at major version 0, no guarantees of backwards-compatibility
|
49
|
+
are made. CHANGELOG will be kept up to date with the different versions.
|
50
|
+
|
51
|
+
License
|
52
|
+
-------
|
53
|
+
Hallon is licensed under a 2-clause (Simplified) BSD license. More information can be found in the `LICENSE.txt` file.
|
54
|
+
|
55
|
+
[spotify gem]: https://rubygems.org/gems/spotify
|
56
|
+
[libspotify]: http://developer.spotify.com/en/libspotify/overview/
|
57
|
+
[Greenstripes]: http://github.com/sarnesjo/greenstripes
|
58
|
+
[Jesper Särnesjö]: http://jesper.sarnesjo.org/
|
data/Rakefile
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
require 'bundler'
|
5
|
+
Bundler::GemHelper.install_tasks
|
6
|
+
|
7
|
+
require 'yard'
|
8
|
+
YARD::Rake::YardocTask.new
|
9
|
+
|
10
|
+
require 'rspec/core/rake_task'
|
11
|
+
|
12
|
+
desc "Run all specs (even those requiring logging in to Spotify)"
|
13
|
+
RSpec::Core::RakeTask.new('spec:full')
|
14
|
+
|
15
|
+
desc "Run all specs like spec:full, but with debug mode and full warnings enabled"
|
16
|
+
RSpec::Core::RakeTask.new('spec:debug') do |task|
|
17
|
+
task.skip_bundler = true
|
18
|
+
task.ruby_opts = '-d -W2'
|
19
|
+
end
|
20
|
+
|
21
|
+
desc "Run all offline specs"
|
22
|
+
RSpec::Core::RakeTask.new('spec') do |task|
|
23
|
+
task.pattern = 'spec/hallon/*_spec.rb'
|
24
|
+
task.rspec_opts = '--tag ~logged_in'
|
25
|
+
end
|
26
|
+
|
27
|
+
desc "Run the full test suite and generate a coverage report"
|
28
|
+
task 'spec:cov' => ['clean', 'spec:full'] do
|
29
|
+
require 'cover_me'
|
30
|
+
require './spec/support/cover_me'
|
31
|
+
|
32
|
+
CoverMe.config.at_exit = proc { `open coverage/index.html` }
|
33
|
+
CoverMe.complete!
|
34
|
+
end
|
35
|
+
|
36
|
+
desc "Process the Hallon codebase, finding out which Spotify methods are being used"
|
37
|
+
task 'spotify:coverage' do
|
38
|
+
require 'set'
|
39
|
+
require 'spotify'
|
40
|
+
|
41
|
+
methods = Spotify.methods(false).map(&:to_s)
|
42
|
+
covered = Set.new(methods)
|
43
|
+
matcher = /Spotify::([\w_]+)[ \(]/
|
44
|
+
|
45
|
+
FileList['lib/**/*.rb'].each do |file|
|
46
|
+
File.read(file).scan(matcher) { |method, _| covered.delete(method) }
|
47
|
+
end
|
48
|
+
|
49
|
+
covered.group_by { |m| m[/[^_]+/] }.each_pair do |group, methods|
|
50
|
+
puts "#{group.capitalize}:"
|
51
|
+
methods.each do |m|
|
52
|
+
puts " #{m}"
|
53
|
+
end
|
54
|
+
puts
|
55
|
+
end
|
56
|
+
|
57
|
+
puts "Coverage: %.02f%%" % (100 * (1 - covered.size.fdiv(methods.size)))
|
58
|
+
end
|
59
|
+
|
60
|
+
task :test => :spec
|
61
|
+
|
62
|
+
#
|
63
|
+
# Custom tasks
|
64
|
+
#
|
65
|
+
desc "Generates YARD documentation and open it."
|
66
|
+
task :doc => :yard do
|
67
|
+
sh 'open doc/index.html'
|
68
|
+
end
|
69
|
+
|
70
|
+
desc "Remove generated files"
|
71
|
+
task :clean do
|
72
|
+
sh 'git clean -fdx --exclude Gemfile.lock --exclude spec/support/config.rb'
|
73
|
+
end
|
74
|
+
|
75
|
+
task :default => [:spec]
|
data/Termfile
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require 'hallon'
|
3
|
+
require './spec/support/config'
|
4
|
+
|
5
|
+
session = Hallon::Session.instance IO.read(ENV['HALLON_APPKEY']) do
|
6
|
+
on(:log_message) do |message|
|
7
|
+
puts "[LOG] #{message}"
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
session.login ENV['HALLON_USERNAME'], ENV['HALLON_PASSWORD']
|
12
|
+
logged_in = session.process_events_on(:logged_in) { |error| error }
|
13
|
+
|
14
|
+
unless logged_in == :ok
|
15
|
+
abort "[ERROR] (:logged_in) #{Hallon::Error.explain(logged_in)}"
|
16
|
+
end
|
17
|
+
|
18
|
+
conn_error = session.process_events_on(:connection_error) do |error|
|
19
|
+
session.logged_in? or error
|
20
|
+
end
|
21
|
+
|
22
|
+
unless conn_error == true
|
23
|
+
abort "[ERROR] (:connection_error) #{Hallon::Error.explain(conn_error)}"
|
24
|
+
end
|
25
|
+
|
26
|
+
puts "Successfully logged in!"
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require 'hallon'
|
3
|
+
require './spec/support/config'
|
4
|
+
|
5
|
+
# Utility
|
6
|
+
def prompt(str)
|
7
|
+
print str
|
8
|
+
gets.chomp
|
9
|
+
end
|
10
|
+
|
11
|
+
# Hallon
|
12
|
+
session = Hallon::Session.instance IO.read(ENV['HALLON_APPKEY']) do
|
13
|
+
on(:log_message) do |message|
|
14
|
+
$stderr.puts "[LOG] #{message}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
while url = prompt("Enter a Spotify URI: ")
|
19
|
+
begin
|
20
|
+
p (link = Hallon::Link.new(url))
|
21
|
+
puts "\tHTTP URL: #{link.to_url}"
|
22
|
+
puts "\tSpotify URI: #{link.to_str}"
|
23
|
+
puts "\tLink type: #{link.type}"
|
24
|
+
rescue ArgumentError => e
|
25
|
+
puts e
|
26
|
+
end
|
27
|
+
end
|
data/hallon.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require './lib/hallon/version'
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.name = "hallon"
|
6
|
+
gem.summary = %Q{Delicious Ruby bindings to the official Spotify API}
|
7
|
+
gem.homepage = "http://github.com/Burgestrand/Hallon"
|
8
|
+
gem.authors = ["Kim Burgestrand"]
|
9
|
+
gem.email = 'kim@burgestrand.se'
|
10
|
+
gem.license = 'GNU AGPL'
|
11
|
+
|
12
|
+
gem.description = IO.read('./README.markdown', encoding: 'utf-8')
|
13
|
+
|
14
|
+
gem.files = `git ls-files`.split("\n")
|
15
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
16
|
+
gem.executables = []
|
17
|
+
gem.require_paths = ["lib"]
|
18
|
+
|
19
|
+
gem.version = Hallon::VERSION
|
20
|
+
gem.platform = Gem::Platform::RUBY
|
21
|
+
gem.required_ruby_version = '~> 1.9'
|
22
|
+
|
23
|
+
gem.add_dependency 'spotify', '~> 8.0.5'
|
24
|
+
gem.add_development_dependency 'rake', '~> 0.8'
|
25
|
+
gem.add_development_dependency 'rspec', '~> 2'
|
26
|
+
gem.add_development_dependency 'autotest-standalone'
|
27
|
+
gem.add_development_dependency 'autotest-growl'
|
28
|
+
gem.add_development_dependency 'cover_me'
|
29
|
+
gem.add_development_dependency 'yard'
|
30
|
+
gem.add_development_dependency 'rdiscount'
|
31
|
+
end
|
data/lib/hallon.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require 'spotify'
|
3
|
+
require 'hallon/ext/spotify'
|
4
|
+
require 'hallon/ext/ffi'
|
5
|
+
|
6
|
+
require 'hallon/synchronizable'
|
7
|
+
require 'hallon/observable'
|
8
|
+
require 'hallon/linkable'
|
9
|
+
|
10
|
+
require 'hallon/version'
|
11
|
+
require 'hallon/error'
|
12
|
+
require 'hallon/session'
|
13
|
+
require 'hallon/link'
|
14
|
+
require 'hallon/user'
|
15
|
+
require 'hallon/image'
|
16
|
+
|
17
|
+
# The Hallon module wraps around all Hallon objects to avoid polluting
|
18
|
+
# the global namespace. To start using Hallon, you most likely want to
|
19
|
+
# be looking for the documentation on {Hallon::Session}.
|
20
|
+
module Hallon
|
21
|
+
# @see Spotify::API_VERSION
|
22
|
+
API_VERSION = Spotify::API_VERSION
|
23
|
+
|
24
|
+
# A regex that matches all Spotify URIs
|
25
|
+
#
|
26
|
+
# @example
|
27
|
+
# Hallon::URI === "spotify:user:burgestrand" # => true
|
28
|
+
URI = /(spotify:(?:
|
29
|
+
(?:artist|album|track|user:[^:]+:playlist):[a-zA-Z0-9]+
|
30
|
+
|user:[^:]+
|
31
|
+
|search:(?:[-\w$\.+!*'(),]+|%[a-fA-F0-9]{2})+
|
32
|
+
))
|
33
|
+
/x
|
34
|
+
end
|
data/lib/hallon/error.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
module Hallon
|
3
|
+
# Thrown by Hallon on libspotify errors.
|
4
|
+
#
|
5
|
+
# @see http://developer.spotify.com/en/libspotify/docs/group__error.html
|
6
|
+
class Error < RuntimeError
|
7
|
+
class << self
|
8
|
+
# Given a number or a symbol, find both the symbol and the error
|
9
|
+
# number it represents.
|
10
|
+
#
|
11
|
+
# @param [Symbol, Fixnum] error
|
12
|
+
# @return [[Fixnum, Symbol]] (error code, error symbol)
|
13
|
+
def disambiguate(error)
|
14
|
+
@enum ||= Spotify::enum_type(:error)
|
15
|
+
|
16
|
+
if error.is_a? Symbol
|
17
|
+
error = @enum[symbol = error]
|
18
|
+
else
|
19
|
+
symbol = @enum[error]
|
20
|
+
end
|
21
|
+
|
22
|
+
if error.nil? || symbol.nil?
|
23
|
+
[-1, nil]
|
24
|
+
else
|
25
|
+
[error, symbol]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Explain a Spotify error with a string message.
|
30
|
+
#
|
31
|
+
# @param [Fixnum, Symbol]
|
32
|
+
# @return [String]
|
33
|
+
def explain(error)
|
34
|
+
Spotify::error_message disambiguate(error)[0]
|
35
|
+
end
|
36
|
+
|
37
|
+
# Raise an {Error} with the given errno, unless it is `0` or `:ok`.
|
38
|
+
#
|
39
|
+
# @param [Fixnum, Symbol] error
|
40
|
+
# @return [nil]
|
41
|
+
def maybe_raise(error)
|
42
|
+
error, symbol = disambiguate(error)
|
43
|
+
|
44
|
+
unless symbol == :ok
|
45
|
+
message = []
|
46
|
+
message << "[#{symbol.upcase}]"
|
47
|
+
message << explain(error)
|
48
|
+
message << "(#{error})"
|
49
|
+
raise Hallon::Error, message.join(' ')
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# Custom extensions to the FFI gem.
|
2
|
+
#
|
3
|
+
# @see https://github.com/ffi/ffi
|
4
|
+
module FFI
|
5
|
+
# FFI::Pointer is the underlying class used to read
|
6
|
+
# and write data to pointers. For more information
|
7
|
+
# see the FFI gem.
|
8
|
+
class Pointer
|
9
|
+
type, _ = begin
|
10
|
+
type = FFI.find_type(:size_t)
|
11
|
+
FFI::TypeDefs.find do |(name, t)|
|
12
|
+
method_defined? "read_#{name}" if t == type
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
unless type.nil?
|
17
|
+
# Read N `size_t` from the start of the pointer.
|
18
|
+
#
|
19
|
+
# @param [Integer] count how many to read
|
20
|
+
# @return a type of appropriate size
|
21
|
+
define_method(:read_size_t) do |*args|
|
22
|
+
public_send("read_#{type}", *args)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|