What's the point of ARGV in Ruby? - ruby

What's the point of ARGV in Ruby?
first, second, third = ARGV
puts "The script is called: #{$0}"
puts "Your first variable is: #{first}"
puts "Your second variable is: #{second}"
puts "Your third variable is: #{third}"
What's the point of this when to run the file I need to do:
ruby ex1.rb
and to put in the first, second and third variables I need to type in
ruby ex1.rb blah blah blah
How does this benefit at all the person running the program? They can't do it anyway since I'd assume it be an executable:
user = ARGV.first
prompt = '> '
puts "Hi #{user}, I'm the #{$0} script."
puts "I'd like to ask you a few questions."
puts "Do you like me #{user}?"
print prompt
likes = STDIN.gets.chomp()
puts "Where do you live #{user}?"
print prompt
lives = STDIN.gets.chomp()
puts "What kind of computer do you have?"
print prompt
computer = STDIN.gets.chomp()
puts <<MESSAGE
Alright, so you said #{likes} about liking me.
You live in #{lives}. Not sure where that is.
And you have a #{computer} computer. Nice.
MESSAGE
Can someone please explain this to me?

What's the point of ARGV in Ruby?
ARGV "contains the arguments passed to your script, one per element."
What's the point of this when to run the file you need to do: ruby ex1.rb and to put in the first, second and third variables you need to type in ruby ex1.rb blah blah blah.
That is the exact point, to be able to specify arguments when running the file like:
ruby ex1.rb -a -b 3
How does this benefit at all the person running the program?
Same benefit any other command-line program gets from being able to do so: the user can specify upfront what they want, and it's easier to script than an interactive CLI.
They can't do it anyway since I'd assume it be an executable.
Yes, they can. You just gave an example of exactly how the user would run your program that way. Whether it's executable or not doesn't change anything.

ARGV has a long tradition and comes from the UNIX/POSIX world where most C programs must contain the following:
int main(int argc, char **argv)
{
return(0);
}
There argc represents the number of arguments supplied, and argv is a low-level pointer to the first of potentially a number of string pointers. The name argv has stuck around in various forms.
Command-line arguments are very popular with developers, though they're usually not used quite as you seem to think. Un-named arguments are much harder to deal with. That's why things like optparse exist to help deal with them.
Here's an example from the OptionParser documentation:
require 'optparse'
options = {}
OptionParser.new do |opts|
opts.banner = "Usage: example.rb [options]"
opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
options[:verbose] = v
end
end.parse!
p options
p ARGV
This allows you to define much more usable command-line arguments and then use them. For your example, I'd expect it to work like this:
test_script --live "New York" --computer "Lenovo" --like
That makes it quite obvious what the arguments are because they're named.

Consider some Ruby command line utilities like rails, rake,gem, or bundle. While none of these are end-user applications (like web apps or GUI apps), they are still programs written in Ruby that users interact with. All of them take arguments:
$ rails new my_app
$ rake tests
$ gem install rails --no-rdoc --no-ri
$ bundle update
It is possible to use ARGV to implement these kinds of command line programs that accept arguments. While we'll often use the OptionParser standard library or some other tool for this purpose, ARGV is the low level collection those tools are built on top of.
So if you've ever run gem, rails, rake, or bundle, or any command line developer tool that is written in Ruby, you will have benefited from ARGV!

The point of ARGV is that it enables scripts to be able to run without human input, and also allows for the case where the input may vary from one iteration to the next.
For example you may be running three different scripts to get three values. These values could then be passed into this script and ARGV can be used to get the values of these options.

I used to run a script that asked me for an email and password. It was used to log into one of 100's of accounts. Like most Ruby scripts, I'd run it from the command line. This had to semblance to being an "executable", especially not one with an icon that you click.
I'd run the program and it would ask me for email and password then log me in using Watir.
puts "Email of Account"
account_email = gets.chomp
puts "Enter Password"
password = gets.chomp
# code that logs me in
However, I found that taking in those values via ARGV was much faster for my purposes.
account_email, password = ARGV
# code that logs me in
Then I could just type
$ ruby my_script.rb my.email#emailplace.com mypassword
and hit enter. Also I could just press the up arrow in the console to run it again and again or recent versions with different names and passwords to test my script quickly.
Taking it a step further, I made a shell script that could run this scripts, args and all in many combinations
ruby my_script.rb oneofmy#emails.com mypassword
ruby my_script.rb another#emails.com otherpassword
Finally, sometimes I needed to set dozens of settings per each time I ran a script, and then run that script many times. So I could also easily insert a dozens variables into a script from the command line
ruby my_script.rb option1 option2 option3 option4
I'm very new to using ARGV and hope to learn better ways to use it, but this is one feature I found very useful.

Related

Ruby: Keep console open after script execution

I wrote a Ruby script like the following example. The basic functionality is the same:
# get input from the user
input = gets.chomp
# do awesome stuf with this input and print the response
puts do_awesome_stuff(input)
The problem is when I run the script it prints the solution I want, but the console window closes right after. I want the console to keep open.
I'm currently on windows, but the solution should be working on every system.
One way is to run the ruby script with a .bat file and pause it, like so:
ruby script.rb
PAUSE
I hope there is a way without the additional .bat file. Does Ruby has a function like PASUE integrated?
It seems like you double click the ruby script file.
Instead issue the following command in cmd shell.
ruby filename.rb
If you don't want that, you can add gets to the end of the script.
# get input from the user
input = gets.chomp
# do awesome stuf with this input and print the response
puts do_awesome_stuff(input)
gets # <----
But this is not recommended because .. if you run the command in cmd shell or terminal you should type extra Enter to return to the shell.
Use the -r options of irb.
irb -r ./filename.rb

Ruby - How to use -r switch with ruby command line tool

I was trying to figure out how to work the command line switch -r.
My understanding is that the code is typed out as follows:
ruby -r*nameOfRequired*
I am finding that this is not the case. When I type out the above and press enter, the terminal expects an "end of input syntax" and does not continue.
What am I missing? Does there need to be a space in between the switch and the name of the required file?
Please and thank you!
EDIT:
I am currently reading "The Well Grounded Rubyist" by David A. Black, and I came up with this question while reading the section on command line switches.
Having said that, I created a "test.rb" file, containing:
puts Date.today
Then, in the terminal, I typed out:
ruby -r date
I thought this would 'require' the date module, and then enable me to run the "test.rb" file, using ruby test.rb (given that I am in the correct directory).
Instead, the terminal cursor moves to a newline, expecting more input. Let me know if I need to clarify anything else. Thanks!
If you just type ruby -rmodule, then Ruby will load the module and wait for you to type the main program that requires that module.
If you just want to run the module and do nothing else, you can do do rubyfull-path-to-module without the -r, or ruby -rmodule -e exit, or ruby -rmodule </dev/null, or similar.
In general, the ruby command does not record any state from one run to the next, so you need to tell it every thing that it needs to know whenever you run it.
Whenever you run it, you need to tell it the main program to run or else it will expect you to type that program on the standard input. The -r does not specify the main program.
Try this:
ruby -rdate test.rb
According to ruby -h:
-rlibrary require the library, before executing your script
Without giving your script file path, it read the script from stdin.
Try following (You can omit script file path when you give -e command):
ruby -r**nameOfRequired** -e ""

Ruby .gets doesn't work

I'm trying to get simple user input in Ruby, but I can't get it working. I'm using the gets method, but the program never stops to ask me for input. I'm using Sublime Text 2 as my text editor, and I run the program in it, too (if this makes a difference).
Here's my code:
puts "What is your name?"
name = gets
puts "Hello " + name + ". How are you?"
And here's the error (and output) given to me:
C:/Users/Sten Sootla/Desktop/Ruby workspace/exercise.rb:3:in `+': can't convert nil into String (TypeError)
from C:/Users/Sten Sootla/Desktop/Ruby workspace/exercise.rb:3:in `'
What is your name?
[Finished in 0.1s with exit code 1]
Why doesn't the program stop to ask me for input?
Try using $stdin.gets instead of just a plain gets, this will force the input to come from stdin
Here's how I understand it. gets and puts are instance methods of IO, and the default IOs are $stdout and $stdin.
Calls to gets/puts will only be effective if the translator is capable of handling stdout/in e.g. IRB
If you run a ruby file from bash it works too.
io_test.rb
puts gets
in bash
ruby io_test.rb
Then it will "put" into stdout whatever it "gets" from stdin.
If you want to run code within ST2, check out the SublimeREPL plugin, available through Package Control. While you can use IRB, its main Ruby interface is through pry, which is a lot more powerful. You can use it as a classic REPL (think Clojure or LISP), and you can also transfer your code from one tab into the running REPL in another tab by selection, line range, or block.
Interestingly, your code above works in IRB for me, but not pry for some reason - it's reading my $EDITOR environment variable, which is set to subl -w but failing with Errno::ENOENT: No such file or directory - subl -w. Strange...
At any rate, I highly highly recommend SublimeREPL, as it's a really powerful tool, and is self-contained within ST2, so you don't have to keep flipping back and forth to your terminal, saving and reloading your programs.

Execute shell commands from Ruby code

Note: If you think of a better title/question, feel free to suggest it. I wasn't sure how to articulate this question in one brief sentence.
I created a command line Mastermind game. To play the game, you type play.rb at the command line.
play.rb is a Ruby script that fires up the game. Within the script, the game is sent an interface, called CommandLineInterface.
If you want to play using a GUI (I'm using a Ruby GUI called Limelight), you cd into the limelight directory and type limelight open production and the GUI opens.
There is a mastermind_game directory that contains a lib, a spec, and a limelight directory. The limelight directory contains a production directory.
Now I'm making a few changes. You can pass arguments to the script at the command line. Either you enter play.rb "command line game" or play.rb "limelight game".
ARGV is an array of the arguments passed at the command line.
if ARGV.include?("command line game")
interface = CommandLineInterface.new
elsif ARGV.include?("limelight game")
interface = LimelightInterface.new
end
If I want to play my command line game, I enter play.rb "command line game" and it works fine.
I want to be able to type play.rb "limelight game" at the command line and have that open the GUI. In ARGV, the argument "limelight game" would be found so interface would be set to LimelightInterface.new. Within my LimelightInterface class I want the initialize method to open the GUI. It should essentially have the same functionality as typing limelight open production at the command line.
I'm not sure if this is possible or how to do it, so any help would be appreciated! Thanks!
EDITED: I'm trying to execute the command rvm use jruby by including this line in my script:
system("rvm use jruby")
I get back: "RVM is not a function, selecting rubies with 'rvm use ...' will not work."
Ryan, there's several ways to call out to the system:
Backticks:
ruby -e 'p ARGV' '1 2' '3 4' # => "[\"1 2\", \"3 4\"]\n"
The %x literal (note that you can use any delimiter you like, you're not restricted to parentheses)
%x(ruby -e 'p ARGV' '1 2' '3 4') # => "[\"1 2\", \"3 4\"]\n"
The system command. The difference here is that it passes stdin / out / err on through. (the above return the stdout, this one prints it on your process' stdout).
system('ruby', '-e p ARGV', '1 2', '3 4')
# >> ["1 2", "3 4"]
And if you need more sophisticated usage, something like open3 from the stdlib has gotten me pretty far. If you really need the big guns (it doesn't sound like you do), there's a gem open4.
Edit:
It sounds like you're wanting to do something like this:
require 'open3'
bash_script = <<SCRIPT
source "$HOME/.rvm/scripts/rvm"
rvm use jruby
ruby -v
exit
SCRIPT
out, err, status = Open3.capture3 'bash', stdin_data: bash_script
puts out
# >> Using /Users/joshcheek/.rvm/gems/jruby-1.6.7
# >> jruby 1.6.7 (ruby-1.8.7-p357) (2012-02-22 3e82bc8) (Java HotSpot(TM) 64-Bit Server VM 1.6.0_29) [darwin-x86_64-java]
But honestly, I don't think it's a good solution for your situation, because there's many legitimate ways to set up jruby for your environment. I think it would be better to just check that the limelight binary exists, and tell your user to fix their environment if it doesn't.
Here's the first result from googling the title: http://tech.natemurray.com/2007/03/ruby-shell-commands.html
If that's not what you need, I don't understand the question.

How to create subcommands for Ruby programs?

I am writing a Ruby CLI (Command Line Interface) program and I would like to be able to call subcommands similar to what rails does when you call rails generate ... or rails server etc. Can anyone point me in the right direction on how to do this?
You just need to get the command line arguments and work with those. They are stored in the global array ARGV:
ARGV.each do|a|
puts "Argv: #{a}"
end
That prints out the arguments sent to the ruby script
The standard library's OptionParser class exists specifically for handling command-line arguments like this. Here's a tutorial. It should simplify your work considerably.

Resources