While going through the Rails guide at http://guides.rubyonrails.org/layouts_and_rendering.html#avoiding-double-render-errors ,
I wrote a test program to test Ruby's && return, and I got this strange behavior:
def test1
puts 'hello' && return
puts 'world'
end
def test2
puts 'hello' and return
puts 'world'
end
This is the result output:
irb(main):028:0> test1
=> nil
irb(main):029:0> test2
hello
world
=> nil
What accounts for the difference?
Check out the difference between and and &&. In the examples you give the method puts is called without parens around it's arguments and the difference in precedence changes how it is parsed.
In test 1 && has higher precedence than the method call. So what's actually happening is puts('hello' && return). Arguments are always evaluated before the methods they're called with -- so we first evaluate 'hello' && return. Since 'hello' is truthy the boolean does not short circuit and return is evaluated. When return we exit the method without doing anything else: so nothing is ever logged and the second line isn't run.
In test 2 and has a lower precedence than the method call. So what happens is puts('hello') and return. The puts method logs what is passed to it and then returns nil. nil is a falsey value so the and expression short circuits and the return expression is never evaluated. We just move to the second line where puts 'world' is run.
Related
I was trying to DRY up a Rails controller by extracting a method that includes a guard clause to return prematurely from the controller method in the event of an error. I thought this may be possible using a to_proc, like this pure Ruby snippet:
def foo(string)
processed = method(:breaker).to_proc.call(string)
puts "This step should not be executed in the event of an error"
processed
end
def breaker(string)
begin
string.upcase!
rescue
puts "Well you messed that up, didn't you?"
return
end
string
end
My thinking was that having called to_proc on the breaker method, calling the early return statement in the rescue clause should escape the execution of foo. However, it didn't work:
2.4.0 :033 > foo('bar')
This step should not be executed in the event of an error
=> "BAR"
2.4.0 :034 > foo(2)
Well you messed that up, didn't you?
This step should not be executed in the event of an error
=> nil
Can anyone please
Explain why this doesn't work
Suggest a way of achieving this effect?
Thanks in advance.
EDIT: as people are wondering why the hell I would want to do this, the context is that I'm trying to DRY up the create and update methods in a Rails controller. (I'm trying to be agressive about it as both methods are about 60 LoC. Yuck.) Both methods feature a block like this:
some_var = nil
if (some complicated condition)
# do some stuff
some_var = computed_value
elsif (some marginally less complicated condition)
#error_msg = 'This message is the same in both actions.'
render partial: "show_user_the_error" and return
# rest of controller actions ...
Hence, I wanted to extract this as a block, including the premature return from the controller action. I thought this might be achievable using a Proc, and when that didn't work I wanted to understand why (which I now do thanks to Marek Lipa).
What about
def foo(string)
processed = breaker(string)
puts "This step should not be executed in the event of an error"
processed
rescue ArgumentError
end
def breaker(string)
begin
string.upcase!
rescue
puts "Well you messed that up, didn't you?"
raise ArgumentError.new("could not call upcase! on #{string.inspect}")
end
string
end
After all this is arguably a pretty good use case for an exception.
It seems part of the confusion is that a Proc or lambda for that matter are distinctly different than a closure (block).
Even if you could convert Method#to_proc to a standard Proc e.g. Proc.new this would simply result in a LocalJumpError because the return would be invalid in this context.
You can use next to break out of a standard Proc but the result would be identical to the lambda that you have now.
The reason Method#to_proc returns a lambda is because a lambda is far more representative of a method call than a standard Proc
For Example:
def foo(string)
string
end
bar = ->(string) { string } #lambda
baz = Proc.new {|string| string }
foo
#=> ArgumentError: wrong number of arguments (given 0, expected 1)
bar.()
#=> ArgumentError: wrong number of arguments (given 0, expected 1)
baz.()
#=> nil
Since you are converting a method to a proc object I am not sure why you would also want the behavior to change as this could cause ambiguity and confusion. Please note that for this reason you can not go in the other direction either e.g. lambda(&baz) does not result in a lambda either as metioned Here.
Now that we have explained all of this and why it shouldn't really be done, it is time to remember that nothing is impossible in ruby so this would technically work:
def foo(string)
# place assignment in the guard clause
# because the empty return will result in `nil` a falsey value
return unless processed = method(:breaker).to_proc.call(string)
puts "This step should not be executed in the event of an error"
processed
end
def breaker(string)
begin
string.upcase!
rescue
puts "Well you messed that up, didn't you?"
return
end
string
end
Example
How can I print a message and then return from a function in ruby?
2.3.4 :038 > def foo(num)
2.3.4 :039?> print "Your number is: #{num}" && return if num > 10
2.3.4 :040?> print "Number too small"
2.3.4 :041?> end
=> :foo
2.3.4 :042 > foo(47)
=> nil
2.3.4 :043 > foo(7)
Number too small => nil
2.3.4 :044 >
When I called foo with 47 why didn't I got Your number is: 47 in output?
PS: This function can be written in other simpler ways also, I just wanted to express my doubt via this function.
Because Ruby reads this line
print "Your number is: #{num}" && return if num > 10
like this
print("Your number is: #{num}" && return) if num > 10
That leads the method to return before it had the chance to print anything.
Adding a pair of parenthesis helps Ruby to resolve the desired order:
print("Your number is: #{num}") || return if num > 10
You have a precedence issue which can be fixed using parentheses. But since you are trying to express a control flow, you should use the control flow operators and and or instead of the boolean operators && and ||.
The idiom works like this: (just examples, it's not limited to return and fail)
do_something and return "it worked"
or:
do_something or fail "it didn't work"
It allows you to evaluate a second expression depending on whether the first expression succeeded or not. do_something is supposed to return a truthy value in case of success and a falsey value in case of failure.
print however always returns nil, so it doesn't work as expected in this regard. You would have to reverse the logic:
print "Your number is: #{num}" or return
But this doesn't read naturally any more, does it? Beside, return is always invoked, because print will never return a truthy value. You actually want:
print "Your number is: #{num}"
return
I would therefore simply write:
def foo(num)
if num > 10
puts "Your number is: #{num}"
else
puts "Number too small"
end
end
def foo(num)
puts(num > 10 ? "Your number is: #{num}" : "Number too small")
end
I think this is the cleaner way to do that, with an If ternary. Hope this helps
Since print (and puts) returns nil, this works just as well:
def foo(num)
return print "Your number is: #{num}" if num > 10
print "Number too small"
end
And to show clearer intention, place the guard clause first:
def foo(num)
return print "Number too small" if num <= 10
print "Your number is: #{num}"
end
Examples:
> foo 47
Your number is: 47=> nil
> foo 7
Number too small=> nil
Just out of curiosity:
def foo(num)
print case num
when -Float::INFINITY...10 then "Number too small"
else "Your number is: #{num}"
end
end
One more way to do one line if statements is with ;
def foo(num)
if num < 10; print "Your number is: #{num}" && return; end
print "Number too small"
end
I've been reading the following blog post , and it's unclear to me why is the author calling eval twice. Wouldn't calling eval just once suffice?
His controller code is this:
class GameController < ApplicationController
def index
end
def handle_command
command = params[:command].split
c = CommandHandler.new
begin
#result = c.send(*command)
rescue Exception =>; e
#result = "Stop being an idiot, and do something useful!"
end
end
end
And example code execution exploits were like this:
eval eval('puts^"Hello^world!"'.gsub('^',('^'.ord-62).chr))
The thing I don't understand is why there is a need for 2 evals, running this in irb, already prints the string by the first eval, and the second eval tries to evaluate nil.
The application is using send to evaluate the function with its arguments. Since eval only expects a single argument, there cannot be any spaces in the second argument. In short, the expression with double evals is executed as:
c.send("eval", "eval('puts^"Hello^world!\"'.gsub('^',('^'.ord-62).chr)"
That is equivalent to:
c.eval "eval('puts^"Hello^world!\"'.gsub('^',('^'.ord-62).chr)"
Which in turn will do the same thing as:
eval('puts^"Hello^world!\"'.gsub('^',('^'.ord-62).chr)
which is:
eval('puts "Hello world!"') #=> Code is actually evaluating
With a single eval you would get:
#input
eval 'puts^"Hello^world!"'.gsub('^',('^'.ord-62).chr)
c.send("eval" "'puts^\"Hello^world!\"'.gsub('^',('^'.ord-62).chr)"
c.eval "'puts^\"Hello^world!\"'.gsub('^',('^'.ord-62).chr)"
#=> "puts \"Hello world!\""
#=> results in a string containing the code. Code is not executed.
This question already has answers here:
what is the point of return in Ruby?
(7 answers)
Closed 8 years ago.
When inside of a method in Ruby, what is the difference between print and return?
def squared_number(num)
return (num**2)
end
and
def squared_number(num)
print (num**2)
end
return ends current method returning passed parameter as a result. Example:
def add(a, b)
return a + b
end
c = add(1, 2)
In Ruby last statement returns value automatically. So we can define our add method like this
def add(a, b)
a + b
end
But return is very useful, if you want to end a method execution prior to the last line. For example:
def specialAdd(a, b)
if a < 0
return -1
end
a + b
end
This method returns always -1 if the first argument is negative. In all other cases it works just like add method.
On the other hand the print method outputs the passed parameter to the standard output (console) returning nil as a result. We can see it using irb - interactive ruby console:
$ irb
irb(main):002:0> print "Hello World\n"
Hello World
=> nil
irb(main):003:0>
Here we see "Hello World" plus newline printed. The returned value of the print method is nil.
A lot.
print will output the number without a newline on the end.
return will return the number from the method.
My goal is to replace methods in the String class with other methods that do additional work (this is for a research project). This works for many methods by writing code in the String class similar to
alias_method :center_OLD, :center
def center(args*)
r = self.send(*([:center_OLD] + args))
#do some work here
#return something
end
For some methods, I need to handle a Proc as well, which is no problem. However, for the scan method, invoking it has the side effect of setting special global variables from the regular expression match. As documented, these variables are local to the thread and the method.
Unfortunately, some Rails code makes calls to scan which makes use of the $& variable. That variable gets set inside my version of the scan method, but because it's local, it doesn't make it back to the original caller which uses the variable.
Does anyone know a way to work around this? Please let me know if the problem needs clarification.
If it helps at all, all the uses I've seen so far of the $& variable are inside a Proc passed to the scan function, so I can get the binding for that Proc. However, the user doesn't seem to be able to change $& at all, so I don't know how that will help much.
Current Code
class String
alias_method :scan_OLD, :scan
def scan(*args, &b)
begin
sargs = [:scan_OLD] + args
if b.class == Proc
r = self.send(*sargs, &b)
else
r = self.send(*sargs)
end
r
rescue => error
puts error.backtrace.join("\n")
end
end
end
Of course I'll do more things before returning r, but this even is problematic -- so for simplicity we'll stick with this. As a test case, consider:
"hello world".scan(/l./) { |x| puts x }
This works fine both with and without my version of scan. With the "vanilla" String class this produces the same thing as
"hello world".scan(/l./) { puts $&; }
Namely, it prints "ll" and "ld" and returns "hello world". With the modified string class it prints two blank lines (since $& was nil) and then returns "hello world". I'll be happy if we can get that working!
You cannot set $&, because it is derived from $~, the last MatchData.
However, $~ can be set and that actually does what you want.
The trick is to set it in the block binding.
The code is inspired by the old Ruby implementation of Pathname.
(The new code is in C and does not need to care about Ruby frame-local variables)
class String
alias_method :scan_OLD, :scan
def scan(*args, &block)
sargs = [:scan_OLD] + args
if block
self.send(*sargs) do |*bargs|
Thread.current[:string_scan_matchdata] = $~
eval("$~ = Thread.current[:string_scan_matchdata]", block.binding)
yield(*bargs)
end
else
self.send(*sargs)
end
end
end
The saving of the thread-local (well, actually fiber-local) variable seems unnecessary since it is only used to pass the value and the thread never reads any other value than the last one set. It probably is there to restore the original value (most likely nil, because the variable did not exist).
One way to avoid thread-locals at all is to create a setter of $~ as a lambda (but it does create a lambda for each call):
self.send(*sargs) do |*bargs|
eval("lambda { |m| $~ = m }", block.binding).call($~)
yield(*bargs)
end
With any of these, your example works!
I wrote simple code simulating the problem:
"hello world".scan(/l./) { |x| puts x }
"hello world".scan(/l./) { puts $&; }
class String
alias_method :origin_scan, :scan
def scan *args, &b
args.unshift :origin_scan
#mutex ||= Mutex.new
begin
self.send *args do |a|
break if !block_given?
#mutex.synchronize do
p $&
case b.arity
when 0
b.call
when 1
b.call a
end
end
end
rescue => error
p error, error.backtrace.join("\n")
end
end
end
"hello world".scan(/l./) { |x| puts x }
"hello world".scan(/l./) { puts $& }
And found the following. The change of containment of the variable $& became inside a :call function, i.e. on 3-rd step before :call $& contains a valid value, but inside the block it becomes the invalid. I guess this become due to the singularity stack and variable restoration during the change process/thread context, because, probably, :call function can't access the :scan local state.
I see two variants: the first is to avoid to use global variables in the specific function redefinitions, and second, may to dig sources of ruby more deeply.