rubysh 0.2.1 → 0.2.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.
@@ -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