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

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.

Related

Getting Console Input with Ruby's Docopt

I'm using Docopt in Ruby to parse my command options, and later in the script I am getting console input using gets.chomp. The problem is that all of the args from the running the program are still left in ARGF after Docopt does its parsing with options = Docopt::docopt(doc), and doing a gets command takes from ARGF before it tries gets'ing from STDIN.
I've tried to clear ARGF, but doing ARGF.gets for some reason tries to run the input as a command. I think clearing ARGF or using another input method could both be solutions, but I haven't found anything yet. I have to imagine that I'm not the first to try to get interactive command line input in Ruby with Docopt, so I'm hoping the answer is out there.
Some more code for those who would like it:
#!/usr/bin/ruby
require 'docopt'
doc=<<eos
Usage:
account_creator.rb --noldap [options]
Options:
-h, --help Show help page
-l, --ldap
-n, --noldap
-s SERVER With or without http[s]://, followed by port
--ad
--od
-d NUM
-u USER
-p PASS
-o OUTPUT-FILE Default behavior is to append output to file if it exists
eos
options = {}
begin
options = Docopt::docopt(doc)
rescue Docopt::Exit => e
puts e.message
exit 1
end
if options['-u']
username = options['-u']
else
while username.eql? '' or username == nil
puts "Enter Username:"
username = Kernel.gets.chomp.strip
end
end
This is unrelated to docopt. Try it on its own:
$ cat test.rb
#!/usr/bin/ruby
puts "Enter Username:"
username = gets
$ ./test.rb something
Enter Username:
./test.rb:4:in `gets': No such file or directory - something (Errno::ENOENT)
from ./test.rb:4:in `gets'
from ./test.rb:4:in `<main>'
Kernel.gets in ruby uses ARGF.gets. Using STDIN.gets should get you your expected behavior. See this SO question.

Determine if a program is running in debug mode

I use RubyMine to write and debug my Ruby 2.0 code. It uses ruby-debug-ide for that purpose. I want to know if a program is running in debug mode.
I know there is the Ruby $DEBUG global variable, but as far as I understand ruby-debug-ide didn't change it, because it didn't use the -d ruby flag.
If I debug my file using Rubymine the command executed looks like this:
/home/user/.rvm/rubies/ruby-2.0.0-p353/bin/ruby -e at_exit{sleep(1)};$stdout.sync=true;$stderr.sync=true;load($0=ARGV.shift) /home/user/.rvm/gems/ruby-2.0.0-p353/gems/ruby-debug-ide-0.4.22/bin/rdebug-ide --disable-int-handler --port 37737 --dispatcher-port 47992 -- /home/user/file.rb
I tried to use ARGV or $0, to determine if the command line contains the string 'rdebug-ide' but ARGV is an empty array and $0 is just '/home/user/file.rb', how can I get the full command line executed by RubyMine?
This is what I did:
I put the following code in an (rails) action and did a diff on the outputs both in debug and non-debug modes:
puts ENV.to_hash.to_yaml
I noticed that one of the differences is in ENV['RUBYLIB'] (there's also IDE_PROCESS_DISPATCHER, DEBUGGER_STORED_RUBYLIB, RUBYOPT, and DEBUGGER_HOST)
So here's how you'd check:
if ENV['RUBYLIB'] =~ /ruby-debug-ide/
puts 'in debug mode'
else
puts 'not in debug mode'
end
You need the global variable $LOAD_PATH.
a = $LOAD_PATH
a.each do |current_path|
puts 'Debug mode' if current_path.include?('rb/gems')
end
$LOAD_PATH has this line "/home/username/RubyMine-6.0.2/rb/gems" if I use debug mode.

Console not ready ruby sublime text file

I'm new to programming. Just about to start learning Ruby. I already took a console class, but I am stuck here.
I'm using a mac 10.6.8. I have done a quick 1+2 in the sublime text editor. I saved it. I went over to my console typed irb and then typed ruby example.rb. I have read elsewhere here that typing require './example' would help....it didn't. I am getting the following
NameError: undefined local variable or method `example' for main:Object
from (irb):2
from /usr/local/rvm/rubies/ruby-1.9.3-p392/bin/irb:16:in `<main>'
I don't understand what I am doing wrong. Thank you for your help. I really appreciate it.
-L
I would do as below:
kirti#kirti-Aspire-5733Z:~$ irb
2.0.0p0 :001 > require 'fileutils'
=> true
2.0.0p0 :002 > FileUtils.pwd
=> "/home/kirti"
2.0.0p0 :003 > FileUtils.cd "/home/kirti/ruby"
=> nil
2.0.0p0 :004 > load "./SO.rb"
3
=> true
2.0.0p0 :005 > require "./SO.rb"
3
=> true
My SO.rb file contains the below line :
puts 1+2
May be you wanna give a try.
Step 1: Navigate to your project/file folder by using command "cd folder_name/folder_location"
Step 2: load './example.rb'
For better solution you may wanna define some function inside example.rb
Like:
def sum
1 + 2
end
And to get the output enter sum in irb after loading the example.rb file.
irb is the interactive ruby shell. Within the shell, everything you type is interpreted as Ruby code, not bash commands. So, for example:
bash> puts 1 + 2
# command not found: puts
# this happens because you're not in a Ruby shell
bash> irb
# now you're in a Ruby shell
irb> puts 1 + 2
# 3
If you wrote some code in example.rb, you have two options:
From the bash shell, run ruby example.rb (from the same directory where your example.rb file is saved.
From the irb console, you can require 'example', which will load the contents of example.rb into your interpreter. In this case, it will immediately execute the Ruby code. If you wrapped the contents of example.rb in a class, it would load the class, but not execute code within it until you instantiated/called it.
Hopefully that helps!
My guess is that you are typing (into irb):
require example.rb
When you need to type:
require './example.rb'
The first tells ruby: "require what is in a variable called example". Because you did not define a variable called example, it results in the no variable or method error.
The second tells ruby: "require a string './example.rb'". Since the require method essentially knows how to find the file name passed as a string and evaluate the file, you'll get the right output
By the way, for this example, example.rb needs to be in the same directory. If example.rb is in another directory, you'll need to use the full path (I won't expand on it here) to source it.
You'll also notice that the output will look something like this:
3
=> true
This is because the file was evaluated (executing the code: puts 1+2) and the require method returns true to indicate it evaluated the file.
If you require the file again, you'll get false because the file is already loaded.

Running Ruby scripts from command line

I have a 2 scripts:
test1.rb
require 'test2.rb'
puts "hello"
test2.rb
puts "test"
I'm running this by executing ruby test2.rb test1.rb.
But only test is printed out and not hello.
You only need to run ruby test1.rb and the require statement should pull in test2.rb for you - you don't need to put it on the command line as well. (That will try and run test2.rb, passing the string 'test1.rb' as an argument, which is not what you want here)
Edit: the require statement does not look in the current directory by default when trying to find 'test2.rb'. You can explicitly specify it by changing it to:
require File.dirname(__FILE__) + '/test2.rb'
in test1.rb do (assuming test2.rb is in same directory, otherwise give its path relative to test1.rb)
require_relative 'test2.rb'
puts "hello"
and on the command line just do ruby test1.rb
This should work as well
require './test2.rb'
puts "hello"
There are some explanation how you can solve your problem, but not what is going wrong.
With ruby test2.rb test1.rb you call the ruby script with the parameter test1.rb.
You have access to the parameters in the constant ARGV.
An example with this script:
puts "test"
puts 'ARGV= %s' % ARGV
The result when you call it:
C:\Temp>ruby test.rb test2.rb
test
ARGV= test2.rb
So you could also write a program like:
require_relative ARGV.first
The first parameter defines a script to be loaded.
Or if you want to load many scripts you could use:
ARGV.each{|script| require_relative script }

rspec test ruby script

say we have a ruby file.rb like:
if __FILE__ == $0 then
if ARGV[0] == 'foo'
puts "working"
# Dir.chdir(../)
v = Someclass.new
v.do_something
end
end
it suppose to print working only if the file was triggered like ruby file.rb foo.
My question: how can that kind of stuf be tested within rspec?
My try is below. The file ran but not in the scope of rspec test:
Dir expected :chdir with (any args) once, but received it 0 times
it 'should work' do
FILE = File.expand_path('file.rb')
RUBY = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name'])
#v = Someclass.new
Someclass.should_receive(:new).and_return #v
#v.should_receive(:do_something)
`#{RUBY} #{FILE} foo`
end
Backticks runs new shell, executes command, and returns result as a string. Thats why it runs outside your scope. Backticks does not care about contents of your script: ruby, bash, or something else.
chdir, of course, applied only to this new shell, so there seems no way to check you sample script for directory changing (except of tracing system calls). Maybe some 'real' script will do something, output more, thus providing more possibilities to check it.

Resources