rmpd 1.0.0
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.
- data/.gitignore +4 -0
- data/.rspec +1 -0
- data/Gemfile +4 -0
- data/Rakefile +14 -0
- data/lib/rmpd.rb +25 -0
- data/lib/rmpd/commands.rb +50 -0
- data/lib/rmpd/commands/admin.rb +21 -0
- data/lib/rmpd/commands/database.rb +17 -0
- data/lib/rmpd/commands/generators.rb +34 -0
- data/lib/rmpd/commands/miscellaneous.rb +33 -0
- data/lib/rmpd/commands/playback.rb +30 -0
- data/lib/rmpd/commands/playlist.rb +62 -0
- data/lib/rmpd/config.rb +77 -0
- data/lib/rmpd/connection.rb +84 -0
- data/lib/rmpd/multi_response.rb +43 -0
- data/lib/rmpd/response.rb +47 -0
- data/lib/rmpd/version.rb +3 -0
- data/rmpd.gemspec +24 -0
- data/spec/fixtures/config.yml +2 -0
- data/spec/fixtures/config_no_hostname.yml +1 -0
- data/spec/fixtures/config_no_password.yml +2 -0
- data/spec/fixtures/config_no_port.yml +1 -0
- data/spec/fixtures/config_rails.yml +3 -0
- data/spec/models/commands_spec.rb +135 -0
- data/spec/models/config_spec.rb +128 -0
- data/spec/models/connection_spec.rb +61 -0
- data/spec/models/generators_spec.rb +66 -0
- data/spec/models/mildred_mpd_spec.rb +15 -0
- data/spec/models/multi_response_spec.rb +54 -0
- data/spec/models/response_spec.rb +36 -0
- data/spec/spec.opts +6 -0
- data/spec/spec_helper.rb +43 -0
- data/spec/spec_rcov.opts +5 -0
- metadata +129 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour
|
data/Gemfile
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
require "rspec/core/rake_task"
|
5
|
+
#require "cucumber"
|
6
|
+
#require "cucumber/rake/task"
|
7
|
+
|
8
|
+
|
9
|
+
RSpec::Core::RakeTask.new
|
10
|
+
# Cucumber::Rake::Task.new("cucumber") do
|
11
|
+
# puts "No cucumber features defined yet."
|
12
|
+
# end
|
13
|
+
|
14
|
+
task :default => [:spec] #, :cucumber]
|
data/lib/rmpd.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "rmpd/config")
|
2
|
+
require File.join(File.dirname(__FILE__), "rmpd/commands")
|
3
|
+
require File.join(File.dirname(__FILE__), "rmpd/connection")
|
4
|
+
require File.join(File.dirname(__FILE__), "rmpd/multi_response")
|
5
|
+
require File.join(File.dirname(__FILE__), "rmpd/response")
|
6
|
+
|
7
|
+
module Rmpd
|
8
|
+
ACK_RE = /^ACK \[(\d+)@(\d+)\] \{([^}]*)\} (.*)$/
|
9
|
+
OK_RE = /^OK.*$/
|
10
|
+
|
11
|
+
class MpdError < Exception ; end
|
12
|
+
|
13
|
+
class MpdConnRefusedError < MpdError ; end
|
14
|
+
|
15
|
+
class MpdAckError < MpdError
|
16
|
+
def initialize(regex_match)
|
17
|
+
@error, @command_list_num, @current_command, @message = regex_match.values_at(1..-1)
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_s
|
21
|
+
"ACK [#{@error}@#{@command_list_num}] {#{@current_command}} #{@message}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "commands/generators")
|
2
|
+
require File.join(File.dirname(__FILE__), "commands/admin")
|
3
|
+
require File.join(File.dirname(__FILE__), "commands/database")
|
4
|
+
require File.join(File.dirname(__FILE__), "commands/miscellaneous")
|
5
|
+
require File.join(File.dirname(__FILE__), "commands/playback")
|
6
|
+
require File.join(File.dirname(__FILE__), "commands/playlist")
|
7
|
+
|
8
|
+
module Rmpd
|
9
|
+
module Commands
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def read_responses(regexp=/(^file: )/i)
|
14
|
+
read_response(MultiResponse, regexp)
|
15
|
+
end
|
16
|
+
|
17
|
+
def receive_server_response
|
18
|
+
lines = []
|
19
|
+
while lines << @socket.readline do
|
20
|
+
puts "recv: #{lines.last.strip} (#{OK_RE === lines.last})" if $DEBUG
|
21
|
+
case lines.last
|
22
|
+
when ACK_RE, OK_RE: break
|
23
|
+
end
|
24
|
+
end
|
25
|
+
return lines.join
|
26
|
+
end
|
27
|
+
|
28
|
+
def send_command(command, *args)
|
29
|
+
tries = 0
|
30
|
+
|
31
|
+
if $DEBUG
|
32
|
+
a = command == "password" ? args.map{|x| "*" * 8} : args
|
33
|
+
Kernel.puts "send: #{command.strip} #{a.join(" ")}".strip
|
34
|
+
end
|
35
|
+
|
36
|
+
begin
|
37
|
+
connect
|
38
|
+
@socket.puts("#{command} #{args.join(" ")}".strip)
|
39
|
+
rescue Errno::EPIPE, EOFError
|
40
|
+
@socket.close
|
41
|
+
if (tries += 1) < 5
|
42
|
+
retry
|
43
|
+
else
|
44
|
+
raise MpdError.new("Retry count exceeded")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Rmpd
|
2
|
+
module Commands
|
3
|
+
|
4
|
+
complex_command :outputs, :regexp => /(^outputid: )/i
|
5
|
+
complex_command :tagtypes, :regexp => /(^tagtype: )/i, :min_version => [0, 13, 0]
|
6
|
+
|
7
|
+
simple_command :disableoutput
|
8
|
+
simple_command :enableoutput
|
9
|
+
simple_command :update
|
10
|
+
|
11
|
+
def kill
|
12
|
+
send_command("kill")
|
13
|
+
@socket.close
|
14
|
+
end
|
15
|
+
|
16
|
+
alias_method :disable_output, :disableoutput
|
17
|
+
alias_method :tag_types, :tagtypes
|
18
|
+
alias_method :enable_output, :enableoutput
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Rmpd
|
2
|
+
module Commands
|
3
|
+
|
4
|
+
complex_command :count, :regexp => /(^songs: )/i
|
5
|
+
complex_command :find, :regexp => /(^file: )/i
|
6
|
+
complex_command :list, :regexp => /(^[^:]+: )/i
|
7
|
+
complex_command :listall, :regexp => /(^[^:]+: )/i
|
8
|
+
complex_command :listallinfo, :regexp => /(^(directory|file): )/i
|
9
|
+
complex_command :lsinfo, :regexp => /(^(directory|file): )/i
|
10
|
+
complex_command :search, :regexp => /(^file: )/i
|
11
|
+
|
12
|
+
alias_method :list_all, :listall
|
13
|
+
alias_method :list_all_info, :listallinfo
|
14
|
+
alias_method :ls_info, :lsinfo
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Rmpd
|
2
|
+
module Commands
|
3
|
+
|
4
|
+
private
|
5
|
+
|
6
|
+
def self.simple_command(name, args={})
|
7
|
+
block = lambda do |*a|
|
8
|
+
if args.include?(:min_version)
|
9
|
+
server_version_at_least(*args[:min_version])
|
10
|
+
end
|
11
|
+
send_command(name.to_s.gsub(/^_*/, ""), *quote(a))
|
12
|
+
read_response unless @in_command_list
|
13
|
+
end
|
14
|
+
send(:define_method, name, &block)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.complex_command(name, args={})
|
18
|
+
args = {:regexp => /(^file: )/i}.merge(args)
|
19
|
+
block = lambda do |*a|
|
20
|
+
if args.include?(:min_version)
|
21
|
+
server_version_at_least(*args[:min_version])
|
22
|
+
end
|
23
|
+
send_command(name.to_s.gsub(/^_*/, ""), *quote(a))
|
24
|
+
read_responses(args[:regexp]) unless @in_command_list
|
25
|
+
end
|
26
|
+
send(:define_method, name, &block)
|
27
|
+
end
|
28
|
+
|
29
|
+
def quote(args)
|
30
|
+
args.collect {|arg| "\"#{arg.to_s.gsub(/"/, "\\\"")}\""}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Rmpd
|
2
|
+
module Commands
|
3
|
+
|
4
|
+
complex_command :commands, :regexp => /(^command: )/i
|
5
|
+
simple_command :notcommands, :regexp => /(^command: )/i
|
6
|
+
|
7
|
+
simple_command :clearerror
|
8
|
+
simple_command :idle
|
9
|
+
simple_command :noidle
|
10
|
+
simple_command :password
|
11
|
+
simple_command :ping
|
12
|
+
simple_command :stats
|
13
|
+
simple_command :status
|
14
|
+
|
15
|
+
def close
|
16
|
+
send_command("close")
|
17
|
+
@socket.close
|
18
|
+
end
|
19
|
+
|
20
|
+
alias_method :clear_error, :clearerror
|
21
|
+
alias_method :not_commands, :notcommands
|
22
|
+
|
23
|
+
def command_list
|
24
|
+
send_command("command_list_begin")
|
25
|
+
@in_command_list = true
|
26
|
+
yield self
|
27
|
+
send_command("command_list_end")
|
28
|
+
read_response
|
29
|
+
ensure
|
30
|
+
@in_command_list = false
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Rmpd
|
2
|
+
module Commands
|
3
|
+
|
4
|
+
simple_command :_volume
|
5
|
+
simple_command :crossfade
|
6
|
+
simple_command :next
|
7
|
+
simple_command :pause
|
8
|
+
simple_command :play
|
9
|
+
simple_command :playid
|
10
|
+
simple_command :previous
|
11
|
+
simple_command :random
|
12
|
+
simple_command :repeat
|
13
|
+
simple_command :seek
|
14
|
+
simple_command :seekid
|
15
|
+
simple_command :setvol
|
16
|
+
simple_command :stop
|
17
|
+
|
18
|
+
def volume(*args)
|
19
|
+
$stderr.puts "volume is deprecated, use setvol instead"
|
20
|
+
_volume(*args)
|
21
|
+
end
|
22
|
+
|
23
|
+
alias_method :cross_fade, :crossfade
|
24
|
+
alias_method :play_id, :playid
|
25
|
+
alias_method :prev, :previous
|
26
|
+
alias_method :seek_id, :seekid
|
27
|
+
alias_method :set_vol, :setvol
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Rmpd
|
2
|
+
module Commands
|
3
|
+
|
4
|
+
complex_command :listplaylist, :min_version => [0, 13, 0]
|
5
|
+
complex_command :listplaylistinfo, :min_version => [0, 12, 0]
|
6
|
+
complex_command :playlistfind, :min_version => [0, 13, 0]
|
7
|
+
complex_command :playlistid
|
8
|
+
complex_command :playlistinfo
|
9
|
+
complex_command :playlistsearch, :min_version => [0, 13, 0]
|
10
|
+
complex_command :plchanges
|
11
|
+
complex_command :plchangesposid, :regexp => /(^Id: )/i
|
12
|
+
|
13
|
+
simple_command :_playlist
|
14
|
+
simple_command :add # The docs on the wiki don't line up with empirical
|
15
|
+
# results using 0.13.0
|
16
|
+
simple_command :clear
|
17
|
+
simple_command :currentsong
|
18
|
+
simple_command :delete
|
19
|
+
simple_command :deleteid
|
20
|
+
simple_command :load
|
21
|
+
simple_command :move
|
22
|
+
simple_command :moveid
|
23
|
+
simple_command :playlistadd, :min_version => [0, 13, 0]
|
24
|
+
simple_command :playlistclear, :min_version => [0, 13, 0]
|
25
|
+
simple_command :playlistdelete, :min_version => [0, 13, 0]
|
26
|
+
simple_command :playlistmove, :min_version => [0, 13, 0]
|
27
|
+
simple_command :rename
|
28
|
+
simple_command :rm
|
29
|
+
simple_command :save
|
30
|
+
simple_command :shuffle
|
31
|
+
simple_command :swap
|
32
|
+
simple_command :swapid
|
33
|
+
|
34
|
+
|
35
|
+
# must be a file only, cannot use with a director
|
36
|
+
def addid(path, pos=nil)
|
37
|
+
# pos is only for r7153+, but what version is that?
|
38
|
+
server_version_at_least(0, 14, 0) if pos
|
39
|
+
send_command("addid #{quote(path)} #{pos.to_s}")
|
40
|
+
read_response
|
41
|
+
end
|
42
|
+
|
43
|
+
alias_method :add_id, :addid
|
44
|
+
alias_method :current_song, :currentsong
|
45
|
+
alias_method :delete_id, :deleteid
|
46
|
+
alias_method :list_playlist, :listplaylist
|
47
|
+
alias_method :list_playlist_info, :listplaylistinfo
|
48
|
+
alias_method :move_id, :moveid
|
49
|
+
alias_method :pl_changes, :plchanges
|
50
|
+
alias_method :pl_changes_pos_id, :plchangesposid
|
51
|
+
alias_method :playlist_add, :playlistadd
|
52
|
+
alias_method :playlist_clear, :playlistclear
|
53
|
+
alias_method :playlist_delete, :playlistdelete
|
54
|
+
alias_method :playlist_find, :playlistfind
|
55
|
+
alias_method :playlist_id, :playlistid
|
56
|
+
alias_method :playlist_info, :playlistinfo
|
57
|
+
alias_method :playlist_move, :playlistmove
|
58
|
+
alias_method :playlist_search, :playlistsearch
|
59
|
+
alias_method :swap_id, :swapid
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
data/lib/rmpd/config.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
require "pathname"
|
2
|
+
require "yaml"
|
3
|
+
|
4
|
+
module Rmpd
|
5
|
+
|
6
|
+
class Config
|
7
|
+
|
8
|
+
DEFAULT_HOSTNAME = "localhost"
|
9
|
+
DEFAULT_PORT = 6600
|
10
|
+
DEFAULT_PASSWORD = nil
|
11
|
+
|
12
|
+
attr_accessor :hostname, :password, :port
|
13
|
+
alias_method :host, :hostname
|
14
|
+
|
15
|
+
def initialize(config_file=nil)
|
16
|
+
case config_file
|
17
|
+
when String, Pathname
|
18
|
+
config = YAML::load_file(config_file)
|
19
|
+
when File
|
20
|
+
config = YAML::load(config_file)
|
21
|
+
else
|
22
|
+
config = {}
|
23
|
+
end
|
24
|
+
config = config[Rails.env] if defined?(Rails) && config[Rails.env]
|
25
|
+
puts "config: #{config.inspect}" if $DEBUG
|
26
|
+
init_host_and_password(config)
|
27
|
+
init_port(config)
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
HOSTNAME_RE = /(.*)@(.*)/
|
34
|
+
|
35
|
+
|
36
|
+
def init_host_and_password(config)
|
37
|
+
if config["hostname"]
|
38
|
+
@hostname = parse_hostname(config["hostname"])
|
39
|
+
@password = parse_password(config["hostname"])
|
40
|
+
elsif ENV["MPD_HOST"]
|
41
|
+
@hostname = parse_hostname(ENV["MPD_HOST"])
|
42
|
+
@password = parse_password(ENV["MPD_HOST"])
|
43
|
+
else
|
44
|
+
@hostname = DEFAULT_HOSTNAME
|
45
|
+
@password = DEFAULT_PASSWORD
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def init_port(config)
|
50
|
+
if config["port"]
|
51
|
+
@port = config["port"].to_i
|
52
|
+
elsif ENV["MPD_PORT"]
|
53
|
+
@port = ENV["MPD_PORT"].to_i
|
54
|
+
else
|
55
|
+
@port = DEFAULT_PORT
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def parse_hostname(hostname)
|
60
|
+
if HOSTNAME_RE === hostname
|
61
|
+
$~[2]
|
62
|
+
else
|
63
|
+
hostname
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def parse_password(hostname)
|
68
|
+
if HOSTNAME_RE === hostname
|
69
|
+
$~[1]
|
70
|
+
else
|
71
|
+
nil
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require "socket"
|
2
|
+
|
3
|
+
|
4
|
+
module Rmpd
|
5
|
+
class Connection
|
6
|
+
|
7
|
+
include Rmpd::Commands
|
8
|
+
include Socket::Constants
|
9
|
+
|
10
|
+
attr_reader :error
|
11
|
+
|
12
|
+
def initialize(config_file=nil)
|
13
|
+
@config = Rmpd::Config.new(config_file)
|
14
|
+
@socket = nil
|
15
|
+
end
|
16
|
+
|
17
|
+
def server_version
|
18
|
+
"#{@server_version_major}.#{@server_version_minor}.#{@server_version_patch}"
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_reader :socket
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def authenticate
|
26
|
+
raise "Socket no good!" if (@socket.nil? || @socket.closed?)
|
27
|
+
send_command("password", @config.password)
|
28
|
+
read_response
|
29
|
+
true
|
30
|
+
end
|
31
|
+
|
32
|
+
def connect
|
33
|
+
return unless @socket.nil? || @socket.closed?
|
34
|
+
error = nil
|
35
|
+
|
36
|
+
Socket::getaddrinfo(@config.hostname, @config.port, nil, SOCK_STREAM).each do |info|
|
37
|
+
begin
|
38
|
+
puts "args: #{info.inspect}" if $DEBUG
|
39
|
+
sockaddr = Socket.pack_sockaddr_in(info[1], info[3])
|
40
|
+
@socket = Socket.new(info[4], info[5], 0)
|
41
|
+
@socket.connect(sockaddr)
|
42
|
+
rescue StandardError => error
|
43
|
+
$stderr.puts "Failed to connect to #{info[3]}: #{error}"
|
44
|
+
@socket = nil
|
45
|
+
else
|
46
|
+
break
|
47
|
+
end
|
48
|
+
end
|
49
|
+
raise MpdConnRefusedError.new(error) if @socket.nil?
|
50
|
+
|
51
|
+
parse_server_version(@socket.readline)
|
52
|
+
authenticate if @config.password
|
53
|
+
end
|
54
|
+
|
55
|
+
def parse_server_version(version)
|
56
|
+
/OK MPD (\d+)\.(\d+)\.(\d+)/.match(version.to_s)
|
57
|
+
@server_version_major = $~[1].to_i
|
58
|
+
@server_version_minor = $~[2].to_i
|
59
|
+
@server_version_patch = $~[3].to_i
|
60
|
+
end
|
61
|
+
|
62
|
+
def read_response(klass=Response, *args)
|
63
|
+
x = klass.new(receive_server_response, *args)
|
64
|
+
@error = x.error
|
65
|
+
x
|
66
|
+
end
|
67
|
+
|
68
|
+
def server_version_at_least(major, minor, patch)
|
69
|
+
connect
|
70
|
+
e = MpdError.new("Requires server version #{major}.#{minor}.#{patch}")
|
71
|
+
|
72
|
+
raise e if major > @server_version_major
|
73
|
+
return true if major < @server_version_major
|
74
|
+
|
75
|
+
raise e if minor > @server_version_minor
|
76
|
+
return true if minor < @server_version_minor
|
77
|
+
|
78
|
+
raise e if patch > @server_version_patch
|
79
|
+
return true if patch < @server_version_patch
|
80
|
+
true
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require "delegate"
|
2
|
+
|
3
|
+
module Rmpd
|
4
|
+
|
5
|
+
class MultiResponse < DelegateClass(Array)
|
6
|
+
|
7
|
+
END_OF_RECORD = "\0x1E" # from ASCII
|
8
|
+
|
9
|
+
def initialize(data, sep)
|
10
|
+
super([])
|
11
|
+
@sep = sep
|
12
|
+
parse(data)
|
13
|
+
end
|
14
|
+
|
15
|
+
def ok?
|
16
|
+
self.all? {|x| x.ok?}
|
17
|
+
end
|
18
|
+
|
19
|
+
def ack?
|
20
|
+
!ok?
|
21
|
+
end
|
22
|
+
|
23
|
+
def error
|
24
|
+
self.find(nil) {|x| x.error}
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def parse(data)
|
31
|
+
data.gsub!(@sep, "#{END_OF_RECORD}\\1")
|
32
|
+
data.split(END_OF_RECORD).each do |datum|
|
33
|
+
if datum.empty?
|
34
|
+
next
|
35
|
+
else
|
36
|
+
r = Response.new(datum)
|
37
|
+
end
|
38
|
+
self << r unless r.keys.size.zero? && r.ok?
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require "delegate"
|
2
|
+
|
3
|
+
module Rmpd
|
4
|
+
|
5
|
+
KEY_VALUE_RE = /^([^:]+):\s*(.*)$/
|
6
|
+
KNOWN_INT_FIELDS = [:time, :pos, :id, :track, :playlistlength, :playlist,
|
7
|
+
:xfade, :repeat, :random, :queued, :volume, :song]
|
8
|
+
|
9
|
+
class Response < DelegateClass(Hash)
|
10
|
+
|
11
|
+
attr_reader :error
|
12
|
+
|
13
|
+
def initialize(data)
|
14
|
+
super({})
|
15
|
+
@error = nil
|
16
|
+
parse(data)
|
17
|
+
end
|
18
|
+
|
19
|
+
def ok?
|
20
|
+
@error.nil?
|
21
|
+
end
|
22
|
+
|
23
|
+
def ack?
|
24
|
+
!ok?
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def register_key_val_pair(r)
|
30
|
+
key, val = r[1].downcase.to_sym, r[2]
|
31
|
+
val = val.to_i if KNOWN_INT_FIELDS.include?(key)
|
32
|
+
self[key] = include?(key) ? ([self[key]] << val).flatten : val
|
33
|
+
self.class.send(:define_method, key) {self[key]}
|
34
|
+
end
|
35
|
+
|
36
|
+
def parse(data)
|
37
|
+
data.split("\n").each do |line|
|
38
|
+
case line
|
39
|
+
when KEY_VALUE_RE: register_key_val_pair($~)
|
40
|
+
when OK_RE: @error = nil
|
41
|
+
when ACK_RE: @error = MpdAckError.new($~.values_at(0..-1))
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
data/lib/rmpd/version.rb
ADDED
data/rmpd.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "rmpd/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "rmpd"
|
7
|
+
s.version = Rmpd::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Eric Wollesen"]
|
10
|
+
s.email = ["ericw@xmtp.net"]
|
11
|
+
s.homepage = ""
|
12
|
+
s.summary = %q{Music Player Daemon client in Ruby}
|
13
|
+
s.description = %q{Music Player Daemon client in Ruby}
|
14
|
+
|
15
|
+
# s.rubyforge_project = "rmpd"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {spec}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
s.add_development_dependency("rspec", "~> 2.6.0")
|
23
|
+
s.add_development_dependency("ruby-debug")
|
24
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
port: 1234
|
@@ -0,0 +1 @@
|
|
1
|
+
hostname: juggernaught@test.host
|
@@ -0,0 +1,135 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
include Rmpd
|
4
|
+
|
5
|
+
|
6
|
+
shared_examples_for "a command with a song pos" do
|
7
|
+
it "should pass along the song pos" do
|
8
|
+
@socket.stub!(:readline).and_return(*@responses)
|
9
|
+
@socket.stub!(:puts).and_return(@socket.puts)
|
10
|
+
@socket.stub!(:eof?).and_return(false)
|
11
|
+
@command.call
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe Rmpd::Commands do
|
16
|
+
|
17
|
+
before(:each) do
|
18
|
+
@socket = mock_socket
|
19
|
+
@config = mock_config
|
20
|
+
@conn = Connection.new
|
21
|
+
end
|
22
|
+
|
23
|
+
def set_password(password="foobar")
|
24
|
+
@config.stub!(:password).and_return(password)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should handle a closed connection gracefully" do
|
28
|
+
@socket.stub!(:puts).and_raise(EOFError)
|
29
|
+
@socket.stub!(:readline).and_return(*(connect_response + ok))
|
30
|
+
@socket.should_receive(:close).exactly(5).times
|
31
|
+
lambda do
|
32
|
+
@conn.ping
|
33
|
+
end.should raise_error(Rmpd::MpdError, "Retry count exceeded")
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should hide password output when in debug mode" do
|
37
|
+
set_password
|
38
|
+
@socket.stub!(:readline).and_return(*(connect_and_auth_responses + ok))
|
39
|
+
Kernel.should_receive(:puts).with("send: password #{"*"*8}")
|
40
|
+
Kernel.should_receive(:puts).with("send: ping")
|
41
|
+
$DEBUG = 1
|
42
|
+
@conn.ping
|
43
|
+
$DEBUG = nil
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should abort after a limited number of tries against a closed connection" do
|
47
|
+
@socket.stub!(:puts).and_raise(EOFError)
|
48
|
+
@socket.stub!(:readline).and_return(*(connect_response + ok))
|
49
|
+
@socket.should_receive(:close).exactly(5).times
|
50
|
+
lambda do
|
51
|
+
@conn.ping
|
52
|
+
end.should raise_error(MpdError, "Retry count exceeded")
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "close" do
|
56
|
+
it "should close the socket" do
|
57
|
+
@socket.should_receive(:puts).with("close")
|
58
|
+
@socket.stub!(:readline).and_return(*connect_response)
|
59
|
+
@socket.should_receive(:close).once
|
60
|
+
@conn.close
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "volume" do
|
65
|
+
it "should be deprecated" do
|
66
|
+
set_password
|
67
|
+
@socket.stub!(:readline).and_return(*connect_and_auth_responses)
|
68
|
+
$stderr.should_receive(:puts).with(/deprecated/i)
|
69
|
+
@conn.volume(0)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe "kill" do
|
74
|
+
before(:each) do
|
75
|
+
set_password
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should kill the daemon" do
|
79
|
+
@socket.should_receive(:puts).with("kill")
|
80
|
+
@socket.stub!(:readline).and_return(*connect_and_auth_responses)
|
81
|
+
@socket.should_receive(:close)
|
82
|
+
@conn.kill
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe "playlistinfo" do
|
87
|
+
before(:each) do
|
88
|
+
set_password
|
89
|
+
@song_id = 1
|
90
|
+
@cmd = "playlistinfo \"#{@song_id}\""
|
91
|
+
@responses = connect_and_auth_responses + ["file: blah\n", "boobies: yay!\n"] + ok
|
92
|
+
@command = Proc.new {@conn.playlistinfo(@song_id)}
|
93
|
+
end
|
94
|
+
|
95
|
+
it_should_behave_like "a command with a song pos"
|
96
|
+
end
|
97
|
+
|
98
|
+
describe "play" do
|
99
|
+
before(:each) do
|
100
|
+
set_password
|
101
|
+
@song_id = 12
|
102
|
+
@cmd = "play \"#{@song_id}\""
|
103
|
+
@responses = connect_and_auth_responses + ok
|
104
|
+
@command = Proc.new {@conn.play(@song_id)}
|
105
|
+
end
|
106
|
+
|
107
|
+
it_should_behave_like "a command with a song pos"
|
108
|
+
end
|
109
|
+
|
110
|
+
describe "delete" do
|
111
|
+
before(:each) do
|
112
|
+
set_password
|
113
|
+
@song_id = 23
|
114
|
+
@cmd = "delete \"#{@song_id}\""
|
115
|
+
@responses = connect_and_auth_responses + ok
|
116
|
+
@command = Proc.new {@conn.delete(@song_id)}
|
117
|
+
end
|
118
|
+
|
119
|
+
it_should_behave_like "a command with a song pos"
|
120
|
+
end
|
121
|
+
|
122
|
+
describe "command_list" do
|
123
|
+
it "shouldn't wait for a response from the first command" do
|
124
|
+
@responses = connect_response + ok
|
125
|
+
@socket.stub!(:readline).and_return(*@responses)
|
126
|
+
@socket.stub!(:puts).and_return(@socket.puts)
|
127
|
+
@socket.stub!(:eof?).and_return(false)
|
128
|
+
|
129
|
+
@conn.command_list do |c|
|
130
|
+
c.status
|
131
|
+
c.stats
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
include Rmpd
|
4
|
+
|
5
|
+
|
6
|
+
describe Rmpd::Config do
|
7
|
+
|
8
|
+
before(:each) do
|
9
|
+
@filename = File.join(File.dirname(__FILE__), "../../spec/fixtures/config.yml")
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "initialization" do
|
13
|
+
it "should parse successfully" do
|
14
|
+
lambda do
|
15
|
+
Rmpd::Config.new(@filename)
|
16
|
+
end.should_not raise_error
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "rails" do
|
21
|
+
before(:each) do
|
22
|
+
class Rails
|
23
|
+
def self.env ; "development" ; end
|
24
|
+
end
|
25
|
+
@filename = File.join(File.dirname(__FILE__), "../../spec/fixtures/config_rails.yml")
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should load the development environment" do
|
29
|
+
lambda do
|
30
|
+
Rmpd::Config.new(@filename)
|
31
|
+
end.should_not raise_error
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should load the development environment" do
|
35
|
+
config = Rmpd::Config.new(@filename)
|
36
|
+
config.hostname.should == "localhost"
|
37
|
+
config.port.should == 3000
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "operation" do
|
42
|
+
before(:each) do
|
43
|
+
@config = Rmpd::Config.new(@filename)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should have the correct port" do
|
47
|
+
1234.should == @config.port
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should have the correct hostname" do
|
51
|
+
@config.hostname.should == "test.host"
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should have the correct password" do
|
55
|
+
@config.password.should == "juggernaught"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "without password" do
|
60
|
+
before(:each) do
|
61
|
+
@filename = File.join(File.dirname(__FILE__), "../../spec/fixtures/config_no_password.yml")
|
62
|
+
@config = Rmpd::Config.new(@filename)
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should have the default password" do
|
66
|
+
@config.password.should == Rmpd::Config::DEFAULT_PASSWORD
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "without hostname" do
|
71
|
+
before(:each) do
|
72
|
+
@filename = File.join(File.dirname(__FILE__), "../../spec/fixtures/config_no_hostname.yml")
|
73
|
+
@config = Rmpd::Config.new(@filename)
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should have the default hostname" do
|
77
|
+
@config.hostname.should == Rmpd::Config::DEFAULT_HOSTNAME
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe "without port" do
|
82
|
+
before(:each) do
|
83
|
+
@filename = File.join(File.dirname(__FILE__), "../../spec/fixtures/config_no_port.yml")
|
84
|
+
@config = Rmpd::Config.new(@filename)
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should have the default port" do
|
88
|
+
@config.port.should == Rmpd::Config::DEFAULT_PORT
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe "by default" do
|
93
|
+
before(:each) do
|
94
|
+
@filename = nil
|
95
|
+
ENV["MPD_HOST"] = nil
|
96
|
+
ENV["MPD_PORT"] = nil
|
97
|
+
@config = Rmpd::Config.new(@filename)
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should have the default hostname" do
|
101
|
+
@config.hostname.should == Rmpd::Config::DEFAULT_HOSTNAME
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should have the default port" do
|
105
|
+
@config.port.should == Rmpd::Config::DEFAULT_PORT
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should have the default password" do
|
109
|
+
@config.password.should == Rmpd::Config::DEFAULT_PASSWORD
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe "with environment variables set" do
|
114
|
+
before(:each) do
|
115
|
+
ENV["MPD_HOST"] = "testing:host"
|
116
|
+
ENV["MPD_PORT"] = -1.to_s
|
117
|
+
@config = Rmpd::Config.new
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should pickup the environment's host" do
|
121
|
+
@config.hostname.should == ENV["MPD_HOST"]
|
122
|
+
end
|
123
|
+
|
124
|
+
it "should pickup the environment's port" do
|
125
|
+
@config.port.should == ENV["MPD_PORT"].to_i
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
include Rmpd
|
4
|
+
|
5
|
+
|
6
|
+
describe Connection do
|
7
|
+
|
8
|
+
before(:each) do
|
9
|
+
@socket = mock_socket
|
10
|
+
@config = mock_config
|
11
|
+
@conn = Connection.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def set_password(password="foobar")
|
15
|
+
@config.stub!(:password).and_return(password)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should connect successfully" do
|
19
|
+
responses = connect_response + ok
|
20
|
+
@socket.should_receive(:readline).and_return(*responses)
|
21
|
+
@conn.ping
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should authenticate successfully" do
|
25
|
+
responses = connect_and_auth_responses + ok
|
26
|
+
set_password
|
27
|
+
@socket.should_receive(:readline).and_return(*responses)
|
28
|
+
@conn.ping
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should generate a server version" do
|
32
|
+
version = "0.1.2"
|
33
|
+
responses = connect_response(version) + ok
|
34
|
+
@socket.should_receive(:readline).and_return(*responses)
|
35
|
+
@conn.ping # so we connect, and get the server version
|
36
|
+
@conn.server_version.should == version
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should handle connection failures gracefully" do
|
40
|
+
@socket.stub!(:connect).and_raise(Errno::ECONNREFUSED.new("test"))
|
41
|
+
lambda do
|
42
|
+
@conn.ping
|
43
|
+
end.should raise_error(Rmpd::MpdError)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should restrict access based on server version" do
|
47
|
+
responses = connect_response("0.1.0")
|
48
|
+
@socket.stub!(:readline).and_return(*responses)
|
49
|
+
@conn.instance_eval do
|
50
|
+
def sval(*args)
|
51
|
+
server_version_at_least(*args)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
lambda do
|
55
|
+
@conn.sval(0, 1, 2)
|
56
|
+
end.should raise_error(MpdError)
|
57
|
+
lambda do
|
58
|
+
@conn.sval(0, 1, 0)
|
59
|
+
end.should_not raise_error(MpdError)
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module Rmpd
|
4
|
+
describe Commands, "Generators" do
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
@config = mock_config
|
8
|
+
@socket = mock_socket
|
9
|
+
@conn = Connection.new
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "when version is ok" do
|
13
|
+
|
14
|
+
before(:each) do
|
15
|
+
version = "0.1.1"
|
16
|
+
responses = connect_response(version) + ok
|
17
|
+
@socket.should_receive(:readline).and_return(*responses)
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "simple_command" do
|
21
|
+
it "should allow based on server version" do
|
22
|
+
Commands::simple_command(:test, :min_version => [0, 1, 1])
|
23
|
+
lambda do
|
24
|
+
@conn.test
|
25
|
+
end.should_not raise_error(MpdError)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "complex_command" do
|
30
|
+
it "should allow based on server version" do
|
31
|
+
Rmpd::Commands::complex_command(:test, :min_version => [0, 1, 1])
|
32
|
+
lambda do
|
33
|
+
@conn.test
|
34
|
+
end.should_not raise_error(MpdError)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "when version is NOT ok" do
|
40
|
+
|
41
|
+
before(:each) do
|
42
|
+
version = "0.1.1"
|
43
|
+
responses = connect_response(version)
|
44
|
+
@socket.should_receive(:readline).and_return(*responses)
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "simple_command" do
|
48
|
+
it "should disallow based on server version" do
|
49
|
+
Rmpd::Commands::simple_command(:test, :min_version => [0, 1, 2])
|
50
|
+
lambda do
|
51
|
+
@conn.test
|
52
|
+
end.should raise_error(MpdError, /^Requires server version/)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe "complex_command" do
|
57
|
+
it "should disallow based on server version" do
|
58
|
+
Rmpd::Commands::complex_command(:test, :min_version => [0, 1, 2])
|
59
|
+
lambda do
|
60
|
+
@conn.test
|
61
|
+
end.should raise_error(MpdError, /^Requires server version/)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
include Rmpd
|
4
|
+
|
5
|
+
describe MpdAckError do
|
6
|
+
before(:each) do
|
7
|
+
@err_msg = "ACK [2@0] {search} too few arguments for \"search\""
|
8
|
+
ACK_RE.match(@err_msg)
|
9
|
+
@error = MpdAckError.new($~)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should generate a useful string message" do
|
13
|
+
@error.to_s.should == @err_msg
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
include Rmpd
|
4
|
+
|
5
|
+
|
6
|
+
describe Rmpd::MultiResponse do
|
7
|
+
describe "on success" do
|
8
|
+
before(:each) do
|
9
|
+
data = <<-EOF
|
10
|
+
foo: cat
|
11
|
+
bar: dog
|
12
|
+
foo: horse
|
13
|
+
bar: giraffe
|
14
|
+
OK
|
15
|
+
EOF
|
16
|
+
@response = Rmpd::MultiResponse.new(data, /(^foo:)/)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should have a size of 2" do
|
20
|
+
@response.should have(2).items
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should be OK" do
|
24
|
+
# @response.should be_ok
|
25
|
+
# doesn't work correctly, see rspec bug #11526
|
26
|
+
@response.ok?.should be_true
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should not be ACK" do
|
30
|
+
# @response.should_not be_ack
|
31
|
+
# doesn't work correctly, see rspec bug #11526
|
32
|
+
@response.ack?.should be_false
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "on error" do
|
37
|
+
before(:each) do
|
38
|
+
@err_msg = "ACK [2@0] {search} too few arguments for \"search\"\n"
|
39
|
+
@response = Rmpd::MultiResponse.new(@err_msg, /(^foo:)/)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should not be OK" do
|
43
|
+
# @response.should_not be_ok
|
44
|
+
# doesn't work correctly, see rspec bug #11526
|
45
|
+
@response.ok?.should be_false
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should be ACK" do
|
49
|
+
# @response.should be_ack
|
50
|
+
# doesn't work correctly, see rspec bug #11526
|
51
|
+
@response.ack?.should be_true
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
include Rmpd
|
4
|
+
|
5
|
+
|
6
|
+
describe Rmpd::Response do
|
7
|
+
before(:each) do
|
8
|
+
data = <<-EOF
|
9
|
+
foo: bar
|
10
|
+
OK
|
11
|
+
EOF
|
12
|
+
@response = Rmpd::Response.new(data)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should have a foo method" do
|
16
|
+
@response.respond_to?(:foo).should be_true
|
17
|
+
@response.foo.should == "bar"
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should have a foo key" do
|
21
|
+
@response.should include(:foo)
|
22
|
+
@response[:foo].should == "bar"
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should be ok" do
|
26
|
+
# @response.should be_ok
|
27
|
+
# doesn't work correctly, see rspec bug #11526
|
28
|
+
@response.ok?.should == true
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should not be ack" do
|
32
|
+
# @response.should_not be_ack
|
33
|
+
# doesn't work correctly, see rspec bug #11526
|
34
|
+
@response.ack?.should_not == true
|
35
|
+
end
|
36
|
+
end
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../lib/rmpd"
|
2
|
+
|
3
|
+
|
4
|
+
def mock_socket
|
5
|
+
socket = mock(Socket)
|
6
|
+
socket.stub!(:puts)
|
7
|
+
socket.stub!(:closed?).and_return(false)
|
8
|
+
socket.stub!(:connect)
|
9
|
+
socket.stub!(:eof?).and_return(false)
|
10
|
+
Socket.stub!(:pack_sockaddr_in).and_return("pack_sockaddr_in")
|
11
|
+
Socket.stub!(:new).and_return(socket)
|
12
|
+
socket
|
13
|
+
end
|
14
|
+
|
15
|
+
def mock_config(opts={})
|
16
|
+
opts = {
|
17
|
+
:hostname => Rmpd::Config::DEFAULT_HOSTNAME,
|
18
|
+
:port => Rmpd::Config::DEFAULT_PORT,
|
19
|
+
:password => Rmpd::Config::DEFAULT_PASSWORD,
|
20
|
+
}.merge(opts)
|
21
|
+
config = mock(Rmpd::Config)
|
22
|
+
config.stub!(:hostname).and_return(opts[:hostname])
|
23
|
+
config.stub!(:password).and_return(opts[:password])
|
24
|
+
config.stub!(:port).and_return(opts[:port])
|
25
|
+
Rmpd::Config.stub!(:new).and_return(config)
|
26
|
+
config
|
27
|
+
end
|
28
|
+
|
29
|
+
def connect_response(version="0.12.0")
|
30
|
+
ok("MPD #{version}")
|
31
|
+
end
|
32
|
+
|
33
|
+
def connect_and_auth_responses
|
34
|
+
connect_response + ok
|
35
|
+
end
|
36
|
+
|
37
|
+
def ok(txt="")
|
38
|
+
txt ? ["OK #{txt}\n"] : ["OK\n"]
|
39
|
+
end
|
40
|
+
|
41
|
+
def ack(x=1, y=2, cmd="foo_command", msg="No one's home dopey!")
|
42
|
+
["ACK [#{x}@#{y}] {#{cmd}} #{msg}"]
|
43
|
+
end
|
data/spec/spec_rcov.opts
ADDED
metadata
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rmpd
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 23
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
- 0
|
10
|
+
version: 1.0.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Eric Wollesen
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-06-12 00:00:00 -06:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: rspec
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 23
|
30
|
+
segments:
|
31
|
+
- 2
|
32
|
+
- 6
|
33
|
+
- 0
|
34
|
+
version: 2.6.0
|
35
|
+
type: :development
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: ruby-debug
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 3
|
46
|
+
segments:
|
47
|
+
- 0
|
48
|
+
version: "0"
|
49
|
+
type: :development
|
50
|
+
version_requirements: *id002
|
51
|
+
description: Music Player Daemon client in Ruby
|
52
|
+
email:
|
53
|
+
- ericw@xmtp.net
|
54
|
+
executables: []
|
55
|
+
|
56
|
+
extensions: []
|
57
|
+
|
58
|
+
extra_rdoc_files: []
|
59
|
+
|
60
|
+
files:
|
61
|
+
- .gitignore
|
62
|
+
- .rspec
|
63
|
+
- Gemfile
|
64
|
+
- Rakefile
|
65
|
+
- lib/rmpd.rb
|
66
|
+
- lib/rmpd/commands.rb
|
67
|
+
- lib/rmpd/commands/admin.rb
|
68
|
+
- lib/rmpd/commands/database.rb
|
69
|
+
- lib/rmpd/commands/generators.rb
|
70
|
+
- lib/rmpd/commands/miscellaneous.rb
|
71
|
+
- lib/rmpd/commands/playback.rb
|
72
|
+
- lib/rmpd/commands/playlist.rb
|
73
|
+
- lib/rmpd/config.rb
|
74
|
+
- lib/rmpd/connection.rb
|
75
|
+
- lib/rmpd/multi_response.rb
|
76
|
+
- lib/rmpd/response.rb
|
77
|
+
- lib/rmpd/version.rb
|
78
|
+
- rmpd.gemspec
|
79
|
+
- spec/fixtures/config.yml
|
80
|
+
- spec/fixtures/config_no_hostname.yml
|
81
|
+
- spec/fixtures/config_no_password.yml
|
82
|
+
- spec/fixtures/config_no_port.yml
|
83
|
+
- spec/fixtures/config_rails.yml
|
84
|
+
- spec/models/commands_spec.rb
|
85
|
+
- spec/models/config_spec.rb
|
86
|
+
- spec/models/connection_spec.rb
|
87
|
+
- spec/models/generators_spec.rb
|
88
|
+
- spec/models/mildred_mpd_spec.rb
|
89
|
+
- spec/models/multi_response_spec.rb
|
90
|
+
- spec/models/response_spec.rb
|
91
|
+
- spec/spec.opts
|
92
|
+
- spec/spec_helper.rb
|
93
|
+
- spec/spec_rcov.opts
|
94
|
+
has_rdoc: true
|
95
|
+
homepage: ""
|
96
|
+
licenses: []
|
97
|
+
|
98
|
+
post_install_message:
|
99
|
+
rdoc_options: []
|
100
|
+
|
101
|
+
require_paths:
|
102
|
+
- lib
|
103
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
104
|
+
none: false
|
105
|
+
requirements:
|
106
|
+
- - ">="
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
hash: 3
|
109
|
+
segments:
|
110
|
+
- 0
|
111
|
+
version: "0"
|
112
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
hash: 3
|
118
|
+
segments:
|
119
|
+
- 0
|
120
|
+
version: "0"
|
121
|
+
requirements: []
|
122
|
+
|
123
|
+
rubyforge_project:
|
124
|
+
rubygems_version: 1.3.7
|
125
|
+
signing_key:
|
126
|
+
specification_version: 3
|
127
|
+
summary: Music Player Daemon client in Ruby
|
128
|
+
test_files: []
|
129
|
+
|