In Rails, blocks can be used as callbacks, e.g.:
class User < ActiveRecord::Base
validates_presence_of :login, :email
before_create {|user| user.name = user.login.capitalize
if user.name.blank?}
end
When a block is used like this, is there any use for break
and return
? I'm asking because normally in a block, break
will break out of the loop, and return
will return from the enclosing method. But in a callback context, I can't get my head round what that means.
The Ruby Programming Language suggests that return
could cause a LocalJumpError
but I haven't been able to reproduce this in a Rails callback.
Edit: with the following code I'd expect a LocalJumpError
, but all the return
does is stop the rest of the callback executing.
class User < ActiveRecord::Base
validates_presence_of :login, :email
before_create do |user|
return
user.name = user.login.capitalize
end
Actually it's kind of interesting...
When you use before_create in Rails 3, we take the block or lambda that you give us and convert it into a method. We then invoke the method with the current ActiveRecord object, for backwards compatibility with the old Rails approach.
As a result, the following is equivalent to your snippet:
class User < ActiveRecord::Base
validates_presence_of :login, :email
before_create do
self.name = login.capitalize if name.blank?
end
end
Because of this behavior, you can call return from the block, and it will behave the same as a return in a normal method (because it is a normal method).
In general, next
in a block "returns" from the block.
The specific behavior of normal blocks is:
next
, you are skipping the rest of the block, and returning control to the method that invoked the block.break
, you are skipping the rest of the block, and also immediately returning from the method that invoked the block.You can see that behavior in normal iterators:
value = [1,2,3,4].each do |i|
next if i == 2
puts i
end
In this case, value
will be [1,2,3,4]
, the normal return value of the each
method, and the output will be:
1
3
4
In the case of break:
value = [1,2,3,4].each do |i|
break if i == 2
puts i
end
In this case, the value
will be nil
, since the break
also immediately returned from the each
method. You can force a return with a specific value by using break n
, which will make value
the same as n
. The output in the above case will be:
1
The important thing is that next
and break
do not just apply to iterators, although their semantics are designed to behave like their equivalents in C in the case of iterators.