pseudo-terminal 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,32 @@
1
+ Pseudo Terminal
2
+ ===============
3
+
4
+ [![Travis CI Build Status](http://travis-ci.org/wenzowski/pseudo-terminal.png)](http://travis-ci.org/wenzowski/pseudo-terminal)
5
+
6
+ This library wraps PTY to ease use of pseudo terminals on unix-based operating systems.
7
+
8
+ Sample Workflow
9
+ ---------------
10
+
11
+ Create a new pseudo terminal, write a command, process result, and close the process:
12
+
13
+ require 'pseudo-terminal'
14
+ pt = PseudoTerminal.new # Create a new pseudo terminal.
15
+ puts pt << 'pwd' # Write command & print result.
16
+ pt.put('pwd') {|l| puts l} # Write command & print each line when it appears on the pipe.
17
+ pt.close # Close pseudo terminal and halt process.
18
+
19
+
20
+ Setup
21
+ -----
22
+
23
+ gem install pseudo-terminal
24
+
25
+
26
+ Meta
27
+ ----
28
+
29
+ Created by Alexander Wenzowski
30
+
31
+
32
+ Released under the [MIT license](http://www.opensource.org/licenses/mit-license.php).
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ lib = File.expand_path(File.dirname(__FILE__) + '/../lib')
4
+ $LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
5
+
6
+ require 'pseudo-terminal'
@@ -0,0 +1,4 @@
1
+ class PseudoTerminal
2
+ require 'pseudo-terminal/client'
3
+ include PseudoTerminal::Client
4
+ end
@@ -0,0 +1,58 @@
1
+ require 'pseudo-terminal/string'
2
+
3
+ class PseudoTerminal::Buffer
4
+ attr_accessor :masks, :raw, :lines, :segment
5
+
6
+ def initialize str_ready, masks=[]
7
+ @raw = ''
8
+ @segment = ''
9
+ @lines = []
10
+ @buff_a = []
11
+ @masks = masks
12
+ @ready = str_ready.to_s
13
+ end
14
+
15
+ def << str
16
+ buffer = str.to_s
17
+ @raw << buffer
18
+ process_raw(buffer)
19
+ # lines
20
+ end
21
+
22
+ private
23
+
24
+ def process_raw buffer
25
+ b = lines_from_raw(buffer)
26
+ b.each {|l| @lines << l}
27
+ end
28
+
29
+ def lines_from buffer
30
+ buffer.lines.to_a.each {|l| begin l.chomp! end while l.end_with_any? "\r\n".chars }
31
+ end
32
+
33
+ def lines_from_raw buffer
34
+ lines = lines_from buffer
35
+ lines = merge_line(lines) if !@segment.empty?
36
+ @segment << lines.pop if truncated
37
+ lines.each {|line| line.strip_ansi_escape_sequences!}
38
+ lines.each_with_index {|line, key| lines.delete_at key if line.match /^#{@ready}/}
39
+ @masks.each do |mask|
40
+ lines.each_with_index {|line, key| lines.delete_at key if line.match /^#{mask}/}
41
+ end
42
+ lines
43
+ end
44
+
45
+ def new_line
46
+ "\n"
47
+ end
48
+
49
+ def truncated
50
+ !@raw.end_with?(new_line) && !@raw.empty?
51
+ end
52
+
53
+ def merge_line buff_a
54
+ buff_a.first.prepend! @segment if buff_a.first
55
+ @segment = ''
56
+ buff_a
57
+ end
58
+ end
@@ -0,0 +1,68 @@
1
+ require 'pty'
2
+ require 'pseudo-terminal/buffer'
3
+
4
+ module PseudoTerminal::Client
5
+ attr_accessor :r, :w, :b, :timeout
6
+
7
+ def initialize opt={}
8
+ opt[:ready] ||= '> '
9
+ opt[:sh] ||= "env PS1='#{opt[:ready]}' TERM=dumb sh -i"
10
+ opt[:timeout] ||= 1
11
+ @timeout = opt[:timeout]
12
+ @r, @w, @pid = PTY.spawn opt[:sh]
13
+ @b = PseudoTerminal::Buffer.new opt[:ready]
14
+ read
15
+ @b.masks << @b.lines.pop while @b.lines.empty? == false
16
+ end
17
+
18
+ def << command, &block
19
+ put command, &block
20
+ end
21
+
22
+ def put command, &block
23
+ @w.puts command
24
+ read &block
25
+ end
26
+
27
+ def is_running?
28
+ begin
29
+ Process.getpgid @pid
30
+ true
31
+ rescue Errno::ESRCH
32
+ false
33
+ end
34
+ end
35
+
36
+ def kill!
37
+ @r.close
38
+ @w.close
39
+ begin
40
+ Process.wait @pid
41
+ rescue PTY::ChildExited
42
+ end
43
+ end
44
+
45
+ def read &block
46
+ begin
47
+ read_loop &block
48
+ rescue IO::WaitReadable
49
+ t = IO.select([@r], nil, nil, @timeout)
50
+ retry unless t.nil?
51
+ @b.lines
52
+ rescue IOError
53
+ nil
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ def read_loop &block
60
+ begin
61
+ if block_given?
62
+ (@b << @r.read_nonblock(1024)).each {|line| yield(line)}
63
+ else
64
+ @b << @r.read_nonblock(1024)
65
+ end
66
+ end while true
67
+ end
68
+ end
@@ -0,0 +1,16 @@
1
+ class String
2
+ def prepend! s
3
+ self.insert 0, s
4
+ end
5
+
6
+ def end_with_any? arr
7
+ r = false
8
+ arr.each {|str| r = true if self.end_with? str }
9
+ r
10
+ end
11
+
12
+ def strip_ansi_escape_sequences!
13
+ self.gsub!(/\e\]\d;(.*)\a/, '')
14
+ self.gsub!(/\e\[[^m]*m/, '')
15
+ end
16
+ end
@@ -0,0 +1,3 @@
1
+ class PseudoTerminal
2
+ VERSION = "0.0.2"
3
+ end
@@ -0,0 +1,50 @@
1
+ require "spec_helper"
2
+ require "pseudo-terminal/buffer"
3
+
4
+ describe PseudoTerminal::Buffer do
5
+ it 'should be instantiated without arguments' do
6
+ lambda { t = PseudoTerminal::Buffer.new(ready='> ') }.should_not raise_error
7
+ lambda { t = PseudoTerminal::Buffer.new(ready='', mask=[]) }.should_not raise_error
8
+ lambda { t = PseudoTerminal::Buffer.new }.should raise_error
9
+ end
10
+
11
+ context 'when string appended to buffer' do
12
+ before :each do
13
+ @ready='> '
14
+ @b = PseudoTerminal::Buffer.new @ready
15
+ end
16
+
17
+ it 'is empty' do
18
+ (@b << '').should be_kind_of(Array)
19
+ (@b << '').should be_empty
20
+ end
21
+
22
+ it 'is ready' do
23
+ (@b << @ready).should be_empty
24
+ end
25
+
26
+ it 'has masked lines' do
27
+ @b.masks = ['error line 1', 'error line 2']
28
+ masks_str = ''
29
+ @b.masks.each {|mask| masks_str << "#{mask}\r\n"}
30
+ (@b << masks_str).should == []
31
+ end
32
+
33
+ it 'contains newline (\r\n)' do
34
+ (@b << "str\r\n").should == ['str']
35
+ end
36
+
37
+ it 'is truncated' do
38
+ (@b << '1').should be_empty
39
+ (@b << '2').should be_empty
40
+ (@b << "3\r\n").should == ['123']
41
+ end
42
+
43
+ it 'lines are stored' do
44
+ (@b << "a\r\nb\r\nc\r\n").should == ['a','b','c']
45
+ (@b << "1\r\n2\r\n3\r\n").should == ['1','2','3']
46
+ @b.lines.should == ['a','b','c','1','2','3']
47
+ @b.raw.should == "a\r\nb\r\nc\r\n1\r\n2\r\n3\r\n"
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,45 @@
1
+ require "spec_helper"
2
+ require "pseudo-terminal"
3
+
4
+ describe PseudoTerminal do
5
+ it 'should be instantiated without failures' do
6
+ lambda { t = PseudoTerminal.new; t.kill! }.should_not raise_error
7
+ lambda { t = PseudoTerminal.new({}); t.kill! }.should_not raise_error
8
+ lambda { t = PseudoTerminal.new(1); t.kill! }.should raise_error
9
+ lambda { t = PseudoTerminal.new(''); t.kill! }.should raise_error
10
+ lambda { t = PseudoTerminal.new([]); t.kill! }.should raise_error
11
+ end
12
+
13
+ context 'terminal' do
14
+ t = PseudoTerminal.new
15
+ it 'is running' do
16
+ t.is_running?.should be_true
17
+ end
18
+ it 'is killed' do
19
+ t.kill!
20
+ t.is_running?.should be_false
21
+ end
22
+ end
23
+
24
+ context 'when spawned terminal' do
25
+ before :each do
26
+ @t = PseudoTerminal.new timeout: 0.1
27
+ end
28
+
29
+ after :each do
30
+ @t.kill!
31
+ end
32
+
33
+ it 'has not been written to' do
34
+ @t.read.should be_empty
35
+ end
36
+
37
+ it 'has a command written to it' do
38
+ (@t << 'pwd').first.should == Dir.pwd
39
+ end
40
+
41
+ it 'has a command written to it while a block is passed to it' do
42
+ @t.put('pwd') {|line| line.should == Dir.pwd}
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,30 @@
1
+ require "spec_helper"
2
+ require "pseudo-terminal/string"
3
+
4
+ describe String do
5
+ it 'string.prepend!' do
6
+ s = 'def'
7
+ s.prepend! 'abc'
8
+ s.should == 'abcdef'
9
+ end
10
+
11
+ context 'does str end with' do
12
+ it 'null' do
13
+ 'abc'.end_with_any?([]).should == false
14
+ ''.end_with_any?(['abc']).should == false
15
+ end
16
+ it 'single-character' do
17
+ 'abc'.end_with_any?(['c']).should == true
18
+ 'abc'.end_with_any?(['b']).should == false
19
+ end
20
+ it 'multi-character string' do
21
+ 'abc'.end_with_any?(['bc']).should == true
22
+ 'abc'.end_with_any?(['ab']).should == false
23
+ end
24
+ end
25
+ it 'strips ansi escape codes from string' do
26
+ s = "\e[34mtesting\e[39;49m\e[0m/\r\n\e]7;file://Alexanders-MacBook-Pro.local/private/tmp/refinerycms\a\e]7;file://Alexanders-MacBook-Pro.local/private/tmp/refinerycms\a\e[0;31m4805\e[0m \xE2\x98\x85 \e[1;30m01:13\e[0m \xE2\x98\x85 \e[0;32muser\e[0m@\e[0;32mdomain.com\e[0m:\e[0;36m~/code/pseudo-terminal\e[0m (\e[1;33mmaster\e[0m) \r\r\n\xC2\xBB "
27
+ s.strip_ansi_escape_sequences!
28
+ s.should == "testing/\r\n4805 \xE2\x98\x85 01:13 \xE2\x98\x85 user@domain.com:~/code/pseudo-terminal (master) \r\r\n\xC2\xBB "
29
+ end
30
+ end
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,18 @@
1
+ require "rubygems"
2
+ require "bundler/setup"
3
+
4
+ require "simplecov"
5
+ SimpleCov.start do
6
+ add_filter "/spec/"
7
+ end
8
+
9
+ require "rspec"
10
+ require "rr"
11
+ require "support/display_message_matcher"
12
+
13
+ RSpec.configure do |config|
14
+ config.color_enabled = true
15
+ config.include DisplayMessageMatcher
16
+ config.after { RR.reset }
17
+ end
18
+
@@ -0,0 +1,49 @@
1
+ module DisplayMessageMatcher
2
+
3
+ def display_message(command, message)
4
+ DisplayMessageMatcher::DisplayMessage.new command, message
5
+ end
6
+
7
+ class DisplayMessage
8
+ def initialize(command, message)
9
+ @command = command
10
+ @message = message
11
+ end
12
+
13
+ def matches?(given_proc)
14
+ displayed_expected_message = false
15
+ @given_messages = []
16
+
17
+ @command.should_receive(:display).
18
+ any_number_of_times do |message, newline|
19
+ @given_messages << message
20
+ displayed_expected_message = displayed_expected_message ||
21
+ message == @message
22
+ end
23
+
24
+ given_proc.call
25
+
26
+ displayed_expected_message
27
+ end
28
+
29
+ def failure_message
30
+ "expected #{ @command } to display the message #{ @message.inspect } but #{ given_messages }"
31
+ end
32
+
33
+ def negative_failure_message
34
+ "expected #{ @command } to not display the message #{ @message.inspect } but it was displayed"
35
+ end
36
+
37
+ private
38
+
39
+ def given_messages
40
+ if @given_messages.empty?
41
+ 'no messages were displayed'
42
+ else
43
+ formatted_given_messages = @given_messages.map(&:inspect).join ', '
44
+ "the follow messages were displayed: #{ formatted_given_messages }"
45
+ end
46
+ end
47
+
48
+ end
49
+ end
metadata ADDED
@@ -0,0 +1,58 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pseudo-terminal
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Alexander Wenzowski
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-08-23 00:00:00.000000000Z
13
+ dependencies: []
14
+ description: Pseudo terminal library to ease interaction with PTY.
15
+ email: alexander@wenzowski.com
16
+ executables:
17
+ - pseudo-terminal
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - README.md
22
+ - bin/pseudo-terminal
23
+ - lib/pseudo-terminal.rb
24
+ - lib/pseudo-terminal/buffer.rb
25
+ - lib/pseudo-terminal/client.rb
26
+ - lib/pseudo-terminal/string.rb
27
+ - lib/pseudo-terminal/version.rb
28
+ - spec/pseudo-terminal/buffer_spec.rb
29
+ - spec/pseudo-terminal/client_spec.rb
30
+ - spec/pseudo-terminal/string_spec.rb
31
+ - spec/spec.opts
32
+ - spec/spec_helper.rb
33
+ - spec/support/display_message_matcher.rb
34
+ homepage: http://github.com/wenzowski/pseudo-terminal
35
+ licenses: []
36
+ post_install_message:
37
+ rdoc_options: []
38
+ require_paths:
39
+ - lib
40
+ required_ruby_version: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ required_rubygems_version: !ruby/object:Gem::Requirement
47
+ none: false
48
+ requirements:
49
+ - - ! '>='
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ requirements: []
53
+ rubyforge_project:
54
+ rubygems_version: 1.8.6
55
+ signing_key:
56
+ specification_version: 3
57
+ summary: Pseudo terminal built on PTY.
58
+ test_files: []