backticks 0.5.0 → 1.0.0rc1
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.
- checksums.yaml +4 -4
- data/README.md +28 -1
- data/lib/backticks/command.rb +43 -15
- data/lib/backticks/runner.rb +6 -13
- data/lib/backticks/version.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a446a43c0be0d08f7ad35f3f9a2fa7791f66229d
|
4
|
+
data.tar.gz: 2fc8f0e3176dae7628cb08225c63f95d97002e8f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 96d973bd4ef27a7bfcfecf7f8cb9e7ea0de2bb05f758aa42fb49c5438ba372230c5a77d07737da4e9492c0109533992ca9f1ad9ccf779fe442e7907d8bd5a7b7
|
7
|
+
data.tar.gz: 378d42242e3efbc5817c89266a34e620ca59ddfe6fc92411b56671fe5aa7104088a5f934235f8b1f9dbcb66c589ef71f8c1c1904f5a755074392317817c59172
|
data/README.md
CHANGED
@@ -81,9 +81,16 @@ r = Backticks::Runner.new(buffered:true)
|
|
81
81
|
|
82
82
|
# or later on
|
83
83
|
r.buffered = false
|
84
|
+
|
85
|
+
# you can also specify invididual stream names to buffer
|
86
|
+
r.buffered = [:stderr]
|
84
87
|
```
|
85
88
|
|
86
|
-
|
89
|
+
When you read the `buffered` attribute of a Runner, it returns the list of
|
90
|
+
stream names that are buffered; this means that even if you _write_ a boolean
|
91
|
+
to this attribute, you will _read_ an Array of Symbol.
|
92
|
+
|
93
|
+
### Interactivity and Real-Time Capture
|
87
94
|
|
88
95
|
If you set `interactive:true` on the Runner, the console of the calling (Ruby)
|
89
96
|
process is "tied" to the child's I/O streams, allowing the user to interact
|
@@ -98,6 +105,26 @@ require 'io/console'
|
|
98
105
|
STDOUT.raw! ; Backticks::Runner.new(interactive:true).run('vi').join
|
99
106
|
```
|
100
107
|
|
108
|
+
You can use the `Command#tap` method to intercept I/O streams in real time,
|
109
|
+
with or without interactivity. To start the tap, pass a block to the method.
|
110
|
+
Your block should accept two parameters: a Symbol stream name (:stdin,
|
111
|
+
:stdout, :stderr) and a String with binary encoding that contains fresh
|
112
|
+
input or output.
|
113
|
+
|
114
|
+
The result of your block is used to decide what to do with the input/output:
|
115
|
+
nil means "discard" and a modified string is captured in place of the original.
|
116
|
+
|
117
|
+
Try loading the README in an editor with all of the vowels scrambled. Scroll
|
118
|
+
around and notice how words change when they leave and enter the screen!
|
119
|
+
|
120
|
+
```ruby
|
121
|
+
STDOUT.raw! ; cmd = Backticks::Runner.new(interactive:true).run('vi', 'README.md') ; cmd.tap do |io, bytes|
|
122
|
+
vowels = 'aeiou'
|
123
|
+
bytes.gsub!(/[#{vowels}]/) { vowels[rand(vowels.size)] } if io == :stdout
|
124
|
+
bytes
|
125
|
+
end ; cmd.join ; nil
|
126
|
+
```
|
127
|
+
|
101
128
|
### Literally Overriding Ruby's Backticks
|
102
129
|
|
103
130
|
It's a terrible idea, but you can use this gem to change the behavior of
|
data/lib/backticks/command.rb
CHANGED
@@ -20,12 +20,18 @@ module Backticks
|
|
20
20
|
# @return [Integer] child process ID
|
21
21
|
attr_reader :pid
|
22
22
|
|
23
|
-
# @return [String] all data captured (so far) from child's stdin/stdout/stderr
|
24
|
-
attr_reader :captured_input, :captured_output, :captured_error
|
25
|
-
|
26
23
|
# @return [nil,Process::Status] result of command if it has ended; nil if still running
|
27
24
|
attr_reader :status
|
28
25
|
|
26
|
+
# @return [String] all input that has been captured so far
|
27
|
+
attr_reader :captured_input
|
28
|
+
|
29
|
+
# @return [String] all output that has been captured so far
|
30
|
+
attr_reader :captured_output
|
31
|
+
|
32
|
+
# @return [String] all output to stderr that has been captured so far
|
33
|
+
attr_reader :captured_error
|
34
|
+
|
29
35
|
# Watch a running command.
|
30
36
|
def initialize(pid, stdin, stdout, stderr)
|
31
37
|
@pid = pid
|
@@ -38,10 +44,23 @@ module Backticks
|
|
38
44
|
@captured_error = String.new.force_encoding(Encoding::BINARY)
|
39
45
|
end
|
40
46
|
|
47
|
+
# @return [String]
|
48
|
+
def to_s
|
49
|
+
"#<Backticks::Command(@pid=#{pid},@status=#{@status || 'nil'})>"
|
50
|
+
end
|
51
|
+
|
41
52
|
def interactive?
|
42
53
|
!@stdin.nil?
|
43
54
|
end
|
44
55
|
|
56
|
+
# Provide a callback to monitor input and output in real time.
|
57
|
+
# @yield
|
58
|
+
# @yieldparam
|
59
|
+
def tap(&block)
|
60
|
+
raise StandardError.new("Tap is already set (#{@tap}); cannot set twice") if @tap && @tap != block
|
61
|
+
@tap = block
|
62
|
+
end
|
63
|
+
|
45
64
|
# Block until the command exits, or until limit seconds have passed. If
|
46
65
|
# interactive is true, pass user input to the command and print its output
|
47
66
|
# to Ruby's output streams. If the time limit expires, return `nil`;
|
@@ -73,14 +92,13 @@ module Backticks
|
|
73
92
|
# - the command produces fresh output on stdout or stderr
|
74
93
|
# - the user passes some input to the command (if interactive)
|
75
94
|
# - the process exits
|
76
|
-
# - the time limit elapses (if provided)
|
95
|
+
# - the time limit elapses (if provided) OR 60 seconds pass
|
77
96
|
#
|
78
97
|
# Return up to CHUNK bytes of fresh output from the process, or return nil
|
79
98
|
# if no fresh output was produced
|
80
99
|
#
|
81
100
|
# @param [Float,Integer] number of seconds to wait before returning nil
|
82
101
|
# @return [String,nil] fresh bytes from stdout/stderr, or nil if no output
|
83
|
-
private
|
84
102
|
def capture(limit=nil)
|
85
103
|
streams = [@stdout, @stderr]
|
86
104
|
streams << STDIN if interactive?
|
@@ -91,15 +109,19 @@ module Backticks
|
|
91
109
|
tf = FOREVER
|
92
110
|
end
|
93
111
|
|
94
|
-
ready, _, _ = IO.select(streams, [], [],
|
112
|
+
ready, _, _ = IO.select(streams, [], [], 0)
|
95
113
|
|
96
114
|
# proxy STDIN to child's stdin
|
97
115
|
if ready && ready.include?(STDIN)
|
98
|
-
|
99
|
-
if
|
100
|
-
@
|
101
|
-
|
116
|
+
data = STDIN.readpartial(CHUNK) rescue nil
|
117
|
+
if data
|
118
|
+
data = @tap.call(:stdin, data) if @tap
|
119
|
+
if data
|
120
|
+
@captured_input << data
|
121
|
+
@stdin.write(data)
|
122
|
+
end
|
102
123
|
else
|
124
|
+
@tap.call(:stdin, nil) if @tap
|
103
125
|
# our own STDIN got closed; proxy this fact to the child
|
104
126
|
@stdin.close unless @stdin.closed?
|
105
127
|
end
|
@@ -109,9 +131,12 @@ module Backticks
|
|
109
131
|
if ready && ready.include?(@stdout)
|
110
132
|
data = @stdout.readpartial(CHUNK) rescue nil
|
111
133
|
if data
|
112
|
-
@
|
113
|
-
|
114
|
-
|
134
|
+
data = @tap.call(:stdout, data) if @tap
|
135
|
+
if data
|
136
|
+
@captured_output << data
|
137
|
+
STDOUT.write(data) if interactive?
|
138
|
+
fresh_output = data
|
139
|
+
end
|
115
140
|
end
|
116
141
|
end
|
117
142
|
|
@@ -119,8 +144,11 @@ module Backticks
|
|
119
144
|
if ready && ready.include?(@stderr)
|
120
145
|
data = @stderr.readpartial(CHUNK) rescue nil
|
121
146
|
if data
|
122
|
-
@
|
123
|
-
|
147
|
+
data = @tap.call(:stderr, data) if @tap
|
148
|
+
if data
|
149
|
+
@captured_error << data
|
150
|
+
STDERR.write(data) if interactive?
|
151
|
+
end
|
124
152
|
end
|
125
153
|
end
|
126
154
|
fresh_output
|
data/lib/backticks/runner.rb
CHANGED
@@ -15,12 +15,8 @@ module Backticks
|
|
15
15
|
# process so the user can view their output and send input to them.
|
16
16
|
# Commands' output is still captured normally when they are interactive.
|
17
17
|
#
|
18
|
-
# Note
|
19
|
-
#
|
20
|
-
# buffers pipe I/O. If you want to send some input to your command, you
|
21
|
-
# may need to send a LOT of input before it receives any; the same problem
|
22
|
-
# applies to reading your command's output. If you set interactive to
|
23
|
-
# true, you usually want to set buffered to false!
|
18
|
+
# Note: if you set `interactive` to true, then stdin and stdout will be
|
19
|
+
# unbuffered regardless of how you have set `buffered`!
|
24
20
|
#
|
25
21
|
# @return [Boolean]
|
26
22
|
attr_accessor :interactive
|
@@ -29,7 +25,10 @@ module Backticks
|
|
29
25
|
# a pseudoterminal.
|
30
26
|
#
|
31
27
|
# This may be a Boolean, or it may be an Array of stream names from the
|
32
|
-
# set [:stdin, stdout, stderr].
|
28
|
+
# set [:stdin, :stdout, :stderr].
|
29
|
+
#
|
30
|
+
# Note: if you set `interactive` to true, then stdin and stdout will be
|
31
|
+
# unbuffered regardless of how you have set `buffered`!
|
33
32
|
#
|
34
33
|
# @return [Array] list of symbolic stream names
|
35
34
|
attr_reader :buffered
|
@@ -66,12 +65,6 @@ module Backticks
|
|
66
65
|
end
|
67
66
|
end
|
68
67
|
|
69
|
-
# @deprecated
|
70
|
-
def command(*sugar)
|
71
|
-
warn 'Backticks::Runner#command is deprecated; please call #run instead'
|
72
|
-
run(*sugar)
|
73
|
-
end
|
74
|
-
|
75
68
|
# Run a command whose parameters are expressed using some Rubyish sugar.
|
76
69
|
# This method accepts an arbitrary number of positional parameters; each
|
77
70
|
# parameter can be a Hash, an array, or a simple Object. Arrays and simple
|
data/lib/backticks/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: backticks
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0rc1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tony Spataro
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-07-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -90,9 +90,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
90
90
|
version: '2.0'
|
91
91
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
92
92
|
requirements:
|
93
|
-
- - "
|
93
|
+
- - ">"
|
94
94
|
- !ruby/object:Gem::Version
|
95
|
-
version:
|
95
|
+
version: 1.3.1
|
96
96
|
requirements: []
|
97
97
|
rubyforge_project:
|
98
98
|
rubygems_version: 2.4.5
|