In this program, I look for a file on the hard drive to choose by name, extension, name and extension, until the code became large, I wanted to know how to reduce it a little.
How to reduce if conditions number of lines in Ruby?
What is the best way to reduce the following condition in Ruby?
require "colorize"
new_name_file = {}
loop do
puts
puts "Will we search for the file by name(1), extension(2), name extension(3) or exit programm(4)?"
print "\n>>>>>> ".magenta
name_extension = gets.to_i
if name_extension == 1 # =========== search file name =================
puts "Enter file name (test, lesson, ruby....etc.) "
print "\n>>>>>> ".cyan
file_name = gets.strip.downcase
puts "Name the hard drive on which we will search for the file(C, D, E, F....e.t.c.): "
print "\n>>>>>> ".green
hdd_search = gets.strip.capitalize
# search file hdd
contents = Dir.glob("#{hdd_search}:/**/#{file_name}.*")
elsif name_extension == 2 # ========= search by file extension =============
puts "Enter file extension(txt, rb, jpg, csv, json) "
print "\n>>>>>> ".cyan
file_extension = gets.strip.downcase
# on which drive we will search put the letter
puts "Name the hard drive on which we will search for the file(C, D, E, F....e.t.c.): "
print "\n>>>>>> ".green
hdd_search = gets.strip.capitalize
# search file hdd
contents = Dir.glob("#{hdd_search}:/**/*.#{file_extension}")
elsif name_extension == 3 # ========= search by name and file extension =============
puts "Enter a name and file extension(test.txt, test.rb, test.jpg, test.csv, test.json..etc) "
print "\n>>>>>> ".cyan
file_extension_name = gets.strip
# on which drive we will search put the letter
puts "Name the hard drive on which we will search for the file(C, D, E, F....e.t.c.): "
print "\n>>>>>> ".green
hdd_search = gets.strip.capitalize
# search file hdd
contents = Dir.glob("#{hdd_search}:/**/#{file_extension_name}")
elsif name_extension == 4
puts "Exit programm".red
exit
end
contents.each do |txt_name|
z_name = File.basename(txt_name) # file name
path = File.expand_path(txt_name) # path file
new_name_file[z_name] = path # everything in the hash
end
new_name_file.each do |k, v| # hash output
puts "file : ".cyan + "#{k} " + " path:".cyan + "#{v}"
end
end
You can wrap the code inside the conditions in a method:
def process_file(file_types)
puts "Enter file name (#{file_types.join(',')}....etc.) "
print "\n>>>>>> ".cyan
file_name = gets.strip.downcase
puts "Name the hard drive on which we will search for the file(C, D, E, F....e.t.c.): "
print "\n>>>>>> ".green
hdd_search = gets.strip.capitalize
# search file hdd
contents = Dir.glob("#{hdd_search}:/**/#{file_name}.*")
end
file_types = {
"1" => ['test', 'lesson', 'ruby']
"2" =>
}
loop do
puts
puts "Will we search for the file by name(1), extension(2), name extension(3) or exit programm(4)?"
print "\n>>>>>> ".magenta
name_extension = gets
if name_extension == '4'
puts "Exit programm".red
exit
end
process_file(file_types[name_extension])
end
You can Rubyize this code a lot by trimming down what the main loop does and focus on the job of getting input and delegating:
class Tester
def select
loop do
puts
puts "Will we search for the file by name(1), extension(2), name extension(3) or exit programm(4)?"
print "\n>>>>>> "
input = gets.chomp
search_method = "search_#{input}"
if (respond_to?(search_method))
contents = send(search_method)
contents.each do |txt_name|
z_name = File.basename(txt_name) # file name
path = File.expand_path(txt_name) # path file
new_name_file[z_name] = path # everything in the hash
end
else
puts "Unknown input: #{input.inspect}, method #{search_method} not defined."
end
end
end
end
Then you can engage this with:
Tester.new.select
This dynamically dispatches to methods that follow a simple naming
convention. To add another method, define one:
def search_1
puts "Enter file name (test, lesson, ruby....etc.) "
print "\n>>>>>> ".cyan
file_name = gets.strip.downcase
puts "Name the hard drive on which we will search for the file(C, D, E, F....e.t.c.): "
print "\n>>>>>> ".green
hdd_search = gets.strip.capitalize
# search file hdd
Dir.glob("#{hdd_search}:/**/#{file_name}.*")
end
These methods should return the contents to be displayed. That's pretty simple.
You can take this a step further by defining classes instead of methods where you can instantiate them and iterate over their results, moving this a lot closer to recommended object-oriented design patterns.
Related
When I run the following code taken from the Learn Ruby the Hard Way Course Exercise 16,
filename = ARGV.first
target = open(filename, 'w+')
puts "Now I'm going to ask you for three lines."
print "line 1: "
line1 = $stdin.gets.chomp
print "line 2: "
line2 = $stdin.gets.chomp
print "line 3: "
line3 = $stdin.gets.chomp
puts "I'm going to write these to the file."
target.write(line1 + "\n" + line2 + "\n" + line3)
puts "And now I'm going to print the file to prove I have altered it."
puts target.read
puts "And finally, we close it."
target.close
the line puts target.read does not print the three input lines, even though the text file does change.
I have tried changing the mode used by the open method and adding a new open method before calling the read method. Creating a separate program with the same script to read and print text file works as expected.
How can I read a file I have just written to? Why does it not work when I write and read within the same program?
why does it not work when I write and read within the same program
The answer is that when you write to a file, your IO stream is set to the end of where you have written. When you read, it continues from this point. In this case, after writing, you have reached the end of the file and there is nothing else to 'read'. You can use IO#rewind to reroll to the beginning and print out what was just written to through the IO Stream.
filename = 'Test.txt'
target = open(filename, 'w+')
text = '12345'
target.write(text) # target points to EOF
# Note that if you print target.write(), it will tell you the 'index' of where the IO stream is pointing. In this case 5 characters into the file.
puts "And now I'm going to rewind the file"
puts target.rewind # go back to the beginning of the file.
# => 0
puts "And now I'm going to print the file to prove I have rewound it."
puts target.read # read the file, target now points to EOF.
# => '12345'
target.close
File.open("my/file/path", "r") do |f|
f.each_line do |line|
puts line
end
end
File is closed automatically at end of block
It is also possible to explicitly close file after as above (pass a block to open closes it for you):
f = File.open("my/file/path", "r")
f.each_line do |line|
puts line
end
f.close
Credit to: https://stackoverflow.com/a/5545284/8328756
I have a school assignment that I need help with.
This is the description of the assignment:
Ruby program that can hold these values:
Artifacts
Values
Assumptions
It shall be possible for a user:
To enter these three types of values.
Search for a value of type Artifacts, Values or Assumptions.
The program shall use loops and at least one class definition.
The only function that won't work out for me are these lines:
f= File.new("Artefacts", "r")
puts "Search for information regarding cultural information"
userinput = gets.chomp
if File.readlines("Artefacts").include?('userinput')
puts "We have found your input."
else
puts "We have not found your input."
f.close
No matter what the user inserts, it only displays "We have not found your input".
Part A: get user input and write to file
def write_to_file(path, string)
# 'a' means append
# it will create the file if it doesnt exist
File.open(path, 'a') do |file|
file.write string + "\n"
end
end
path = "Artefacts"
num_inputs = 3
num_inputs.times do |i|
puts "enter input (#{i + 1} / #{num_inputs}):"
write_to_file path, gets.chomp
end
puts `cat #{path}`
# if you entered "foo" for each input,
# this will show:
# foo
# foo
# foo
Part B: read a file and check if it contains a string:
path = "./Artefacts"
query = "foo"
text = File.read path
# this will be a string with all the text
lines = File.readlines path
# this will be an array of strings (one for each line)
is_text_found = text.include? query
# or
is_text_found = lines.any? do |line|
line.include? query
end
I am trying to accept the first_name, the middle name, and the last name of the user and then display the abbreviated first, middle name of the user with the unaltered last name. I've written the following code and managed to get for a single user.
h = Hash.new
puts "Enter the first name for user"
h["First_name"] = gets.capitalize
puts "Enter the Middle name for user"
h["Middle_name"] = gets.capitalize
puts "Enter the Last name for user"
h["Last_name"]= gets.capitalize
puts "The name entered is #{h["First_name"][0]}" +
"." + "#{h["Middle_name"][0]}" + "." + "#{h["Last_name"]}"
I want this to happen for five users repeatedly, and display all five name at the end. Can any one help me find a solution for this problem?
Put the whole thing in an array (and end the block with h):
a = Array.new(5) do
h = Hash.new
puts "Enter the first name for user"
h["First_name"] = gets.capitalize
puts "Enter the Middle name for user"
h["Middle_name"] = gets.capitalize
puts "Enter the Last name for user"
h["Last_name"] = gets.capitalize
h
end
In the end, a will have five hashes of the kind you had. You might want to put chomp after each gets to get rid of the newline character.
You can print each name by iterating over a.
a.each do |h|
puts "The name entered is " + h["First_name"][0] +
"." + h["Middle_name"][0] + "." + h["Last_name"]
end
Couldn't find a better way to write the following:
def get_name
print "Please enter your name: "
name = ""
loop do
break if (name = gets.chomp).match(/^[[:alpha:]]+$/)
print "Please enter your name again (must be one or more letters): "
end
name
end
How can I write this ruby method in a better way?
Problem with your code is that it's trying to do two quite different functions at the same time: validating format and handling user input. Better to separate the two. What do you think of this?
def format_ok?(name)
name =~ /\A[[:alpha:]]+\z/
end
def get_name
print "Please enter your name: "
loop do
name = gets.chomp
return name if format_ok?(name)
print "Please enter your name again (must be one or more letters): "
end
end
Here's another way to write it:
def get_name
print "Please enter your name: "
until gets =~ /^[[:alpha:]]+$/
print "Please enter your name again (must be one or more letters): "
end
$_.chomp
end
It reads a line from standard input until it matches the regular expression, printing an error message otherwise. Upon success, it returns the chomped line (gets assigns to $_).
Just to provide one more variation:
def prompt_name(p)
puts p
gets.chomp.strip
end
def get_name
name = ''
name = prompt_name("Please enter your name #{name.empty? ? '' : 'again '}:") while name !~ /\A[[:alpha:]]+\z/
name
end
how about this?
def get_name
print "Please enter your name: "
name = gets.chomp
until name =~ /[[:alpha:]]/
print "Please enter your name again (must be one or more letters): "
name = gets.chomp
end
puts "your name is #{name}"
end
get_name
> get_name
# Please enter your name: 3212
# Please enter your name again (must be one or more letters): 12Gagan
# your name is 12Gagan
Demo
This seems to work correctly and display the results I want
Dir["/Users/ondrovic/RubyTest/**/*.txt"].each do |i|
puts i.green if File.readlines(i).grep(/test/).any?
end
Why when I try like this does it not populate info
print "Enter search dir"
src = gets
print "Enter search term"
trm = gets
puts "Looking in #{src} for #{trm}"
Dir["#{src}/**/*.txt"].each do |i|
puts i.green if File.readlines(i).grep(/"#{trm}"/).any?
end
I have also tried it this way
Dir[src + "/**/*.txt"].each do |i|
puts i.green if File.readlines(i).grep(/ + trm + /).any?
end
Working code
require 'colorize'
print "Enter search dir\n".green
src = gets.chomp
print "Enter search term\n".blue
trm = gets.chomp
puts "Looking in #{src} for #{trm}"
Dir["#{src}/**/*.txt"].each do |i|
puts i.green if File.readlines(i).grep(/#{trm}/).any?
end
The return value of the gets includes trailing newline:
>> gets
asdf
=> "asdf\n" # <----
>>
Remove it using String#chomp:
print "Enter search dir"
src = gets.chomp
print "Enter search term"
trm = gets.chomp
...
In addition to that, the pattern /"#{trm}"/ includes ". Remove them:
/#{trm}/
Alternatively, you can use Regexp::new:
Regexp.new(trm)