Avoiding eval

Hey all,
If I have a string like "Math", I know I can use const_get to turn it into
an instance of Class. Awesome.

But what if I have something like "Math::PI". Am I stuck using eval to
resolve that?

One typical idiom looks like this:

a_class = str.split('::').inject(Object) {|cl,n| cl.const_get n}

Cheers

  robert

···

On 27.08.2010 17:24, Andrew Wagner wrote:

If I have a string like "Math", I know I can use const_get to turn it into
an instance of Class. Awesome.

But what if I have something like "Math::PI". Am I stuck using eval to
resolve that?

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

Well, sure. That's a good point. But now you're taking 3 times as long as
eval, instead of 1/5 the time, according to my tests.

···

On Fri, Aug 27, 2010 at 11:40 AM, Robert Klemme <shortcutter@googlemail.com>wrote:

On 27.08.2010 17:24, Andrew Wagner wrote:

If I have a string like "Math", I know I can use const_get to turn it into

an instance of Class. Awesome.

But what if I have something like "Math::PI". Am I stuck using eval to
resolve that?

One typical idiom looks like this:

a_class = str.split('::').inject(Object) {|cl,n| cl.const_get n}

Cheers

       robert

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

Here is how Rails implements Robert's suggestion:

(see method constantize)

···

On Fri, Aug 27, 2010 at 10:40 AM, Robert Klemme <shortcutter@googlemail.com>wrote:

On 27.08.2010 17:24, Andrew Wagner wrote:

If I have a string like "Math", I know I can use const_get to turn it into

an instance of Class. Awesome.

But what if I have something like "Math::PI". Am I stuck using eval to
resolve that?

One typical idiom looks like this:

a_class = str.split('::').inject(Object) {|cl,n| cl.const_get n}

Cheers

       robert

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

Err, I think I wasn't very clear in my last email, so I'll say it in a
benchmark this time.

require 'benchmark'

Benchmark.bm do |b|

  b.report "using eval" do
    1_000_000.times { eval "Math" }
  end

  b.report "using const_get" do
    1_000_000.times { Kernel.const_get "Math" }

  end

  b.report "using eval on Math::PI" do
    1_000_000.times { eval "Math::Pi" }

  end

  b.report "using const_get injection on Math::PI" do
    1_000_000.times { "Math::PI".split("::").inject(Object) { |cl,n|
cl.const_get n } }

  end

end

using eval 6.5
using const_get 1.297
using eval on Math::Pi 6.875
using const_get injection on Math::PI 18.65

Without the performance benefit, it seems like there's not much benefit in
avoiding eval :(.

···

On Fri, Aug 27, 2010 at 11:48 AM, Andrew Wagner <wagner.andrew@gmail.com>wrote:

Well, sure. That's a good point. But now you're taking 3 times as long as
eval, instead of 1/5 the time, according to my tests.

On Fri, Aug 27, 2010 at 11:40 AM, Robert Klemme > <shortcutter@googlemail.com>wrote:

> On 27.08.2010 17:24, Andrew Wagner wrote:
>
> If I have a string like "Math", I know I can use const_get to turn it
into
>> an instance of Class. Awesome.
>>
>> But what if I have something like "Math::PI". Am I stuck using eval to
>> resolve that?
>>
>
> One typical idiom looks like this:
>
> a_class = str.split('::').inject(Object) {|cl,n| cl.const_get n}
>
> Cheers
>
> robert
>
> --
> remember.guy do |as, often| as.you_can - without end
> http://blog.rubybestpractices.com/
>
>

Please do not top post.

···

On 27.08.2010 17:48, Andrew Wagner wrote:

Well, sure. That's a good point. But now you're taking 3 times as long as
eval, instead of 1/5 the time, according to my tests.

Is it a performance issue? If not, why bother now? The version with eval is much less secure plus eval might not work under all circumstances with your string (see $SAFE).

Cheers

  robert

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/