Emulating a Groovy feature?

Guys,

There's something called Safe Navigation in Groovy (http://groovy.codehaus.org/Statements#Statements-Safenavigation) that I find very appealing.

So, Groovy will allow you to safely walk an object graph, even if that graph has nulls in it (the walk will short circuit):

def foo = null
def bar = foo?.something?.myMethod()
assert bar == null

Is there an easy way to hack an equivalent out in Ruby?

Thanks,
John

Something like this?

irb(main):001:0> class Object
irb(main):002:1> def method_missing meth_id
irb(main):003:2> nil
irb(main):004:2> end
irb(main):005:1> end
=> nil
irb(main):006:0> a = String.new
=> ""
irb(main):007:0> a.blah
=> nil
irb(main):008:0> a.blah.whatever
=> nil
irb(main):009:0> a.length
=> 0
irb(main):010:0> a.length.whatever
=> nil
irb(main):011:0>

HTH,

Felix

···

-----Original Message-----
From: John Wells [mailto:lists@sourceillustrated.com]
Sent: Saturday, August 18, 2007 5:37 AM
To: ruby-talk ML
Subject: Emulating a Groovy feature?

Guys,

There's something called Safe Navigation in Groovy
(http://groovy.codehaus.org/Statements#Statements-Safenavigati
on) that I find very appealing.

So, Groovy will allow you to safely walk an object graph,
even if that graph has nulls in it (the walk will short circuit):

def foo = null
def bar = foo?.something?.myMethod()
assert bar == null

Is there an easy way to hack an equivalent out in Ruby?

Thanks,
John

Sure, NilClass can be reopened just fine:

   class NilClass
     def method_missing(name, *args)
       nil
     end
   end

   nil.invoices.find(5) # -> nil

Reopening NilClass is something really handy sometimes.

-- fxn

···

On Aug 18, 2007, at 2:36 PM, John Wells wrote:

Guys,

There's something called Safe Navigation in Groovy (http://groovy.codehaus.org/Statements#Statements-Safenavigation\) that I find very appealing.

So, Groovy will allow you to safely walk an object graph, even if that graph has nulls in it (the walk will short circuit):

def foo = null
def bar = foo?.something?.myMethod()
assert bar == null

Is there an easy way to hack an equivalent out in Ruby?

John Wells wrote:

Guys,

There's something called Safe Navigation in Groovy (http://groovy.codehaus.org/Statements#Statements-Safenavigation\) that I find very appealing.

So, Groovy will allow you to safely walk an object graph, even if that graph has nulls in it (the walk will short circuit):

def foo = null
def bar = foo?.something?.myMethod()
assert bar == null

Is there an easy way to hack an equivalent out in Ruby?

irb(main):001:0> def nil.method_missing(*_) end
=> nil
irb(main):002:0> nil.a.b.c
=> nil

Cheers,
Dave

John Wells wrote:

Guys,

There's something called Safe Navigation in Groovy
(http://groovy.codehaus.org/Statements#Statements-Safenavigation\) that I
find very appealing.

So, Groovy will allow you to safely walk an object graph, even if that
graph has nulls in it (the walk will short circuit):

def foo = null
def bar = foo?.something?.myMethod()
assert bar == null

Is there an easy way to hack an equivalent out in Ruby?

Thanks,
John

Good to see that I'm not alone on this :slight_smile:
I still intend to write an RCR for this. You might want to read through

Regards
Stefan

···

--
Posted via http://www.ruby-forum.com/\.

Try this in IRB

class Object
  class Holder
    def __ref() @ref end

    def initialize(ref,&b)
      @ref = ref
      instance_eval(&b)
    end

    def method_missing(s,*a,&b)
      @ref = @ref.send(s,*a,&b) rescue nil
      self
    end
  end

  def safe(&b)
    Holder.new(self,&b).__ref
  end
end
F = Struct.new :name
f=F.new "foo"
f.safe { name.size > 10 }
f.safe { name.xxx }

Not too nice though...

Kind regards

robert

···

2007/8/18, John Wells <lists@sourceillustrated.com>:

Guys,

There's something called Safe Navigation in Groovy (http://groovy.codehaus.org/Statements#Statements-Safenavigation\) that I find very appealing.

So, Groovy will allow you to safely walk an object graph, even if that graph has nulls in it (the walk will short circuit):

def foo = null
def bar = foo?.something?.myMethod()
assert bar == null

Is there an easy way to hack an equivalent out in Ruby?

Sure, NilClass can be reopened just fine:

   class NilClass
     def method_missing(name, *args)
       nil
     end
   end

   nil.invoices.find(5) # -> nil

The only problem I can see here is that it's not quite optional.

Arlen

PS: Forgot to mention: you can always use the "rescue" modifier:

x = foo.bar.baz rescue nil

···

2007/8/20, Robert Klemme <shortcutter@googlemail.com>:

2007/8/18, John Wells <lists@sourceillustrated.com>:
> Guys,
>
> There's something called Safe Navigation in Groovy (http://groovy.codehaus.org/Statements#Statements-Safenavigation\) that I find very appealing.
>
> So, Groovy will allow you to safely walk an object graph, even if that graph has nulls in it (the walk will short circuit):
>
> def foo = null
> def bar = foo?.something?.myMethod()
> assert bar == null
>
> Is there an easy way to hack an equivalent out in Ruby?

Try this in IRB

class Object
  class Holder
    def __ref() @ref end

    def initialize(ref,&b)
      @ref = ref
      instance_eval(&b)
    end

    def method_missing(s,*a,&b)
      @ref = @ref.send(s,*a,&b) rescue nil
      self
    end
  end

  def safe(&b)
    Holder.new(self,&b).__ref
  end
end
F = Struct.new :name
f=F.new "foo"
f.safe { name.size > 10 }
f.safe { name.xxx }

Not too nice though...

Kind regards

robert

Yeah, that's the basic technique.

You could use some naming convention as in Groovy as well (which you would check in the body of method_missing), or perhaps wrap the activation of that catchall in a block like this:

   with_safe_navigation do
     regular code
   end

Once you can reopen NilClass like that I think there are ways to emulate that feature.

-- fxn

···

On Aug 18, 2007, at 2:55 PM, Arlen Christian Mart Cuss wrote:

Sure, NilClass can be reopened just fine:

   class NilClass
     def method_missing(name, *args)
       nil
     end
   end

   nil.invoices.find(5) # -> nil

The only problem I can see here is that it's not quite optional.

Interesting thread....glad someone else has hit this need.

How does the above work, exactly? You're rescuing without a block?
Doesn't it have to be something like:

begin
  x = foo.bar.baz
rescue nil
end

? At least the documentation would suggest so...

Thanks,
Jake

···

On Aug 20, 2007 4:07 AM, Robert Klemme <shortcutter@googlemail.com> wrote:

x = foo.bar.baz rescue nil

You don't need begin unless you want to catch just a section of code. Single
lines works wonders for concise error handling, such as

this_method_can_throw rescue "default value"

Or for a full method (Rails)

def action
  does_stuff_that_can_throw
rescue
  default_error_handling
end

Ruby's awesome.

Jason

···

On Nov 21, 2007 12:24 PM, Jake Cutter <cutter38@gmail.com> wrote:

On Aug 20, 2007 4:07 AM, Robert Klemme <shortcutter@googlemail.com> wrote:
> x = foo.bar.baz rescue nil

Interesting thread....glad someone else has hit this need.

How does the above work, exactly? You're rescuing without a block?
Doesn't it have to be something like:

begin
x = foo.bar.baz
rescue nil
end

? At least the documentation would suggest so...

Thanks,
Jake

The value of the expression is replaced by the value of the expression after the "rescue" in this form. And no, you don't need a block. The Pickaxe 1st edition does not seem to mention it but it's definitively a legal Ruby feature.

Kind regards

  robert

···

On 21.11.2007 18:24, Jake Cutter wrote:

On Aug 20, 2007 4:07 AM, Robert Klemme <shortcutter@googlemail.com> wrote:

x = foo.bar.baz rescue nil

Interesting thread....glad someone else has hit this need.

How does the above work, exactly? You're rescuing without a block?
Doesn't it have to be something like:

begin
  x = foo.bar.baz
rescue nil
end

? At least the documentation would suggest so...

Begin-Rescue-End blocks tend to slow things down significantly - does JUST including rescue also take those penalties to speed?

- Ari Brown

···

On Nov 21, 2007, at 2:40 PM, Robert Klemme wrote:

The value of the expression is replaced by the value of the expression after the "rescue" in this form. And no, you don't need a block. The Pickaxe 1st edition does not seem to mention it but it's definitively a legal Ruby feature.

Sebastian Delmont did some benchmarking on this last year.

http://www.notsostupid.com/blog/2006/08/31/the-price-of-a-rescue/

His conclusions: "Using rescue is not much more expensive than running naked. In my tests in particular, it never was more than 5% slower. It might even be cheaper than multiple tests."

Cheers from NYC,
Luke

···

On Nov 21, 2007, at 2:47 PM, thefed wrote:

Begin-Rescue-End blocks tend to slow things down significantly - does JUST including rescue also take those penalties to speed?

Luke Melia wrote:

Begin-Rescue-End blocks tend to slow things down significantly - does JUST including rescue also take those penalties to speed?

Sebastian Delmont did some benchmarking on this last year.

http://www.notsostupid.com/blog/2006/08/31/the-price-of-a-rescue/

His conclusions: "Using rescue is not much more expensive than running naked. In my tests in particular, it never was more than 5% slower. It might even be cheaper than multiple tests."

It slows down JRuby a bit more than Ruby 1.8, unfortunately, because of the way the JVM manages exception handling and operand stacks. But generally you won't notice it, since nontrivial code bodies will be quite a bit faster than 1.8.

~/NetBeansProjects/jruby-current $ jruby -J-server test.rb
              user system total real
plain 0.493000 0.000000 0.493000 ( 0.493000)
plain 0.474000 0.000000 0.474000 ( 0.474000)
plain 0.222000 0.000000 0.222000 ( 0.222000)
plain 0.220000 0.000000 0.220000 ( 0.220000)
plain 0.219000 0.000000 0.219000 ( 0.219000)
safe 0.431000 0.000000 0.431000 ( 0.431000)
safe 0.338000 0.000000 0.338000 ( 0.337000)
safe 0.320000 0.000000 0.320000 ( 0.320000)
safe 0.317000 0.000000 0.317000 ( 0.316000)
safe 0.319000 0.000000 0.319000 ( 0.319000)
rescue 0.315000 0.000000 0.315000 ( 0.315000)
rescue 0.313000 0.000000 0.313000 ( 0.313000)
rescue 0.314000 0.000000 0.314000 ( 0.314000)
rescue 0.312000 0.000000 0.312000 ( 0.312000)
rescue 0.312000 0.000000 0.312000 ( 0.313000)
~/NetBeansProjects/jruby-current $ ruby test.rb
              user system total real
plain 0.210000 0.000000 0.210000 ( 0.214133)
plain 0.210000 0.000000 0.210000 ( 0.228721)
plain 0.200000 0.000000 0.200000 ( 0.206997)
plain 0.210000 0.000000 0.210000 ( 0.206290)
plain 0.200000 0.010000 0.210000 ( 0.205982)
safe 0.240000 0.000000 0.240000 ( 0.238373)
safe 0.230000 0.000000 0.230000 ( 0.243327)
safe 0.240000 0.000000 0.240000 ( 0.237573)
safe 0.230000 0.000000 0.230000 ( 0.242121)
safe 0.240000 0.000000 0.240000 ( 0.237385)
rescue 0.230000 0.000000 0.230000 ( 0.238207)
rescue 0.230000 0.000000 0.230000 ( 0.235387)
rescue 0.230000 0.000000 0.230000 ( 0.234333)
rescue 0.240000 0.000000 0.240000 ( 0.234938)
rescue 0.230000 0.000000 0.230000 ( 0.240186)

- Charlie

···

On Nov 21, 2007, at 2:47 PM, thefed wrote:

Charles Oliver Nutter wrote:

It slows down JRuby a bit more than Ruby 1.8, unfortunately, because of the way the JVM manages exception handling and operand stacks. But generally you won't notice it, since nontrivial code bodies will be quite a bit faster than 1.8.

Actually, it just occurred to me that this is entirely a factor of local variable handling; when rescue is involved, JRuby has to switch from stack-based local variables to heap-based local vars. I'm working on a few ways to mitigate this cost for the general case, so this perf hit may go away.

- Charlie

It seems however that the rescue clause is not part of Ruby1.9
anymore, maybe, if this is confirmed, you might not want to optimize
this.

Cheers
Robert

···

On Nov 23, 2007 10:07 AM, Charles Oliver Nutter <charles.nutter@sun.com> wrote:

Charles Oliver Nutter wrote:
> It slows down JRuby a bit more than Ruby 1.8, unfortunately, because of
> the way the JVM manages exception handling and operand stacks. But
> generally you won't notice it, since nontrivial code bodies will be
> quite a bit faster than 1.8.

Actually, it just occurred to me that this is entirely a factor of local
variable handling; when rescue is involved, JRuby has to switch from
stack-based local variables to heap-based local vars. I'm working on a
few ways to mitigate this cost for the general case, so this perf hit
may go away.

- Charlie

--
what do I think about Ruby?
http://ruby-smalltalk.blogspot.com/

You mean the one line rescue syntax mentioned before? Interesting, I
can't find any docs via Google on this being removed in 1.9. Where are
you finding this info?

Seems strange that such a useful feature would be removed...

···

On 11/23/07, Robert Dober <robert.dober@gmail.com> wrote:

It seems however that the rescue clause is not part of Ruby1.9
anymore, maybe, if this is confirmed, you might not want to optimize
this.

> It seems however that the rescue clause is not part of Ruby1.9
> anymore, maybe, if this is confirmed, you might not want to optimize
> this.

You mean the one line rescue syntax mentioned before? Interesting, I
can't find any docs via Google on this being removed in 1.9. Where are
you finding this info?

From Ruby 1.9 itself :wink:
I thought, :frowning:

But I just cross checked and it works like charm.

Very sorry about the noise.

Cheers
Robert

···

On Nov 23, 2007 3:11 PM, John Wells <lists@sourceillustrated.com> wrote:

On 11/23/07, Robert Dober <robert.dober@gmail.com> wrote:

Seems strange that such a useful feature would be removed...

--
what do I think about Ruby?
http://ruby-smalltalk.blogspot.com/