How can I make RSpec output to console when run as a command %x[rspec] from Ruby script? - ruby

I have a class with an instance method that runs RSpec using the %x[] notation:
class TestRunner
def run_rspec
# do stuff
%x[rspec spec -c -f documentation]
# do more stuff
end
end
When I do this:
> tr = TestRunner.new
> tr.run_rspec
The documentation (group and example names) does not appear in the console.
To contrast, when I run rspec straight from the command line I get this:
$ rspec spec -c -f documentation
a group name
an example
another example
...
I don't want to do this:
puts %x[rspec spec -c -f documentation
Because then the output all spits out in one huge clump at the very end. I want it to run in "real time," with each example showing up as each test is run.
Is there a way, with the setup I have, to get RSpec to announce what it's doing, as it's doing it (as it does when run normally from the command line)?

I've been advised that system() and the other shell methods can be dangerous to use, so I've opted to switch to the even-better approach of using RSpec itself:
RSpec::Core::Runner.run(['spec', '-c', '-f', 'documentation'])
rather than calling it via shell from my Ruby script.
Ruby offers several options for running programs from the command line. I was using %x[], the wrong choice for my use case.
Solution: Use system(), not %x[] -- rspec will write to STDOUT in real-time when I call it with system('rspec spec').
Some background in case it's helpful to anyone who stumbles upon this question:
Consider the differences between Ruby's command-line options:
%x[command] accumulates the result of command and returns it, in one chunk.
exec('command') will output command as command runs, but will replace whatever process called it -- i.e., if you use exec in your Ruby script, your Ruby script won't finish.
system('command') executes command in a subshell, and returns to the calling script.
This is why system was the choice for my script.

Related

With Ruby: how can I test to see if a linux command is available without returning the output of the command I'm testing for?

I'm using Ruby on Linux.
I'd like to test for the existence of a command on the Linux system.
I'd like to not get back the output of the command that I'm testing for.
I'd also like to not get back any output that results from the shell being unable to find the command.
I want to avoid using shell redirection from within the command that I send to the shell. So something like system("foo > /dev/null") would be unsuitable.
I'm ok with using redirection if there is a way to do it from Ruby.
The simplest thing would be just to use system. Let's say you're looking for ls.
irb(main):005:0> system("which ls")
/bin/ls
=> true
If that's off the table, you could peek into the directories in ENV["PATH"] for the executable you're looking for. ENV["PATH"].split(":") would give you an array of directory names to check for the desired command. If you find a file with the right name, you may want to ensure it's an executable.
I want to avoid using shell redirection from within the command that I
send to the shell. So something like system("foo > /dev/null") would
be unsuitable. I'm ok with using redirection if there is a way to do it from Ruby.
system("exec which cmd", out: "/dev/null")
puts "Command is available." if ($?).success?
The exec is to explicitly avoid unnecessary forking in the shell.
As a sidenote type -P can be used instead of which, but it relies on Bash and may have surprising effects if script is ported to an environment with a different default shell.

Execute commands and print their output

Is there a way in ruby to execute a command line utility and have its output displayed in real time, something like myrubyscript rspec, where myrubyscript runs rspec and immediately prints output as it receives it from rspec?
Currently, if I invoke rspec with backticks or system, I don't see the rspec output immediately. Rather, it prints at the end.
Preferably, I want the solution to have block form, so that I run code before and after I exec the passed argument.
Use Open3.popen3.
require "open3"
Open3.popen3...

how to build wrapper script

Sort of an odd question, but: how would one go about creating a wrapper shell script that can be used in the #! line in other scripts.
wrap.sh
#!/bin/bash
set -e
echo "wrapper!"
exec ruby "$#"
test.rb
#!/usr/bin/env wrap.sh
puts RUBY_VERSION
puts "the ducks come from the trucks"
wrap.sh is in the path, and test.rb is marked as executable.
Now I do:
./test.rb
wrapper!
ruby: no Ruby script found in input (LoadError)
The goal is to execute the ruby script via the wrapper (The ruby version can be either local or comes from a traveling ruby install that is shipped along with the app).
As far as I can tell ruby is invoked, it's just unhappy with the #! in the test.rb and refuses to run the script. I cannot remove the #! because that's how the script is executed in the first place.
Any workarounds for this?
So, I cannot use rbenv/rvm/etc. There is more logic in the wrapper than this, but this is the gist of it.
Looks to me like the arguments are not being passed to Ruby in "$#". I don't think the bang-hash line is the problem.
I don't see anything in your script which actually passes the contents of test.rb to wrapper.sh, which is the bigger issue.
Perhaps the real problem can be solved by some other means? For example, is the problem you're trying to solve to run arbitrary commands prior to the invocation of any Ruby script from the command line? Perhaps it can be approached that way...
It looks like Ruby just checks that the hash-bang line contains "ruby": https://github.com/ruby/ruby/blob/v2_2_2/ruby.c#L1580 So basically having ruby somewhere in the #! line is all it takes.

How can I preserve output color when executing a process in Ruby?

I'm using a helper script to execute rspec tests.
command = "rake spec #{path} #{scope}"
output = `#{command}`
puts output
This works fine, except that I lose all the colors from the rake rspec output. The appropriate ANSI codes do not appear to be contained within the output string.
How can I execute a process so that it returns output which includes the text color?
Kernel.exec() gets me the solution I want (colored rspec output), but it does so by replacing my ruby script process with the rspec process. That means I can't do anything with the output or run anything after the rspec call.
That's acceptable in my particular situation, but less than ideal as a general solution. So I'd like a better answer if available.
RSpec will disable colour if it is not writing to a tty (i.e. the console).
In case of RSpec you can force colouring by rspec --tty or via rake by rake spec SPEC_OPTS=' --tty'.
See also Ruby popen3 and ANSI colour
However this solution is still specific to Rspec. I'd be interested to hear a general one.
Turns out it's possible to run commands in a pseudo terminal via the PTY module in order to preserve a user facing terminal-like behaviour. Credits go to the creator of the tty-command gem (see this issue) who implemented this behaviour in his gem:
require 'tty-command'
cmd = TTY::Command.new(pty: true)
cmd.run('rake', 'rspec')
Keep in mind that using a pseudo terminal may have unwanted side effects, such as certain git commands using a pager which will essentially cause commands to hang. So introducing the functionality might be a breaking change.
If you don't want to replace your ruby process with that command, use Kernel.system() or Kernel.spawn() instead of a Kernel.exec(). Both of them execute your command in a subshell, system waits for the subprocess to finish, spawn returns its pid and you have to wait by yourself using Process.wait pid.
command = "rake spec #{path} #{scope}"
system(command)
or
command = "rake spec #{path} #{scope}"
pid = spawn(command)
# Some other stuff here
Process.wait pid

is it possible to pass through command-line options to a script in rspec

I have an RSpec script that tests a program in a different language I am developing. Since I can run and test 32 and 64-bit versions of this application, I would like to have a way to signal this on the command-line.
What I really want is to do something like this:
rspec -c myspec.rb lin32
or
rspec -c myspec.rb lin64
and have the lin32 or lin64 be passed as a string I can access in the ruby file itself. Is this possible? This site mentions environment variables but that is cumbersome. It also mentioned doing ARGV manipulation -- is this a possible way of doing it?
From David Chelimsky
You can't pass arbitrary arguments to the rspec command, but you can set an environment variable like this:
SLEEP=10 rspec test.rb
Then within the script, the value of ENV["SLEEP"] is "10", so you can say:
sleep(ENV["SLEEP"].to_f)
Try the -- parameter. It's used to tell an app to stop processing parameters and pass the remaining ones to a child process. I don't know if rspec understands it but it's worth a try.
`rspec -c myspec.rb -- lin32`
`rspec -c myspec.rb -- lin64`

Resources