Ruby Forum Ruby-core > BasicObject.instance_eval

Posted by Sam Ruby (rubys)
on 04.01.2008 04:07
(Received via mailing list)
I'm looking at converting some code over from BlankSlate to BasicObject,
but hit a roadblock: I can't figure out any way to do the equivalent of

class Foo < BlankSlate
   def initialize
     @secret = 99
   end
end

foo = Foo.new
foo.instance_eval {@secret}

Any suggestions?

- Sam Ruby
Posted by Yukihiro Matsumoto (Guest)
on 04.01.2008 05:26
(Received via mailing list)
Hi,

In message "Re: BasicObject.instance_eval"
    on Fri, 4 Jan 2008 12:06:06 +0900, Sam Ruby <rubys@intertwingly.net> 
writes:

|I'm looking at converting some code over from BlankSlate to BasicObject, 
|but hit a roadblock: I can't figure out any way to do the equivalent of
|
|class Foo < BlankSlate
|   def initialize
|     @secret = 99
|   end
|end
|
|foo = Foo.new
|foo.instance_eval {@secret}
|
|Any suggestions?

Hmm, that's tough question.  Since BasicObject should not have as few
methods as possible to do the method_missing trick, instance_eval is
not among one of few methods it has.

Currently, it has ==, equal? !, !=, __send__, method_missing,
singleton_method_added, singleton_method_removed, and
singleton_method_undefined.  Should we add instance_eval as well?

              matz.
Posted by Brian Mitchell (Guest)
on 04.01.2008 05:33
(Received via mailing list)
On Jan 3, 2008 10:06 PM, Sam Ruby <rubys@intertwingly.net> wrote:
> foo.instance_eval {@secret}
>
> Any suggestions?

Interesting situation. Part of me would want Ruby to allow something 
like:

  # Doesn't work obviously...
  Object.instance_method(:instance_eval).bind(foo).call {@secret}

On the other hand, it isn't hard to write a method that can be used to
extract information w/o dirtying the interface:

  class << BasicObject
    def accessor_for(name)
      @accessors_for ||= {}
      unless @accessors_for[name]
        tmp = "__tmp#{Thread.current.object_id}__"
        class_eval %[
          def #{tmp}
            #{name}
          end
        ]
        accessors_for[name] = instance_method(tmp)
        undef_method(tmp)
      end
      lambda {|obj| @accessors_for[name].bind(obj).call}
    end
  end

Now you can use:

  BasicObject.accessor_for('@test').call(foo)

Not perfect (I really don't like using class_eval + undef_method like
that), but it does work and it does avoid making a complete mess of
the namespace with __ methods. Maybe an alternative that Ruby 1.9
could provide would be a few useful methods like instance_eval
available as unbound methods via the class:

  # No need to force Object instance methods to work on BasicObject
  # if there are a few special cases made via the class.
  BasicObject.unbound_instance_eval.bind(foo).call {@secret}

Brian.
Posted by Marcel Molina Jr. (Guest)
on 04.01.2008 05:40
(Received via mailing list)
On Jan 3, 2008, at 10:25 PM, Yukihiro Matsumoto <matz@ruby-lang.org>
wrote:

> |
>
> Hmm, that's tough question.  Since BasicObject should not have as few
> methods as possible to do the method_missing trick, instance_eval is
> not among one of few methods it has.
>
> Currently, it has ==, equal? !, !=, __send__, method_missing,
> singleton_method_added, singleton_method_removed, and
> singleton_method_undefined.  Should we add instance_eval as well?
>
>                            matz.

One of the most frequent things I've done in the past with BlankSlate
is to define method_missing and delegate to it by passing the object a
block via instance_eval. I am all for keeping BasicObject basic but
instance_eval is, at least for me, a very common use case.

marcel
Posted by Sam Ruby (rubys)
on 04.01.2008 05:41
(Received via mailing list)
Yukihiro Matsumoto wrote:
> |     @secret = 99
> not among one of few methods it has.
> 
> Currently, it has ==, equal? !, !=, __send__, method_missing,
> singleton_method_added, singleton_method_removed, and
> singleton_method_undefined.  Should we add instance_eval as well?

The code in question is from Builder::CSS [1].

Adding instance_eval would help this one use case.  An alternative would
be to add a method to Proc or perhaps to Kernel.  If none of those were
done, I see no reason why BlankSlate couldn't continue to be used.

I guess the question comes down to: what purpose was BasicObject
intended for?

- Sam Ruby

[1] http://builder.rubyforge.org/svn/trunk/lib/builder/css.rb
Posted by Dave Thomas (Guest)
on 04.01.2008 05:45
(Received via mailing list)
On Jan 3, 2008, at 10:32 PM, Brian Mitchell wrote:

> Not perfect (I really don't like using class_eval + undef_method like
> that), but it does work and it does avoid making a complete mess of
> the namespace with __ methods. Maybe an alternative that Ruby 1.9
> could provide would be a few useful methods like instance_eval
> available as unbound methods via the class:
>
>   # No need to force Object instance methods to work on BasicObject
>   # if there are a few special cases made via the class.
>   BasicObject.unbound_instance_eval.bind(foo).call {@secret}


Or even have a BasicObject method, which by default just reurns a
BasicObject class. Then define + on it, which then binds those
unbound methods into a singleton instance, so you could write


    class Foo < BasicObject + :instance_eval

       ...

    end


Dave
Posted by David A. Black (Guest)
on 04.01.2008 12:44
(Received via mailing list)
Hi --

On Fri, 4 Jan 2008, Brian Mitchell wrote:

>> foo = Foo.new
> extract information w/o dirtying the interface:
>        ]
>        accessors_for[name] = instance_method(tmp)
>        undef_method(tmp)
>      end
>      lambda {|obj| @accessors_for[name].bind(obj).call}
>    end
>  end
>
> Now you can use:
>
>  BasicObject.accessor_for('@test').call(foo)

However, until the question of the metaclass/Class thing is resolved,
you end up with this:

irb(main):001:0> class << BasicObject; def x; 1; end; end
=> nil
irb(main):002:0> String.x
=> 1

In other words, #accessor_for will be defined directly on Class. As
per the other thread, I believe this must be a bug, but I'm waiting
for a pronouncement.


David
Posted by James Gray (bbazzarrakk)
on 04.01.2008 15:20
(Received via mailing list)
On Jan 3, 2008, at 10:25 PM, Yukihiro Matsumoto wrote:

> |
>
> Hmm, that's tough question.  Since BasicObject should not have as few
> methods as possible to do the method_missing trick, instance_eval is
> not among one of few methods it has.
>
> Currently, it has ==, equal? !, !=, __send__, method_missing,
> singleton_method_added, singleton_method_removed, and
> singleton_method_undefined.  Should we add instance_eval as well?

I feel like instance_eval() is a pretty basic Ruby method and thus
valuable to have in BasicObject.  I also feel like there's not much
danger that it will conflict with an intent to catch an
"instance_eval" message in method_missing().

James Edward Gray II
Posted by Brian Mitchell (Guest)
on 04.01.2008 15:52
(Received via mailing list)
On Jan 4, 2008 6:43 AM, David A. Black <dblack@rubypal.com> wrote:
> >>    def initialize
> >
> >        tmp = "__tmp#{Thread.current.object_id}__"
> >  end
> irb(main):002:0> String.x
> => 1
>
> In other words, #accessor_for will be defined directly on Class. As
> per the other thread, I believe this must be a bug, but I'm waiting
> for a pronouncement.

Good catch. I think it might be related to [ruby-core:14690].

Brian.
Posted by Yukihiro Matsumoto (Guest)
on 04.01.2008 17:43
(Received via mailing list)
Hi,

In message "Re: BasicObject.instance_eval"
    on Fri, 4 Jan 2008 13:40:52 +0900, Sam Ruby <rubys@intertwingly.net> 
writes:

|Adding instance_eval would help this one use case.  An alternative would 
|be to add a method to Proc or perhaps to Kernel.  If none of those were 
|done, I see no reason why BlankSlate couldn't continue to be used.
|
|I guess the question comes down to: what purpose was BasicObject 
|intended for?

The primary purpose is to replace BlankSlate so that defining
instance_eval seems important.  I will add it to the trunk.
Thank you.

              matz.
Posted by Yukihiro Matsumoto (Guest)
on 04.01.2008 17:56
(Received via mailing list)
Hi,

In message "Re: BasicObject.instance_eval"
    on Fri, 4 Jan 2008 13:45:18 +0900, Dave Thomas <dave@pragprog.com> 
writes:

|Or even have a BasicObject method, which by default just reurns a  
|BasicObject class. Then define + on it, which then binds those  
|unbound methods into a singleton instance, so you could write
|
|
|    class Foo < BasicObject + :instance_eval
|       ...
|    end

Interesting idea.  I'd rather try similar idea with Traits in Ruby
2.0 in the future.

              matz.
Posted by Charles Oliver Nutter (Guest)
on 05.01.2008 01:46
(Received via mailing list)
James Gray wrote:
>> |
>>
> danger that it will conflict with an intent to catch an "instance_eval" 
> message in method_missing().

Perhaps to be sure it should have an "internal" name...

__internal_eval__ or something

- Charlie
Posted by Charles Oliver Nutter (Guest)
on 05.01.2008 01:48
(Received via mailing list)
Charles Oliver Nutter wrote:
> __internal_eval__ or something

Oops, of course I meant __instance_eval__

- Charlie