Ruby write to file exception - ruby

I'm having some troubles with a Ruby script I wrote to unzip an archive and then make files.
script.rb:66: syntax error, unexpected unary+, expecting '}'
...images/pages/"+ filename.to_s +".jpg', alt=''"
#fileList.each do |filename|
File.open('templates/layouts/_partials/page-' + filename.to_s + '.jade', 'w') { |file|
file.puts ".c-page\n"
file.puts " img(src='images/pages/"+ filename.to_s +".jpg', alt=''"
}
end
If I do this:
File.open('templates/layouts/_partials/page-' + filename.to_s + '.jade', 'w') { |file|
file.puts ".c-page\n"
file.puts " img(src='images/pages/"
file.puts filename.to_s
file.puts ".jpg', alt=''"
}
I don't have the result I want, because it prints a new line every time.

Try this
#fileList.each do |filename|
File.open("templates/layouts/_partials/page-#{filename.to_s}.jade", 'w') do |file|
file.puts ".c-page"
file.puts " img(src='images/pages/#{filename.to_s}.jpg', alt=''"
end
end
puts adds a new line character so I see no need for the first one and string interpolation will work better than appending with the + sign. Another note that I try and hold to is using {} syntax for single line and do..end for multi-line blocks. It makes it cleaner and easier to read.

You need space between a string and a plus sign:
pry(main)> " img(src='images/pages/"+ filename.to_s +".jpg', alt=''"
SyntaxError: unexpected unary+, expecting end-of-input
" img(src='images/pages/"+ filename.to_s +".jpg', alt=''"
and this works:
pry(main)> " img(src='images/pages/" + filename.to_s + ".jpg', alt=''"
=> " img(src='images/pages/whatever.jpg', alt=''"
Or just use string interpolation, as engineersmnky suggested in the comment.
"string"+ is treaten as running unary operator + on the string object.

Related

Remove concrete string from CSV in Ruby

I got a ruby script which take XML files and create from them CSV. This CSV use semicolons as delimiter -> but, content of XML contains these tags:
- &
- <
- >
And this of course break the structure of CSV file. I need clean it up. This cleaner must be writen in Ruby. I try next code, but this complete destroy the file.
#Clean up CSV file
#Remove: & \< >
file_names = ['terms.csv']
file_names.each do |file_name|
text = File.read(file_name)
new_contents = text.gsub(/&/, " and ")
# To merely print the contents of the file, use:
puts new_contents
# To write changes to the file, use:
File.open(file_name, "w") {|file| file.puts new_contents }
end
file_names.each do |file_name|
text = File.read(file_name)
new_contents = text.gsub(/</, " < ")
puts new_contents
File.open(file_name, "w") {|file| file.puts new_contents }
end
file_names.each do |file_name|
text = File.read(file_name)
new_contents = text.gsub(/>/, " > ")
puts new_contents
File.open(file_name, "w") {|file| file.puts new_contents }
end
I never use Ruby - this is my first contact. Is there better way how to do this?
I solved it... I change CSV delimiter from ";" to "#" in FOR cycle which create a CSV file. It is not ideal solution, but it works.

Ruby script to search a string and delete entire raw?

I have a text file and I need to search for a string, if that string is found I want to delete entire raw containing that string and add a new raw. As for now I just try to find a string and delete entire raw. And the string I search should be given as an argument.
Code I have as far goes here:
arg1, arg2, arg3 = ARGV
read_file = File.new('conf.txt', "r").read
write_file = File.new('conf.txt', "w")
read_file.each_line do |line|
write_file.write(line) unless line.include? arg1 arg2 arg3
end
But the output I get is as follows:
C:/Ruby/tests/replace3.rb:9:in `block in <main>': undefined method `arg2' for ma
in:Object (NoMethodError)
from C:/Ruby/tests/replace3.rb:8:in `each_line'
from C:/Ruby/tests/replace3.rb:8:in `<main>'
Another approach I try is the following:
file_names = ['config.txt']
file_names.each do |file_name|
text = File.read(file_name)
new_contents = text.gsub(/regex_string/, "new_string")
File.open(file_name, "w") {|file| file.puts new_contents }
end
But the problem here is that the regex string finds only certan string and doesn't take all that raw. Maybe you have any idea how I can optimize this code for my problem.
So include? expects to receive a single argument:
line.include? single_variable
Take a look here:
http://apidock.com/ruby/Array/include%3F
EDIT:
You could try something like this:
unwanted_array = ['unwanted', 'error', 'string']
read_file.each_line do |line|
write_file.write(line) unless unwanted_array.map { |val| line.include? val }.reduce(:|)
end
Unwanted array could also be built up by doing:
unwanted_array = [arg1, arg2, arg3]

Writing regex result into a new file

I've got a list of devices:
ipc-bei640-r-br-01
ipc-bei640-r-br-02
ipc-bei640-r-br-03
ipc-bei640-r-br-04
ipc-bei640-r-br-05
ipc-bem640-r-br-01
ipc-bem640-r-br-02
ipc-bem640-r-br-03
ipc-crg660-r-br-02
ipc-geb680-r-br-04
ipc-lgv630-r-br-01
This small little ruby script counts the lines of the file braslist.txt scans it with a regex and writes the results to a newfile called "strippedfile.txt"
lines = IO.readlines("/usr/local/bin/braslist.txt")
# Linecount is forwarded to StdOut.
puts lines.length
str = File.read('braslist.txt')
file_name = ['strippedfile.txt']
file_name.each do |file_name|
text = File.read(file_name)
new_contents = str.scan(/^ipc-(?<bng>[a-z]{3}\d{3})-r-br(?<nr>-\d{2})$/)
# open and write to a file with ruby
open('strippedfile.txt', 'w') { |f|
f.print new_contents
}
end
Now what i cant seem to fix, is in the new file "strippedfile" the results are always ["bei640", "-01"] ["bei640", "-02"] ["bei640", "-03"]
And i am trying to get all results in this format:
bei640-01
bei640-02
bei640-03
bei640-04
scan returns an array of matches, you probably want to join them:
- new_contents = str.scan(/^ipc-(?<bng>[a-z]{3}\d{3})-r-br(?<nr>-\d{2})$/)
+ new_contents = str.scan(/^ipc-(?<bng>[a-z]{3}\d{3})-r-br(?<nr>-\d{2})$/).map(&:join)
To print everything without quotes and brackets line by line:
- f.print new_contents
+ f.puts new_contents
Assuming your resultant array is
a = [["bei640", "-02"], ["bei640", "-03"]]
You can use join to get your desired result
a.map{|i| i.join } #=> ["bei640-02", "bei640-03"]
or use shortcut as mudasobwa answered
a.map(&:join) #=> ["bei640-02", "bei640-03"]

Ruby: Writing to file in subdirectory

I am trying to write to a file inside a subdirectory. This file is created by the code but, once the file is created, it is empty after the script finishes its execution. What am I doing wrong?
# Creating output files
print "Creating output files and filling root menu..."
FileUtils.cd(outdir) do
file = File.new("directory.xml", "w")
file.puts "<?php header(\"Content-type: text/xml\"); ?>"
file.puts "<CiscoIPPhoneMenu>"
file.puts "<Title>Telefonbuch</Title>"
file.puts "<Prompt>Dir External</Prompt>"
letters_used.each do |letter|
filename = "contacts_" + letter + ".xml"
FileUtils.touch(filename)
file.puts "<MenuItem>"
file.puts "<Name>" + letter.upcase + "</Name>"
file.puts "<URL>http://" + HOSTNAME + WEBSERV_DIR + "/" + filename + "</URL>"
file.puts "</MenuItem>"
end
file.puts "</CiscoIPPhoneMenu>"
file.rewind
end
print "Done\n"
"directory.xml" should link to each "contacts_letter.xml" file, which is created by the script too, however directory.xml is empty. Why?
Idiomatic Ruby would write to the file using a block:
File.new("directory.xml", "w") do |fo|
fo.puts "<?php header(\"Content-type: text/xml\"); ?>"
fo.puts "<CiscoIPPhoneMenu>"
fo.puts "<Title>Telefonbuch</Title>"
fo.puts "<Prompt>Dir External</Prompt>"
letters_used.each do |letter|
filename = "contacts_" + letter + ".xml"
FileUtils.touch(filename)
fo.puts "<MenuItem>"
fo.puts "<Name>" + letter.upcase + "</Name>"
fo.puts "<URL>http://" + HOSTNAME + WEBSERV_DIR + "/" + filename + "</URL>"
fo.puts "</MenuItem>"
end
fo.puts "</CiscoIPPhoneMenu>"
end
This closes the file automatically at the end of the block.

Too much nesting in ruby?

Surely there must be a better way of doing this:
File.open('Data/Networks/to_process.txt', 'w') do |out|
Dir['Data/Networks/*'].each do |f|
if File.directory?(f)
File.open("#{f}/list.txt").each do |line|
out.puts File.basename(f) + "/" + line.split(" ")[0]
end
end
end
end
Cheers!
You can rid of 1 level of nesting by utilizing Guard Clause pattern:
File.open('Data/Networks/to_process.txt', 'w') do |out|
Dir['Data/Networks/*'].each do |f|
next unless File.directory?(f)
File.open("#{f}/list.txt").each do |line|
out.puts File.basename(f) + "/" + line.split(" ")[0]
end
end
end
See Jeff Atwood's article on this approach.
IMHO there's nothing wrong with your code, but you could do the directory globbing and the check from the if in one statement, saving one level of nesting:
Dir.glob('Data/Networks/*').select { |fn| File.directory?(fn) }.each do |f|
...
end
Since you're looking for a particular file in each of the directories, just let Dir#[] find them for you, completely eliminating the need to check for a directory. In addition, IO#puts will accept an array, putting each element on a new line. This will get rid of another level of nesting.
File.open('Data/Networks/to_process.txt', 'w') do |out|
Dir['Data/Networks/*/list.txt'] do |file|
dir = File.basename(File.dirname(file))
out.puts File.readlines(file).map { |l| "#{dir}/#{l.split.first}" }
end
end
Reducing the nesting a bit by separating the input from the output:
directories = Dir['Data/Networks/*'].find_all{|f| File.directory?(f)}
output_lines = directories.flat_map do |f|
output_lines_for_directory = File.open("#{f}/list.txt").map do |line|
File.basename(f) + "/" + line.split(" ")[0]
end
end
File.open('Data/Networks/to_process.txt', 'w') do |out|
out.puts output_lines.join("\n")
end

Resources