Ruby command line option that lists operations to perform - ruby

Is it possible for me to do something like this:
>ruby some_file.rb
>Your options are:
> 1. delete file blah.txt
> 2. delete file blah2.txt
> 3. delete file blah3.txt
> x to exit
> 1
> blah.txt was deleted
> 1. delete file blah.txt
> 2. delete file blah2.txt
> 3. delete file blah3.txt
> x to exit
> x
>
And this would read it's configuration from a file which would have all the files to delete.
I want this to be in a loop, so it keeps asking what to do unless you press 'x'.
How could you do this in Ruby?

You can read user input from standard input using Kernel#gets. This should hopefully point you in the right direction.

You need to create the configuration file your talking about that has all of the files you
need to delete.
Read the configuration file
Create a menu based on the configuration file
Read in user input and perform operations.

You can start from here:
def menu(choice)
case choice
when 1
# do_something
when 2
# ...
end
end
while ((a = gets) != "x\n")
menu(a)
end
as #Hunter McMillen stated you can use some configuration file to create the case-when programmatically.

if ARGV.size > 0
if FileTest.exists? ARGV[0]
files=[]
IO.readlines(ARGV[0]).each{|l| files.push(l.chop) if FileTest.exists? l.chop}
while files.size > 0
files.each_with_index{|f,i| puts "#{i+1}. #{f}"}
puts "x to exit"
opt = $stdin.gets.chop
break if opt == 'x'
opt = opt.to_i
puts "#{files.delete_at(opt-1)} was deleted" if opt > 0 && opt <= files.size && File.delete(files[opt-1]) > 0
end
end
end

Related

How to return to beginning of program from inside of if statement?

I'm practicing some basic coding, I'm running a simple math program running in the terminal on Visual Studio Code.
How do I create an option to return to the beginning of the program, or exit the program after getting caught in an if statement?
Example:
#beginning of program
user_input=input('Please select "this" or "that": ')
findings=user_input
If findings == this:
print(this)
# How can I redirect back to first user input question, instead
# of just ending here?
if findings == that:
print (that)
# Again, How do I redirect back to first user input, instead of
# the program ending here?
# Can I setup a Play_again here with options to return to user_input,
# or exit program? And then have all other If statements
# redirect here after completion? How would I do that? with
# another If? or with a for loop?
#end program
You can try wrapping the whole program in a while loop like this:
while(True):
user_input=input('Please select "this" or "that": ')
this = 'foo'
if user_input == this:
print(this)
continue
if user_input == this:
print(this)
continue
Unfortunately, that 'another technique' I thought of using didn't work.
Here's the code (the first example modified):
import sys
def main():
# Lots of setup code here.
def start_over():
return #Do nothing and continue from the next line
condition = 1==1 #Just a sample condition, replace it with "check(condition)".
float_condition = condition
def play(*just_define:bool):
if not just_define:
play_again = input('play again? "y" or "n"')
if play_again == 'y':
start_over() #Jump to the beginning of the prohram.
if play_again == 'n':
sys.exit() #Exit the program
while True:
if float_condition == True:
# print(float_condition)
play() #skip to play_again from here?
if float_condition == False:
#print(float_condition)
play() #skip to play_again from here?
#I removed the extra "main()" which was here because it'd cause an infinite loop-like error.
main()
Output:
play again? "y" or "n"y
play again? "y" or "n"n
Process finished with exit code 0
The * in the play(*just_define:bool) function makes the just_define parameter optional. Use the parameter if you want to only tell Python to search for this function, so it doesn't throw a ReferenceError and not execute anything that's after the line if not just_define:. How to call like so: play(just_define=True).
I've used nested functions. I've defined play so that you can call it from other places in your code.
Thanks to #Hack3r - I was finally able to choose to return back to the beginning of the program or exit out. But it resulted in a new issue. Now my print(results) are printing 4 or 5 times...
Here is the actual code I built and am working with:
def main():
math_Options=['Addition +','Subtraction -','Multiplication *','Division /']
math_func=['+','-','*','/']
for options in math_Options:
print(options)
print('Lets do some Math! What math function would you like to use? ')
while True:
my_Math_Function = input('Please make your choice from list above using the function symbol: ')
my_Number1=input('Please select your first number: ')
x=float(my_Number1)
print('Your 1st # is: ', x)
my_Number2=input('Please select your Second Number: ')
y=float(my_Number2)
print('Your 2nd # is: ', y)
z=float()
print('')
for Math_function in math_func:
if my_Math_Function == math_func[0]:
z=x+y
if my_Math_Function == math_func[1]:
z=x-y
if my_Math_Function == math_func[2]:
z=x*y
if my_Math_Function == math_func[3]:
z=x/y
if (z % 2) == 0 and z>0:
print(z, ' Is an EVEN POSITIVE Number')
if (z % 2) == 1 and z>0:
print(z, ' IS a ODD POSTIVE Number')
if (z % 2) == 0 and z<0:
print(z, ' Is an EVEN NEGATIVE Number')
if (z % 2) ==1 and z<0:
print(z, ' IS a ODD NEGATIVE Number')
if z==0:
print(z, 'Is is Equal to Zero')
print('')
play_again=input('Would you like to play again? "y" or "n" ')
if play_again == 'y':
continue
if play_again == 'n':
break
main()
main()

Ruby is there a way to stop the user from calling a function/procedure through case before they have accessed a different function/procedure?

I have a text file that I want to open first for reading or writing, but want the user to manually enter the text_file name (which opens the file for reading) first like so:
def read_in_albums
puts "Enter file name: "
begin
file_name = gets().chomp
if (file_name == "albums.txt")
puts "File is open"
a_file = File.new("#{file_name}", "r")
puts a_file.gets
finished = true
else
puts "Please re-enter file name: "
end
end until finished
end
From this unfinished code below, selecting 1 would go to the above procedure. I want the user to select 1 first, and if they choose 2 without having gone through read_in_albums they just get some sort of message like "no file selected and sent back to menu screen.
def main()
finished = false
begin
puts("Main Menu:")
puts("1- Read in Album")
puts("2- Display Album Info")
puts("3- Play Album")
puts("4- Update Album")
puts("5- Exit")
choice = read_integer_in_range("Please enter your choice:", 1, 5)
case choice
when 1
read_in_albums
when 2
display_album_info
when 5
finished = true
end
end until finished
end
main()
The only thing I can think of is something like
when 2
if(read_in_albums == true)
display_album_info
and have it return true from read_in_albums.
which I don't want to do as it just goes through read_in_albums again, when I only want it to do that if the user pressed 1.
All of your application's functionality depends on whether the album data has been read. You are no doubt storing this data as an object in memory referenced by some variable.
$album_data = File.read 'album.txt'
You can test whether this data is present in order to determine whether the file data has been read:
if $album_data.nil?
# ask user for album file
else
# show album user interface
end
There is no need for a separate flag. The mere presence of the data in memory serves as a flag already.
You could either set a flag when option 1 was selcted
has_been_read = false
...
when 1
read_in_albums
has_been_read = true
when 2
if has_been_read
display_album_info
else
puts "Select Option 1 first"
end
Or just test if your file name is a valid string.

Executing a TCL script from line N

I have a TCL script that say, has 30 lines of automation code which I am executing in the dc shell (Synopsys Design Compiler). I want to stop and exit the script at line 10, exit the dc shell and bring it back up again after performing a manual review. However, this time, I want to run the script starting from line number 11, without having to execute the first 10 lines.
Instead of having two scripts, one which contains code till line number 10 and the other having the rest, I would like to make use of only one script and try to execute it from, let's say, line number N.
Something like:
source a.tcl -line 11
How can I do this?
If you have Tcl 8.6+ and if you consider re-modelling your script on top of a Tcl coroutine, you can realise this continuation behaviour in a few lines. This assumes that you run the script from an interactive Tcl shell (dc shell?).
# script.tcl
if {[info procs allSteps] eq ""} {
# We are not re-entering (continuing), so start all over.
proc allSteps {args} {
yield; # do not run when defining the coroutine;
puts 1
puts 2
puts 3
yield; # step out, once first sequence of steps (1-10) has been executed
puts 4
puts 5
puts 6
rename allSteps ""; # self-clean, once the remainder of steps (11-N) have run
}
coroutine nextSteps allSteps
}
nextSteps; # run coroutine
Pack your script into a proc body (allSteps).
Within the proc body: Place a yield to indicate the hold/ continuation point after your first steps (e.g., after the 10th step).
Create a coroutine nextSteps based on allSteps.
Protect the proc and coroutine definitions in a way that they do not cause a re-definition (when steps are pending)
Then, start your interactive shell and run source script.tcl:
% source script.tcl
1
2
3
Now, perform your manual review. Then, continue from within the same shell:
% source script.tcl
4
5
6
Note that you can run the overall 2-phased sequence any number of times (because of the self-cleanup of the coroutine proc: rename):
% source script.tcl
1
2
3
% source script.tcl
4
5
6
Again: All this assumes that you do not exit from the shell, and maintain your shell while performing your review. If you need to exit from the shell, for whatever reason (or you cannot run Tcl 8.6+), then Donal's suggestion is the way to go.
Update
If applicable in your case, you may improve the implementation by using an anonymous (lambda) proc. This simplifies the lifecycle management (avoiding re-definition, managing coroutine and proc, no need for a rename):
# script.tcl
if {[info commands nextSteps] eq ""} {
# We are not re-entering (continuing), so start all over.
coroutine nextSteps apply {args {
yield; # do not run when defining the coroutine;
puts 1
puts 2
puts 3
yield; # step out, once first sequence of steps (1-10) has been executed
puts 4
puts 5
puts 6
}}
}
nextSteps
The simplest way is to open the text file, parse it to get the first N commands (info complete is useful there), and then evaluate those (or the rest of the script). Doing this efficiently produces slightly different code when you're dropping the tail as opposed to when you're dropping the prefix.
proc ReadAllLines {filename} {
set f [open $filename]
set lines {}
# A little bit careful in case you're working with very large scripts
while {[gets $f line] >= 0} {
lappend lines $line
}
close $f
return $lines
}
proc SourceFirstN {filename n} {
set lines [ReadAllLines $filename]
set i 0
set script {}
foreach line $lines {
append script $line "\n"
if {[info complete $script] && [incr i] >= $n} {
break
}
}
info script $filename
unset lines
uplevel 1 $script
}
proc SourceTailN {filename n} {
set lines [ReadAllLines $filename]
set i 0
set script {}
for {set j 0} {$j < [llength $lines]} {incr j} {
set line [lindex $lines $j]
append script $line "\n"
if {[info complete $script]} {
if {[incr i] >= $n} {
info script $filename
set realScript [join [lrange $lines [incr j] end] "\n"]
unset lines script
return [uplevel 1 $realScript]
}
# Dump the prefix we don't need any more
set script {}
}
}
# If we get here, the script had fewer than n lines so there's nothing to do
}
Be aware that the kinds of files you're dealing with can get pretty large, and Tcl currently has some hard memory limits. On the other hand, if you can source the file at all, you're already within that limit…

What do test automation frameworks provide that my approach of scripting with Selenium Webdriver doesn't?

I am a newbie to software test automation and have written the following test script in Selenium Webdriver and Ruby binding. It performs user actions (clicks, enters, fills up values etc.). I have put basic asserts that match the screen text value with value I provide. Below is the code:
puts "Test Run 1 has started ""["+Time.now.strftime('%H:%M:%S CST')+"]"
require "selenium-webdriver"
require "colorize"
profile = Selenium::WebDriver::Firefox::Profile.new
profile['browser.cache.disk.enable'] = false
browser = $browser = Selenium::WebDriver.for :firefox, :profile => profile
browser.manage().window().maximize();
browser.get "https://cameleon-6945--dev.cs11.cloudforce.com"
main_window = browser.window_handle
browser.find_element(name:"username").clear()
browser.find_element(name:"username").send_keys "abcd#vertex.com"
browser.find_element(name:"pw").send_keys "1234"
browser.find_element(name:"Login").click
browser.find_element(link_text:"Cameleon Quotes").click
wait = Selenium::WebDriver::Wait.new(:timeout => 45)
#Open Test Run 1 created quote
wait.until {browser.find_element(:css,"#bodyCell > div.bRelatedList > div.hotListElement > div > div.pbBody > table > tbody > tr.dataRow.even.first > th > a")}
browser.find_element(:css,"#bodyCell > div.bRelatedList > div.hotListElement > div > div.pbBody > table > tbody > tr.dataRow.even.first > th > a").click
wait.until {browser.find_element(:xpath,"/html/body/div[3]/div[3]/div[2]/div[3]/div[2]/div[2]/div[2]/form/div[3]/div/div[2]/div[2]/div/div/div/table")}
#browser.save_screenshot "Cart Overview - RegressionRun # "+Time.now.strftime('%Y-%m-%d %H%M%S')+".jpeg"
#wait.until {browser.find_element(:css,"body > div.mainPartBox > div.boxBody > div.main > div.processBar > div.backgroundProcessBarMiddle > a:nth-child(7) > div.processBarElement.noSelected > div")}
#browser.find_element(:css,"body > div.mainPartBox > div.boxBody > div.main > div.processBar > div.backgroundProcessBarMiddle > a:nth-child(7) > div.processBarElement.noSelected > div").click
#wait.until {browser.find_element(:css,"body > div.CombinedBox > div.boxBody > div.main > div:nth-child(8) > iframe")}
#wait.until {browser.find_element(:xpath,"/html/body/div[3]/div[3]/div[2]/div[3]/iframe")}
browser.manage.timeouts.page_load = 35
#browser.switch_to.frame(cart_frame)
puts "\n\n"
puts "Assertions to verify cart content values\n\n".yellow
element_value1 = browser.find_element(:css,"#total > tbody > tr > td:nth-child(4) > span").text
if element_value1 == "$314,507.30"
puts 'Contract Sales Price = ' +element_value1
puts 'Value as expected in the cart, Test Passed'
else
puts 'Test failed, Contract Sales price value does not match the expected value'
end
puts "\n"
element_value2 = browser.find_element(:css,"#total > tbody > tr > td:nth-child(6) > span").text
if element_value2 == "$157,253.65"
puts 'Contract Cost Price = ' +element_value2
puts 'Value as expected in the cart, Test Passed'
else
puts 'Test failed, Contract Cost price value does not match the expected value'.red
end
puts "\n"
element_value3 = browser.find_element(:css,"#total > tbody > tr > td:nth-child(8) > span").text
if element_value3 == "50.00"
puts 'Contract gross margin = ' +element_value3
puts 'Value as expected in the cart, Test Passed'
else
puts 'Test failed, Gross margin value does not match the expected value'
end
puts "\n\n\t\t\t\t\tTest Case 1 passed successfully, proceeding to Test Case 2 - Generate Document\n".green
It's not integrated with any framework, it's just a test script.
My questions are:
Is this test totally worthless if compared to tools like Cucumber/Capybara etc.?
I have captured most of the elements using XPaths and this way the only way since it lacked classes, ids etc. Now if there's a minor change in page structure, like a new div is introduced, this script will fail with NoElemenFoundError. Is there any way we can avoid it? Or this is the only way test scripts are written and we need to update them regularly with new developments?
That exercise is worth doing once for your own experience. A couple of ways that using rspec for example would be an improvement are that rspec gives you a way to organize multiple tests, and that it boils down the results of your tests to a single indicator of success or failure, so that you can just look at the last line of output (or have your CI server look at the exit status) instead of reading pages of messages to see whether all your tests passed. Cucumber differs from rspec in that it allows you to read your entire test in English without reading any code in between, which is extremely valuable for thinking through requirements and might allow you to collaborate on your tests with non-programmers. Capybara provides methods that do much what you're doing in your script but are more succinct.
Sensitivity to page structure is an issue even with more sophisticated tools. One way to minimize the issue is to not assert any more of the page structure than you have to. If there is only one table on the page and only one row of results then td:nth-child(6) is all the selector you need. Another way to deal with that issue is to add IDs or classes to your pages to support testing.
I would say it is worth to have such scripts, but as soon as your number of scripts grows you need to manage them in a way where minimum maintenance is required.
So you can use various strategies to design a framework where you can have central object repository and test data. Refer here! for more details on designs.
I agree with Dave, to avoid assert on page structure. And if you maintain a central repository then there would be little modification needed if there is changes in structure of a page.

Ruby data extraction from a text file

I have a relatively big text file with blocks of data layered like this:
ANALYSIS OF X SIGNAL, CASE: 1
TUNE X = 0.2561890123390808
Line Frequency Amplitude Phase Error mx my ms p
1 0.2561890123391E+00 0.204316425208E-01 0.164145385871E+03 0.00000000000E+00 1 0 0 0
2 0.2562865535359E+00 0.288712798671E-01 -.161563284233E+03 0.97541196785E-04 1 0 0 0
(they contain more lines and then are repeated)
I would like first to extract the numerical value after TUNE X = and output these in a text file. Then I would like to extract the numerical value of LINE FREQUENCY and AMPLITUDE as a pair of values and output to a file.
My question is the following: altough I could make something moreorless working using a simple REGEXP I'm not convinced that it's the right way to do it and I would like some advices or examples of code showing how I can do that efficiently with Ruby.
Generally, (not tested)
toggle=0
File.open("file").each do |line|
if line[/TUNE/]
puts line.split("=",2)[-1].strip
end
if line[/Line Frequency/]
toggle=1
next
end
if toggle
a = line.split
puts "#{a[1]} #{a[2]}"
end
end
go through the file line by line, check for /TUNE/, then split on "=" to get last item.
Do the same for lines containing /Line Frequency/ and set the toggle flag to 1. This signify that the rest of line contains the data you want to get. Since the freq and amplitude are at fields 2 and 3, then split on the lines and get the respective positions. Generally, this is the idea. As for toggling, you might want to set toggle flag to 0 at the next block using a pattern (eg SIGNAL CASE or ANALYSIS)
file = File.open("data.dat")
#tune_x = #frequency = #amplitude = []
file.each_line do |line|
tune_x_scan = line.scan /TUNE X = (\d*\.\d*)/
data_scan = line.scan /(\d*\.\d*E[-|+]\d*)/
#tune_x << tune_x_scan[0] if tune_x_scan
#frequency << data_scan[0] if data_scan
#amplitude << data_scan[0] if data_scan
end
There are lots of ways to do it. This is a simple first pass at it:
text = 'ANALYSIS OF X SIGNAL, CASE: 1
TUNE X = 0.2561890123390808
Line Frequency Amplitude Phase Error mx my ms p
1 0.2561890123391E+00 0.204316425208E-01 0.164145385871E+03 0.00000000000E+00 1 0 0 0
2 0.2562865535359E+00 0.288712798671E-01 -.161563284233E+03 0.97541196785E-04 1 0 0 0
ANALYSIS OF X SIGNAL, CASE: 1
TUNE X = 1.2561890123390808
Line Frequency Amplitude Phase Error mx my ms p
1 1.2561890123391E+00 0.204316425208E-01 0.164145385871E+03 0.00000000000E+00 1 0 0 0
2 1.2562865535359E+00 0.288712798671E-01 -.161563284233E+03 0.97541196785E-04 1 0 0 0
ANALYSIS OF X SIGNAL, CASE: 1
TUNE X = 2.2561890123390808
Line Frequency Amplitude Phase Error mx my ms p
1 2.2561890123391E+00 0.204316425208E-01 0.164145385871E+03 0.00000000000E+00 1 0 0 0
2 2.2562865535359E+00 0.288712798671E-01 -.161563284233E+03 0.97541196785E-04 1 0 0 0
'
require 'stringio'
pretend_file = StringIO.new(text, 'r')
That gives us a StringIO object we can pretend is a file. We can read from it by lines.
I changed the numbers a bit just to make it easier to see that they are being captured in the output.
pretend_file.each_line do |li|
case
when li =~ /^TUNE.+?=\s+(.+)/
print $1.strip, "\n"
when li =~ /^\d+\s+(\S+)\s+(\S+)/
print $1, ' ', $2, "\n"
end
end
For real use you'd want to change the print statements to a file handle: fileh.print
The output looks like:
# >> 0.2561890123390808
# >> 0.2561890123391E+00 0.204316425208E-01
# >> 0.2562865535359E+00 0.288712798671E-01
# >> 1.2561890123390808
# >> 1.2561890123391E+00 0.204316425208E-01
# >> 1.2562865535359E+00 0.288712798671E-01
# >> 2.2561890123390808
# >> 2.2561890123391E+00 0.204316425208E-01
# >> 2.2562865535359E+00 0.288712798671E-01
You can read your file line by line and cut each by number of symbol, for example:
to extract tune x get symbols from
10 till 27 on line 2
to extract LINE FREQUENCY get
symbols from 3 till 22 on line 6+n

Resources