rubysh 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,6 +5,10 @@ module Rubysh
5
5
  #
6
6
  # - freeze after initialize?
7
7
  class BaseCommand
8
+ def opts
9
+ @opts || {}
10
+ end
11
+
8
12
  def stringify_arg(arg)
9
13
  case arg
10
14
  when BaseCommand, BaseDirective
@@ -4,10 +4,11 @@ module Rubysh
4
4
 
5
5
  class ExecError < BaseError
6
6
  # Exception klass and caller from the child process
7
- attr_accessor :klass, :caller
7
+ attr_accessor :raw_message, :klass, :caller
8
8
 
9
- def initialize(message, klass, caller)
9
+ def initialize(message, raw_message, klass, caller)
10
10
  super(message)
11
+ @raw_message = raw_message
11
12
  @klass = klass
12
13
  @caller = caller
13
14
  end
@@ -126,7 +126,9 @@ module Rubysh
126
126
  :target_name => target_name,
127
127
  :read_pos => 0,
128
128
  :subprocess_fd_number => Util.to_fileno(source),
129
- :tee => @opts[:tee]
129
+ :tee => @opts[:tee],
130
+ :on_read => @opts[:on_read],
131
+ :on_write => @opts[:on_write],
130
132
  }
131
133
  end
132
134
 
@@ -36,6 +36,7 @@ module Rubysh
36
36
  # read(:how => :nonblock): Return whatever is immediately available
37
37
  def read(target=nil, opts=nil)
38
38
  raise Rubysh::Error::AlreadyRunError.new("Can only read from a runner in runner_state :started or :waited, not #{@runner_state.inspect}") unless @runner_state == :started || @runner_state == :waited
39
+ raise Rubysh::Error::BaseError.new("Can't read from a runner where :on_read has been provided") if command.opts[:on_read]
39
40
 
40
41
  if target.kind_of?(Hash)
41
42
  opts = target
@@ -263,28 +264,36 @@ module Rubysh
263
264
  # there.
264
265
  def prepare_io
265
266
  @parallel_io = Subprocess::PidAwareParallelIO.new(readers, writers, subprocesses)
266
- @parallel_io.on_read do |target_name, data|
267
- state = @targets[target_name]
268
- buffer = state[:buffer]
269
- if data == Subprocess::ParallelIO::EOF
270
- Rubysh.log.debug("EOF reached on #{target_name.inspect}")
271
- buffer.close_write
272
- else
273
- Rubysh.log.debug("Just read #{data.inspect} on #{target_name.inspect}")
274
- tee = state[:tee]
275
- tee.write(data) if tee
276
-
277
- # Seek to end
278
- buffer.pos = buffer.length
279
- buffer.write(data)
267
+ if on_read = command.opts[:on_read]
268
+ @parallel_io.on_read(on_read)
269
+ else
270
+ @parallel_io.on_read do |target_name, data|
271
+ state = @targets[target_name]
272
+ buffer = state[:buffer]
273
+ if data == Subprocess::ParallelIO::EOF
274
+ Rubysh.log.debug("EOF reached on #{target_name.inspect}")
275
+ buffer.close_write
276
+ else
277
+ Rubysh.log.debug("Just read #{data.inspect} on #{target_name.inspect}")
278
+ tee = state[:tee]
279
+ tee.write(data) if tee
280
+
281
+ # Seek to end
282
+ buffer.pos = buffer.length
283
+ buffer.write(data)
284
+ end
280
285
  end
281
286
  end
282
287
 
283
- @parallel_io.on_write do |target_name, written, remaining|
284
- if data == Subprocess::ParallelIO::EOF
285
- Rubysh.log.debug("EOF reached on #{target_name.inspect}")
286
- else
287
- Rubysh.log.debug("Just wrote #{written.inspect} on #{target_name.inspect}")
288
+ if on_write = command.opts[:on_write]
289
+ @parallel_io.on_write(on_write)
290
+ else
291
+ @parallel_io.on_write do |target_name, written, remaining|
292
+ if data == Subprocess::ParallelIO::EOF
293
+ Rubysh.log.debug("EOF reached on #{target_name.inspect}")
294
+ else
295
+ Rubysh.log.debug("Just wrote #{written.inspect} on #{target_name.inspect}")
296
+ end
288
297
  end
289
298
  end
290
299
  end
@@ -123,7 +123,6 @@ module Rubysh
123
123
  def exec_program
124
124
  begin
125
125
  Kernel.exec([command, command], *args)
126
- raise Rubysh::Error::UnreachableError.new("This code should be unreachable. If you are seeing this exception, it means someone overrode Kernel.exec. That's not very nice of them.")
127
126
  rescue Exception => e
128
127
  msg = {
129
128
  'message' => e.message,
@@ -135,6 +134,8 @@ module Rubysh
135
134
  # Note: atexit handlers will fire in this case. May want to do
136
135
  # something about that.
137
136
  exit(1)
137
+ else
138
+ raise Rubysh::Error::UnreachableError.new("This code should be unreachable. If you are seeing this exception, it means someone overrode Kernel.exec. That's not very nice of them.")
138
139
  end
139
140
  end
140
141
 
@@ -145,7 +146,7 @@ module Rubysh
145
146
  when false
146
147
  # success!
147
148
  when Hash
148
- @exec_error = Rubysh::Error::ExecError.new("Failed to exec in subprocess: #{msg['message']}", msg['klass'], msg['caller'])
149
+ @exec_error = Rubysh::Error::ExecError.new("Failed to exec in subprocess: #{msg['message']}", msg['message'], msg['klass'], msg['caller'])
149
150
  else
150
151
  @exec_error = Rubysh::Error::BaseError.new("Invalid message received over the exec_status pipe: #{msg.inspect}")
151
152
  end
@@ -15,12 +15,14 @@ class Rubysh::Subprocess
15
15
  @writer_buffers = {}
16
16
  end
17
17
 
18
- def on_read(&blk)
19
- @on_read = blk
18
+ def on_read(method=nil, &blk)
19
+ raise "Can't provide both method and block" if method && blk
20
+ @on_read = method || blk
20
21
  end
21
22
 
22
- def on_write(&blk)
23
- @on_write = blk
23
+ def on_write(method=nil, &blk)
24
+ raise "Can't provide both method and block" if method && blk
25
+ @on_write = method || blk
24
26
  end
25
27
 
26
28
  def write(writer_name, data, close_on_complete=true)
@@ -43,7 +43,7 @@ class Rubysh::Subprocess
43
43
  def self.trigger_breaker(writer)
44
44
  begin
45
45
  writer.write_nonblock('a') unless writer.closed?
46
- rescue Errno::EAGAIN, Errno::EPIPE
46
+ rescue Errno::EAGAIN, Errno::EPIPE, IOError
47
47
  end
48
48
  end
49
49
 
@@ -1,3 +1,3 @@
1
1
  module Rubysh
2
- VERSION = '0.2.1'
2
+ VERSION = '0.2.2'
3
3
  end
@@ -3,7 +3,7 @@ require 'bundler/setup'
3
3
 
4
4
  require 'minitest/autorun'
5
5
  require 'minitest/spec'
6
- require 'mocha'
6
+ require 'mocha/setup'
7
7
 
8
8
  $:.unshift(File.expand_path('../lib', File.dirname(__FILE__)))
9
9
 
@@ -0,0 +1,34 @@
1
+ require File.expand_path('../_lib', File.dirname(__FILE__))
2
+
3
+ module RubyshTest::Functional
4
+ class ReadTest < FunctionalTest
5
+ describe 'when using :on_read' do
6
+ it 'calls back as output is streamed' do
7
+ stdout = ''
8
+ stderr = ''
9
+
10
+ runner = Rubysh('ruby', '-e', 'puts "hi"; $stderr.puts "hullo there"; puts "hello"',
11
+ Rubysh.>, Rubysh.stderr > :stderr,
12
+ :on_read => Proc.new do |target_name, data|
13
+ case target_name
14
+ when :stdout then stdout << data unless data == Rubysh::Subprocess::ParallelIO::EOF
15
+ when :stderr then stderr << data unless data == Rubysh::Subprocess::ParallelIO::EOF
16
+ else
17
+ raise "Invalid name: #{target_name.inspect}"
18
+ end
19
+ end
20
+ ).run
21
+ assert_equal("hi\nhello\n", stdout)
22
+ assert_equal("hullo there\n", stderr)
23
+ end
24
+
25
+ it 'does not allow reading' do
26
+ runner = Rubysh('echo', 'hi',
27
+ Rubysh.>, Rubysh.stderr > :stderr,
28
+ :on_read => Proc.new {}
29
+ ).run
30
+ assert_raises(Rubysh::Error::BaseError) {runner.read}
31
+ end
32
+ end
33
+ end
34
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubysh
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -114,6 +114,7 @@ files:
114
114
  - test/functional/lib/fd-lister
115
115
  - test/functional/lib/kill.rb
116
116
  - test/functional/lib/leaked_fds.rb
117
+ - test/functional/lib/on_output.rb
117
118
  - test/functional/lib/read.rb
118
119
  - test/functional/lib/redirect_ordering.rb
119
120
  - test/functional/lib/triple_less_than.rb
@@ -142,7 +143,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
142
143
  version: '0'
143
144
  segments:
144
145
  - 0
145
- hash: 737846493172444945
146
+ hash: -1131101407467327753
146
147
  required_rubygems_version: !ruby/object:Gem::Requirement
147
148
  none: false
148
149
  requirements:
@@ -151,7 +152,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
151
152
  version: '0'
152
153
  segments:
153
154
  - 0
154
- hash: 737846493172444945
155
+ hash: -1131101407467327753
155
156
  requirements: []
156
157
  rubyforge_project:
157
158
  rubygems_version: 1.8.23
@@ -168,6 +169,7 @@ test_files:
168
169
  - test/functional/lib/fd-lister
169
170
  - test/functional/lib/kill.rb
170
171
  - test/functional/lib/leaked_fds.rb
172
+ - test/functional/lib/on_output.rb
171
173
  - test/functional/lib/read.rb
172
174
  - test/functional/lib/redirect_ordering.rb
173
175
  - test/functional/lib/triple_less_than.rb