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:

http://stackoverflow.com/questions/3835170/ruby-assignment-and-comparison-in-one-line

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.