vlc-client 0.0.1 → 0.0.2

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e93182279e83b89bc91c7fd08478ad97dda296da
4
+ data.tar.gz: 5ad6b5688d2babebc662003918331874878405e8
5
+ SHA512:
6
+ metadata.gz: 06f1446c87b4cf6939bbc84a470881246803091f4af8f658c3f73a865828759caee8a988d43e1e3e159d603f052b08074dc63f1e3abe54eb6a88348a719c5bcf
7
+ data.tar.gz: 8f80b8fb1b534516441df59b3f10c3a502eafa0bc7c40377a1db6e721bdda04ffb2f49aa18067dae0605a6e9d0db30f2b967f656730181a054948265df7eddee
data/.gitignore CHANGED
@@ -19,3 +19,4 @@ tmp
19
19
  .sublime/
20
20
  Guardfile
21
21
  trials
22
+ .ruby-*
@@ -1,10 +1,13 @@
1
1
  language: ruby
2
2
  script: bundle exec rake --verbose --trace
3
+ bundler_args: --without development
3
4
  matrix:
4
5
  allow_failures:
5
6
  - rvm: ruby-head
6
7
  rvm:
8
+ - jruby-19mode
7
9
  - 1.8.7
8
10
  - 1.9.2
9
11
  - 1.9.3
12
+ - 2.0.0
10
13
  - ruby-head
data/Gemfile CHANGED
@@ -1,4 +1,20 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
+ gem 'rake'
4
+
5
+ group :development do
6
+ gem 'guard-rspec', '~> 2.5.1'
7
+ gem 'redcarpet'
8
+ gem 'yard'
9
+ gem 'pry'
10
+ end
11
+
12
+ group :test do
13
+ gem 'mime-types', '< 2.0.0'
14
+ gem 'rspec', '~> 2.14.1', :require => false
15
+ gem 'simplecov', :require => false
16
+ gem 'coveralls', :require => false
17
+ end
18
+
3
19
  # Specify your gem's dependencies in vlc.gemspec
4
20
  gemspec
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # vlc-client [![Build Status](https://secure.travis-ci.org/mguinada/vlc-client.png?branch=master)](http://travis-ci.org/mguinada/vlc-client)
2
2
 
3
- vlc-client manages a [VLC media player](http://www.videolan.org/vlc/) through it's RC interface.
3
+ vlc-client manages a [VLC media player](http://www.videolan.org/vlc/) instance through it's RC interface.
4
4
 
5
5
  ## Installation
6
6
 
@@ -23,17 +23,20 @@ Or install it yourself as:
23
23
  ```ruby
24
24
 
25
25
  #Expects a VLC media player running on 192.168.1.10:9999
26
- #e.g. vlc --extraintf rc --rc-host 192.168.1.10:9999"
26
+ #e.g. vlc --extraintf rc --rc-host 192.168.1.10:9999
27
27
 
28
28
  vlc = VLC::Client.new('192.168.1.10', 9999)
29
29
  # => "#<VLC::Client:0x0000000229c370 @server=#<VLC::Server:0x0000000229c6e0 @headless=false, @port=9999, @host=\"192.168.1.10\", @pid=10514>, @self_managed=true, @connection=#<VLC::Connection:0x0000000229c258 @port=9999, @host=\"192.168.1.10\", @socket=#<TCPSocket:fd 5>>>"
30
30
 
31
31
  vlc.connect
32
32
  # => true
33
+
33
34
  vlc.play('http://example.org/media.mp3') #play media
34
35
  # => true
36
+
35
37
  vlc.playing?
36
38
  # => true
39
+
37
40
  vlc.fullscreen
38
41
  # => true
39
42
  #...
@@ -41,13 +44,12 @@ vlc.fullscreen
41
44
  ```
42
45
 
43
46
  ### Create a self managed client/server system.
44
- _Most of the time we want a local client/server VLC media player system_
47
+ Most of the time we want a local client/server VLC media player system
45
48
 
46
- _(NOTE: requires a local installation of VLC media player)_
47
49
 
48
50
  ```ruby
49
51
 
50
- vlc = VLC::System.new #A local VLC client/server system where a local VLC server is automaticaly managed
52
+ vlc = VLC::System.new #A local VLC client/server system where a local VLC installation is automaticaly managed
51
53
  # => "#<VLC::System:0x00000001bbb1a0 @client=#<VLC::Client:0x00000001bbade0 @server=#<VLC::Server:0x00000001bbb178 @headless=false, @port=9595, @host=\"localhost\", @pid=11225>, @connection=#<VLC::Connection:0x00000001bbacc8 @port=9595, @host=\"localhost\", @socket=#<TCPSocket:fd 5>>>>"
52
54
 
53
55
  vlc.connected? #auto_connect
@@ -66,7 +68,6 @@ vlc = VLC::Client.new(VLC::Server.new('localhost', 9595, false))
66
68
  ```
67
69
 
68
70
  ###Get local VLC server lifecycle management control
69
- _(NOTE: requires a local installation of VLC media player)_
70
71
 
71
72
  ```ruby
72
73
 
@@ -75,8 +76,10 @@ vlc = VLC::System.new('127.0.0.1', 9999, auto_start: false)
75
76
 
76
77
  vlc.server.running?
77
78
  # => false
79
+
78
80
  vlc.server.start
79
81
  # => 12672
82
+
80
83
  vlc.connect
81
84
  # => true
82
85
  ```
@@ -85,10 +88,17 @@ vlc.connect
85
88
 
86
89
  [reference](http://rdoc.info/github/mguinada/vlc-client)
87
90
 
88
- ## Support
91
+ ## Notice
92
+
93
+ vlc-client has been tested on linux but it should work on any VLC installation as long and the command line is responsive for `vlc` and `cvlc` calls. On Mac OSX these are not available by default. They can be created with:
94
+
95
+ ```
96
+ ln -s /Applications/VLC.app/Contents/MacOS/VLC /usr/local/bin/vlc
97
+ ln -s /Applications/VLC.app/Contents/MacOS/VLC /usr/local/bin/cvlc
98
+ ```
89
99
 
90
- vlc-client has been tested on linux only.
91
- If you need support for other OS a pull request is welcome.
100
+ VLC media player 2.0.3 seems to [ship with a bug](http://www.linuxquestions.org/questions/slackware-14/problem-vlc-2-0-3-playing-youtube-videos-4175429135) that invalidates YouTube streaming.
101
+ [Please use VLC 2.0.4 or later](http://www.videolan.org/). The last tested version is VLC media player 2.1.2 Rincewind
92
102
 
93
103
  ## Contributing
94
104
 
@@ -108,3 +118,4 @@ Copyright (c) 2012 Miguel Guinada
108
118
  [LICENSE][] for details.
109
119
 
110
120
  [license]: https://github.com/mguinada/vlc-client/blob/master/LICENSE
121
+ [![githalytics.com alpha](https://cruel-carlota.pagodabox.com/8b1a3dc0229bef37fd7746bb62d29997 "githalytics.com")](http://githalytics.com/mguinada/vlc-client)
data/Rakefile CHANGED
@@ -1,7 +1,6 @@
1
1
  #!/usr/bin/env rake
2
2
  require 'bundler/gem_tasks'
3
3
  require 'rspec/core/rake_task'
4
- require 'yard'
5
4
 
6
5
  task :test => :spec
7
6
  task :default => :spec
@@ -21,10 +20,16 @@ task :console do
21
20
  end
22
21
 
23
22
  namespace :doc do
24
- desc "Generate documentation"
25
- YARD::Rake::YardocTask.new do |t|
26
- t.files = ['lib/**/*.rb']
27
- t.options = ['--no-private', '--protected', '--markup', 'markdown']
23
+ begin
24
+ require 'yard'
25
+
26
+ desc "Generate documentation"
27
+ YARD::Rake::YardocTask.new do |t|
28
+ t.files = ['lib/**/*.rb']
29
+ t.options = ['--no-private', '--protected', '--markup', 'markdown']
30
+ end
31
+ rescue LoadError
32
+ puts "yard not available"
28
33
  end
29
34
  end
30
35
 
@@ -10,6 +10,7 @@ require 'vlc-client/connection'
10
10
  require 'vlc-client/errors'
11
11
 
12
12
  require 'vlc-client/client/media_controls'
13
+ require 'vlc-client/client/playlist_controls'
13
14
  require 'vlc-client/client/video_controls'
14
15
  require 'vlc-client/client/connection_management'
15
16
 
@@ -19,6 +20,7 @@ module VLC
19
20
  # The VLC client
20
21
  class Client
21
22
  include VLC::Client::MediaControls
23
+ include VLC::Client::PlaylistControls
22
24
  include VLC::Client::VideoControls
23
25
  include VLC::Client::ConnectionManagement
24
26
 
@@ -67,9 +69,9 @@ module VLC
67
69
  bind_server(server, options) unless server.nil?
68
70
  end
69
71
 
70
- private
71
72
  attr_reader :connection
72
73
 
74
+ private
73
75
  def bind_server(server, options = {})
74
76
  @connection.host = server.host
75
77
  @connection.port = server.port
@@ -97,5 +99,16 @@ module VLC
97
99
  end
98
100
  args
99
101
  end
102
+
103
+ def media_arg(media)
104
+ case media
105
+ when File
106
+ media.path
107
+ when String, URI
108
+ media
109
+ else
110
+ raise ArgumentError, "Can not play: #{media}"
111
+ end
112
+ end
100
113
  end
101
- end
114
+ end
@@ -99,18 +99,6 @@ module VLC
99
99
  def volume=(level)
100
100
  volume(level)
101
101
  end
102
-
103
- private
104
- def media_arg(media)
105
- case media
106
- when File
107
- media.path
108
- when String, URI
109
- media
110
- else
111
- raise ArgumentError, "Can play: #{media}"
112
- end
113
- end
114
102
  end
115
103
  end
116
- end
104
+ end
@@ -0,0 +1,55 @@
1
+ module VLC
2
+ class Client
3
+ module PlaylistControls
4
+ # @private
5
+ PLAYLIST_TERMINATOR = "+----[ End of playlist ]"
6
+
7
+ # Adds media to the playlist
8
+ #
9
+ # @param media [String, File, URI] the media to be played
10
+ #
11
+ def add_to_playlist(media)
12
+ connection.write("enqueue #{media_arg(media)}")
13
+ end
14
+
15
+ # Lists tracks on the playlist
16
+ def playlist
17
+ connection.write("playlist")
18
+
19
+ list = []
20
+ begin
21
+ list << connection.read
22
+ end while list.last != PLAYLIST_TERMINATOR
23
+ process_raw_playlist(list)
24
+ end
25
+
26
+ # Plays the next element on the playlist
27
+ def next
28
+ connection.write("next")
29
+ end
30
+
31
+ # Plays the previous element on the playlist
32
+ def previous
33
+ connection.write("prev")
34
+ end
35
+
36
+ # Clears the playlist
37
+ def clear
38
+ connection.write("clear")
39
+ end
40
+ private
41
+ def process_raw_playlist(list)
42
+ list.map do |i|
43
+ match = i.match(/^\|[\s]*(\d{1,2})\s-\s(.+)\((\d\d:\d\d:\d\d)\)(\s\[played\s(\d+)\stime[s]?\])?/)
44
+
45
+ next if match.nil?
46
+
47
+ {:number => match[1].to_i,
48
+ :title => match[2].strip,
49
+ :length => match[3],
50
+ :times_played => match[5].to_i}
51
+ end.compact
52
+ end
53
+ end
54
+ end
55
+ end
@@ -1,3 +1,3 @@
1
1
  module VLC
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -1,7 +1,12 @@
1
1
  require 'simplecov'
2
- require 'pry'
2
+ require 'coveralls'
3
+
4
+ # SimpleCov & Coveralls setup
5
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
6
+ SimpleCov::Formatter::HTMLFormatter,
7
+ Coveralls::SimpleCov::Formatter
8
+ ]
3
9
 
4
- #setup simplecov
5
10
  SimpleCov.start do
6
11
  add_filter "/spec"
7
12
  end
@@ -13,8 +18,8 @@ module Mocks
13
18
  tcp = double()
14
19
  TCPSocket.stub(:new).and_return do
15
20
  if opts.fetch(:defaults, true)
21
+ tcp.stub(:flush)
16
22
  tcp.should_receive(:gets).with(no_args).at_least(:twice).and_return("")
17
- tcp.should_receive(:flush).with(no_args).any_number_of_times
18
23
  tcp.should_receive(:close).with(no_args) if opts.fetch(:close, true)
19
24
  end
20
25
 
@@ -37,8 +42,8 @@ module Mocks
37
42
  end
38
43
  Process.should_receive(:pid).once.and_return(99)
39
44
 
40
- rd = stub('rd', :close => true)
41
- wd = stub('wd', :write => true, :close => true)
45
+ rd = double('rd', :close => true)
46
+ wd = double('wd', :write => true, :close => true)
42
47
  IO.stub(:pipe).and_return([rd, wd])
43
48
 
44
49
  [STDIN, STDOUT, STDERR].each { |std| std.stub(:reopen) }
@@ -47,8 +52,8 @@ module Mocks
47
52
  else
48
53
  Process.stub(:fork).and_return(true)
49
54
 
50
- rd = stub('rd', :read => 99, :close => true)
51
- wd = stub('wd', :close => true)
55
+ rd = double('rd', :read => 99, :close => true)
56
+ wd = double('wd', :close => true)
52
57
  IO.stub(:pipe).and_return([rd, wd])
53
58
 
54
59
  Process.should_receive(:kill).once.with('INT', 99) if opts.fetch(:kill, true)
@@ -59,6 +64,14 @@ module Mocks
59
64
  mock_system_calls
60
65
  mock_tcp_server
61
66
  end
67
+
68
+ def mock_file(filename)
69
+ File.stub(:open).and_return {
70
+ f = File.new('./LICENSE', 'r')
71
+ f.should_receive(:path).once.and_return(filename)
72
+ f
73
+ }
74
+ end
62
75
  end
63
76
 
64
77
  RSpec.configure do |cfg|
@@ -11,11 +11,7 @@ describe VLC::Client::MediaControls do
11
11
  end
12
12
 
13
13
  it 'from a file descriptor' do
14
- File.stub(:open).and_return {
15
- f = File.new('./LICENSE', 'r')
16
- f.should_receive(:path).once.and_return('./media.mp3')
17
- f
18
- }
14
+ mock_file('./media.mp3')
19
15
 
20
16
  mock_tcp_server.should_receive(:puts).once.with('add ./media.mp3')
21
17
  vlc.connect
@@ -42,7 +38,7 @@ describe VLC::Client::MediaControls do
42
38
  def tcp_mock
43
39
  tcp = mock_tcp_server(:defaults => false)
44
40
 
45
- tcp.should_receive(:flush).with(no_args).any_number_of_times
41
+ tcp.should_receive(:flush).with(no_args).at_least(1).times
46
42
  tcp.should_receive(:gets).with(no_args).twice.and_return("")
47
43
 
48
44
  tcp.should_receive(:puts).once.with('add http://example.org/media.mp3')
@@ -190,7 +186,7 @@ describe VLC::Client::MediaControls do
190
186
 
191
187
  it 'is aware of current status' do
192
188
  tcp = mock_tcp_server(:defaults => false)
193
- tcp.should_receive(:flush).with(no_args).any_number_of_times
189
+ tcp.should_receive(:flush).with(no_args).at_least(1).times
194
190
 
195
191
  tcp.should_receive(:gets).with(no_args).twice.and_return("")
196
192
 
@@ -0,0 +1,52 @@
1
+ describe VLC::Client::PlaylistControls do
2
+ let(:vlc) { VLC::Client.new(:self_managed => false) }
3
+ let!(:server) { mock_tcp_server }
4
+ after(:each) { vlc.disconnect }
5
+
6
+ it 'enqueues media' do
7
+ server.should_receive(:puts).once.with('enqueue media.mp3')
8
+ server.should_receive(:puts).once.with('enqueue media2.mp3')
9
+ vlc.connect
10
+
11
+ vlc.add_to_playlist("media.mp3")
12
+ vlc.add_to_playlist("media2.mp3")
13
+ end
14
+
15
+ context 'manages the playlist' do
16
+ it 'lists playlist contents' do
17
+ vlc.connect
18
+
19
+ vlc.connection.should_receive(:write).once.with("playlist")
20
+ vlc.connection.should_receive(:read).once.with(no_args).and_return("+----[ Playlist - Test ]")
21
+ vlc.connection.should_receive(:read).once.with(no_args).and_return("| 2 - Playlist")
22
+ vlc.connection.should_receive(:read).once.with(no_args).and_return("| 1 - Track 1 (00:01:30) [played 2 times]")
23
+ vlc.connection.should_receive(:read).once.with(no_args).and_return("| 2 - Track 2 (00:00:23)")
24
+ vlc.connection.should_receive(:read).once.with(no_args).and_return("| 3 - Media Library")
25
+ vlc.connection.should_receive(:read).once.with(no_args).and_return("+----[ End of playlist ]")
26
+
27
+ vlc.playlist.should eq([{:number => 1, :title => "Track 1", :length => "00:01:30", :times_played => 2},
28
+ {:number => 2, :title => "Track 2", :length => "00:00:23", :times_played => 0}])
29
+ end
30
+
31
+ it 'plays the next element on the playlist' do
32
+ vlc.connect
33
+
34
+ vlc.connection.should_receive(:write).once.with("next")
35
+ vlc.next
36
+ end
37
+
38
+ it 'plays the previous element on the playlist' do
39
+ vlc.connect
40
+
41
+ vlc.connection.should_receive(:write).once.with("prev")
42
+ vlc.previous
43
+ end
44
+
45
+ it 'clear the playlist' do
46
+ vlc.connect
47
+
48
+ vlc.connection.should_receive(:write).once.with("clear")
49
+ vlc.clear
50
+ end
51
+ end
52
+ end
@@ -16,14 +16,5 @@ Gem::Specification.new do |gem|
16
16
  gem.version = VLC::VERSION
17
17
  gem.date = Time.now.utc.strftime("%Y-%m-%d")
18
18
 
19
- #package
20
- gem.add_dependency 'retryable', '~> 1.3'
21
-
22
- #development
23
- gem.add_development_dependency 'rake'
24
- gem.add_development_dependency 'rspec'
25
- gem.add_development_dependency 'simplecov'
26
- gem.add_development_dependency 'pry'
27
- gem.add_development_dependency 'yard'
28
- gem.add_development_dependency 'maruku'
19
+ gem.add_dependency 'retryable', '~> 1.3'
29
20
  end
metadata CHANGED
@@ -1,93 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vlc-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
5
- prerelease:
4
+ version: 0.0.2
6
5
  platform: ruby
7
6
  authors:
8
7
  - Miguel Guinada
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2012-11-01 00:00:00.000000000 Z
11
+ date: 2013-12-15 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: retryable
16
- requirement: &22175840 !ruby/object:Gem::Requirement
17
- none: false
15
+ requirement: !ruby/object:Gem::Requirement
18
16
  requirements:
19
17
  - - ~>
20
18
  - !ruby/object:Gem::Version
21
19
  version: '1.3'
22
20
  type: :runtime
23
21
  prerelease: false
24
- version_requirements: *22175840
25
- - !ruby/object:Gem::Dependency
26
- name: rake
27
- requirement: &22174780 !ruby/object:Gem::Requirement
28
- none: false
29
- requirements:
30
- - - ! '>='
31
- - !ruby/object:Gem::Version
32
- version: '0'
33
- type: :development
34
- prerelease: false
35
- version_requirements: *22174780
36
- - !ruby/object:Gem::Dependency
37
- name: rspec
38
- requirement: &22174160 !ruby/object:Gem::Requirement
39
- none: false
40
- requirements:
41
- - - ! '>='
42
- - !ruby/object:Gem::Version
43
- version: '0'
44
- type: :development
45
- prerelease: false
46
- version_requirements: *22174160
47
- - !ruby/object:Gem::Dependency
48
- name: simplecov
49
- requirement: &22173600 !ruby/object:Gem::Requirement
50
- none: false
51
- requirements:
52
- - - ! '>='
53
- - !ruby/object:Gem::Version
54
- version: '0'
55
- type: :development
56
- prerelease: false
57
- version_requirements: *22173600
58
- - !ruby/object:Gem::Dependency
59
- name: pry
60
- requirement: &22172800 !ruby/object:Gem::Requirement
61
- none: false
62
- requirements:
63
- - - ! '>='
64
- - !ruby/object:Gem::Version
65
- version: '0'
66
- type: :development
67
- prerelease: false
68
- version_requirements: *22172800
69
- - !ruby/object:Gem::Dependency
70
- name: yard
71
- requirement: &22172160 !ruby/object:Gem::Requirement
72
- none: false
73
- requirements:
74
- - - ! '>='
75
- - !ruby/object:Gem::Version
76
- version: '0'
77
- type: :development
78
- prerelease: false
79
- version_requirements: *22172160
80
- - !ruby/object:Gem::Dependency
81
- name: maruku
82
- requirement: &22171740 !ruby/object:Gem::Requirement
83
- none: false
22
+ version_requirements: !ruby/object:Gem::Requirement
84
23
  requirements:
85
- - - ! '>='
24
+ - - ~>
86
25
  - !ruby/object:Gem::Version
87
- version: '0'
88
- type: :development
89
- prerelease: false
90
- version_requirements: *22171740
26
+ version: '1.3'
91
27
  description: vlc-client allows to control VLC media player over TCP
92
28
  email:
93
29
  - mguinada@gmail.com
@@ -106,6 +42,7 @@ files:
106
42
  - lib/vlc-client.rb
107
43
  - lib/vlc-client/client/connection_management.rb
108
44
  - lib/vlc-client/client/media_controls.rb
45
+ - lib/vlc-client/client/playlist_controls.rb
109
46
  - lib/vlc-client/client/video_controls.rb
110
47
  - lib/vlc-client/connection.rb
111
48
  - lib/vlc-client/core_ext/array.rb
@@ -118,6 +55,7 @@ files:
118
55
  - spec/system_spec.rb
119
56
  - spec/vlc-client/client/connection_management_spec.rb
120
57
  - spec/vlc-client/client/media_controls_spec.rb
58
+ - spec/vlc-client/client/playlist_controls_spec.rb
121
59
  - spec/vlc-client/client/video_controls_spec.rb
122
60
  - spec/vlc-client/connection_spec.rb
123
61
  - spec/vlc-client/server_spec.rb
@@ -125,33 +63,33 @@ files:
125
63
  - vlc-client.gemspec
126
64
  homepage: https://github.com/mguinada/vlc-client
127
65
  licenses: []
66
+ metadata: {}
128
67
  post_install_message:
129
68
  rdoc_options: []
130
69
  require_paths:
131
70
  - lib
132
71
  required_ruby_version: !ruby/object:Gem::Requirement
133
- none: false
134
72
  requirements:
135
- - - ! '>='
73
+ - - '>='
136
74
  - !ruby/object:Gem::Version
137
75
  version: '0'
138
76
  required_rubygems_version: !ruby/object:Gem::Requirement
139
- none: false
140
77
  requirements:
141
- - - ! '>='
78
+ - - '>='
142
79
  - !ruby/object:Gem::Version
143
80
  version: '0'
144
81
  requirements: []
145
82
  rubyforge_project:
146
- rubygems_version: 1.8.15
83
+ rubygems_version: 2.1.11
147
84
  signing_key:
148
- specification_version: 3
85
+ specification_version: 4
149
86
  summary: vlc-client is a TCP client for VLC media player
150
87
  test_files:
151
88
  - spec/helper.rb
152
89
  - spec/system_spec.rb
153
90
  - spec/vlc-client/client/connection_management_spec.rb
154
91
  - spec/vlc-client/client/media_controls_spec.rb
92
+ - spec/vlc-client/client/playlist_controls_spec.rb
155
93
  - spec/vlc-client/client/video_controls_spec.rb
156
94
  - spec/vlc-client/connection_spec.rb
157
95
  - spec/vlc-client/server_spec.rb