pseudo-terminal 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,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: []