Why is my program given an unexpected token: $end error? - ruby

Here is my unfinished code:
#When convert button is pressed
File.rename("*.osz", "*.zip$")
dialog.directory(
def extract_zip(file, destination) FileUtils.mkdir_p(destination)
file_path = "./convert_temp/*.zip"
destination = "./convert_temp/osz/"
extract_zip(file_path, destination)
until File.exists?( ".osu$" ) == false do
File.rename("./convert_temp/osz/*.osu$", "*.txt$")
File.foreach(filename) do |file|
file_string = File.read('./convert_temp/osz/*.txt$')
if file_string.include?('Mode: 1')
puts 'Yes'
else
puts 'No'
end
end
end
Robocop giving the following syntax error:
unexpected token $end (Using Ruby 2.2 parser; configure using `TargetRubyVersion` parameter, under `AllCops`)

Actually, Rubocop is not even able to parse the file, because it has syntax errors.
The error message syntax error: unexpected token $end means that the ruby parser was parsing along happily, but then it suddenly encountered an $end, which is the parser's way to say "the end of the file". It was expecting more code, but instead it found the end of the file.
This is what your code looks like with proper indentation:
#When convert button is pressed
File.rename("*.osz", "*.zip$")
dialog.directory(
def extract_zip(file, destination) FileUtils.mkdir_p(destination)
file_path = "./convert_temp/*.zip"
destination = "./convert_temp/osz/"
extract_zip(file_path, destination)
until File.exists?( ".osu$" ) == false do
File.rename("./convert_temp/osz/*.osu$", "*.txt$")
File.foreach(filename) do |file|
file_string = File.read('./convert_temp/osz/*.txt$')
if file_string.include?('Mode: 1')
puts 'Yes'
else
puts 'No'
end
end
end
Using this kind of indentation makes it easy to see that there are some missing ends/parentheses, because the last line is left hanging in the air instead of closing back to the left edge where it started from.
Additional notes:
dialog.directory(
def extract_zip(file, destination) FileUtils.mkdir_p(destination)
It's very unconventional to define a new method inside a method call. File.open(def hello_world(..)) Doesn't make a lot of sense.
until File.exists?( ".osu$" ) == false do
Are you using $ as a way to indicate "filename ends in .osu"? If yes, it does not work like that. This would look for a file that has .osu$ as name.
File.foreach(filename) do |file|
The file parameter is not used in the block that follows, you use file_string.
file_string = File.read('./convert_temp/osz/*.txt$')
You can't read multiple files at once like that. Also, File.foreach above would read the file line by line, so here you are trying to read it again, inside the loop that is reading it already.

Related

How to scan text file for string, and run code if string isn't there?

I have tried:
elsif file.grep(/Mode: 1/)
puts "test"
else
puts "test but else"
Codacy is saying this is wrong. Is there a way to improve this? According to Codacy:
unexpected token kELSIF (Using Ruby 2.2 parser; configure using `TargetRubyVersion` parameter, under `AllCops`)
unexpected token kELSE (Using Ruby 2.2 parser; configure using `TargetRubyVersion` parameter, under `AllCops`)
You can use IO::read to assign text file content and then String#include? for checking.
file_string = File.read('path/to/file')
if file_string.include?('substring')
puts 'Yes'
else
puts 'No'
end
Also you can replace it by String#match? (it is faster).
file_string.match?(/pattern/) will return true or false.

Breaking early from 'CSV.foreach' enumerator

I would like to validate my file.
Should parse the file line by line (and not read the whole file into memory).
Should exit as soon as it passes the test.
Should consider the file valid if any of the vendor_codes match the provided vendor_code.
Some code:
error = "WRONG VENDOR DUMMY. This is #{account.vendor_code}"
CSV.foreach(file, options) do |row|
if row[:vendor].to_s == account.vendor_code.to_s
error = false
break
else
next
end
end
raise(error) if error
is there a more elegant way to do this?
I think this approach is better:
valid = CSV.foreach(file, options).any? do |row|
row[:vendor].to_s == account.vendor_code.to_s
end
raise("WRONG VENDOR DUMMY. This is #{account.vendor_code}") unless valid

Ruby: unexpected ',', expecting keyword_end

Very new to Ruby, unable to see the titular syntax error in this bit of code:
#! /usr/bin/env ruby
require 'sensu-plugin/metric/cli'
class MetricAvailableUpdates < Sensu::Plugin::Metric::CLI::Graphite
option :scheme,
description: 'Metric naming scheme',
long: '--scheme SCHEME',
short: '-s SCHEME',
default: "#{Socket.gethostname}"
def run
# Get the metrics.
output = %x[/usr/lib/update-notifier/apt-check --human-readable]
output_lines = output.split(/(\n)/)
metrics = {}
updates_pattern = " packages can be updated."
updates = output_lines[0].tr(upgrades_pattern, "").to_i
metrics[:available_updates] = updates
security_updates_pattern = " updates are security updates."
security_updates = output_lines[2].tr(security_updates_pattern, "").to_i
metrics[:available_security_updates] = security_updates
# Print them in graphite format.
metrics.each do |k, v|
output [config[:scheme], k].join('.'), v
end
# Done
ok
end
end
I can add the code that precedes this if the syntax error is in fact before this section. Edit: added complete file contents per comment request
The complete error, in case that is useful:
./metrics-available-updates.rb:29: syntax error, unexpected ',', expecting keyword_end
output [config[:scheme], k].join('.'), v
If you play around a bit, you will notice that the syntax error goes away either when you comment out the offending line, or alternatively the line
output = %x[/usr/lib/update-notifier/apt-check --human-readable]
When Ruby parses a file, it needs to guess, whether a symbol denotes a method call, or a variable reference. In this case, output springs into existence as a variable, but further down, you write
output [config[:scheme], k].join('.'), v
which means it suddenly becomes a method call.
I admit that the Ruby lexer should give a more helpful error message....
Add the parentheses
...
metrics.each do |k, v|
output([config[:scheme], k].join('.'), v)
end
...

Using rescue and ensure in the middle of code

Still new to Ruby - I've had a look at some of the answers to seemingly similar questions but, to be honest, I couldn't get my head around them.
I have some code that reads a .csv file. The data is split into groups of 40-50 rows per user record and validates data in the rows against a database accessed via a website.
A login is required for each record, but once that user has logged in each row in the .csv file can be checked until the next user is reached, at which point the user logs out.
All that's working, however, if an error occurs (e.g. a different result on the website than the expected result on the .csv file) the program stops.
I need something that will
a) at tell me which line on the file the error occurred
b) log the row to be output when it's finished running, and
iii) restart the program from the next line in the .csv file
The code I have so far is below
Thanks in advance,
Peter
require 'csv-mapper'
loginrequired = true
Given(/^I compare the User Details from rows "(.*?)" to "(.*?)"$/) do |firstrow, lastrow|
data = CsvMapper.import('C:/auto_test_data/User Data csv.csv') do
[dln, nino, pcode, endor_cd, ct_cd]
end
#Row number changed because Excel starts at 'row 1' and Ruby starts counting at 'row 0'
(firstrow.to_i-1..lastrow.to_i-1).each do |row|
#licnum1 = data.at(row).dln
#licnum2 = data.at(row+1).dln
#nino = data.at(row).nino
#postcode = data.at(row).pcode
#endor_cd = data.at(row).endor_cd
#ct_cd = data.at(row).ct_cd
#Login only required once for each new user-account
if
loginrequired == true
logon_to_vdr #def for this is in hooks
click_on 'P and D'
loginrequired = false
end
#This is the check against the database and is required for every line in the .csv file
check_ctcd #def for this is in hooks
#Need something in here to log errors and move on to the next line in the .csv file
#Compare the ID for the next record and logout if they're different
if #licnum1 == #licnum2
loginrequired = false
else
loginrequired = true`enter code here`
click_on 'Logout'
end
end
end
It seems like you need some error logging since you apparently don't know what type of error you're receiving or where. If this script is standalone you can redirect $stderr to file so that you can read what went wrong.
# put this line at the top of your script
$stderr = File.open("/path/to/your/logfile.log","a")
When an error occurs, ruby will automatically write the error message, class, and backtrace to the log file you specify so that you can trace back the line where things are not going as expected. (When you run a script from the command line, normally this information will just get blurted back to the terminal when an error happens.)
For example, on my desktop I created a file log_stderr.rb with the following (line numbers included):
1 $stderr = File.open("C:/Users/me/Desktop/my_log.log","w")
2
3 #require a file which will raise an error to see the backtrace
4 require_relative 'raise_error.rb'
5
6 puts "code that will never be reached"
Also on my desktop I created the file raise_error.rb with the following (to deepen the backtrace for better example output):
1 # call raise to generate an error arbitrarily
2 # to halt execution and exit the program.
3 raise RuntimeError, 'the program stopped working!'
When I run ruby log_stderr.rb from the command line, my_log.log is created on my desktop with the following:
C:/Users/me/Desktop/raise_error.rb:3:in `<top (required)>': the program stopped working! (RuntimeError)
from C:/Users/me/Desktop/log_stderr.rb:4:in `require_relative'
from C:/Users/me/Desktop/log_stderr.rb:4:in `<main>'
If you are working in a larger environment where your script is being called amidst other scripts then you probably do not want to redirect $stderr because this would affect everything else running in the environment. ($stderr is global as indicated by the $ variable prefix.) If this is the case you would want to implement a begin; rescue; end structure and also make your own logfile so that you don't affect $stderr.
Again, since you don't know where the error is happening you want to wrap the whole script with begin; end
# at the very top of the script, begin watching for weirdness
begin
logfile = File.open("/path/to/your/logfile.log", "w")
require 'csv-mapper'
#. . .
# rescue and end at the very bottom to capture any errors that have happened
rescue => e
# capture details about the error in your logfile
logfile.puts "ERROR:", e.class, e.message, e.backtrace
# pass the error along since you don't know what it is
# and there may have been a very good reason to stop the program
raise e
end
If you find that your error is happening only in the block (firstrow.to_i-1..lastrow.to_i-1).each do |row| you can place the begin; end inside of this block to have access to the local row variable, or else create a top level variable independent of the block and assign it during each iteration of the block to report to your logfile:
begin
logfile = File.open("/path/to/your/logfile.log", "w")
csv_row = "before csv"
#. . .
(firstrow.to_i-1..lastrow.to_i-1).each do |row|
csv_row = row
#. . .
end
csv_row = "after csv"
rescue => e
logfile.puts "ERROR AT ROW: #{csv_row}", e.class, e.message, e.backtrace
raise e
end
I hope this helps!
It doesn't seem like you need to rescue exception here. But what you could do is in your check_ctcd method, raise error if records doesn't match. Then you can rescue from it. In order to know which line it is, in your iteration, you could use #each_with_index and log the index when things go wrong.
(firstrow.to_i-1..lastrow.to_i-1).each_with_index do |row, i|
#licnum1 = data.at(row).dln
#licnum2 = data.at(row+1).dln
#nino = data.at(row).nino
#postcode = data.at(row).pcode
#endor_cd = data.at(row).endor_cd
#ct_cd = data.at(row).ct_cd
#Login only required once for each new user-account
if
loginrequired == true
logon_to_vdr #def for this is in hooks
click_on 'P and D'
loginrequired = false
end
#This is the check against the database and is required for every line in the .csv file
check_ctcd #def for this is in hooks
rescue => e
# log the error and index here
...
And you can make your own custom error, and rescue only the certain type so that you don't silently rescue other errors.

Toggling true/false: editing a file in ruby

I have some code that tries to change 'false' to 'true' in a ruby file, but it only works once while the script is running.
toggleto = true
text = File.read(filename)
text.gsub!("#{!toggleto}", "#{toggleto}")
File.open(filename, 'w+') {|file| file.write(text); file.close}
As far as I know, as long as I close a file, i should be able to read it it afterwards with what I previously wrote and thus change it back and forth no matter how many times.
Larger Context:
def toggleAutoAction
require "#{#require_path}/options"
filename = "#{#require_path}/options.rb"
writeToggle(filename, !OPTIONS[:auto])
0
end
def writeToggle(filename, toggleto)
text = File.read(filename)
text.gsub!(":auto => #{!toggleto}", ":auto => #{toggleto}")
File.open(filename, 'w+') {|file| file.write(text); file.close}
end
def exitOrMenu
puts "Are you done? (y/n)"
prompt
if gets.chomp == 'n'
whichAction
else
exit
end
end
def whichAction
if action == 5
toggleAutoAction
else
puts "Sorry, that isn't an option...returning"
return 1
end
exitOrMenu
end
The problem lays within this method:
def toggleAutoAction
require "#{#require_path}/options" # here
filename = "#{#require_path}/options.rb"
writeToggle(filename, !OPTIONS[:auto])
0
end
Ruby will not load the options.rb a second time (i.e. with the exact same path name), hence your !OPTIONS[:auto] will only be evaluated once (otherwise you would get a constant-already-defined-warning, provided OPTIONS is defined in options.rb). See Kernel#require docs.
You could, of course, do crazy stuff like
eval File.read("#{#require_path}/options.rb")
but I would not recommend that (performance wise).
As noted above, reading/writing from/to YAML files is less painful ;-)

Resources