Using Ruby to execute arbitrary system calls - ruby

This problem is to get into an internship within a devops department:
"Write a ruby library that executes arbitrary system calls (eg: “dmesg", "ping -c 1 www.google.com”) and provides separated output streams of stderr and stdout as well are providing the final return code of the process. Show your work with unit tests.”
Am I supposed to use already established system calls and replicate them in Ruby code? That seems silly to me. Am I supposed to come up with my own arbitrary calls and write a library complete with errors and status calls?
I am not looking for someone to write this for me. I feel that the first step to solving this problem is understanding it.

Get Clarification from the Source
The assignment is poorly worded, and misuses a number of terms. In addition, we can only guess what they really expect; the appropriate thing to do would be to ask the company directly for clarification.
Re-Implement Open3
With that said, what they probably want is a way to process any given command and its arguments as a decorated method, akin to the way Open3#capture3 from the standard library works. That means the code you write should take the command and any arguments as parameters.
For example, using Open3#capture3 from the standard library:
require 'open3'
def command_wrapper cmd, *args
stdout_str, stderr_str, status = Open3.capture3 "#{cmd} #{args.join ' '}"
end
command_wrapper "/bin/echo", "-n", "foo", "bar", "baz"
#=> ["foo bar baz", "", #<Process::Status: pid 31661 exit 0>]
I sincerely doubt that it's useful to re-implement this library, but that certainly seems like what they're asking you to do. Shrug.
You're also supposed to write unit tests for the re-implementation, so you will have to swot something up with a built-in framework like Test::Unit or MiniTest, or an external testing framework like RSpec, or Wrong. See the Ruby Toolbox for a more comprehensive list of available unit testing frameworks.

Luckily for you, Ruby makes it very easy to interact with the underlying operative system.
You can start by reading the documentation for these methods:
Kernel#`
Kernel#system
Kernel#exec
Kernel#spawn
IO#popen
Also, there is the Open3 module from the stdlib.

Related

Why should native functions be used instead of system calls?

The code reviewer requested changes in a piece of code similar to this:
system("grep foo /tmp/bar")
or this:
res, err, st = Open3.capture3("grep foo /tmp/bar")
The reviewer said:
Just read the file natively in Ruby, no need to fork a sub-command.
Why does the reviewer believe it is better to use Ruby's native functions like for example File.read even though there is no untrusted data on the sub-command call?
The best thing would obviously be to ask the reviewer, not us. But I can speculate.
Calling out to a subshell is less efficient, plain and simple. It requires an additional process, whereas File.read runs in the current process.
Your code is now OS-dependent. Windows, for instance, does not have a grep command, but File.read will work on any OS that runs Ruby.
At a glance, a subshell makes me think something funny is going on. If I see File.read, I expect that in Ruby code. If I see a system call, I begin to suspect something strange is going on, causing me to doubt the nature of the code.
For what it's worth, grepping in Ruby is still very simple. In fact, it's literally called grep.
File.readlines("/tmp/bar").grep(/foo/)
If I encountered the grep code in the question in a code review, I likely would have flagged it for the reasons above.

How to design a Ruby command line interface that can be tested with RSpec?

I'm trying to create a Ruby command line interface with the following options:
ruby cli.rb -g # Prints a list of people sorted by gender
ruby cli.rb -n # Prints a list of people sorted by last name
ruby cli.rb -b # Prints a list of people sorted by birth date
ruby cli.rb -a LastName FirstName DateOfBirth # Add a new person to the list of people
I have already created a Directory class that stores a list of people and also has methods to sort people and add people. The next step is to build a CLI around this class.
How can I create this in a way that can be tested with RSpec? My initial attempt involved a looped gets.chomp instead of running a Ruby file with flags, but this loop does not play well with RSpec. The examples I've found online just use Ruby's OptionParser in a simple script and not inside an actual class. Also, how would I handle creating ARGV parameters in RSpec? I'm just looking for tips on the general structure of this CLI class so that I can write tests.
I do not want to use any gems for this.
I'm not sure what the context of your problem is, so I'm just going to give you a little brain dump, the merit of which is yours to determine:
A cold hard fact: In the wild, I've seen few people write unit tests for command line arguments. The reason for this probably has to do with the fact that a well-written command line tool is a lot like a very skinny router (in the MVC sense). It looks at the arguments, and routes them to the proper function (eg, "-g" routes to something like MyObject.print_by_gender). And if you use a battle-tested options parser (like OptionParser) on the incoming side and a well-tested piece of code that the router calls (eg, MyObject.print_by_gender, assuming that's the name of a function, and that you've tested it well), then there's very little to test in the command line file itself.
That context in place, let's consider your problem. You want to test an object that interacts with the external world in 2 ways. Namely, stdin and stdout. This means that you could create an integration-style test, and actually pass thing in on stdin and test the output the command creates on stdout - as you mentioned - or you can mock stdin and stdout. In my experience, mocking external things like stdin and stdout are a better approach. That said, I'll recommend these two SO questions/answers to explain how to mock STDIN (this and this).

Running an interactive program from Ruby

I am trying to run gnuplot from ruby (not using an external gem) and parsing its textual output also. I tried IO.popen, PTY.spawn and Open3.popen3 but whenever I try to get the output it just "hangs" -I guess waiting for more output to come. I feel like its somehow done using Thread.new but I couldn't find the correct way to implement it.
Anyone know how it is done?
I guess this is what you want:
require 'pty'
require 'expect'
PTY.spawn('gnuplot') do |input, output, pid|
str = input.expect(/gnuplot>/)
puts str
output.puts "mlqksdf"
str = input.expect(/gnuplot>/)
puts str
output.puts "exit"
end
The problem is that the sub-program is waiting for input that isn't being sent.
Typically, when we call a program that expects input on STDIN, we have to close STDIN, which then signals that program to begin processing. Look through the various Open3 methods and you'll see where stdin.close occurs in many examples, but they don't explain why.
Open3 also includes capture2 and capture3, which make it nice when trying to deal with a program that wants STDIN and you don't have anything to send to it. In both methods, STDIN is immediately closed, and the method returns the STDOUT, STDERR and exit status of the called program.
You need "expect" functionality. Ruby's Pty class includes an expect method.
Creates and managed pseudo terminals (PTYs). See also en.wikipedia.org/wiki/Pseudo_terminal
It's not very well documented though, and doesn't offer a lot of functionality from what I've seen. An example of its use is available at "Using Ruby Expect Library to Reboot Ruckus Wireless Access Points via ssh".
Instead, you might want to look at RubyExpect which is better documented and appears to be current.

Is it idiomatic Ruby to add an assert( ) method to Ruby's Kernel class?

I'm expanding my Ruby understanding by coding an equivalent of Kent Beck's xUnit in Ruby. Python (which Kent writes in) has an assert() method in the language which is used extensively. Ruby does not. I think it should be easy to add this but is Kernel the right place to put it?
BTW, I know of the existence of the various Unit frameworks in Ruby - this is an exercise to learn the Ruby idioms, rather than to "get something done".
No it's not a best practice. The best analogy to assert() in Ruby is just raising
raise "This is wrong" unless expr
and you can implement your own exceptions if you want to provide for more specific exception handling
I think it is totally valid to use asserts in Ruby. But you are mentioning two different things:
xUnit frameworks use assert methods for checking your tests expectations. They are intended to be used in your test code, not in your application code.
Some languages like C, Java or Python, include an assert construction intended to be used inside the code of your programs, to check assumptions you make about their integrity. These checks are built inside the code itself. They are not a test-time utility, but a development-time one.
I recently wrote solid_assert: a little Ruby library implementing a Ruby assertion utility and also a post in my blog explaining its motivation. It lets you write expressions in the form:
assert some_string != "some value"
assert clients.empty?, "Isn't the clients list empty?"
invariant "Lists with different sizes?" do
one_variable = calculate_some_value
other_variable = calculate_some_other_value
one_variable > other_variable
end
And they can be deactivated, so assert and invariant get evaluated as empty statements. This let you avoid performance problems in production. But note that The Pragmatic Programmer: from journeyman to master recommends against deactivating them. You should only deactivate them if they really affect the performance.
Regarding the answer saying that the idiomatic Ruby way is using a normal raise statement, I think it lacks expressivity. One of the golden rules of assertive programming is not using assertions for normal exception handling. They are two completely different things. If you use the same syntax for the two of them, I think your code will be more obscure. And of course you lose the capability of deactivating them.
Some widely-regarded books that dedicate whole sections to assertions and recommend their use:
The Pragmatic Programmer: from Journeyman to Master by Andrew Hunt and David Thomas
Code Complete: A Practical Handbook of Software Construction by Steve McConnell
Writing Solid Code by Steve Maguire
Programming with
assertions
is an article that illustrates well what assertive programming is about and
when to use it (it is based in Java, but the concepts apply to any
language).
What's your reason for adding the assert method to the Kernel module? Why not just use another module called Assertions or something?
Like this:
module Assertions
def assert(param)
# do something with param
end
# define more assertions here
end
If you really need your assertions to be available everywhere do something like this:
class Object
include Assertions
end
Disclaimer: I didn't test the code but in principle I would do it like this.
It's not especially idiomatic, but I think it's a good idea. Especially if done like this:
def assert(msg=nil)
if DEBUG
raise msg || "Assertion failed!" unless yield
end
end
That way there's no impact if you decide not to run with DEBUG (or some other convenient switch, I've used Kernel.do_assert in the past) set.
My understanding is that you're writing your own testing suite as a way of becoming more familiar with Ruby. So while Test::Unit might be useful as a guide, it's probably not what you're looking for (because it's already done the job).
That said, python's assert is (to me, at least), more analogous to C's assert(3). It's not specifically designed for unit-tests, rather to catch cases where "this should never happen".
How Ruby's built-in unit tests tend to view the problem, then, is that each individual test case class is a subclass of TestCase, and that includes an "assert" statement which checks the validity of what was passed to it and records it for reporting.

ruby idioms for using command-line options

I'm trying to pick up ruby by porting a medium-sized (non-OO) perl program. One of my personal idioms is to set options like this:
use Getopt::Std;
our $opt_v; # be verbose
getopts('v');
# and later ...
$opt_v && print "something interesting\n";
In perl, I kind of grit my teeth and let $opt_v be (effectively) a global.
In ruby,the more-or-less exact equivalent would be
require 'optparse'
opts.on("-v", "--[no-]verbose", TrueClass, "Run verbosely") {
|$opt_verbose|
}
opts.parse!
end
where $opt_verbose is a global that classes could access. Having classes know about global flags like that seems ... er ... wrong. What's the OO-idiomatic way of doing this?
Let the main routine take care of all option-related stuff and have the classes just return things to it that it decides how to deal with?
Have classes implement optional behaviour (e.g., know how to be verbose) and set a mode via an attr_writer sort of thing?
updated: Thanks for the answers suggesting optparse, but I should have been clearer that it's not how to process command-line options I'm asking about, but more the relationship between command-line options that effectively set a global program state and classes that should ideally be independent of that sort of thing.
A while back I ran across this blog post (by Todd Werth) which presented a rather lengthy skeleton for command-line scripts in Ruby. His skeleton uses a hybrid approach in which the application code is encapsulated in an application class which is instantiated, then executed by calling a "run" method on the application object. This allowed the options to be stored in a class-wide instance variable so that all methods in the application object can access them without exposing them to any other objects that might be used in the script.
I would lean toward using this technique, where the options are contained in one object and use either attr_writers or option parameters on method calls to pass relevant options to any additional objects. This way, any code contained in external classes can be isolated from the options themselves -- no need to worry about the naming of the variables in the main routine from within the thingy class if your options are set with a thingy.verbose=true attr_writer or thingy.process(true) call.
The optparse library is part of the standard distribution, so you'll be able to use it without requiring any third party stuff.
I haven't used it personally, but rails seems to use it extensively and so does rspec, which I guess is a pretty solid vote of confidence
This example from rails' script/console seems to show how to use it pretty easily and nicely
The first hit on google for "processing command line options in ruby" is an article about Trollop which seems to be a good tool for this job.

Resources