Ruby: unexpected ',', expecting keyword_end - ruby

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
...

Related

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

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.

Syntax error in for loop (Ruby/RSpec)

While running the RSPEC test as shown below im getting this error:
Using Accessor#strict_set for specs
SyntaxError: /home/sam/projects/logstash.king-foo.dev/ansible/roles/logstash/spec/syslog.rb:6: syntax error, unexpected kEND
end
^
load at org/jruby/RubyKernel.java:1101
(root) at /opt/logstash/vendor/bundle/jruby/1.9/gems/rspec-core-2.14.7/lib/rspec/core/configuration.rb:1
each at org/jruby/RubyArray.java:1613
load_spec_files at /opt/logstash/vendor/bundle/jruby/1.9/gems/rspec-core-2.14.7/lib/rspec/core/configuration.rb:896
load_spec_files at /opt/logstash/vendor/bundle/jruby/1.9/gems/rspec-core-2.14.7/lib/rspec/core/configuration.rb:896
run at /opt/logstash/vendor/bundle/jruby/1.9/gems/rspec-core-2.14.7/lib/rspec/core/command_line.rb:22
I tried messing around with the syntax but without success.
files = Dir['../configs/filter*.conf']
##configuration = String.new
files.sort.each.do |file|
##configuration << File.read(file)
end
describe "my first logstash rspec test", :if => RUBY_ENGINE == "jruby" do
extend LogStash::RSpec
config(##configuration)
... some code here ...
end
Does anybody know what i'm doing wrong?
Why do i get a syntax error for the end statement ander the ##configuration variable?
The error means there was an unexpected end in your code. Just simply replace the 3rd line with
files.sort.each do |file|
I optionally recommend you use { and } instead of do and end. The { and } are space-insensitive and you are less likely to receive an error than do and end.

Bug I cannot identify

I ran this piece of code earlier:
require "awesome_print"
require "rexml/document"
require "debugger"
include REXML
class Scrapper
attr_reader :data
def initialize
file = File.new("./cia-1996.xml")
#data = REXML::Document.new(file)
end
def get_country_inflation
inflation_hash = {}
XPath.match( data, "//country").map { |element|
inflation_hash[element.attributes["name"]] = element.attributes["inflation"].to_i}
nested_array = inflation_hash.to_a
sorted_array = nested_array.sort_by {|country, inflation_value| inflation_value}.reverse
puts "The countries with the highest inflation indexes in 1996 were:"
first_five = sorted_array.first(5)
first_five.each do |item|
puts "#{item[0]}, with an inflation index of #{item[1]}"
end
end
end
end
sample = Scrapper.new
sample.get_country_inflation
After making some edits, I now get error message
economics_challenge.rb:36: syntax error, unexpected keyword_end, expecting end-of-input
Can you please give me pointers as to where the mistake/typo might be (been starring at it for a while now and would appreciate feedback from a new set of eyes).
Thank you so much!
Edit:
so I made the changes suggested but I got more error messages:
economics_challenge.rb:26: syntax error, unexpected tSTRING_DEND, expecting keyword_end
economics_challenge.rb:29: syntax error, unexpected tSTRING_DEND, expecting '}'
...flation_value| inflation_value}.reverse
... ^
economics_challenge.rb:35: syntax error, unexpected keyword_end, expecting '}'
economics_challenge.rb:46: syntax error, unexpected end-of-input, expecting '}'
line 26 refers to the 2nd line in the piece of code below: piece of code (and I think this is where the original problem is):
XPath.match( data, "//country").map do |element|
inflation_hash[element.attributes["name"]] = element.attributes["inflation"].to_i}
end
line 29 is:
sorted_array = nested_array.sort_by {|country, inflation_value| inflation_value}.reverse
I will attempt to fix the error in 29 by calling reverse on sorted array and saving that to a variable.
Line 35 is an end statement and there is no line 46.
Any tips?
Thank you!
2nd Edit:
Wow! I cannot believe I failed to realize that I didn't end many things. I will be sticking to the do andend syntax from now on.
Thank you both for helping me out so much … really appreciate it!
The problem is you have one extra end
As #david-grayson states, had your indentations been correct, you may have spotted it.
This of course, is given the code as you presented it here. It may not be exactly that, though the error message matches the found issue.
Here is the code with indentation, some style changes, and no syntax errors:
require 'awesome_print'
require 'rexml/document'
require 'debugger'
include REXML
class Scrapper
attr_reader :data
def initialize
file = File.new('./cia-1996.xml')
#data = REXML::Document.new(file)
end
def get_country_inflation
inflation_hash = {}
XPath.match(data, '//country').map do |element|
inflation_hash[element.attributes['name']] = element.attributes['inflation'].to_i
end
nested_array = inflation_hash.to_a
sorted_array = nested_array.sort_by do |country, inflation_value|
inflation_value
end.reverse
puts 'The countries with the highest inflation indexes in 1996 were:'
first_five = sorted_array.first(5)
first_five.each do |item|
puts "#{item[0]}, with an inflation index of #{item[1]}"
end
end
end
sample = Scrapper.new
sample.get_country_inflation
Your indentation is messed up starting here:
XPath.match( data, "//country").map { |element|
inflation_hash[element.attributes["name"]] = element.attributes["inflation"].to_i}
nested_array = inflation_hash.to_a
The last line of that excerpt should be unindented by one level because the block you passed to "map" was terminated on the second line by the right bracket.
Try fixing that and everything after it.
Also, here is a tip: always write multi-line blocks using do and end and put the end on its own line. Then you could would be:
XPath.match( data, "//country").map do |element|
inflation_hash[element.attributes["name"]] = element.attributes["inflation"].to_i
end
nested_array = inflation_hash.to_a

Catch errors in vim/ruby

I'm writing a vim plugin using the ruby interface.
When I execute VIM::command(...), how can I detect if vim raised an error during execution of this command, so that I can skip further commands and also present a better message to the user?
Vim's global variable v:errmsg will give you the last error. If you want to check whether an error occured, you can first set it to an empty string and then check for it:
let v:errmsg = ""
" issue your command
if v:errmsg != ""
" handle the error
endif;
I'll leave it up to you to transfer this to the Ruby API. Also see :h v:errmsg from inside Vim. Other useful global variables may be:
v:exception
v:throwpoint
Edit – this should work (caution: some magic involved):
module VIM
class Error < StandardError; end
class << self
def command_with_error *args
command('let v:errmsg=""')
command(*args)
msg = evaluate('v:errmsg')
raise ::VIM::Error, msg unless msg.empty?
end
end
end
# Usage
# use sil[ent]! or the error will bubble up to Vim
begin
VIM::command_with_error('sil! foobar')
rescue VIM::Error => e
puts 'Rescued from: ' + e.message;
end
# Output
Rescued from: E492: Not an editor command: sil! foobar

How do I pass an array in ARGV command line arguments?

I have the following:
=== My program test.rb
def test (app, download, launch )
for i in 0..(app.length - 1)do
#DO SOMETHING HERE WITH THIS
p app[i].to_s + download[i].to_s + launch[i].to_s
end
end
test (ARGV[0].split(//) , ARGV[2].split(//) ,ARGV[1].split(//))
=== other program "other.rb" that parses and executes test.rb
app = ["fda","asdf"]
down = ["ok","nok"]
la = ["ok","ok"]
system("ruby test.rb #{app} #{down} #{la}")
I would like to print it as something like this:
fda ok ok
asdf no nok
However it is printed like this:
foo
dkk
ano
aok
sk
d
f
How can I pass arrays to the app correctly?
I copied your source and pasted it into two files:
test.rb:
def test (app, download, launch )
for i in 0..(app.length - 1)do
#DO SOMETHING HERE WITH THIS
p app[i].to_s + download[i].to_s + launch[i].to_s
end
end
test (ARGV[0].split(//) , ARGV[2].split(//) ,ARGV[1].split(//))
and test2.rb:
app = ["fda","asdf"]
down = ["ok","nok"]
la = ["ok","ok"]
system("ruby test.rb #{app} #{down} #{la}")
Using Ruby 1.9.2p290, I get this result when I run your code:
ruby test2.rb
test.rb:7: syntax error, unexpected ',', expecting ')'
test (ARGV[0].split(//) , ARGV[2].split(//) ,ARGV[1].split(//))
^
test.rb:7: syntax error, unexpected ',', expecting $end
test (ARGV[0].split(//) , ARGV[2].split(//) ,ARGV[1].split(//))
That tells me you didn't accurately report the problem you're seeing.
Fixing the syntax error returns this:
ruby test2.rb
"[[a"
"fos"
"dkd"
"a,f"
",]"
The output I got is what I would expect from your code, however it doesn't resemble what you said you got, so I doubt your reported output is correct. Please be accurate when you ask for help, both in the code you've written and the output you've seen.
Looking at the data your code is passing shows the arguments are:
0 [fda,
1 asdf]
2 [ok,
3 nok]
4 [ok,
5 ok]
FYI, I got those using this bit of code in place of test.rb:
ARGV.each_with_index do |a, i|
puts "#{i} #{a}"
end
I'd expect that, because system("ruby test.rb #{app} #{down} #{la}") doesn't do what you think it does. Here is what the actual command sent to the sub-shell looks like when system processes it:
test.rb [fda, asdf] [ok, nok] [ok, ok]
Again, that matches what the arguments look like when the app sees them.
To understand what is happening, you need to read the documentation for system:
system([env,] command... [,options]) -> true, false or nil
[...]
command... is one of following forms.
[...]
cmdname, arg1, ... : command name and one or more arguments (no shell)
So, system allows us to send all the parameters separately, which is really what you want. Changing test2.rb to:
app = %w[ fda asdf ]
down = %w[ ok nok ]
la = %w[ ok ok ]
system(
"echo",
"test.rb",
%Q["#{ app.join(',') }"],
%Q["#{ down.join(',') }"],
%Q["#{ la.join(',') }"]
)
gives me this on the command-line:
ruby test2.rb
test.rb "fda,asdf" "ok,nok" "ok,ok"
Which seems a bit more usable. Because of how system works, I can clean it up and reduce that a bit to:
app = %w[ fda asdf ]
down = %w[ ok nok ]
la = %w[ ok ok ]
system(
"ruby", # execute this
"test.rb", # with this script name
app.join(','), down.join(','), la.join(',') # and these parameters
)
Looking at what test.rb sees shows:
ruby test2.rb
0 fda,asdf
1 ok,nok
2 ok,ok
Again, an improvement and closer to what your code expected.
I am not going to finish correcting your code because I suspect this is a homework assignment and not a practical use. Some hints to help you are:
Your use of split isn't correct, nor is it where I would put it.
Your use of for is not idiomatic Ruby. Look at Array.each AND learn why it is preferred instead of for.
Your use of to_s is unnecessary, since you are already dealing with strings and characters.
you could use Thor which is a dsl for creating command line apps

Resources