Using Named Captures with regex match in Ruby's case...when? - ruby

I want to parse user input using named captures for readability.
When they type a command I want to capture some params and pass them. I'm using RegExps in a case statement and thus I can't assign the return of /pattern/.named_captures.
Here is what I would like to be able to do (for example):
while command != "quit"
print "Command: "
command = gets.chomp
case command
when /load (?<filename>\w+)/
load(filename)
end
end

named captures set local variables when this syntax.
regex-literal =~ string
Dosen't set in other syntax. # See rdoc(re.c)
regex-variable =~ string
string =~ regex
regex.match(string)
case string
when regex
else
end
I like named captures too, but I don't like this behavior.
Now, we have to use $~ in case syntax.
case string
when /(?<name>.)/
$~[:name]
else
end

This is ugly but works for me in Ruby 1.9.3:
while command != "quit"
print "Command: "
command = gets.chomp
case command
when /load (?<filename>\w+)/
load($~[:filename])
end
end
Alternatively you can use the English extension of $~, $LAST_MATCH_INFO.

Related

Vargrantfile - Ruby - unexpected end of file

I am new to ruby. I was trying to modify existing Vargrantfile which is of ruby syntax.
I have below
def has_program(program)
ENV['PATH'].split(File::PATH_SEPARATOR).any? do |directory|
File.executable?(File.join(directory, program.to_s))
end
end
is_exist = has_program("some-command")
puts is_exist
$my_script = %{
if is_exist == false
if ! some-command status; then
#Do some staff
fi
end
# do some staff
}
Vagrant.configure("2") do |config|
node.vm.provision "shell", inline: $my_script
end
Then while running vagrant up --provision I am getting below error
syntax error: unexpected end of file
Can you please let me know what error I am doing?
With regards,
-M-
This is a syntax error but not one in your Ruby code as such. It's an unfinished statement in the shell code that you're executing from the Ruby script.
This can happen if you leave a block unclosed. The parser expects to find its end but encounters the end of the script instead.
Let's look at the part where you're executing a shell command
$my_script = %{
if is_exist == false
if ! some-command status; then
#Do some staff
fi
end
# do some staff
}
Now, let's strip the Ruby parts you have around it. The assignment $my_script = is still Ruby code. The part in curly braces is a string literal in the % notation that you later execute as a shell script using Vagrant's inline... However, it appears that you're switching back to Ruby syntax before ending the string literal.
What the interpreter parses as a shell script is this part:
if is_exist == false
if ! some-command status; then
#Do some staff
fi
end
# do some staff
Notice that the whole outer if expression uses Ruby's if syntax. It's not a valid shell command, hence the error.
I'm not sure what the semantics of this expression are in your case but you need to convert it to a shell if or move it outside the string literal you're passing to Vagrant using the inline option. On a side note, the logic inside seems strange. You're calling some-command if has_program("some-command") returns false. But that's a separate story :)

File.exist? always returns false even when file does exist

I have a program that tries to open a file:
Dir.chdir(File.dirname(__FILE__))
puts "Enter file name: ";
relPath = gets;
absPath = Dir.pwd << "/" << relPath;
if File.exist?(absPath) then
puts "File exists";
file = File.open(absPath, "r");
other code...
else
puts "File does not exist";
end
It always prints "File does not exist" even when the current directory exists and the file also exists. The file and script are in the same directory.
I am running it on Mac OS X Yosemite (10.10.3) and Ruby 2.2.0p0.
I can't explain why (albeit I have strong belief that it's for some whitespace characters) but with this little contribution it works ok.
Dir.chdir(File.dirname(__FILE__))
print "Enter file name:";
relPath = gets.chomp; #intuitively used this, and it wroked fine
absPath = File.expand_path(relPath) #used builtin function expand_path instead of string concatenation
puts absPath
puts File.file?(absPath)
if File.exist?(absPath) then
puts "File exists";
puts File.ctime(absPath) #attempting a dummy operation :)
else
puts "File does not exist";
end
runnning code
$ ls -a anal*
analyzer.rb
$ ruby -v
ruby 2.2.0p0 (2014-12-25 revision 49005) [x86_64-linux]
ziya#ziya:~/Desktop/code/ruby$ ruby fileexists.rb
Enter file name:analyzer.rb
/home/ziya/Desktop/code/ruby/analyzer.rb #as a result of puts absPath
true #File.file?(absPath) => true
File exists
2015-06-11 12:48:31 +0500
That code has syntax error ("if" doesnt need "then"), and you dont have to put ";" after each line.
try
Dir.chdir(File.dirname(__FILE__))
puts "Enter file name: "
relPath = gets
absPath = "#{Dir.pwd}/#{relPath.chop}"
if File.exist?(absPath)
puts "File exists"
file = File.open(absPath, "r")
else
puts "File does not exist"
end
remember that gets will add a new line character so you will need to do a chomp, and that way to concatenate string won't work on ruby.
Your code is not idiomatic Ruby. I'd write it something like this untested code:
Dir.chdir(File.dirname(__FILE__))
puts 'Enter file name: '
rel_path = gets.chomp
abs_path = File.absolute_path(rel_path)
if File.exist?(abs_path)
puts 'File exists'
File.foreach(abs_path) do |line|
# process the line
end
else
puts 'File does not exist'
end
While Ruby supports the use of ;, they're for use when we absolutely must provide multiple commands on one line. The ONLY time I can think of needing that is when using Ruby to execute single-line commands at the command-line. In normal scripts I've never needed ; between statements.
then is used with if when we're using a single line if expression, however, we have trailing if which removes the need for then. For instance, these accomplish the same thing but the second is idiomatic, shorter, less verbose and easier to read:
if true then a = 1 end
a = 1 if true
See "What is the difference between "if" statements with "then" at the end?" for more information.
Instead of relPath and absPath we use snake_case for variables, so use rel_path and abs_path. It_is_a_readability AndMaintenanceThing.
File.absolute_path(rel_path) is a good way to take the starting directory and return the absolute path given a relative directory.
File.foreach is a very fast way to read a file, faster than slurping it using something like File.read. It is also scalable whereas File.read is not.

Randomize the echo char in Highline's ask method?

I am trying to randomize the echo character in the Highline gem's ask method, but could not get it to work. Did I not do this right?
srand
ask("password: ") { |q| q.echo = ('a'.ord+rand(26)).chr }
The character is randomized for each ask() call, but not each character. The first run will echo the same character, i.e. 'cccc'. The next run will echo 'mmmm', etc.
echo is a variable value used to determine whether to echo output. From the highline source:
# [echo] Can be set to +true+ or +false+ to control whether or not input will
# be echoed back to the user. A setting of +true+ will cause echo to
# match input, but any other true value will be treated as a String to
# echo for each character typed.
Your code (('a'.ord+rand(26)).chr) is being evaluated once per ask, stored in the echo variable within highline, and then printed out for each character entered.
You can't get it to print a different random character per input character without modifying highline.

Regex on directory for linux

I have tried the regex from the answer of this question: check directory path for symbols range and ".." up directory sign
Which was not working for me. If I passed my script a directory like this "/home/local/NKU/dixonc3/test/" it would not match the regex.
So I just tried to start out with something simple such as if a directory starts with a slash / or a tilde ~ it will pass.
^[~/].*$
even when I use this as my regex in the code below if I pass it a ~ tilde it gives me an error
./rename.rb:23:in `exists?': can't convert nil into String (TypeError)
from ./rename.rb:23:in `rename'
from ./rename.rb:33:in `<main>'
Below is my Ruby code
currDir = ""
#
# regex is from stack overflow question:
#dirRegex = Regexp.new '^(?!.*[\\/]\.{2}[\\/])(?!\.{2}[\\/])[-\w.\\/]+$'
dirRegex = Regexp.new '^[~/]*$'
if ARGV.length == 1 && ($1.to_s.match dirRegex)
currDir = $1
puts $1
puts "#{currDir}"
puts ARGV.length
else
currDir = "./"
puts $1
puts "#{currDir}"
puts ARGV.length
end
Your regex is matching on a single character (~ or / or nothing). It should be:
dirRegex = Regexp.new '^[~/].*$'
To match on an opening character of ~ or / and the rest.

Named groups from regular expression in case statement [duplicate]

I want to parse user input using named captures for readability.
When they type a command I want to capture some params and pass them. I'm using RegExps in a case statement and thus I can't assign the return of /pattern/.named_captures.
Here is what I would like to be able to do (for example):
while command != "quit"
print "Command: "
command = gets.chomp
case command
when /load (?<filename>\w+)/
load(filename)
end
end
named captures set local variables when this syntax.
regex-literal =~ string
Dosen't set in other syntax. # See rdoc(re.c)
regex-variable =~ string
string =~ regex
regex.match(string)
case string
when regex
else
end
I like named captures too, but I don't like this behavior.
Now, we have to use $~ in case syntax.
case string
when /(?<name>.)/
$~[:name]
else
end
This is ugly but works for me in Ruby 1.9.3:
while command != "quit"
print "Command: "
command = gets.chomp
case command
when /load (?<filename>\w+)/
load($~[:filename])
end
end
Alternatively you can use the English extension of $~, $LAST_MATCH_INFO.

Resources