How to require one of two possible options in a Ruby script? - ruby

I'm trying to configure a couple options in a Ruby script using OptionParser. I'd like them both to be optional but require that at least one is used. One option would allow for a single value to be passed from the command line. The other option will tell the script to use a file containing multiple values to be iterated over.
This is all I've been able to come up with:
options = {}
parser = OptionParser.new do |opts|
opts.banner = "Usage: sat_server_delete.rb [options]"
opts.on('-f', '--file file', 'File') do |file|
options[:file] = file;
end
opts.on('-s', '--server server', 'Server') do |server|
options[:server] = server
end
end.parse!
I haven't worked out the logic each will use yet. Right now I'd be happy to just see an error indicating that neither have been provided. So far I'm not getting even that. The script simply runs to completion and returns to a prompt.
I looked at the Ruby Doc for OptionParser, but It isn't clear to me how to accomplish what I'm trying. Is it even possible to have an either/or situation with options? I'm not even sure if what I already have makes sense. I'm basically just attempting to copy the logic without fully understanding what it is doing.

You can check the options hash after the parse! as in
if options[:file] && options[:server]
puts "may have only one of -f and -s"
exit
elsif !options[:file] && !options[:server]
puts "must have at least one of -f and -s"
exit
end

Related

Use a command line option to call a method

So I guess this is kind of related to my last question, but I was wondering if there was a way to call a method by using a command line option. Say you had a method like this:
def b
puts "Hello brian"
end
is there a way to write something like this:
ruby mine.rb -b
and get this
Hello brian
I already tried looking for this online and discovered OptionParser but I have yet to discover anything involving the OptionParser calling a method previously created.
There are a lot of ways to do this, depending on the use case. The below code is taken from the Ruby docs with the extra method added.
Realistically you'd probably want a class that handles the different options and encapsulates the method instead of having them at the file scope.
#!/usr/bin/env ruby
require 'optparse'
options = {}
OptionParser.new do |opts|
opts.banner = "Usage: example.rb [options]"
opts.on("-b", "Run method 'b'") do |v|
options[:b] = true
end
end.parse!
def b
puts "Hello Brian"
end
if options[:b]
b
end
I've also added a shebang at the top that will automatically call ruby. As long as your script file is executable you can call it directly (ie. ./mine.rb -b).

How to use a conditional within a Ruby expect script

I'm playing with expect in Ruby but I'm a little lost as to how I can branch my code based on the behavior of a device I am logging into. How could I do say foo.run if I get the correct prompt below > but run foo.fail if I do not? Even further, how can I evaluate all of the text that comes back between entering the password and receiving the > prompt? Is there a way to look at all text that the device prints somehow?
def device_test(password)
$expect_verbose = true
PTY.spawn("ssh my-router") do |reader, writer, pid|
reader.expect("password:")
writer.puts(password)
reader.expect(">")
puts "logged in"
sleep(15)
end
end
It appears that the expect method can only look for a single pattern (unlike the Tcl expect library where you can look for multiple patterns simultaneously).
It looks like you'll have to pass a "timeout" parameter and check the return value:
if reader.expect(">", 2)
puts "foo.run"
else
# did not see ">" within 2 seconds
puts "foo.fail
end

Ruby gets method throws an exception when arguments are passed from the console

I have experienced some ODD behavior from the code below:
require 'CSV'
$DEBUG = ARGV.empty? ? false : ARGV[0] #Global debug flag.
class PhoneBook
#class code here etc etc
end
PhoneBook.start_dir = "file-io-samples/phonebooks/"
puts "Enter a phonebook!"
name = gets #This is the problem.
puts "Using #{name}.."
When I pass true to have $DEBUG set to true on execution I get an error from name = gets and I have no idea why. If I don't pass parameters via the command line everything works fine.
This is the error output:
C:\Pickaxe>ruby PhoneBook.rb
Enter a phonebook!
Hurrah! Works
Using Hurrah! Works
..
C:\Pickaxe>ruby PhoneBook.rb true
Enter a phonebook!
Exception `Errno::ENOENT' at PhoneBook.rb:62 - No such file or directory - true
PhoneBook.rb:62:in `gets': No such file or directory - true (Errno::ENOENT)
from PhoneBook.rb:62:in `gets'
from PhoneBook.rb:62:in `<main>'
C:\Pickaxe>
If I need to I can post the class definition, but I don't think it's part of the problem.
gets reads from stdin if no arguments are passed, and from the file that was passed as an argument otherwise. You are passing an argument true, ergo gets tries to read from a file named true, which apparently doesn't exist.
This is the very first sentence of the documentation of gets:
Returns (and assigns to $_) the next line from the list of files in ARGV (or $*)
This wouldn't cause a problem on *nix, but I expect Windows, or Ruby on Windows, isn't handling the additional command-line parameter the same way. On *nix, we can use -- between the script name and the parameter to tell the OS not to pass the parameter as a flag. In other words, Ruby wouldn't see true, your script would.
ruby some_script.rb -- options
But, in general, I think you're doing it wrong and recommend handling your command-line options in a standard way by using the OptionParser class:
require 'optparse'
OptionParser.new do |opt|
opt.on('-d', '--[no-]debug') { |o| $DEBUG = o }
end.parse!
puts $DEBUG
Running that several times on my Mac OS system, with different parameters, gives me:
$ ruby test.rb
false
$ ruby test.rb --no-debug
false
$ ruby test.rb -d
true
$ ruby test.rb --debug
true
You might still have to use -- to tell the OS and called app which parameters belong to what.

Ruby OptionParser: How do I get the PATH? if the command is just "command path/to/some/file"?

I want create a command that has the following structure command path/to/some/file
How do I enable this without entering the option -p or --path?
#!/usr/bin/env ruby
require 'optparse'
options = {}
option = OptionParser.new() do |opts|
opts.on('-p PATH', '--path PATH', 'First path argument') do |path| // How do I enable this without entering the option -p or --path?
options[:path] = path
end
end
option.parse!(ARGV)
It's not stated very explicitly in the docs, but OptionParser will extract all options from ARGV and leave the rest, so the right way is just to use ARGV as if it were the list of paths.
Doesnt ARGV[0] give you access to the first part of your argument? In your case this would be the path, aka path/to/some/file. The whole parser code is not necessary here imo.

How to make a ruby command line application with pager?

I'm making a command line tool using Ruby. It will print a lot of text on screen. Currently, I'm using shell pipeline (may_app | more) to do so. But I think it's better to has a default pager.
It's just like what you see when execute git log . One can disable pager by using git --nopager log.
I've done quite much google work and find one gem: hirb , but it seems a little overkill.
After many tries, I'm current using shell wrapper to do so:
#!/bin/bash
# xray.rb is the core script
# doing the main logic and will
# output many rows of text on
# screen
XRAY=$HOME/fdev-xray/xray.rb
if [ "--nopager" == "$1" ]; then
shift
$XRAY $*
else
$XRAY $* | more
fi
It works. But is there a better way?
You are doing it right. But instead using more you'd better get a pager from $PAGER environment variable, if any.
Some people prefer less to more for example, and others have their favorite parser options set in this var.
You can use the pipe in Ruby via a call to system and provide the options (along with a nice help interface) like so:
require 'optparse'
pager = ENV['PAGER'] || 'more'
option_parser = OptionParser.new do |opts|
opts.on("--[no-]pager",
"[don't] page output using #{pager} (default on)") do |use_pager|
pager = nil unless use_pager
end
end
option_parser.parse!
command = "cat #{ARGV[0]}"
command += " | #{pager}" unless pager.nil?
unless system(command)
STDERR.puts "Problem running #{command}"
exit 1
end
Now, you support --pager and --no-pager on the command line, which is nice to do.

Resources