One-liner shoulda syntax using braces - ruby

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

Related

Why can't I use curly braces with a `for` loop in Ruby?

The Ruby Documentation says that "do/end is equivalent to curly braces", so why is it that when I attempt to do the following I do not receive any output:
a = [1, 2, 3]
for i in a {
puts i
}
When I perform the above I receive no output (but I don't receive an error message either). However, when I do the following everything is as it should be:
a = [1, 2, 3]
for i in a do
puts i
end
#=> 1
#=> 2
#=> 3
I know this can be done more idiomatically with the each statement, but that's not what I'm asking. What am I not understanding here?
The Ruby Documentation says that "do/end is equivalent to curly braces"
No, it doesn't. It says (bold emphasis mine):
In this context, do/end is equivalent to curly braces […]
What "in this context" means is defined directly before the half-sentence you quoted:
do
Paired with end, can delimit a code block
So, "in this context" here refers to the context of a block.
so why is it that when I attempt to do the following I do not receive any output
Because this is a completely different context, again quoting from the documentation you linked to:
do can also (optionally) appear at the end of a for/in statement. (See for for an example.)
The "also" in that sentence makes it very clear that this is a different usage of the keyword do that has nothing to do with the usage discussed in this section. And if you look at the documentation of for, you can see that there is no mention of curly braces being allowed.
When I perform the above I receive no output (but I don't receive an error message either).
That is not true. Your code is syntactically invalid because it is missing the end keyword to end the for/in expression, therefore you get a "syntax error, unexpected end-of-input" on line 4:
ruby -c -e 'a = [1, 2, 3]
for i in a {
puts i
}'
# -e:4: syntax error, unexpected end-of-input
And if you add the missing end, you get a in `<main>': undefined method `a' for main:Object (NoMethodError) on line 2:
ruby -e 'a = [1, 2, 3]
for i in a {
puts i
}
end'
# -e:2:in `<main>': undefined method `a' for main:Object (NoMethodError)
Again, this is expected because curly braces delimit a code block, so
a {
puts i
}
is interpreted as a code block being passed to a and since variables cannot receive arguments, only methods can, a must be a method. Therefore, Ruby rightfully complains about not finding a method named a.
There are three ways of delimiting the iterator expression from the loop body expression in a for/in loop (and the same applies to while and until loops, actually):
An expression separator. An expression separator can either be
a semicolon ;
a newline
The keyword do
So, the following would all be valid fixes for your code:
# non-idiomatic
for i in a; puts i end
# non-idiomatic
for i in a
puts i end
# the same but with idiomatic indentation and whitespace
for i in a
puts i
end
# idiomatic
for i in a do puts i end
# redundant, non-idiomatic
for i in a do
puts i
end
Note, that when I say "idiomatic" above, that is to be interpreted relative, since actually for/in loops as a whole are completely non-idiomatic, and you would rather do this:
a.each do |i|
puts i
end
or maybe
a.each(&method(:puts))
It is in general preferred to not mix I/O and data transformation, so another idiomatic solution would be to transform the data to the desired output first, then output it, like this:
puts a.join("\n")
Except that Kernel#puts will already treat Array arguments special and print each element on its own line (as documented at IO#puts), so the real correct idiomatic solution for your code would be just:
puts a
Take a look to the documentation here: For loop
It states:
Like while and until, the do is optional. The for loop is similar to
using each, but does not create a new variable scope.
And also
The for loop is rarely used in modern ruby programs.
So, be less Pythonic :) using Enumerator#each instead:
a.each { |a| puts a }

how to execute multiple statements within a ruby code block

I have the following simplified code example:
class MyTimerClass
def timed_execution(&block)
...
end
def elapsed_time
...
return ...
end
end
...
t = MyTimerClass.new
t.timed_execution {1000.times {"foo".equal? "foo"}}
puts "block took #{t.elapsed_time} seconds to run."
What I want to do is print out "executed" on every execution of "foo".equal? "foo". Instead of putting this inside the timed_execution method I want to add it to the passed code block. something like...
t.timed_execution {1000.times {"foo".equal? "foo" puts "executed"}}
which is wrong. I guess what I really want to know is how to have multiple statements within a code block. Really simple question I know...
You can use do .. end instead of braces. Or you can even separate the statements with a semicolon.
t.timed_execution do
1000.times do
"foo".equal? "foo"
puts "executed"
end
end
OR
t.timed_execution { 1000.times { "foo".equal? "foo" ; puts "executed" } }
From the question, it seems that you had the idea that a code block in Ruby should be written on a single line. In fact, code blocks in Ruby can span over multiple lines.
Code blocks in Ruby are created by writing statements inside an opening '{' and an ending '}' or an opening 'do' and an ending 'end'. Arguments to the block go on the same line as the block opening. Example :
# Using 'do' and 'end'.
5.times do |i|
puts i
end
# Using '{' and '}'.
5.times { |i|
puts i
}
The above way of writing code blocks are preferred if you are going to have more than one statement inside the block. If you are sure you are only gonna need a single statement then you can also opt to follow one line block style (many people do I think, I myself personally go for this one for a single statement). Example :
# Using a single line.
5.times { |i| puts i }
It's allowed to put multiple statements inside a single line block with the help of semi-colons to separate statements, also it's okay to use multi-line block for a single statement. But its a good idea to not do the former one for better readability.
Also from my opinion it's better to use '{' and '}' for single line blocks and 'do' and 'end' for multi-line blocks.

Precedence of and/or versus method arguments in ruby

Here are two tests:
if [1,2,3,4].include? 2 && nil.nil?
puts :hello
end
#=>
and
if [1,2,3,4].include?(2) && nil.nil?
puts :hello
end
#=> hello
The above tells me that && has higher precedence than method arguments so it logically ands 2 && nil.nil? which is true and passes that as an argument to include?.
However, there is this test:
if [1,2,3,4].include? 2 and nil.nil?
puts :hello
end
#=> hello
So this is telling me that method arguments and 'and' have the same precedence (or method args are higher than 'and') since it passed 2 to include? before it processed 'and'.
Note: I understand that && and and have different precedence. The question is not regarding this but regarding and or or vs the arguments to a ruby method.
I can't find documentation that affirms this. For instances, this doesn't mention method arguments at all: http://phrogz.net/programmingruby/language.html#table_18.4 or http://romhack.wikia.com/wiki/Ruby_operators.
Could anyone explain this behavior? Namely in that how does ruby know to pass values as arguments to a method vs. process operators?
As you said && and and have different precedence, however the explanation for the following example:
if [1,2,3,4].include? 2 and nil.nil?
puts :hello
end
#=> hello
is the binding strenght of the and as you can read here:
Difference between "and" and && in Ruby?
This basically explains that 2 and nil.nil? will be evaluated as nil, however it will return 2 as can be seen in this example:
foo = :foo
bar = nil
a = foo and bar
# => nil
a
# => :foo
a = foo && bar
# => nil
a
# => nil
I've never seen any documentation about method argument precedence, but one rule of thumb I use when seeing method arguments is to mentally strip the whitespace wherever possible in the arguments and still have the same expression. This normally gives me the precedence:
[1,2,3,4].include? 2&&nil.nil? is the same expression, but you cannot strip the whitespace in
[1,2,3,4].include? 2 and nil.nil? and therefore, the precedence is left to right ... I.e. Method argument is 2.
Anyway, the better question is why on earth would you write statements like this?
Omitting method parenthesis is only useful for code readability. However, your statements are hardly readable and makes one pause over the code and think about it more than he should. If I was to review code like this, I would definitely fail the code review due to poor readability.
In fact, many style guides explicitly state that most methods with arguments should be parenthesized (is this even a word ;). For example:
Ruby style guide

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) {

Ruby - What is this output

I know that this code may be not quite correct:
def print_string(&str)
puts str
end
print_string{"Abder-Rahman"}
But, when I run it, this is what I get:
#<Proc:0x03e25d98#r.rb:5>
What is this output?
That's the default string representation of a Proc object. Because "Abder-Rahman" is in braces, Ruby thinks you're defining a block. Did you mean to put str.call inside of your function definition? That should call your block and return the string expression you defined inside it.
The problem is that you've declared that the "print_string" method takes a block argument (confusingly named "str") and you simply print the proc itself. You'd probably like to call the given procedure to see the string value it returns:
def call_proc(&proc)
proc.call
end
call_proc { 'Foobar' }
# => "Foobar"
What you've discovered is the syntax sugar that if you decorate the last argument of a method definition with an ampersand & then it will be bound to the block argument to the method call. An alternative way of accomplishing the same task is as follows:
def call_proc2
yield if block_given?
end
call_proc2 { 'Example' }
# => 'Example'
Note also that procedures can be handled directly as objects by using Proc objects (or the "lambda" alias for the Proc constructor):
p1 = Proc.new { 'Foo' }
p1.call # => "Foo"
p2 = lambda { 'Bar' }
p2.call # => "Bar"
You're passing a block to the method, as denoted by the & prefix and how you're calling it. That block is then converted into a Proc internally.
puts str.call inside your method would print the string, although why you'd want to define the method this way is another matter.
See Proc:
http://www.ruby-doc.org/core/classes/Proc.html
When the last argument of function/method is preceded by the & character, ruby expect a proc object. So that's why puts's output is what it is.
This blog has an article about the unary & operator.

Resources