tty-process-ctl 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +39 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +63 -0
- data/Rakefile +49 -0
- data/VERSION +1 -0
- data/examples/irb.rb +22 -0
- data/examples/ls.rb +7 -0
- data/lib/tty-process-ctl.rb +106 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/stub +114 -0
- data/spec/tty-process-ctl_spec.rb +159 -0
- data/tty-process-ctl.gemspec +69 -0
- metadata +162 -0
data/.document
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
cli (1.1.1)
|
5
|
+
diff-lcs (1.1.3)
|
6
|
+
git (1.2.5)
|
7
|
+
jeweler (1.8.4)
|
8
|
+
bundler (~> 1.0)
|
9
|
+
git (>= 1.2.5)
|
10
|
+
rake
|
11
|
+
rdoc
|
12
|
+
json (1.7.5)
|
13
|
+
multi_json (1.3.6)
|
14
|
+
rake (0.9.2.2)
|
15
|
+
rdoc (3.12)
|
16
|
+
json (~> 1.4)
|
17
|
+
rspec (2.11.0)
|
18
|
+
rspec-core (~> 2.11.0)
|
19
|
+
rspec-expectations (~> 2.11.0)
|
20
|
+
rspec-mocks (~> 2.11.0)
|
21
|
+
rspec-core (2.11.1)
|
22
|
+
rspec-expectations (2.11.2)
|
23
|
+
diff-lcs (~> 1.1.3)
|
24
|
+
rspec-mocks (2.11.2)
|
25
|
+
simplecov (0.6.4)
|
26
|
+
multi_json (~> 1.0)
|
27
|
+
simplecov-html (~> 0.5.3)
|
28
|
+
simplecov-html (0.5.3)
|
29
|
+
|
30
|
+
PLATFORMS
|
31
|
+
ruby
|
32
|
+
|
33
|
+
DEPENDENCIES
|
34
|
+
bundler (~> 1.1)
|
35
|
+
cli (~> 1.0)
|
36
|
+
jeweler (~> 1.8)
|
37
|
+
rdoc (~> 3.12)
|
38
|
+
rspec (~> 2.8)
|
39
|
+
simplecov
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Jakub Pastuszek
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
= tty-process-ctl
|
2
|
+
|
3
|
+
This gem was created to enable control of interactive terminal applications.
|
4
|
+
It is using pseudo tty to communicate with the process via simple API.
|
5
|
+
|
6
|
+
== Example
|
7
|
+
|
8
|
+
=== Reading program output
|
9
|
+
|
10
|
+
Here we run 'ls' command and iterate its output:
|
11
|
+
|
12
|
+
TTYProcessCtl.new('ls').each do |line|
|
13
|
+
puts line
|
14
|
+
end
|
15
|
+
|
16
|
+
Result:
|
17
|
+
|
18
|
+
Gemfile LICENSE.txt Rakefile lib
|
19
|
+
Gemfile.lock README.rdoc examples spec
|
20
|
+
|
21
|
+
=== irb example
|
22
|
+
|
23
|
+
This example show how to send command to irb process.
|
24
|
+
Output can be skipped with wait_until and iterated until pattern matches wit each_until.
|
25
|
+
|
26
|
+
irb = TTYProcessCtl.new('irb')
|
27
|
+
|
28
|
+
# send command
|
29
|
+
irb.send_command('2 + 2')
|
30
|
+
|
31
|
+
# wait until prompt line was printed
|
32
|
+
irb.wait_until(/:001 >/)
|
33
|
+
|
34
|
+
# print all output lines until we get to the result line (including)
|
35
|
+
irb.each_until(/=>/) do |line|
|
36
|
+
puts line
|
37
|
+
end
|
38
|
+
|
39
|
+
# ask irb to quit
|
40
|
+
irb.send_command('quit')
|
41
|
+
|
42
|
+
# wait irb to exit
|
43
|
+
irb.wait_exit
|
44
|
+
|
45
|
+
Result:
|
46
|
+
|
47
|
+
=> 4
|
48
|
+
|
49
|
+
== Contributing to tty-process-ctl
|
50
|
+
|
51
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
52
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
53
|
+
* Fork the project.
|
54
|
+
* Start a feature/bugfix branch.
|
55
|
+
* Commit and push until you are happy with your contribution.
|
56
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
57
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
58
|
+
|
59
|
+
== Copyright
|
60
|
+
|
61
|
+
Copyright (c) 2012 Jakub Pastuszek. See LICENSE.txt for
|
62
|
+
further details.
|
63
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
17
|
+
gem.name = "tty-process-ctl"
|
18
|
+
gem.homepage = "http://github.com/jpastuszek/tty-process-ctl"
|
19
|
+
gem.license = "MIT"
|
20
|
+
gem.summary = %Q{Control of interactive terminal applications via simple API.}
|
21
|
+
gem.description = %Q{This gem was created to enable control of interactive terminal applications. It is using pseudo tty to communicate with the process via simple API.}
|
22
|
+
gem.email = "jpastuszek@gmail.com"
|
23
|
+
gem.authors = ["Jakub Pastuszek"]
|
24
|
+
# dependencies defined in Gemfile
|
25
|
+
end
|
26
|
+
Jeweler::RubygemsDotOrgTasks.new
|
27
|
+
|
28
|
+
require 'rspec/core'
|
29
|
+
require 'rspec/core/rake_task'
|
30
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
31
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
32
|
+
end
|
33
|
+
|
34
|
+
RSpec::Core::RakeTask.new(:rcov) do |spec|
|
35
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
36
|
+
spec.rcov = true
|
37
|
+
end
|
38
|
+
|
39
|
+
task :default => :spec
|
40
|
+
|
41
|
+
require 'rdoc/task'
|
42
|
+
Rake::RDocTask.new do |rdoc|
|
43
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
44
|
+
|
45
|
+
rdoc.rdoc_dir = 'rdoc'
|
46
|
+
rdoc.title = "tty-process-ctl #{version}"
|
47
|
+
rdoc.rdoc_files.include('README*')
|
48
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
49
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.1
|
data/examples/irb.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require_relative '../lib/tty-process-ctl'
|
3
|
+
|
4
|
+
irb = TTYProcessCtl.new('irb')
|
5
|
+
|
6
|
+
# send command
|
7
|
+
irb.send_command('2 + 2')
|
8
|
+
|
9
|
+
# wait until prompt line was printed
|
10
|
+
irb.wait_until(/:001 >/)
|
11
|
+
|
12
|
+
# print all output lines until we get to the result line (including)
|
13
|
+
irb.each_until(/=>/) do |line|
|
14
|
+
puts line
|
15
|
+
end
|
16
|
+
|
17
|
+
# ask irb to quit
|
18
|
+
irb.send_command('quit')
|
19
|
+
|
20
|
+
# wait irb to exit
|
21
|
+
irb.wait_exit
|
22
|
+
|
data/examples/ls.rb
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'thread'
|
2
|
+
require 'pty'
|
3
|
+
|
4
|
+
class TTYProcessCtl
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
def initialize(command, options = {})
|
8
|
+
@max_queue_length = options[:max_queue_length] || 4000
|
9
|
+
@max_messages = options[:max_messages] || 4000
|
10
|
+
@command = command
|
11
|
+
|
12
|
+
@out_queue = Queue.new
|
13
|
+
@messages = []
|
14
|
+
|
15
|
+
@r, @w, @pid = PTY.spawn(@command)
|
16
|
+
@thread = Thread.start do
|
17
|
+
begin
|
18
|
+
abort_on_exception = true
|
19
|
+
@r.each_line do |line|
|
20
|
+
enqueue_message line
|
21
|
+
end
|
22
|
+
rescue Errno::EIO
|
23
|
+
ensure
|
24
|
+
@exit_status = PTY.check(@pid)
|
25
|
+
@r.close
|
26
|
+
@w.close
|
27
|
+
enqueue_end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
attr_reader :exit_status
|
33
|
+
|
34
|
+
def alive?
|
35
|
+
@thread.alive?
|
36
|
+
end
|
37
|
+
|
38
|
+
def send_command(command)
|
39
|
+
@w.puts command
|
40
|
+
rescue Errno::EIO
|
41
|
+
raise IOError.new("process '#{@command}' (pid: #{@pid}) not accepting input")
|
42
|
+
end
|
43
|
+
|
44
|
+
def messages
|
45
|
+
@messages
|
46
|
+
end
|
47
|
+
|
48
|
+
def each
|
49
|
+
return enum_for(:each) unless block_given?
|
50
|
+
while !@out_queue.empty? or alive? do
|
51
|
+
yield (dequeue or break)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def each_until(pattern)
|
56
|
+
return enum_for(:each_until, pattern) unless block_given?
|
57
|
+
each do |message|
|
58
|
+
yield message
|
59
|
+
break if message =~ pattern
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def each_until_exclude(pattern)
|
64
|
+
return enum_for(:each_until_exclude, pattern) unless block_given?
|
65
|
+
each do |message|
|
66
|
+
break if message =~ pattern
|
67
|
+
yield message
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def wait_exit
|
72
|
+
each{}
|
73
|
+
@thread.join
|
74
|
+
end
|
75
|
+
|
76
|
+
def wait_until(pattern)
|
77
|
+
each_until(pattern){}
|
78
|
+
end
|
79
|
+
|
80
|
+
def flush
|
81
|
+
loop do
|
82
|
+
dequeue(true)
|
83
|
+
end
|
84
|
+
rescue ThreadError
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def dequeue(block = false)
|
90
|
+
message = @out_queue.pop(block)
|
91
|
+
return nil unless message
|
92
|
+
@messages << message
|
93
|
+
@messages.pop while @messages.length > @max_messages
|
94
|
+
message
|
95
|
+
end
|
96
|
+
|
97
|
+
def enqueue_message(message)
|
98
|
+
@out_queue << message
|
99
|
+
@out_queue.pop while @out_queue.length > @max_queue_length
|
100
|
+
end
|
101
|
+
|
102
|
+
def enqueue_end
|
103
|
+
@out_queue << nil
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
+
require 'rspec'
|
4
|
+
require 'tty-process-ctl'
|
5
|
+
|
6
|
+
# Requires supporting files with custom matchers and macros, etc,
|
7
|
+
# in ./support/ and its subdirectories.
|
8
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
9
|
+
|
10
|
+
RSpec.configure do |config|
|
11
|
+
|
12
|
+
end
|
data/spec/stub
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'cli'
|
4
|
+
|
5
|
+
options = CLI.new do
|
6
|
+
option :delay,
|
7
|
+
cast: Float
|
8
|
+
switch :exit
|
9
|
+
end.parse!
|
10
|
+
|
11
|
+
$delay = options.dealy
|
12
|
+
|
13
|
+
def out(line)
|
14
|
+
STDOUT << line + "\n"
|
15
|
+
STDOUT.flush
|
16
|
+
sleep $delay.to_f if $delay
|
17
|
+
end
|
18
|
+
|
19
|
+
def err(line)
|
20
|
+
STDERR << line + "\n"
|
21
|
+
STDERR.flush
|
22
|
+
sleep $delay.to_f if $delay
|
23
|
+
end
|
24
|
+
|
25
|
+
$expects = {}
|
26
|
+
|
27
|
+
def expect(command, &response)
|
28
|
+
$expects[command] = response
|
29
|
+
end
|
30
|
+
|
31
|
+
out '151 recipes'
|
32
|
+
out '16 achievements'
|
33
|
+
err '2011-09-10 12:58:55 [INFO] Starting minecraft server version Beta 1.7.3'
|
34
|
+
err '2011-09-10 12:58:55 [WARNING] **** NOT ENOUGH RAM!'
|
35
|
+
err '2011-09-10 12:58:55 [WARNING] To start the server with more ram, launch it as "java -Xmx1024M -Xms1024M -jar minecraft_server.jar"'
|
36
|
+
err '2011-09-10 12:58:55 [INFO] Loading properties'
|
37
|
+
err '2011-09-10 12:58:55 [INFO] Starting Minecraft server on *:25565'
|
38
|
+
err '2011-09-10 12:58:55 [WARNING] **** SERVER IS RUNNING IN OFFLINE/INSECURE MODE!'
|
39
|
+
err '2011-09-10 12:58:55 [WARNING] The server will make no attempt to authenticate usernames. Beware.'
|
40
|
+
err '2011-09-10 12:58:55 [WARNING] While this makes the game possible to play without internet access, it also opens up the ability for hackers to connect with any username they choose.'
|
41
|
+
err '2011-09-10 12:58:55 [WARNING] To change this, set "online-mode" to "true" in the server.settings file.'
|
42
|
+
err '2011-09-10 12:58:55 [INFO] Preparing level "world"'
|
43
|
+
err '2011-09-10 12:58:55 [INFO] Preparing start region for level 0'
|
44
|
+
err '2011-09-10 12:58:56 [INFO] Preparing spawn area: 32%'
|
45
|
+
err '2011-09-10 12:58:57 [INFO] Preparing spawn area: 77%'
|
46
|
+
err '2011-09-10 12:58:58 [INFO] Preparing start region for level 1'
|
47
|
+
err '2011-09-10 12:58:59 [INFO] Preparing spawn area: 4%'
|
48
|
+
err '2011-09-10 12:59:00 [INFO] Preparing spawn area: 52%'
|
49
|
+
err '2011-09-10 12:59:01 [INFO] Preparing spawn area: 97%'
|
50
|
+
err '2011-09-10 12:59:01 [INFO] Done (5887241893ns)! For help, type "help" or "?"'
|
51
|
+
|
52
|
+
expect 'list' do
|
53
|
+
err '2011-09-20 14:42:04 [INFO] Connected players: kazuya'
|
54
|
+
end
|
55
|
+
|
56
|
+
expect 'stop' do
|
57
|
+
err '2011-09-19 22:12:00 [INFO] Stopping server'
|
58
|
+
err '2011-09-19 22:12:00 [INFO] Saving chunks'
|
59
|
+
err '2011-09-19 22:12:00 [INFO] Saving chunks'
|
60
|
+
exit
|
61
|
+
end
|
62
|
+
|
63
|
+
expect 'help' do
|
64
|
+
err '2011-09-21 13:33:46 [INFO] To run the server without a gui, start it like this:'
|
65
|
+
err '2011-09-21 13:33:46 [INFO] java -Xmx1024M -Xms1024M -jar minecraft_server.jar nogui'
|
66
|
+
err '2011-09-21 13:33:46 [INFO] Console commands:'
|
67
|
+
err '2011-09-21 13:33:46 [INFO] help or ? shows this message'
|
68
|
+
err '2011-09-21 13:33:46 [INFO] kick <player> removes a player from the server'
|
69
|
+
err '2011-09-21 13:33:46 [INFO] ban <player> bans a player from the server'
|
70
|
+
err '2011-09-21 13:33:46 [INFO] pardon <player> pardons a banned player so that they can connect again'
|
71
|
+
err '2011-09-21 13:33:46 [INFO] ban-ip <ip> bans an IP address from the server'
|
72
|
+
err '2011-09-21 13:33:46 [INFO] pardon-ip <ip> pardons a banned IP address so that they can connect again'
|
73
|
+
err '2011-09-21 13:33:46 [INFO] op <player> turns a player into an op'
|
74
|
+
err '2011-09-21 13:33:46 [INFO] deop <player> removes op status from a player'
|
75
|
+
err '2011-09-21 13:33:46 [INFO] tp <player1> <player2> moves one player to the same location as another player'
|
76
|
+
err '2011-09-21 13:33:46 [INFO] give <player> <id> [num] gives a player a resource'
|
77
|
+
err '2011-09-21 13:33:46 [INFO] tell <player> <message> sends a private message to a player'
|
78
|
+
err '2011-09-21 13:33:46 [INFO] stop gracefully stops the server'
|
79
|
+
err '2011-09-21 13:33:46 [INFO] save-all forces a server-wide level save'
|
80
|
+
err '2011-09-21 13:33:46 [INFO] save-off disables terrain saving (useful for backup scripts)'
|
81
|
+
err '2011-09-21 13:33:46 [INFO] save-on re-enables terrain saving'
|
82
|
+
err '2011-09-21 13:33:46 [INFO] list lists all currently connected players'
|
83
|
+
err '2011-09-21 13:33:46 [INFO] say <message> broadcasts a message to all players'
|
84
|
+
err '2011-09-21 13:33:46 [INFO] time <add|set> <amount> adds to or sets the world time (0-24000)'
|
85
|
+
err '2011-09-21 13:33:46 [INFO] gamemode <player> <mode> sets player\'s game mode (0 or 1)'
|
86
|
+
end
|
87
|
+
|
88
|
+
expect 'say' do |*args|
|
89
|
+
err "2011-09-21 14:01:18 [INFO] [CONSOLE] #{args.join(' ')}"
|
90
|
+
end
|
91
|
+
|
92
|
+
expect 'save-all' do
|
93
|
+
err '2011-09-21 14:02:22 [INFO] CONSOLE: Forcing save..'
|
94
|
+
err '2011-09-21 14:02:22 [INFO] CONSOLE: Save complete.'
|
95
|
+
end
|
96
|
+
|
97
|
+
expect 'stream' do
|
98
|
+
10.times do |n|
|
99
|
+
err "2011-09-21 14:02:#{n} [INFO] #{n}"
|
100
|
+
sleep 1
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
exit if options.exit
|
105
|
+
|
106
|
+
loop do
|
107
|
+
cmd, *args = STDIN.gets.split(' ')
|
108
|
+
if $expects.member? cmd
|
109
|
+
$expects[cmd].call(*args)
|
110
|
+
else
|
111
|
+
err '2011-09-20 14:42:52 [INFO] Unknown console command. Type "help" for help.'
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
@@ -0,0 +1,159 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
|
3
|
+
describe TTYProcessCtl do
|
4
|
+
subject do
|
5
|
+
TTYProcessCtl.new('spec/stub')
|
6
|
+
end
|
7
|
+
|
8
|
+
describe 'process output enumeration' do
|
9
|
+
subject do
|
10
|
+
TTYProcessCtl.new('spec/stub --exit')
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should allow iterating the output lines' do
|
14
|
+
lines_count = 0
|
15
|
+
subject.each do |line|
|
16
|
+
lines_count += 1
|
17
|
+
end
|
18
|
+
lines_count.should == 20
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should allow iterating the output lines with enumerator' do
|
22
|
+
subject.each.to_a.length.should == 20
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should be Enumerable' do
|
26
|
+
subject.should respond_to :take
|
27
|
+
subject.take(2).should == ["151 recipes\r\n", "16 achievements\r\n"]
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should return nothing if iterating on dead process' do
|
31
|
+
subject.each.to_a.length.should == 20
|
32
|
+
subject.each.to_a.should be_empty
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should allow iteration until pattern is found in message' do
|
36
|
+
subject.each_until(/NOT ENOUGH RAM/).to_a.last.should == "2011-09-10 12:58:55 [WARNING] **** NOT ENOUGH RAM!\r\n"
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should allow iteration until pattern is found in message excluding that message' do
|
40
|
+
subject.each_until_exclude(/NOT ENOUGH RAM/).to_a.last.should == "2011-09-10 12:58:55 [INFO] Starting minecraft server version Beta 1.7.3\r\n"
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should allow waiting for message matching pattern' do
|
44
|
+
subject.wait_until(/NOT ENOUGH RAM/)
|
45
|
+
subject.each.to_a.first.should == "2011-09-10 12:58:55 [WARNING] To start the server with more ram, launch it as \"java -Xmx1024M -Xms1024M -jar minecraft_server.jar\"\r\n"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe 'sending commands' do
|
50
|
+
it 'should allow sending commands to controled process' do
|
51
|
+
subject.send_command 'stop'
|
52
|
+
subject.each.to_a.last.should == "2011-09-19 22:12:00 [INFO] Saving chunks\r\n"
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'should allow sending commands and iterating output' do
|
56
|
+
subject.send_command 'list'
|
57
|
+
subject.each_until(/Connected players/).to_a.last.should == "2011-09-20 14:42:04 [INFO] Connected players: kazuya\r\n"
|
58
|
+
|
59
|
+
subject.send_command 'stop'
|
60
|
+
subject.each.to_a.last.should == "2011-09-19 22:12:00 [INFO] Saving chunks\r\n"
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'should echo sent command' do
|
64
|
+
subject.each_until(/Done/).to_a
|
65
|
+
subject.send_command 'stop'
|
66
|
+
subject.each.to_a.first.should == "stop\r\n"
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'should raise error when sending command to dead process' do
|
70
|
+
subject.send_command 'stop'
|
71
|
+
subject.wait_exit
|
72
|
+
|
73
|
+
expect {
|
74
|
+
subject.send_command 'help'
|
75
|
+
}.to raise_error IOError
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe 'messages' do
|
80
|
+
subject do
|
81
|
+
TTYProcessCtl.new('spec/stub --exit')
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'should allow access to previously outputed messages' do
|
85
|
+
subject.each.to_a
|
86
|
+
subject.messages.length.should == 20
|
87
|
+
end
|
88
|
+
|
89
|
+
describe 'message flushing' do
|
90
|
+
subject do
|
91
|
+
TTYProcessCtl.new('spec/stub')
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'should allow flushing queued messages before iteration' do
|
95
|
+
subject.each_until(/SERVER IS RUNNING/).to_a
|
96
|
+
sleep 0.2
|
97
|
+
|
98
|
+
subject.flush
|
99
|
+
|
100
|
+
subject.send_command 'list'
|
101
|
+
subject.each_until(/Connected players/).to_a.should == ["list\r\n", "2011-09-20 14:42:04 [INFO] Connected players: kazuya\r\n"]
|
102
|
+
|
103
|
+
subject.send_command 'stop'
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe 'process status query' do
|
109
|
+
it 'should allow querying if process is alive' do
|
110
|
+
subject.should be_alive
|
111
|
+
subject.send_command 'stop'
|
112
|
+
subject.each.to_a
|
113
|
+
subject.should_not be_alive
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'should allow waiting for porcess to exit' do
|
117
|
+
subject.should be_alive
|
118
|
+
subject.send_command 'stop'
|
119
|
+
subject.wait_exit
|
120
|
+
subject.should_not be_alive
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'should provide exit status when porcess exits' do
|
124
|
+
subject.each_until(/Done/).to_a
|
125
|
+
subject.exit_status.should be_nil
|
126
|
+
|
127
|
+
subject.send_command 'stop'
|
128
|
+
subject.each.to_a
|
129
|
+
subject.exit_status.should be_a Process::Status
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
describe 'limiting' do
|
134
|
+
it 'should allow defining maximum number of messages that can be queued' do
|
135
|
+
subject = TTYProcessCtl.new('spec/stub', max_queue_length: 2)
|
136
|
+
|
137
|
+
subject.each_until(/Done/).to_a
|
138
|
+
subject.send_command 'help'
|
139
|
+
subject.send_command 'stop'
|
140
|
+
|
141
|
+
sleep 0.2
|
142
|
+
subject.each.to_a.length.should == 2
|
143
|
+
subject.wait_exit
|
144
|
+
end
|
145
|
+
|
146
|
+
it 'should allow defining maximum number of messages that can be remembered' do
|
147
|
+
subject = TTYProcessCtl.new('spec/stub', max_messages: 2)
|
148
|
+
|
149
|
+
subject.each_until(/Done/).to_a
|
150
|
+
subject.send_command 'help'
|
151
|
+
subject.flush
|
152
|
+
subject.send_command 'stop'
|
153
|
+
subject.wait_exit
|
154
|
+
|
155
|
+
subject.messages.length.should == 2
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "tty-process-ctl"
|
8
|
+
s.version = "0.1.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Jakub Pastuszek"]
|
12
|
+
s.date = "2012-09-01"
|
13
|
+
s.description = "This gem was created to enable control of interactive terminal applications. It is using pseudo tty to communicate with the process via simple API."
|
14
|
+
s.email = "jpastuszek@gmail.com"
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".rspec",
|
22
|
+
"Gemfile",
|
23
|
+
"Gemfile.lock",
|
24
|
+
"LICENSE.txt",
|
25
|
+
"README.rdoc",
|
26
|
+
"Rakefile",
|
27
|
+
"VERSION",
|
28
|
+
"examples/irb.rb",
|
29
|
+
"examples/ls.rb",
|
30
|
+
"lib/tty-process-ctl.rb",
|
31
|
+
"spec/spec_helper.rb",
|
32
|
+
"spec/stub",
|
33
|
+
"spec/tty-process-ctl_spec.rb",
|
34
|
+
"tty-process-ctl.gemspec"
|
35
|
+
]
|
36
|
+
s.homepage = "http://github.com/jpastuszek/tty-process-ctl"
|
37
|
+
s.licenses = ["MIT"]
|
38
|
+
s.require_paths = ["lib"]
|
39
|
+
s.rubygems_version = "1.8.23"
|
40
|
+
s.summary = "Control of interactive terminal applications via simple API."
|
41
|
+
|
42
|
+
if s.respond_to? :specification_version then
|
43
|
+
s.specification_version = 3
|
44
|
+
|
45
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
46
|
+
s.add_development_dependency(%q<rspec>, ["~> 2.8"])
|
47
|
+
s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
|
48
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.1"])
|
49
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.8"])
|
50
|
+
s.add_development_dependency(%q<simplecov>, [">= 0"])
|
51
|
+
s.add_development_dependency(%q<cli>, ["~> 1.0"])
|
52
|
+
else
|
53
|
+
s.add_dependency(%q<rspec>, ["~> 2.8"])
|
54
|
+
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
55
|
+
s.add_dependency(%q<bundler>, ["~> 1.1"])
|
56
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8"])
|
57
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
58
|
+
s.add_dependency(%q<cli>, ["~> 1.0"])
|
59
|
+
end
|
60
|
+
else
|
61
|
+
s.add_dependency(%q<rspec>, ["~> 2.8"])
|
62
|
+
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
63
|
+
s.add_dependency(%q<bundler>, ["~> 1.1"])
|
64
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8"])
|
65
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
66
|
+
s.add_dependency(%q<cli>, ["~> 1.0"])
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
metadata
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tty-process-ctl
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Jakub Pastuszek
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-09-01 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '2.8'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '2.8'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rdoc
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '3.12'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '3.12'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: bundler
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '1.1'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.1'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: jeweler
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '1.8'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '1.8'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: simplecov
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: cli
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ~>
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '1.0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ~>
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '1.0'
|
110
|
+
description: This gem was created to enable control of interactive terminal applications.
|
111
|
+
It is using pseudo tty to communicate with the process via simple API.
|
112
|
+
email: jpastuszek@gmail.com
|
113
|
+
executables: []
|
114
|
+
extensions: []
|
115
|
+
extra_rdoc_files:
|
116
|
+
- LICENSE.txt
|
117
|
+
- README.rdoc
|
118
|
+
files:
|
119
|
+
- .document
|
120
|
+
- .rspec
|
121
|
+
- Gemfile
|
122
|
+
- Gemfile.lock
|
123
|
+
- LICENSE.txt
|
124
|
+
- README.rdoc
|
125
|
+
- Rakefile
|
126
|
+
- VERSION
|
127
|
+
- examples/irb.rb
|
128
|
+
- examples/ls.rb
|
129
|
+
- lib/tty-process-ctl.rb
|
130
|
+
- spec/spec_helper.rb
|
131
|
+
- spec/stub
|
132
|
+
- spec/tty-process-ctl_spec.rb
|
133
|
+
- tty-process-ctl.gemspec
|
134
|
+
homepage: http://github.com/jpastuszek/tty-process-ctl
|
135
|
+
licenses:
|
136
|
+
- MIT
|
137
|
+
post_install_message:
|
138
|
+
rdoc_options: []
|
139
|
+
require_paths:
|
140
|
+
- lib
|
141
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
142
|
+
none: false
|
143
|
+
requirements:
|
144
|
+
- - ! '>='
|
145
|
+
- !ruby/object:Gem::Version
|
146
|
+
version: '0'
|
147
|
+
segments:
|
148
|
+
- 0
|
149
|
+
hash: -464255591999730974
|
150
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
151
|
+
none: false
|
152
|
+
requirements:
|
153
|
+
- - ! '>='
|
154
|
+
- !ruby/object:Gem::Version
|
155
|
+
version: '0'
|
156
|
+
requirements: []
|
157
|
+
rubyforge_project:
|
158
|
+
rubygems_version: 1.8.23
|
159
|
+
signing_key:
|
160
|
+
specification_version: 3
|
161
|
+
summary: Control of interactive terminal applications via simple API.
|
162
|
+
test_files: []
|