Using a heredoc as a hash value - ruby

I have a method Embed.toggler that takes a hash argument. With the following code, I'm trying to use a heredoc in the hash.
Embed.toggler({
title: <<-RUBY
#{entry['time']}
#{entry['group']['who']
#{entry['name']}
RUBY
content: content
})
However, I'm getting the following error trace:
syntax error, unexpected ':', expecting tSTRING_DEND
content: content
^
can't find string "RUBY" anywhere before EOF
syntax error, unexpected end-of-input, expecting tSTRING_CONTENT or tSTRING_DBEG or tSTRING_DVAR or tSTRING_END
title: <<-RUBY
^
How I can avoid getting this error?

Add a comma after your <<-RUBY:
Embed.toggler({
title: <<-RUBY,
#{entry['time']}
#{entry['group']['who']
#{entry['name']}
RUBY
content: content
})
this does work in general. I am not sure why it wasn't working in my code though.
It didn't work because hashes require key/value pair to be separated by a comma, like {title: 'my title', content: 'my content' } and your code just didn't have the comma. It was hard to see that because of the cumbersome HEREDOC syntax.
Do you know if there is a way to perform operations on the string?
You're playing with fire. It's always safer (and usually cleaner) to extract a variable and do post-processing on a variable itself:
title = <<-RUBY
#{entry['time']}
#{entry['group']['who']
#{entry['name']}
RUBY
Embed.toggler(title: title.upcase, content: content)
However, if you feel dangerous today, you can just add operations after opening HEREDOC literal, just as you've added the comma:
Embed.toggler({
title: <<-RUBY.upcase,
#{entry['time']}
#{entry['group']['who']
#{entry['name']}
RUBY
content: content
})
But I discourage you from this because it destroys readability.

Related

backslash at end of string causes error when inserting into InfluxDB

I have a string:
string = "\\"
puts string
# => \
I am interpolating this into a new string and sending to a database. However the database (InfluxDB) uses backslashes as escape characters so pushing this string can cause an error.
For example, if I pass the following to Influx it will cause an "unterminated string" error:
insert_cmd = <<-TXT
INSERT INTO my_db.default.my_measurement,my_tag=1 my_val="#{string}"
TXT
My question is how can I replace \ in a string with \\ (two actual backslashes).
I have it working with gsub("\\", "\\\\\\") but I don't understand why this works and the following doesn't:
string.gsub("\\", "\\\\")
# SyntaxError: (irb):10: syntax error, unexpected $undefined, expecting end-of-input
Why doesn't this work? Why does gsub("\\", "\\\\\\") work? Is there a better way?
solved
As I mentioned in a comment, actually I am not manually interpolating into a INSERT INTO string. I am using influxdb-ruby:
INFLUXDB_CLIENT.write_point("things", time: Time.now.to_i, values: { foo: "\\" })
It turns out this is a bug with that gem: https://github.com/influxdata/influxdb-ruby/issues/200
It is fixed in v 0.4.2 and i was using 0.4.1
You just use parameterized query strings:
INSERT INTO my_db.default.my_measurement,my_tag=1 my_val=%{1}
Where when you call it you do this:
influxdb.query("...query...", params: [ string ])
What you did was create a classic injection bug by sending unescaped data into a query. The same principle applies in any database with a plain-text string representation, or even other data formats like HTML and JavaScript.

Ruby String interpolation throws syntax error in 1.9.3 but not 2.3.1

Sorry in advance for the large chunk of code. I'm concerned that I may have missed something that is obvious in context.
def remove_runbook_items(runbook_id, runbook_item_id)
item_id = runbook_item_id.to_s
method = 'POST'
url = #the_endpoint
data = {
invokeDetail: {
process: 'remove_runbook_items',
parameters: {
runbook_id: runbook_id,
runbook_items: {
"#{item_id}": {
cores: options[:cores],
ram: options[:ram],
rank: options[:rank],
wait_time: options[:wait_time]
}
}
}
}
}
data.merge! #common_data
result = send_request(method, url, data.to_json)
result['resultDetail'] # Returns contents of response from REST API server
end
The offending line is the one that says "#{item_id}": {
The block of text called "data" is converted into a json, so I must interpolate the string "item_id" or else it will literally spit out "item_id" in the resulting request, rather than item_id's contents. Actually, if there's a way to get at the contents without interpolation, I'd love to know it.
So when I interpolate my string in this way, it works just fine on Ruby 2.3.1. However, when I try to run the same code on a machine using Ruby 1.9.3, I get a litany of syntax errors anywhere I have interpolated a string like this:
/home/mydir/mydir/mydir/mydir/mydir/restapi_helper.rb:1122: syntax error, unexpected ':', expecting tASSOC
"#{device_id}": {
^
/home/mydir/mydir/mydir/mydir/mydir/restapi_helper.rb:1128: syntax error, unexpected '}', expecting keyword_end
/home/mydir/mydir/mydir/mydir/mydir/restapi_helper.rb:1163: syntax error, unexpected ':', expecting tASSOC
"#{item_id}": {
^
/home/mydir/mydir/mydir/mydir/mydir/restapi_helper.rb:1169: syntax error, unexpected '}', expecting keyword_end
/home/mydir/mydir/mydir/mydir/mydir/restapi_helper.rb:1257: syntax error, unexpected ':', expecting tASSOC
"#{item_id}": {
^
/home/mydir/mydir/mydir/mydir/mydir/restapi_helper.rb:1263: syntax error, unexpected '}', expecting keyword_end (SyntaxError)
Does anybody have any advice? Upgrading the version of ruby on the test machines is unfortunately not an option.
It is not iterpolation that is the problem.
{ symbol: value } is a new syntax, which is a shortcut for { :symbol => value }. In its first iteration, I don't think it supported { "symbol": value } automatic string symbolification feature. Use the old-fashioned { "symbol".to_sym => value } if backward compatibility is a goal.
(If you know that all you're doing is converting to JSON, you can even just leave it as { "string" => value }, comforted by the knowledge that JSON does not make a distinction between strings and symbols; but I'd consider it a code smell.)

Is it safe to parse json with YAML.load?

I am using ruby 2.1.0
I have a json file.
For example: test.json
{
"item":[
{"apple": 1},
{"banana": 2}
]
}
Is it safe to load this file with YAML.load?
YAML.load(File.read('test.json'))
I am trying to load a file which is in either json or yaml format.
YAML can load JSON
YAML.load('{"something": "test", "other": 4 }')
=> {"something"=>"test", "other"=>4}
JSON will not be able to load YAML.
JSON.load("- something\n")
JSON::ParserError: 795: unexpected token at '- something'
There will be some obscure cases that work and produce different output.
YAML.load("")
=> false
JSON.load("")
=> nil
But generally the YAML construct is not JSON compliant.
So, try the JSON.load first because it's probably better at obscure JSON things.Catch the JSON::ParserError error and fall back to YAML.load.
In recent work I did I found a corner case of the sort alluded to by Matt. For example
puts JSON.load('{"x": "foo\/bar"}')['x']
succeeds in printing
foo/bar
despite the gratuitous escaping¹ whereas
puts YAML.load('{"x": "foo\/bar"}')['x']
fails:
Psych::SyntaxError ((<unknown>): found unknown escape character while parsing a quoted scalar at line 1 column 7)
¹In this case by Java as per net.sf.json.util.JSONUtils.quote. Note that they forgot to do the same quoting in their own Javadoc, ironically enough, so you have to browse source to understand!

Parsing valid JSON throwing errors

I’m confused as to why this throws an error:
s = <<JSON
{"s": "This is \"valid\" JSON"}
JSON
JSON.parse(s) # => JSON::ParserError: 757: unexpected token at '{"s": "This is "valid" JSON"}'
Based on using http://jsonlint.com I can confirm that this is valid JSON, so what’s the deal? I get the feeling that I could be using %q{} here and things would be escaped properly, but I’d really rather use a heredoc here.
It turns out that Ruby supports disabling interpolation in heredocs by surrounding the opening identifier with single quotes, so in my example above, it would look like this:
s = <<'JSON'
{"s": "This is \"valid\" JSON"}
JSON
JSON.parse(s) # => {"s"=>"This is \"valid\" JSON"}

Ruby Error: syntax error, unexpected tGVAR, expecting $end

I am getting Ruby Error: syntax error, unexpected tGVAR, expecting $end.
I am using Mechanize to access a website and then I need to enter data into the form to search. When I pp page the site to get the form information I get:
#<Mechanize::Form
<name nil>
<method "POST">
<action "">
<fields
...
...
[text:0xb43f9c type: text name: ct100$MainContent$txtNumber value: ]
...
My code that is throwing this is:
Check_form = page.form()
Check_form.ct100$MainContent$txtNumber = 'J520518'
Any ideas on what is causing the error? Thank you in advance for the help!
Since this is not a valid variable or syntactically valid method name, you should use the alternate method to fetch or assign the values:
check_form = page.form
check_form['ct100$MainContent$txtNumber'] = 'J520518'
Variables are of the form #x for class instance variables, ##x for class variables, $x for global variables and x for plain variables, but in all cases the variable must consist of a letter or underscore followed by any number of letters, numbers, or underscores. $ cannot appear anywhere but the beginning, and when it does that means "global variable", something rarely used in most Ruby programming.
The error is telling you that there is a global variable where Ruby doesn't expect one. And there is: $txtNumber is a global variable, but it doesn't make sense for a global variable to appear at that place in your code.
Another way to make it legal would be
Check_form.send(:"ct100$MainContent$txtNumber=", 'J520518')

Resources