backticks 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +4 -0
- data/README.md +63 -0
- data/Rakefile +6 -0
- data/backticks.gemspec +25 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/lib/backticks.rb +22 -0
- data/lib/backticks/cli.rb +107 -0
- data/lib/backticks/command.rb +124 -0
- data/lib/backticks/runner.rb +93 -0
- data/lib/backticks/version.rb +3 -0
- metadata +102 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 4c09ea76ce613d4920ab0f2f0102fa56eb0ddc86
|
4
|
+
data.tar.gz: 013d631b062d46df861787bf64107cf802fe4580
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4d05727a43ef3634d734eef36990cf8915072da01ccca1e57993a58b83fc5af2d638366e3d3c9fbe123d7aa99b0e03a9239a79cc178589cb1b0c6d411fab7421
|
7
|
+
data.tar.gz: 60dba8be758e0ca850c573882ce14ed3da1200f9a6cb62020b2a9b92e2590b7acb396fd6b2914572b5337d7c9b92c559aa2f429158e203beff3c682ff6e80abf
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# Contributor Code of Conduct
|
2
|
+
|
3
|
+
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
|
4
|
+
|
5
|
+
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
|
6
|
+
|
7
|
+
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
|
8
|
+
|
9
|
+
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
|
10
|
+
|
11
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
|
12
|
+
|
13
|
+
This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
# Backticks
|
2
|
+
|
3
|
+
Backticks is an intuitive OOP wrapper for invoking command-line processes and
|
4
|
+
interacting with them. It uses PTYs
|
5
|
+
|
6
|
+
By default, processes that you invoke
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
Add this line to your application's Gemfile:
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
gem 'backticks'
|
14
|
+
```
|
15
|
+
|
16
|
+
And then execute:
|
17
|
+
|
18
|
+
$ bundle
|
19
|
+
|
20
|
+
Or install it yourself as:
|
21
|
+
|
22
|
+
$ gem install backticks
|
23
|
+
|
24
|
+
## Usage
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
require 'backticks'
|
28
|
+
|
29
|
+
# The easy way
|
30
|
+
output = Backticks.command('ls', R:true, '*.rb')
|
31
|
+
puts "Exit status #{$?.to_i}. Output:"
|
32
|
+
puts output
|
33
|
+
|
34
|
+
# The hard way; allows customization such as interactive mode, which proxies
|
35
|
+
# the child process's stdin, stdout and stderr to the parent process.
|
36
|
+
command = Backticks::Runner.new(interactive:true).command('ls', R:true, '*.rb')
|
37
|
+
command.join
|
38
|
+
puts "Exit status: #{command.status.to_i}. Output:"
|
39
|
+
puts command.captured_output
|
40
|
+
```
|
41
|
+
|
42
|
+
### Buffering
|
43
|
+
|
44
|
+
By default, Backticks allocates a pseudo-TTY for stdout and two Unix pipes for
|
45
|
+
stderr/stdin; this captures stdout in real-time, but stderr and
|
46
|
+
stdin are subject to unavoidable Unix pipe buffering.
|
47
|
+
|
48
|
+
To use pipes for all io streams, enable buffering when you construct your
|
49
|
+
Runner:
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
Backticks::Runner.new(buffered:true)
|
53
|
+
```
|
54
|
+
|
55
|
+
## Development
|
56
|
+
|
57
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
58
|
+
|
59
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
60
|
+
|
61
|
+
## Contributing
|
62
|
+
|
63
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/xeger/backticks. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct.
|
data/Rakefile
ADDED
data/backticks.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'backticks/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "backticks"
|
8
|
+
spec.version = Backticks::VERSION
|
9
|
+
spec.authors = ["Tony Spataro"]
|
10
|
+
spec.email = ["xeger@xeger.net"]
|
11
|
+
|
12
|
+
spec.summary = %q{Intuitive OOP wrapper for command-line processes}
|
13
|
+
spec.description = %q{Captures processes' stdout, stderr and (optionally) stdin; uses PTY to avoid buffering.}
|
14
|
+
spec.homepage = "https://github.com/xeger/backticks"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
spec.bindir = "exe"
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.10"
|
23
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
24
|
+
spec.add_development_dependency "rspec"
|
25
|
+
end
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "backticks"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/lib/backticks.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require "backticks/version"
|
2
|
+
require "backticks/cli"
|
3
|
+
require "backticks/command"
|
4
|
+
require "backticks/runner"
|
5
|
+
|
6
|
+
module Backticks
|
7
|
+
# Run a command.
|
8
|
+
#
|
9
|
+
# @return [Backticks::Command] a running command
|
10
|
+
def self.new(*argv)
|
11
|
+
Backticks::Runner.new.command(*argv)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Run a command and return its stdout.
|
15
|
+
#
|
16
|
+
# @return [String] the command's output
|
17
|
+
def self.command(*argv)
|
18
|
+
command = self.new(*argv)
|
19
|
+
command.join
|
20
|
+
command.captured_output
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
module Backticks
|
2
|
+
module CLI
|
3
|
+
# Command-line parameter generator that relies on traditional *nix getopt
|
4
|
+
# conventions. Getopt doesn't know about GNU conventions such as short and
|
5
|
+
# long options; it doesn't know about abbreviations; it doesn't know about
|
6
|
+
# conventions such as `--X` vs. `--no-X` or `-d` vs. `-D`.
|
7
|
+
#
|
8
|
+
# Although Getopt is simple, it has the tremendous advantage of being
|
9
|
+
# compatible with a wide range of other schemes including GNU getopt-long,
|
10
|
+
# golang flags, and most Java utilities. It's a great choice of default
|
11
|
+
# CLI.
|
12
|
+
module Getopt
|
13
|
+
# Translate a series of positional and keyword arguments into command-line
|
14
|
+
# line parameters consisting of words and options.
|
15
|
+
#
|
16
|
+
# Each positional argument can be a Hash, an Array, or another object.
|
17
|
+
# They are handled as follows:
|
18
|
+
# - Hash is translated to a sequence of options; see #options
|
19
|
+
# - Array is appended to the command line as a sequence of words
|
20
|
+
# - other objects are turned into a strong with #to_s and appended to the command line as a single word
|
21
|
+
#
|
22
|
+
# @return [Array] list of String words and options
|
23
|
+
#
|
24
|
+
# @example recursively find all text files
|
25
|
+
# parameters('ls', l:true, R:true, '*.txt') => 'ls -l -R *.txt
|
26
|
+
#
|
27
|
+
# @example install your favorite gem
|
28
|
+
# parameters('gem', 'install', no_document:true, 'backticks')
|
29
|
+
def self.parameters(*cmd)
|
30
|
+
argv = []
|
31
|
+
|
32
|
+
cmd.each do |item|
|
33
|
+
case item
|
34
|
+
when Array
|
35
|
+
# list of words to append to argv
|
36
|
+
argv.concat(item.map { |e| e.to_s })
|
37
|
+
when Hash
|
38
|
+
# list of options to convert to CLI parameters
|
39
|
+
argv.concat(options(item))
|
40
|
+
else
|
41
|
+
# single word to append to argv
|
42
|
+
argv << item.to_s
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
argv
|
47
|
+
end
|
48
|
+
|
49
|
+
# Translate Ruby method parameters into command-line parameters using a
|
50
|
+
# notation that is compatible with traditional Unix getopt. Command lines
|
51
|
+
# generated by this method are also mostly compatible with the following:
|
52
|
+
# - GNU getopt
|
53
|
+
# - Ruby trollop gem
|
54
|
+
# - Golang flags package
|
55
|
+
#
|
56
|
+
# This method accepts an unbounded set of keyword arguments (i.e. you can
|
57
|
+
# pass it _any_ valid Ruby symbol as a kwarg). Each kwarg has a
|
58
|
+
# value; the key/value pair is translated into a CLI option using the
|
59
|
+
# following heuristic:
|
60
|
+
# 1) Snake-case keys are hyphenated, e.g. :no_foo => "--no-foo"
|
61
|
+
# 2) boolean values indicate a CLI flag; true includes the flag, false or nil omits it
|
62
|
+
# 3) all other values indicate a CLI option that has a value.
|
63
|
+
# 4) single character keys are passed as short options; {X: V} becomes "-X V"
|
64
|
+
# 5) multi-character keys are passed as long options; {Xxx: V} becomes "--XXX=V"
|
65
|
+
#
|
66
|
+
# The generic translator doesn't know about short vs. long option names,
|
67
|
+
# abbreviations, or the GNU "X vs. no-X" convention, so it does not
|
68
|
+
# produce the most idiomatic or compact command line for a given program;
|
69
|
+
# its output is, however, almost always valid for utilities that use
|
70
|
+
# Unix-like parameters.
|
71
|
+
#
|
72
|
+
# @return [Array] list of String command-line options
|
73
|
+
def self.options(**opts)
|
74
|
+
flags = []
|
75
|
+
|
76
|
+
# Transform opts into golang flags-style command line parameters;
|
77
|
+
# append them to the command.
|
78
|
+
opts.each do |kw, arg|
|
79
|
+
if kw.length == 1
|
80
|
+
if arg == true
|
81
|
+
# true: boolean flag
|
82
|
+
flags << "-#{kw}"
|
83
|
+
elsif arg
|
84
|
+
# truthey: option that has a value
|
85
|
+
flags << "-#{kw}" << arg.to_s
|
86
|
+
else
|
87
|
+
# falsey: omit boolean flag
|
88
|
+
end
|
89
|
+
else
|
90
|
+
kw = kw.to_s.gsub('_','-')
|
91
|
+
if arg == true
|
92
|
+
# true: boolean flag
|
93
|
+
flags << "--#{kw}"
|
94
|
+
elsif arg
|
95
|
+
# truthey: option that has a value
|
96
|
+
flags << "--#{kw}=#{arg}"
|
97
|
+
else
|
98
|
+
# falsey: omit boolean flag
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
flags
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
module Backticks
|
2
|
+
# Represents a running process; provides mechanisms for capturing the process's
|
3
|
+
# output, passing input, waiting for the process to end, and learning its
|
4
|
+
# exitstatus.
|
5
|
+
#
|
6
|
+
# Interactive commands print their output to Ruby's STDOUT and STDERR
|
7
|
+
# in realtime, and also pass input from Ruby's STDIN to the command's stdin.
|
8
|
+
class Command
|
9
|
+
# Time value that is used internally when a user is willing to wait
|
10
|
+
# "forever" for the command.
|
11
|
+
#
|
12
|
+
# Using a definite time-value helps simplify the looping logic internally,
|
13
|
+
# but it does mean that this class will stop working in February of 2106.
|
14
|
+
# You have been warned!
|
15
|
+
FOREVER = Time.at(2**32-1).freeze
|
16
|
+
|
17
|
+
# Number of bytes to read from the command in one "chunk".
|
18
|
+
CHUNK = 1_024
|
19
|
+
|
20
|
+
# @return [Integer] child process ID
|
21
|
+
attr_reader :pid
|
22
|
+
|
23
|
+
# @return [String] all data captured (so far) from child's stdin/stdout/stderr
|
24
|
+
attr_reader :captured_input, :captured_output, :captured_error, :status
|
25
|
+
|
26
|
+
# Watch a running command.
|
27
|
+
def initialize(pid, stdin, stdout, stderr, interactive:false)
|
28
|
+
@pid = pid
|
29
|
+
@stdin = stdin
|
30
|
+
@stdout = stdout
|
31
|
+
@stderr = stderr
|
32
|
+
@interactive = interactive
|
33
|
+
|
34
|
+
@captured_input = String.new.force_encoding(Encoding::BINARY)
|
35
|
+
@captured_output = String.new.force_encoding(Encoding::BINARY)
|
36
|
+
@captured_error = String.new.force_encoding(Encoding::BINARY)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Block until the command exits, or until limit seconds have passed. If
|
40
|
+
# interactive is true, pass user input to the command and print its output
|
41
|
+
# to Ruby's output streams. If the time limit expires, return `nil`;
|
42
|
+
# otherwise, return self.
|
43
|
+
#
|
44
|
+
# @param [Float,Integer] limit number of seconds to wait before returning
|
45
|
+
def join(limit=nil)
|
46
|
+
if limit
|
47
|
+
tf = Time.now + limit
|
48
|
+
else
|
49
|
+
tf = FOREVER
|
50
|
+
end
|
51
|
+
|
52
|
+
until (t = Time.now) >= tf
|
53
|
+
capture(tf - t)
|
54
|
+
res = Process.waitpid(@pid, Process::WNOHANG)
|
55
|
+
if res
|
56
|
+
@status = $?
|
57
|
+
return self
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
return nil
|
62
|
+
end
|
63
|
+
|
64
|
+
# Block until one of the following happens:
|
65
|
+
# - the command produces fresh output on stdout or stderr
|
66
|
+
# - the user passes some input to the command (if interactive)
|
67
|
+
# - the process exits
|
68
|
+
# - the time limit elapses (if provided)
|
69
|
+
#
|
70
|
+
# Return up to CHUNK bytes of fresh output from the process, or return nil
|
71
|
+
# if no fresh output was produced
|
72
|
+
#
|
73
|
+
# @param [Float,Integer] number of seconds to wait before returning nil
|
74
|
+
# @return [String,nil] fresh bytes from stdout/stderr, or nil if no output
|
75
|
+
private def capture(limit=nil)
|
76
|
+
streams = [@stdout, @stderr]
|
77
|
+
streams << STDIN if @interactive
|
78
|
+
|
79
|
+
if limit
|
80
|
+
tf = Time.now + limit
|
81
|
+
else
|
82
|
+
tf = FOREVER
|
83
|
+
end
|
84
|
+
|
85
|
+
ready, _, _ = IO.select(streams, [], [], 1)
|
86
|
+
|
87
|
+
# proxy STDIN to child's stdin
|
88
|
+
if ready && ready.include?(STDIN)
|
89
|
+
input = STDIN.readpartial(CHUNK) rescue nil
|
90
|
+
@captured_input << input
|
91
|
+
if input
|
92
|
+
@stdin.write(input)
|
93
|
+
else
|
94
|
+
# our own STDIN got closed; proxy this fact to the child
|
95
|
+
@stdin.close
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# capture child's stdout and maybe proxy to STDOUT
|
100
|
+
if ready && ready.include?(@stdout)
|
101
|
+
data = @stdout.readpartial(CHUNK) rescue nil
|
102
|
+
if data
|
103
|
+
@captured_output << data
|
104
|
+
STDOUT.write(data) if @interactive
|
105
|
+
fresh_output = data
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# capture child's stderr and maybe proxy to STDERR
|
110
|
+
if ready && ready.include?(@stderr)
|
111
|
+
data = @stderr.readpartial(CHUNK) rescue nil
|
112
|
+
if data
|
113
|
+
@captured_error << data
|
114
|
+
STDERR.write(data) if @interactive
|
115
|
+
end
|
116
|
+
end
|
117
|
+
fresh_output
|
118
|
+
rescue Interrupt
|
119
|
+
# Proxy Ctrl+C to the child
|
120
|
+
(Process.kill('INT', @pid) rescue nil) if @interactive
|
121
|
+
raise
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'pty'
|
2
|
+
|
3
|
+
module Backticks
|
4
|
+
# An easy-to-use interface for invoking commands and capturing their output.
|
5
|
+
# Instances of Runner can be interactive, which prints the command's output
|
6
|
+
# to the terminal and also allows the user to interact with the command.
|
7
|
+
# They can also be unbuffered, which uses a pseudo-tty to capture the
|
8
|
+
# command's output with no delay or
|
9
|
+
class Runner
|
10
|
+
# If true, commands will have their stdio streams tied to the parent
|
11
|
+
# process so the user can view their output and send input to them.
|
12
|
+
# Commands' output is still captured normally when they are interactive.
|
13
|
+
#
|
14
|
+
# Note that interactivity doesn't work very well with unbuffered commands;
|
15
|
+
# we use pipes to connect to the command's stdio, and the OS forcibly
|
16
|
+
# buffers pipe I/O. If you want to send some input to your command, you
|
17
|
+
# may need to send a LOT of input before it receives any; the same problem
|
18
|
+
# applies to reading your command's output. If you set interactive to
|
19
|
+
# true, you usually want to set buffered to false!
|
20
|
+
#
|
21
|
+
# @return [Boolean]
|
22
|
+
attr_accessor :interactive
|
23
|
+
|
24
|
+
# If true, commands will be invoked with a pseudo-TTY for stdout in order
|
25
|
+
# to capture output as it is generated instead of waiting for pipe buffers
|
26
|
+
# to fill.
|
27
|
+
#
|
28
|
+
# @return [Boolean]
|
29
|
+
attr_accessor :buffered
|
30
|
+
|
31
|
+
# Create an instance of Runner.
|
32
|
+
# @param [#parameters] cli object ysed to convert Ruby method parameters into command-line parameters
|
33
|
+
def initialize(cli:Backticks::CLI::Getopt)
|
34
|
+
@interactive = false
|
35
|
+
@buffered = false
|
36
|
+
@cli = cli
|
37
|
+
end
|
38
|
+
|
39
|
+
# Run a command whose parameters are expressed using some Rubyish sugar.
|
40
|
+
# This method accepts an arbitrary number of positional parameters; each
|
41
|
+
# parameter can be a Hash, an array, or a simple Object. Arrays and simple
|
42
|
+
# objects are appended to argv as "bare" words; Hashes are translated to
|
43
|
+
# command-line options and then appended to argv.
|
44
|
+
#
|
45
|
+
# The
|
46
|
+
#
|
47
|
+
# @return [Command] the running command
|
48
|
+
#
|
49
|
+
# @example Run docker-compose with complex parameters
|
50
|
+
# command('docker-compose', {file: 'joe.yml'}, 'up', {d:true}, 'mysvc')
|
51
|
+
#
|
52
|
+
# @see #options for information on Hash-to-flag translation
|
53
|
+
def command(*cmd)
|
54
|
+
argv = @cli.parameters(*cmd)
|
55
|
+
|
56
|
+
if self.buffered
|
57
|
+
run_buffered(argv)
|
58
|
+
else
|
59
|
+
run_unbuffered(argv)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Run a command. Use a pty to capture the unbuffered output.
|
64
|
+
#
|
65
|
+
# @param [Array] argv command to run; argv[0] is program name and the
|
66
|
+
# remaining elements are parameters and flags
|
67
|
+
# @return [Command] the running command
|
68
|
+
private def run_unbuffered(argv)
|
69
|
+
stdout, stdout_w = PTY.open
|
70
|
+
stdin_r, stdin = IO.pipe
|
71
|
+
stderr, stderr_w = IO.pipe
|
72
|
+
pid = spawn(*argv, in: stdin_r, out: stdout_w, err: stderr_w)
|
73
|
+
stdin_r.close
|
74
|
+
stdout_w.close
|
75
|
+
stderr_w.close
|
76
|
+
|
77
|
+
Command.new(pid, stdin, stdout, stderr, interactive:@interactive)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Run a command. Perform no translation or substitution. Use a pipe
|
81
|
+
# to read the output, which may be buffered by the OS. Return the program's
|
82
|
+
# exit status and stdout.
|
83
|
+
#
|
84
|
+
# @param [Array] argv command to run; argv[0] is program name and the
|
85
|
+
# remaining elements are command-line arguments.
|
86
|
+
# @return [Command] the running command
|
87
|
+
private def run_buffered(argv)
|
88
|
+
stdin, stdout, stderr, thr = Open3.popen3(*argv)
|
89
|
+
|
90
|
+
Command.new(thr.pid, stdin, stdout, stderr, interactive:@interactive)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
metadata
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: backticks
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tony Spataro
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-12-24 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.10'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.10'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: Captures processes' stdout, stderr and (optionally) stdin; uses PTY to
|
56
|
+
avoid buffering.
|
57
|
+
email:
|
58
|
+
- xeger@xeger.net
|
59
|
+
executables: []
|
60
|
+
extensions: []
|
61
|
+
extra_rdoc_files: []
|
62
|
+
files:
|
63
|
+
- ".gitignore"
|
64
|
+
- ".rspec"
|
65
|
+
- ".travis.yml"
|
66
|
+
- CODE_OF_CONDUCT.md
|
67
|
+
- Gemfile
|
68
|
+
- README.md
|
69
|
+
- Rakefile
|
70
|
+
- backticks.gemspec
|
71
|
+
- bin/console
|
72
|
+
- bin/setup
|
73
|
+
- lib/backticks.rb
|
74
|
+
- lib/backticks/cli.rb
|
75
|
+
- lib/backticks/command.rb
|
76
|
+
- lib/backticks/runner.rb
|
77
|
+
- lib/backticks/version.rb
|
78
|
+
homepage: https://github.com/xeger/backticks
|
79
|
+
licenses:
|
80
|
+
- MIT
|
81
|
+
metadata: {}
|
82
|
+
post_install_message:
|
83
|
+
rdoc_options: []
|
84
|
+
require_paths:
|
85
|
+
- lib
|
86
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
requirements: []
|
97
|
+
rubyforge_project:
|
98
|
+
rubygems_version: 2.4.5
|
99
|
+
signing_key:
|
100
|
+
specification_version: 4
|
101
|
+
summary: Intuitive OOP wrapper for command-line processes
|
102
|
+
test_files: []
|