session 2.4.0 → 3.1.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/LICENSE +3 -0
- data/README +168 -0
- data/Rakefile +233 -0
- data/gemspec.rb +62 -0
- data/lib/session.rb +10 -103
- data/sample/bash.rb +60 -0
- data/sample/bash.rb.out +51 -0
- data/sample/driver.rb +47 -0
- data/sample/session_idl.rb +25 -0
- data/sample/session_sh.rb +29 -0
- data/sample/sh0.rb +23 -0
- data/sample/stdin.rb +17 -0
- data/sample/threadtest.rb +42 -0
- data/session.gemspec +27 -0
- data/test/session.rb +3 -0
- metadata +70 -33
- data/lib/session-2.4.0.rb +0 -744
data/LICENSE
ADDED
data/README
ADDED
@@ -0,0 +1,168 @@
|
|
1
|
+
URLS: |
|
2
|
+
|
3
|
+
http://raa.ruby-lang.org/project/session/
|
4
|
+
http://www.codeforpeople.com/lib/ruby/session/
|
5
|
+
|
6
|
+
|
7
|
+
NAME: |
|
8
|
+
|
9
|
+
Session
|
10
|
+
::Sh
|
11
|
+
::Bash
|
12
|
+
::Shell
|
13
|
+
::IDL
|
14
|
+
|
15
|
+
SYNOPSIS: |
|
16
|
+
|
17
|
+
Session::* offers a set of classes built upon Open3::popen3 for driving
|
18
|
+
external progams via pipes. It offers a significant abstraction over
|
19
|
+
Open3::popen in that the stdout/stderr of each command sent can be deliniated:
|
20
|
+
|
21
|
+
open3:
|
22
|
+
|
23
|
+
i,o,e = Open3::popen3 '/bin/sh'
|
24
|
+
|
25
|
+
i.puts 'ls'
|
26
|
+
i.puts 'echo 42'
|
27
|
+
|
28
|
+
now, how to determine the boundry between the output from 'ls' and 'echo'?
|
29
|
+
the only (simple) way is start a process for each command
|
30
|
+
|
31
|
+
i,o,e = Open3::popen3 '/bin/sh'
|
32
|
+
i.puts 'ls'
|
33
|
+
i.close
|
34
|
+
stdout, stderr = o.read, e.read
|
35
|
+
|
36
|
+
i,o,e = Open3::popen3 '/bin/sh'
|
37
|
+
i.puts 'echo 42'
|
38
|
+
i.close
|
39
|
+
stdout, stderr = o.read, e.read
|
40
|
+
|
41
|
+
session:
|
42
|
+
|
43
|
+
sh = Session::new
|
44
|
+
|
45
|
+
stdout, stderr = sh.execute 'ls'
|
46
|
+
stdout, stderr = sh.execute 'echo 42'
|
47
|
+
|
48
|
+
Both stderr and stdout can be redirected, and the exit_status of each command
|
49
|
+
is made available:
|
50
|
+
|
51
|
+
bash = Session::Bash.new
|
52
|
+
stdout, stderr = StringIO::new, StringIO::new
|
53
|
+
|
54
|
+
bash.execute 'ls', :stdout => stdout, :stderr => stderr
|
55
|
+
# bash.execute 'ls', 1 => stdout, 2 => stderr # same thing
|
56
|
+
# bash.execute 'ls', :o => stdout, :e => stderr # same thing
|
57
|
+
|
58
|
+
exit_status = bash.exit_status
|
59
|
+
|
60
|
+
A block form can be used to specify a callback to be invoked whenever output
|
61
|
+
has become availible:
|
62
|
+
|
63
|
+
bash = Session::Bash.new
|
64
|
+
|
65
|
+
bash.execute( 'long_running_command.exe' ) do |out, err|
|
66
|
+
logger << out if out
|
67
|
+
elogger << err if err
|
68
|
+
end
|
69
|
+
|
70
|
+
Sessions are Thread safe (in the sense that they do not block on io
|
71
|
+
operations) allowing commands spawned from guis to update widgets with output
|
72
|
+
while running in the background.
|
73
|
+
|
74
|
+
button.configure 'action' => lambda do
|
75
|
+
sh = Session::new
|
76
|
+
sh.execute(cmd) do |o,e|
|
77
|
+
out_widget.update o if o
|
78
|
+
err_widget.update e if e
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
SAMPLES: |
|
83
|
+
|
84
|
+
see samples/*
|
85
|
+
|
86
|
+
HISTORY: |
|
87
|
+
3.1.0:
|
88
|
+
- patches from @headius
|
89
|
+
|
90
|
+
3.0.0
|
91
|
+
- move to github
|
92
|
+
|
93
|
+
2.4.0:
|
94
|
+
- added ability to specify stdin for Session::Bash and Session::Sh
|
95
|
+
|
96
|
+
sh = Session::new
|
97
|
+
|
98
|
+
sh.execute 'cat', :stdin => io
|
99
|
+
sh.execute 'cat', :stdin => string
|
100
|
+
sh.execute 'cat', :stdin => stringio
|
101
|
+
|
102
|
+
2.3.0:
|
103
|
+
- fixed warning of @debug being un-initialized
|
104
|
+
|
105
|
+
2.2.0:
|
106
|
+
- added a private munged version of Open3::open3. the builtin one causes
|
107
|
+
the child process to become a child of init, this was very inconvenient
|
108
|
+
because it was sometimes hard to crawl proces trees - the parent was lost.
|
109
|
+
now the seesion is a child process that has been detached using
|
110
|
+
Process::detach. this results in less suprising behaviour; for instance
|
111
|
+
sending signal TERM to a process results in any sessions it had open dying
|
112
|
+
as well. you can use Session::use_open3=true or
|
113
|
+
ENV['SESSION_USE_OPEN3']='1' for the old behaviour if you need it.
|
114
|
+
- added Session::Bash::Login class. this class opens a session which has
|
115
|
+
all the normal settings of a bash loging shell (.bashrc is sourced). this
|
116
|
+
if often convenient as paths, aliases, etc. are set as normal.
|
117
|
+
- moved the Spawn module inside the Session module. now the Session module
|
118
|
+
is the namespace for everything so using session pollutes namespace less.
|
119
|
+
|
120
|
+
2.1.9:
|
121
|
+
- fixed bug where setting track history after creation caused later failure in
|
122
|
+
execute (@history =[] only in ctor). thanks leon breedt
|
123
|
+
<bitserf@gmail.com>!
|
124
|
+
- updates to README
|
125
|
+
- included session-x.x.x.rpa file - thanks batsman <batsman.geo@yahoo.com>
|
126
|
+
- to_str/to_s/to_yaml for History/Command is now valid yaml (updated samples
|
127
|
+
to reflect this)
|
128
|
+
- inspect for History/Command is now ruby's default
|
129
|
+
|
130
|
+
2.1.8:
|
131
|
+
- greatly simplified read loop using two reader threads, one for stderr and
|
132
|
+
one for stdout alongside a mutex to protect data. this streamlined the code
|
133
|
+
alot vs. the old select method including allowing removal of the linbuffer
|
134
|
+
class. the interface remains exactly as before however.
|
135
|
+
|
136
|
+
2.1.7:
|
137
|
+
- improved thread safe non-blocking read method
|
138
|
+
- gemspec
|
139
|
+
|
140
|
+
2.1.6:
|
141
|
+
- wrapped send_command in a Thread (send async) so output processing can
|
142
|
+
commend immeadiately. this was o.k. before, but had strange behaviour when
|
143
|
+
using popen3 from threads. thanks to tanaka akira for this suggestion.
|
144
|
+
- iff ENV['SESSION_USE_SPAWN'] is set Session uses Spawn::spawn instead of
|
145
|
+
Open3::popen3. also noted that spawn seems to be a bit faster.
|
146
|
+
- added tests for threads.
|
147
|
+
- run 'sh SESSION_USE_SPAWN=1 ruby test/session.rb' to test using spawn
|
148
|
+
- added test for idl so it's test is not run if system doesn't have it, all
|
149
|
+
that should be required for 'ruby test/session.rb' is should be sh'
|
150
|
+
- removed sample/tcsh and note about Tcsh and Csh in README - stderr
|
151
|
+
redirection/separation is flaky in those shells
|
152
|
+
|
153
|
+
2.1.5:
|
154
|
+
- added Session.use_spawn=, AbstractSession.use_spawn=, and an :use_session=>
|
155
|
+
option to AbstractSession#initialize. if any of them are set the code uses
|
156
|
+
Spawn::spawn to create external processes instead of Open3::popen3.
|
157
|
+
Spawn::spawn uses named pipes (fifos) for IPC instead of forking and pipes.
|
158
|
+
the fork used in popen3 can cause strange behaviour with multi-threaded apps
|
159
|
+
(like a tk app). see source for details
|
160
|
+
|
161
|
+
2.1.4:
|
162
|
+
- added Thread.exclusive{} wrapper when io is read to works in multi
|
163
|
+
threaded apps
|
164
|
+
|
165
|
+
|
166
|
+
AUTHOR: |
|
167
|
+
|
168
|
+
ara.t.howard@noaa.gov
|
data/Rakefile
ADDED
@@ -0,0 +1,233 @@
|
|
1
|
+
|
2
|
+
This.rubyforge_project = 'codeforpeople'
|
3
|
+
This.author = "Ara T. Howard"
|
4
|
+
This.email = "ara.t.howard@gmail.com"
|
5
|
+
This.homepage = "http://github.com/ahoward/#{ This.lib }/tree/master"
|
6
|
+
|
7
|
+
|
8
|
+
task :default do
|
9
|
+
puts(Rake::Task.tasks.map{|task| task.name} - ['default'])
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
task :gemspec do
|
14
|
+
ignore_extensions = 'git', 'svn', 'tmp', /sw./, 'bak', 'gem'
|
15
|
+
ignore_directories = 'pkg'
|
16
|
+
ignore_files = 'test/log'
|
17
|
+
|
18
|
+
shiteless =
|
19
|
+
lambda do |list|
|
20
|
+
list.delete_if do |entry|
|
21
|
+
next unless test(?e, entry)
|
22
|
+
extension = File.basename(entry).split(%r/[.]/).last
|
23
|
+
ignore_extensions.any?{|ext| ext === extension}
|
24
|
+
end
|
25
|
+
list.delete_if do |entry|
|
26
|
+
next unless test(?d, entry)
|
27
|
+
dirname = File.expand_path(entry)
|
28
|
+
ignore_directories.any?{|dir| File.expand_path(dir) == dirname}
|
29
|
+
end
|
30
|
+
list.delete_if do |entry|
|
31
|
+
next unless test(?f, entry)
|
32
|
+
filename = File.expand_path(entry)
|
33
|
+
ignore_files.any?{|file| File.expand_path(file) == filename}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
lib = This.lib
|
38
|
+
object = This.object
|
39
|
+
version = This.version
|
40
|
+
files = shiteless[Dir::glob("**/**")]
|
41
|
+
executables = shiteless[Dir::glob("bin/*")].map{|exe| File.basename(exe)}
|
42
|
+
has_rdoc = true #File.exist?('doc')
|
43
|
+
test_files = "test/#{ lib }.rb" if File.file?("test/#{ lib }.rb")
|
44
|
+
summary = object.respond_to?(:summary) ? object.summary : "summary: #{ lib } kicks the ass"
|
45
|
+
description = object.respond_to?(:description) ? object.description : "description: #{ lib } kicks the ass"
|
46
|
+
|
47
|
+
extensions = This.extensions
|
48
|
+
if extensions.nil?
|
49
|
+
%w( Makefile configure extconf.rb ).each do |ext|
|
50
|
+
extensions << ext if File.exists?(ext)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
extensions = [extensions].flatten.compact
|
54
|
+
|
55
|
+
template =
|
56
|
+
if test(?e, 'gemspec.erb')
|
57
|
+
Template{ IO.read('gemspec.erb') }
|
58
|
+
else
|
59
|
+
Template {
|
60
|
+
<<-__
|
61
|
+
## #{ lib }.gemspec
|
62
|
+
#
|
63
|
+
|
64
|
+
Gem::Specification::new do |spec|
|
65
|
+
spec.name = #{ lib.inspect }
|
66
|
+
spec.version = #{ version.inspect }
|
67
|
+
spec.platform = Gem::Platform::RUBY
|
68
|
+
spec.summary = #{ lib.inspect }
|
69
|
+
spec.description = #{ description.inspect }
|
70
|
+
|
71
|
+
spec.files = #{ files.inspect }
|
72
|
+
spec.executables = #{ executables.inspect }
|
73
|
+
|
74
|
+
spec.require_path = "lib"
|
75
|
+
|
76
|
+
spec.has_rdoc = #{ has_rdoc.inspect }
|
77
|
+
spec.test_files = #{ test_files.inspect }
|
78
|
+
#spec.add_dependency 'lib', '>= version'
|
79
|
+
spec.add_dependency 'fattr'
|
80
|
+
|
81
|
+
spec.extensions.push(*#{ extensions.inspect })
|
82
|
+
|
83
|
+
spec.rubyforge_project = #{ This.rubyforge_project.inspect }
|
84
|
+
spec.author = #{ This.author.inspect }
|
85
|
+
spec.email = #{ This.email.inspect }
|
86
|
+
spec.homepage = #{ This.homepage.inspect }
|
87
|
+
end
|
88
|
+
__
|
89
|
+
}
|
90
|
+
end
|
91
|
+
|
92
|
+
open("#{ lib }.gemspec", "w"){|fd| fd.puts template}
|
93
|
+
This.gemspec = "#{ lib }.gemspec"
|
94
|
+
end
|
95
|
+
|
96
|
+
task :gem => [:clean, :gemspec] do
|
97
|
+
Fu.mkdir_p This.pkgdir
|
98
|
+
before = Dir['*.gem']
|
99
|
+
cmd = "gem build #{ This.gemspec }"
|
100
|
+
`#{ cmd }`
|
101
|
+
after = Dir['*.gem']
|
102
|
+
gem = ((after - before).first || after.first) or abort('no gem!')
|
103
|
+
Fu.mv gem, This.pkgdir
|
104
|
+
This.gem = File.basename(gem)
|
105
|
+
end
|
106
|
+
|
107
|
+
task :readme do
|
108
|
+
samples = ''
|
109
|
+
prompt = '~ > '
|
110
|
+
lib = This.lib
|
111
|
+
version = This.version
|
112
|
+
|
113
|
+
Dir['sample*/*'].sort.each do |sample|
|
114
|
+
samples << "\n" << " <========< #{ sample } >========>" << "\n\n"
|
115
|
+
|
116
|
+
cmd = "cat #{ sample }"
|
117
|
+
samples << Util.indent(prompt + cmd, 2) << "\n\n"
|
118
|
+
samples << Util.indent(`#{ cmd }`, 4) << "\n"
|
119
|
+
|
120
|
+
cmd = "ruby #{ sample }"
|
121
|
+
samples << Util.indent(prompt + cmd, 2) << "\n\n"
|
122
|
+
|
123
|
+
cmd = "ruby -e'STDOUT.sync=true; exec %(ruby -I ./lib #{ sample })'"
|
124
|
+
samples << Util.indent(`#{ cmd } 2>&1`, 4) << "\n"
|
125
|
+
end
|
126
|
+
|
127
|
+
template =
|
128
|
+
if test(?e, 'readme.erb')
|
129
|
+
Template{ IO.read('readme.erb') }
|
130
|
+
else
|
131
|
+
Template {
|
132
|
+
<<-__
|
133
|
+
NAME
|
134
|
+
#{ lib }
|
135
|
+
|
136
|
+
DESCRIPTION
|
137
|
+
|
138
|
+
INSTALL
|
139
|
+
gem install #{ lib }
|
140
|
+
|
141
|
+
SAMPLES
|
142
|
+
#{ samples }
|
143
|
+
__
|
144
|
+
}
|
145
|
+
end
|
146
|
+
|
147
|
+
open("README", "w"){|fd| fd.puts template}
|
148
|
+
end
|
149
|
+
|
150
|
+
|
151
|
+
task :clean do
|
152
|
+
Dir[File.join(This.pkgdir, '**/**')].each{|entry| Fu.rm_rf(entry)}
|
153
|
+
end
|
154
|
+
|
155
|
+
|
156
|
+
task :release => [:clean, :gemspec, :gem] do
|
157
|
+
gems = Dir[File.join(This.pkgdir, '*.gem')].flatten
|
158
|
+
raise "which one? : #{ gems.inspect }" if gems.size > 1
|
159
|
+
raise "no gems?" if gems.size < 1
|
160
|
+
cmd = "rubyforge login && rubyforge add_release #{ This.rubyforge_project } #{ This.lib } #{ This.version } #{ This.pkgdir }/#{ This.gem }"
|
161
|
+
puts cmd
|
162
|
+
system cmd
|
163
|
+
end
|
164
|
+
|
165
|
+
|
166
|
+
|
167
|
+
|
168
|
+
|
169
|
+
BEGIN {
|
170
|
+
$VERBOSE = nil
|
171
|
+
|
172
|
+
require 'ostruct'
|
173
|
+
require 'erb'
|
174
|
+
require 'fileutils'
|
175
|
+
|
176
|
+
Fu = FileUtils
|
177
|
+
|
178
|
+
This = OpenStruct.new
|
179
|
+
|
180
|
+
This.file = File.expand_path(__FILE__)
|
181
|
+
This.dir = File.dirname(This.file)
|
182
|
+
This.pkgdir = File.join(This.dir, 'pkg')
|
183
|
+
|
184
|
+
lib = ENV['LIB']
|
185
|
+
unless lib
|
186
|
+
lib = File.basename(Dir.pwd)
|
187
|
+
end
|
188
|
+
This.lib = lib
|
189
|
+
|
190
|
+
version = ENV['VERSION']
|
191
|
+
unless version
|
192
|
+
require "./lib/#{ This.lib }"
|
193
|
+
This.name = lib.capitalize
|
194
|
+
This.object = eval(This.name)
|
195
|
+
version = This.object.send(:version)
|
196
|
+
end
|
197
|
+
This.version = version
|
198
|
+
|
199
|
+
abort('no lib') unless This.lib
|
200
|
+
abort('no version') unless This.version
|
201
|
+
|
202
|
+
module Util
|
203
|
+
def indent(s, n = 2)
|
204
|
+
s = unindent(s)
|
205
|
+
ws = ' ' * n
|
206
|
+
s.gsub(%r/^/, ws)
|
207
|
+
end
|
208
|
+
|
209
|
+
def unindent(s)
|
210
|
+
indent = nil
|
211
|
+
s.each do |line|
|
212
|
+
next if line =~ %r/^\s*$/
|
213
|
+
indent = line[%r/^\s*/] and break
|
214
|
+
end
|
215
|
+
indent ? s.gsub(%r/^#{ indent }/, "") : s
|
216
|
+
end
|
217
|
+
extend self
|
218
|
+
end
|
219
|
+
|
220
|
+
class Template
|
221
|
+
def initialize(&block)
|
222
|
+
@block = block
|
223
|
+
@template = block.call.to_s
|
224
|
+
end
|
225
|
+
def expand(b=nil)
|
226
|
+
ERB.new(Util.unindent(@template)).result(b||@block)
|
227
|
+
end
|
228
|
+
alias_method 'to_s', 'expand'
|
229
|
+
end
|
230
|
+
def Template(*args, &block) Template.new(*args, &block) end
|
231
|
+
|
232
|
+
Dir.chdir(This.dir)
|
233
|
+
}
|
data/gemspec.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
lib, version, *ignored = ARGV
|
4
|
+
|
5
|
+
unless lib
|
6
|
+
lib = File.basename(Dir.pwd)
|
7
|
+
end
|
8
|
+
|
9
|
+
unless version
|
10
|
+
mod = lib.capitalize
|
11
|
+
require "./lib/#{ lib }"
|
12
|
+
version = eval(mod).send(:version)
|
13
|
+
end
|
14
|
+
|
15
|
+
abort('no lib') unless lib
|
16
|
+
abort('no version') unless version
|
17
|
+
|
18
|
+
puts "### gemspec: #{ lib }-#{ version }"
|
19
|
+
|
20
|
+
$VERBOSE = nil
|
21
|
+
|
22
|
+
shiteless = lambda{|list| list.delete_if{|file| file =~ %r/\.(git|svn|tmp|sw.|bak)$/}}
|
23
|
+
|
24
|
+
files = shiteless[Dir::glob("**/**")]
|
25
|
+
executables = shiteless[Dir::glob("bin/*")].map{|exe| File.basename(exe)}
|
26
|
+
has_rdoc = true #File.exist?('doc')
|
27
|
+
test_files = "test/#{ lib }.rb" if File.file?("test/#{ lib }.rb")
|
28
|
+
|
29
|
+
extensions = []
|
30
|
+
%w( Makefile configure extconf.rb rakefile Rakefile mkrf_conf ).each do |ext|
|
31
|
+
extensions << ext if File.exists?(ext)
|
32
|
+
end
|
33
|
+
|
34
|
+
template = <<-__
|
35
|
+
|
36
|
+
Gem::Specification::new do |spec|
|
37
|
+
spec.name = #{ lib.inspect }
|
38
|
+
spec.version = #{ version.inspect }
|
39
|
+
spec.platform = Gem::Platform::RUBY
|
40
|
+
spec.summary = #{ lib.inspect }
|
41
|
+
|
42
|
+
spec.files = #{ files.inspect }
|
43
|
+
spec.executables = #{ executables.inspect }
|
44
|
+
|
45
|
+
spec.require_path = "lib"
|
46
|
+
|
47
|
+
spec.has_rdoc = #{ has_rdoc.inspect }
|
48
|
+
spec.test_files = #{ test_files.inspect }
|
49
|
+
#spec.add_dependency 'lib', '>= version'
|
50
|
+
#spec.add_dependency 'fattr'
|
51
|
+
|
52
|
+
spec.extensions.push(*#{ extensions.inspect })
|
53
|
+
|
54
|
+
spec.rubyforge_project = 'codeforpeople'
|
55
|
+
spec.author = "Ara T. Howard"
|
56
|
+
spec.email = "ara.t.howard@gmail.com"
|
57
|
+
spec.homepage = "http://github.com/ahoward/#{ lib }/tree/master"
|
58
|
+
end
|
59
|
+
|
60
|
+
__
|
61
|
+
|
62
|
+
puts template
|