README.md in backticks-0.1.1 vs README.md in backticks-0.3.0

- old
+ new

@@ -1,12 +1,31 @@ # Backticks Backticks is an intuitive OOP wrapper for invoking command-line processes and -interacting with them. It uses PTYs +interacting with them. It improves on Ruby's built-in invocation methods in a +few ways: + - Uses [pseudoterminals](https://en.wikipedia.org/wiki/Pseudoterminal) for unbuffered I/O + - Captures input as well as output + - Intuitive API that accepts CLI parameters as Ruby positional and keyword args -By default, processes that you invoke +If you want to write a record/playback application for the terminal, or write +functional tests that verify your program's output in real time, Backticks is +exactly what you've been looking for! +For an example of the intuitive API, let's consider how we list a bunch of +files or search for some text with Backticks: + +```ruby +# invokes "ls -l -R" +Backticks.run 'ls', l:true, R:true + +# invokes "grep -H --context=2 --regexp=needle haystack.txt" +Backticks.run 'grep', {H:true, context:2, regexp:'needle'}, 'haystack.txt' +``` + +Notice how running commands feels like a plain old Ruby method call. + ## Installation Add this line to your application's Gemfile: ```ruby @@ -24,43 +43,75 @@ ## Usage ```ruby require 'backticks' -# The lazy way; provides no CLI sugar, but benefits from unbuffered output. -# Many Unix utilities produce colorized output when stdout is a TTY; be -# prepared to handle escape codes in the output. +# The lazy way; provides no CLI sugar, but benefits from unbuffered output, +# and allows you to override Ruby's built-in backticks method. shell = Object.new ; shell.extend(Backticks::Ext) shell.instance_eval do puts `ls -l` raise 'Oh no!' unless $?.success? end +# The just-as-lazy but less-magical way. +Backticks.system('ls -l') || raise('Oh no!') -# The easy way. -output = Backticks.command('ls', R:true, '*.rb') +# The easy way. Uses default options; returns the command's output as a String. +output = Backticks.run('ls', R:true, '*.rb') puts "Exit status #{$?.to_i}. Output:" puts output -# The hard way; allows customization such as interactive mode, which proxies -# the child process's stdin, stdout and stderr to the parent process. +# The hard way. Allows customized behavior; returns a Command object that +# allows you to interact with the running command. command = Backticks::Runner.new(interactive:true).command('ls', R:true, '*.rb') command.join puts "Exit status: #{command.status.to_i}. Output:" puts command.captured_output ``` ### Buffering -By default, Backticks allocates a pseudo-TTY for stdout and two Unix pipes for -stderr/stdin; this captures stdout in real-time, but stderr and -stdin are subject to unavoidable Unix pipe buffering. +By default, Backticks allocates a pseudo-TTY for stdin/stdout and a Unix pipe +for stderr; this captures the program's output and the user's input in realtime, +but stderr is buffered according to the whim of the kernel's pipe subsystem. -To use pipes for all io streams, enable buffering when you construct your -Runner: +To use pipes for all I/O streams, enable buffering on the Runner: ```ruby -Backticks::Runner.new(buffered:true) +# at initialize-time +r = Backticks::Runner.new(buffered:true) + +# or later on +r.buffered = false ``` + +### Interactivity + +If you set `interactive:true` on the Runner, the console of the calling (Ruby) +process is "tied" to the child's I/O streams, allowing the user to interact +with the child process even as its input and output are captured for later use. + +If the child process will use raw input, you need to set the parent's console +accordingly: + +```ruby +require 'io/console' +# In IRB, call raw! on same line as command; IRB prompt uses raw I/O +STDOUT.raw! ; Backticks::Runner.new(interactive:true).command('vi').join +``` + +### Literally Overriding Ruby's Backticks + +It's a terrible idea, but you can use this gem to change the behavior of +backticks system-wide by mixing it into Kernel. + +```ruby +require 'backticks' +include Backticks::Ext +`echo Ruby lets me shoot myself in the foot` +``` + +If you do this, I will hunt you down and scoff at you. You have been warned! ## Development 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.