An idea for new syntactic sugar in Ruby

I was looking at this bit of code within Rails:

# File actionpack/lib/action_controller/metal/flash.rb, line 12 def redirect_to(options = {}, response_status_and_flash = {}) #:doc:   if alert = response_status_and_flash.delete(:alert)     flash[:alert] = alert   end

  if notice = response_status_and_flash.delete(:notice)     flash[:notice] = notice   end

  if other_flashes = response_status_and_flash.delete(:flash)     flash.update(other_flashes)   end

  super(options, response_status_and_flash) end

And I thought: ouch, aren't those supposed to be double-equals? Well, no, it's an assignment within an if statement. It saves a line of code per usage, thus three lines of code in that method.

But I thought: wow, that sure violates the principle of least surprise. Perhaps it would be nice if there were a way to reference the most recent if-expression. I recall how a lone underscore in irb will contain the value of whatever was last evaluated. So perhaps something like this would be nice:

  if response_status_and_flash.delete(:notice)     flash[:notice] = _   end

instead of what we see above:

  if notice = response_status_and_flash.delete(:notice)     flash[:notice] = notice   end

I was looking at this bit of code within Rails:

# File actionpack/lib/action_controller/metal/flash.rb, line 12 def redirect_to(options = {}, response_status_and_flash = {}) #:doc: if alert = response_status_and_flash.delete(:alert)    flash[:alert] = alert end

if notice = response_status_and_flash.delete(:notice)    flash[:notice] = notice end

if other_flashes = response_status_and_flash.delete(:flash)    flash.update(other_flashes) end

super(options, response_status_and_flash) end

And I thought: ouch, aren't those supposed to be double-equals? Well, no, it's an assignment within an if statement. It saves a line of code per usage, thus three lines of code in that method.

But I thought: wow, that sure violates the principle of least surprise. Perhaps it would be nice if there were a way to reference the most recent if-expression. I recall how a lone underscore in irb will contain the value of whatever was last evaluated. So perhaps something like this would be nice:

if response_status_and_flash.delete(:notice)    flash[:notice] = _ end

instead of what we see above:

if notice = response_status_and_flash.delete(:notice)    flash[:notice] = notice end

Interesting, but I think using _ is even more surprising isn't it? What happens when you have this...

if something-that-returns-a-value-and-passes-the-if-test   if something-that-returns-a-value-but-fails-the-if-test     # never happens   else     # what does _ reference? The very top level if? Or the second one?
    # Both were evaluated, but one failed so does it count towards _ or not?     # How do I access the first _ if Ruby decides _ is the second one?
  end end

Ok, I found this feature request:

http://redmine.ruby-lang.org/issues/show/1141

It was rejected. I guess one could do:

notice = response_status_and_flash.delete(:notice) flash[:notice] if notice

It's one line shorter, and perhaps less confusing. The only drawback would be that the notice variable stays in scope, which is not so nice.

I also found this on stackoverflow:

Yeah, great point. How to know how deep to go. I was looking at some of the Ruby global vars:

http://www.rubyist.net/~slagell/ruby/globalvars.html

This one is similar: $n the nth subexpression in the last match (same as $~[n])

But it seems somehow not so clean.

We have this nifty idiom:

x ||= "hello"

But I can't think of a neat syntax for what I'm wanting.

In that situation I would code it as if ( other_flashes = response_status_and_flash.delete(:flash) ) which arguably makes the intention clearer with only a few extra characters an no run time penalty.

Colin

if notice = response_status_and_flash.delete(:notice)

flash[:notice] = notice

end

This is a common C idiom.

The best solution is to always write your == with the constant on the LEFT instead of the right.

:smiley:

assuming that the intent is to call response_status_and_flash.delete(:notice) just once, test the result, and then use a truthy result in the flash[:notice], this is almost exactly what I typically type.

However, I’d really put ( ) around the expression.

if (notice = response_status_and_flash.delete(:notice))

flash[:notice] = notice

end

This is a trick from JSLint which will complain about assignments in the conditional expression of an if, but an extra pair of parentheses will signal your intent and silence the warning.

-Rob

Rob Biedenharn

Rob@AgileConsultingLLC.com http://AgileConsultingLLC.com/

rab@GaslightSoftware.com http://GaslightSoftware.com/

if notice = response_status_and_flash.delete(:notice) flash[:notice] = notice end

This is a common C idiom.

The best solution is to always write your == with the constant on the LEFT instead

of the right.

:smiley:

– make haste slowly
festina lente \

mobile +1_415_632_6001 curtis.schofield@gmail.com http://robotarmyma.de

assuming that the intent is to call response_status_and_flash.delete(:notice) just once, test the result, and then use a truthy result in the flash[:notice], this is almost exactly what I typically type.

However, I’d really put ( ) around the expression.

if (notice = response_status_and_flash.delete(:notice))

flash[:notice] = notice

end

This is definitely understandable without surprise, while I for sure did a double take before understanding Kai’s initial example.