How to change optparse's default error message in ruby? - ruby

require 'optparse'
params = ARGV.getopts("a:", "AA")
ruby a.rb -a shows:
a.rb:3:in `<main>': missing argument: -a (OptionParser::MissingArgument)
ruby a.rb -b shows:
a.rb:3:in `<main>': invalid option: -b (OptionParser::InvalidOption)
I want to show my help message, how do I do that?

Actually you can use on_tail inside the block of OptionParser.new.
But since you're simply trying with ARGV.getopts, a hack to show your own help message would be rescuing exceptions:
require 'optparse'
help_msg = <<EOM
This is help message:
Hello buddy, you may do something wrong
...
EOM
begin
params = ARGV.getopts("a:", "AA")
rescue => e
puts e.message
puts '=' * 80
puts help_msg
end
Output:
ruby a.rb -a
#=>
missing argument: -a
================================================================================
This is help message:
Hello buddy, you may do something wrong
...
ruby a.rb -b
#=>
invalid option: -b
================================================================================
This is help message:
Hello buddy, you may do something wrong
...

Related

How to source a shell script and execute its functions in ruby

I'm new to ruby . I'm trying to source my shell script in ruby and execute functions in sourced shell script.
below is my shell script /tmp/test.sh
#!/bin/bash
function hello {
echo "hello, this script is being called from ruby"
}
below is my ruby script test.rb
#!/usr/bin/ruby
system("source /tmp/test.sh")
puts $?.exitstatus
system("hello")
puts $?.exitstatus
output using system
[root#localhost ~]# ruby test.rb
127
127
I even tried the back tick approach, but i got below error
code :
#!/usr/bin/ruby
status=`source /root/test.sh`
puts status
status2=`hello`
puts status2
error:
ruby test.rb
test.rb:3:in ``': No such file or directory - source (Errno::ENOENT)
from test.rb:3:in `<main>'
can anyone tell what is wrong in my code.
You can use session gem, or write a solution yourself.
script.sh:
#!/bin/bash
function hello() {
echo "Hello, World!"
}
Ruby file:
IO.popen('bash', 'r+') do |sh|
sh.puts 'source script.sh'
sh.puts 'hello'
sh.close_write
puts sh.gets
end
# => Hello, World!

Make Thor show message for top level command

Is there any way to make Thor show a general message for the top level command?
$my_command help
I'd like to show a welcome message here.
Commands:
my_command help [COMMAND]
The closest thing I can think of is adding a default task and using it to invoke the help task. You'd get this message when calling $my_command with no arguments
require 'thor'
class MyCLI < Thor
desc "hello NAME", "say hello to NAME"
def hello(name)
puts "Hello #{name}"
end
desc "greeting", "this is just a greeting"
def greeting
puts "Welcome to MyCLI"
invoke :help
end
default_task :greeting
end
MyCLI.start(ARGV)
# $my_command
# output:
# Welcome to MyCLI
# Commands:
# test.rb greeting # this is just a greeting
# test.rb hello NAME # say hello to NAME
# test.rb help [COMMAND] # Describe available commands or one spec...

OptionParser with subcommands

I'm trying to create a command line program with sub-commands using OptionParser. I'm following "ruby's OptionParser to get subcommands".
The problem is that it does not allow for a use case like this:
ruby main.rb --version
#=> main.rb in `<main>': undefined method `order!' for nil:NilClass (NoMethodError)
But it does allow for this:
ruby main.rb foo --options
ruby main.rb --options foo
ruby main.rb --options foo --options
How would I be properly handle command line arguments, in the case that no subcommand is given.
My example code is:
global = OptionParser.new do |opts|
opts.banner = "Usage: opt.rb [options] [subcommand [options]]"
opts.on("-v", "--version", "Print the version") do |v|
options[:version] = v
end
opts.separator ""
opts.separator subtext
end
The lines with the error:
global.order!
command = ARGV.shift
subcommands[command].order!
If global.order! uses all of ARGV, then command is nil. So... check for that.
global.order!
command = ARGV.shift
unless command
STDERR.puts "ERROR: no subcommand"
STDERR.puts global # prints usage
exit(-1)
end
subcommands[command].order!
Maybe this'll help:
require 'optparse'
VERSION = '1.0.0'
options = {}
OptionParser.new do |opt|
opt.on('-f', '--foo', 'Foo it') { |o| options[:foo] = o }
opt.on_tail('-v', '--version') do
puts VERSION
exit
end
end.parse!
puts options
Saving it as "test.rb" and running it with ruby test.rb returns:
{}
Running it with ruby test.rb -f or --foo returns:
{:foo=>true}
Running it with ruby test.rb -v or --version returns:
1.0.0
For more fun, running ruby test.rb -h or --help returns:
Usage: test [options]
-f, --foo Foo it
even though I didn't define -h or --help.
If I wanted the -v and --version flags to appear in the list then I'd change them from a on_tail method to a normal on method:
require 'optparse'
VERSION = '1.0.0'
options = {}
OptionParser.new do |opt|
opt.on('-f', '--foo', 'Foo it') { |o| options[:foo] = o }
opt.on('-v', '--version', 'Returns the version') do
puts VERSION
exit
end
end.parse!
puts options
which would return:
Usage: test [options]
-f, --foo Foo it
-v, --version Returns the version
I can add:
puts ARGV
to the end of the script and see that OptionParser is correctly handling flags and parameters:
>ruby test.rb bar --foo
{:foo=>true}
bar
>ruby test.rb --foo bar
{:foo=>true}
bar
See "Pass variables to Ruby script via command line" for more information.
There is no way your example code will handle your sample inputs using --options. No handler for --options is defined. Nor is subtext. Your code returns:
undefined local variable or method `subtext' for main:Object (NameError)
Stripping the block to:
global = OptionParser.new do |opts|
opts.on("-v", "--version", "Print the version") do |v|
options[:version] = v
end
end
and running again returns:
invalid option: --options (OptionParser::InvalidOption)
So, again, your example doesn't match the results you say you're getting.

OptionParser's make_switch error with '-?'

I'm running into an issue with OptionParser's make_switch.
My code parses three arguments and runs a test to see if my MANDATORY argument is here:
#!/usr/bin/env ruby
require 'optparse'
require 'ostruct'
options = OpenStruct.new
#argv = ARGV
optparse = OptionParser.new do |opts|
#opts=opts
usage = "USAGE: ./#{File.basename($0)} [-v] -p xxxxxx"
#opts.banner = usage
#opts.on( '-p', '--pdu [PDU]', 'Specify a PDU to configure') do |res|
options.pdu = true
$pdu_name = res
end
#opts.on( '-v', '--[no-]verbose', 'Run verbosely') do
options.verbose = true
end
#opts.on( '-?', '-help','Show this message') do
puts "Help Me!"
puts #opts
exit 1
end
end
begin
if not #argv.empty?
optparse.order!(#argv)
if !options.pdu
$stderr.puts "Options -p missing."
$stderr.puts "#{#opts}\n\n"
exit 1
end
else
$stderr.puts "ERROR: Arguments Required."
$stderr.puts "#{#opts}\n\n"
exit 1
end
rescue OptionParser::InvalidOption
$stderr.puts "ERROR: Invalid option."
$stderr.puts "#{#opts}\n\n"
exit 1
end
Everything works except -?:
xxx$ ./myscript.rb -?
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/optparse.rb:451:in
`parse': missing argument: -? (OptionParser::MissingArgument)
from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/optparse.rb:1295:in `parse_in_order'
from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/optparse.rb:1254:in `catch'
from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/optparse.rb:1254:in `parse_in_order'
from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/optparse.rb:1248:in `order!'
blabla
However -help works perfectly:
xxxx$ ./myscript.rb -help
Help me!
USAGE: ./myscript.rb [-v] -p xxxxxx
-p, --pdu [PDU] Specify a PDU to configure
-v, --[no-]verbose Run verbosely
-?, -help Show this message
More surprisingly, -? -v works too:
xxxx$ ./myscript.rb -? -v
Help Me!
USAGE: ./myscript.rb [-v] -p xxxxxx
-p, --pdu [PDU] Specify a PDU to configure
-v, --[no-]verbose Run verbosely
-?, -help Show this message
What did I do wrong?
The same issue occurs if I replace -? with -h in the code.
Perhaps a quick look at the (somewhat confusing) documentation would shed some light on the situation. If you look at the docs, you'll end up at OptionParser#make_switch where you'll find an explanation of what the opt.on arguments look like:
Long style switch:
Specifies a long style switch which takes a mandatory, optional or no argument. It’s a string of the following form:
"--switch=MANDATORY" or "--switch MANDATORY"
"--switch[=OPTIONAL]"
"--switch"
Short style switch:
Specifies short style switch which takes a mandatory, optional or no argument. It’s a string of the following form:
"-xMANDATORY"
"-x[OPTIONAL]"
"-x"
Note the -xMANDATORY and then look closer at your #opts.on call:
#opts.on( '-?', '-help','Show this message') do
# ---------------^^^^^
That -help defines a -h option with a required elp argument. Presumably the option parser is interpreting that to mean that -h is an alias for -? and since -h is defined with a required argument, -? also requires an argument. If you use --help (i.e. a long style switch) then you'll probably have a better time:
#opts.on('-?', '--help', 'Show this message') do
I working from the Ruby 2.0 version but I doubt much has changed in the option parser since the older version of Ruby that you appear to be using.

Running command line commands from Thor executable

In my executable Ruby file I have the following:
#!/usr/bin/env ruby
require 'thor'
include Thor::Actions
class UI < Thor
# def self.source_root
# File.dirname(__FILE__)
# end
desc "makecal", "Generates postscript calendar to your desktop"
def makecal
# puts `ls ~`
puts run('ls ~')
# puts run "pcalmakecal -B -b all -d Helvetica/8 -t Helvetica/16 -S #{Time.now.month} #{Time.now.year} > ~/Desktop/#{Time.now.month}-#{Time.now.year}"
end
end
UI.start
In the terminal when I run the file as is I get an empty line as Thor's run command is returning a NilClass.
However, when I un-comment the puts `ls ~` and comment out Thor's run method I get an output of my home directory as expected.
I'm having trouble figuring out why I can't get Thor's run method to work like Ruby's ticks.
Any ideas where I may have went wrong?
Thanks for looking
I didn't put the include statement inside my class and that messed things up. The code should be:
#!/usr/bin/env ruby
require 'makecal'
class UI < Thor
include Thor::Actions
# def self.source_root
# File.dirname(__FILE__)
# end
#
desc "makecal", "Generates postscript calendar to your desktop"
def makecal
# puts `ls ~`
puts run('ls ~')
# puts run "pcal -B -b all -d Helvetica/8 -t Helvetica/16 -S #{Time.now.month} #{Time.now.year} > ~/Desktop/#{Time.now.month}-#{Time.now.year}"
end
end
UI.start
Thor's documentation on this method is actually wrong and incomplete. It documents that it returns the "contents of the command" (which I assume means the standard output), but it, by defualt, does nothing.
But, you can, apparently, use the :capture option to get what you want:
unless options[:pretend]
config[:capture] ? `#{command}` : system("#{command}")
end
So, try doing
puts run("ls ~", :capture => true)
And see if that does it.

Resources