Ruby Kernel.raise method throws error when enclosing parameters in parenthesis - ruby

I like method parameters enclosed in parenthesis, this is some Pascal nostalgia. When cleaning up code, if I find a method parameters without it I enclose them immediately.
Today it caused my working code throwing errors although my syntax looks okay according to the documentation.
Kernel.raise's documentation has this format:
(Object) raise(exception[, string [, array]])
These are all working:
> raise TypeError
TypeError: TypeError
> raise (TypeError)
TypeError: TypeError
> raise "Error message"
RuntimeError: Error message
> raise ("Error message")
RuntimeError: Error message
But the enclosed version of the next throws syntax error:
> raise TypeError, "Error message"
TypeError: Error message
> raise (TypeError, "Error message")
SyntaxError: unexpected ')', expecting $end
I can live without it, I just want to know why this ends with an error.

You probably already know that in idiomatic Ruby one would never insert a space between the end of a method and an argument list in parenthesis. Some style guides explicitly forbid it.
There's a pragmatic reason too.
1.9.2-p290 > def concat(a, b)
1.9.2-p290 > a + b
1.9.2-p290 > end
1.9.2-p290 > concat 'foo', 'bar'
=> "foobar"
1.9.2-p290 > concat('foo', 'bar')
=> "foobar"
1.9.2-p290 > concat ('foo', 'bar')
SyntaxError: (irb):27: syntax error, unexpected ',', expecting ')'
You'll encounter errors calling any method this way, not just Kernel.raise.
I'm not familiar with Ruby internals, but I would imagine the reason for this is that when a space precedes the argument list, Ruby is expecting the "no-parens" style. So of course this works:
1.9.2-p290 :035 > concat ("bar"), ("foo")
=> "barfoo"
Presumably Ruby is trying to evaluate the contents of each parenthesized expression before passing the result to the method. I'd speculate that writing raise (TypeError, "Error message") is asking Ruby to evaluate just TypeError, "Error message", which of course fails.

Parentheses are used for expression grouping and precedence override in Ruby. So, when you say
foo (bar, baz)
You are sending the message :foo with a single argument which is the result of evaluating the expression bar, baz. And bar, baz is not a valid expression, therefore you get a SyntaxError.
foo (bar)
works, because bar is a valid expression.
foo (if bar then baz else quux end)
would work, too.
If you want Ruby to interpret parentheses not for expression grouping but for passing multiple arguments along with a message send, the opening parenthesis needs to directly follow the message selector:
foo(bar, baz)
This has nothing to do with Kernel#raise, BTW. You cannot change the syntax for message sends in Ruby (in fact, you cannot change any syntax in Ruby), therefore whatever is true for Kernel#raise must also be true for every other method.

Related

ruby optionnal argument unexpected *

I have this very simple piece of code (using Ruby 3)
def eat(main, dessert*)
if dessert.empty?
puts "I eat #{main}"
else
puts "I eat #{main} than #{dessert}."
end
end
Wher I run eat("mushrooms") that provokes errors:
argu.rb:1: syntax error, unexpected '*', expecting ')'
def manger(plat, dessert*)
argu.rb:7: syntax error, unexpected `end', expecting end-of-input
I don't see why.
Splat operator should put before parameters so your signature should be
def eat(main, *dessert)
Not sure where you got the idea from using dessert*, but you could define your method as
def eat(main, dessert = [])
to provide a default argument (of course it must be one which can respond to empty?).
Of course it is up to you to justify, why "main" can be anything (i.e. a String), but dessert must be a collection. I would test for dessert as
if dessert.nil?
and hence provide nil as default value for the dessert.

Why does ruby throw error when calling to_sym against a select-one?

I'm running up against an error when I call to_sym against an element of type "select-one".
begin
["//select", "//input"].each do |t|
puts 'finding input for : ' + t
elements = all(:xpath, t)
elements.each do |e|
puts "found element #{e[:id]} of type #{e[:type]}" if #debug
puts "to symbol result: " + e[:id].to_sym #This line explodes
#...
rescue
puts "failed to enter fields on #{#name}"
raise
end
I get the error "Failed to enter fields on pageName". This error occurs when I call to_sym on Element of type select-one. How can I pinpoint the cause of this error and resolve it?
UPDATE
Per #axeltetzlaff I installed Pry. I noticed the values[salutation] returns nil where I expect a value given below:
[1] pry(#<Step>)> values[e[:salutation]]
=> nil
[2] pry(#<Step>)> values
=> {:z=>"z",
:a=>"a",
:b=>"b",
:c=>"c",
:d=>"d",
:e=>"e",
:f=>"f",
:g=>"",
:h=>"",
:salutation=>"Mr.",#See...I have a value
Update2
I took out the puts I am using for debugging and the issue went away:
puts "to symbol result: " + e[:id].to_sym #This line explodes
The code no longer breaks on the next line. By simple deduction, the issue is that I cannot concatenate a string and a symbol. I am guessing there is some ruby rule somewhere that says I can't do this, but I do not have one available.
to_sym converts a string to a symbol. For example, "a".to_sym becomes :a
Make sure your e[:id] returns a string object on which you are calling to_sym method. Try inspecting:
puts e[:id].inspect
puts e[:id].class
Update:
You can't concat a string and a symbol in Ruby. That will throw no implicit conversion of Symbol into String (TypeError) exception.
Instead of doing:
puts "to symbol result: " + e[:id].to_sym
you could do the same thing using string interpolation like this:
puts "to symbol result: #{e[:id].to_sym}"

multiple assignment in rescue clause?

I ran into this sample code as an idiom for exception handling in Ruby:
begin
eval string
rescue SyntaxError, NameError => boom
print "String doesn't compile: " + boom
rescue StandardError => bang
print "Error running script: " + bang
end
I'm confused particularly about the local variable assignment line with multiple exceptions:
rescue SyntaxError, NameError => boom. Does that mean the local var boom will take on either the SyntaxError or NameError object? Or is it just the NameError that will be assigned?
It's further confusing because the code itself throws a TypeError, I think perhaps because string is undefined, but that may be beside the point.
I found the code above at http://phrogz.net/programmingruby/tut_exceptions.html. Was that your source?
In any event, the local variable in that code is assigned whichever error is raised; it's just specified after the last one.
And yes, it's throwing TypeError because the errors do not implicitly coerce to a string in today's Ruby. Perhaps they used to when the book was initially published. You need to add .message to the local variable reference to get the error message (e.g. + boom.message).

Whats the ruby syntax for calling a method with multiple parameters and a block?

Ruby doesn't like this:
item (:name, :text) {
label('Name')
}
And I don't know why. I'm attempting to create a DSL. The 'item' method looks like this:
def item(name, type, &block)
i = QbeItemBuilder.new(#ds, name, QbeType.gettype(type))
i.instance_exec &block
end
Take a name for the item, a type for the item, and a block. Construct an item builder, and execute the block in its context.
Regardless of whether or not I need to use instance_exec (I'm thinking that I don't - it can be stuffed in the initialiser), I get this:
SyntaxError (ds_name.ds:5: syntax error, unexpected ',', expecting ')'
item (:name, :text) {
^
How do I invoke method with multiple arguments and a block? What does ruby think I'm trying to do?
The space before parentheses is causing ruby to evaluate (:name, :text) as single argument before calling the method which results in a syntax error. Look at these examples for illustration:
puts 1 # equivalent to puts(1) - valid
puts (1) # equivalent to puts((1)) - valid
puts (1..2) # equivalent to puts((1..2)) - valid
puts (1, 2) # equivalent to puts((1, 2)) - syntax error
puts(1, 2) # valid
Your way of providing the block is syntactically valid, however when the block is not in the same line as the method call it is usually better to use do ... end syntax.
So to answer your question you can use:
item(:name, :text) { label('Name') }
or:
item(:name, :text) do
label('Name')
end
Remove the space before the ( in item (:name, :text) {

One-liner shoulda syntax using braces

In the book Rails Test Prescriptions (b10.0, page 176), there are examples of one-liner assertions like the following:
should "be successful" { assert_response :success }
This doesn’t appear to be valid ruby syntax to me, and ruby reports that the left curly brace is unexpected. In order for it to be parsed, I have to change it to
should "be successful"; do assert_response :success end
What's wrong with the syntax of the first example?
This is valid Ruby syntax. Well, sort of. It just doesn't make sense!
Since the precedence of a literal block using curly braces is higher than passing an argument without parentheses, the block gets bound to the argument instead of the method call. If the argument is itself a method call, then you won't even get a syntax error. You'll just scratch your head wondering why your block doesn't get called.
To fix this, you either put parentheses around the argument, since parentheses have higher precedence than curly braces, or use the do / end form, which is lower precedence than an argument list without parentheses.
def foo; yield if block_given?; 'foo' end
puts foo { puts 'block' }
# block
# foo
puts(foo) { puts 'block' }
# foo
puts foo do puts 'block' end
# foo
puts foo { puts 'block' }, foo { puts 'block' }
# block
# block
# foo
# foo
puts 'foo' { puts 'block' }
# SyntaxError: (irb):19: syntax error, unexpected '{', expecting $end
This may be a mistake on my part in trying to get the example to take up fewer lines. The block is probably binding to the argument and not the method call, as Jorg said.
I think that the proper way to rewrite this as a one-liner is:
should("be successful") { assert_response :success }
But really, the way to go is to use the shoulda macro:
should respond_with(:success)
Thanks,
Noel

Resources