Instance_eval to evaluate methods inside a block in context

http://pastie.caboo.se/114839

Can anyone tell me why this wouldn't/shouldn't/should work? I don't
want to pull out all of my hair trying to figure this out if it isn't
even possible.

Thanks,
Adam

class Calculator
  def evaluate(&script)
    self.instance_eval(&script)
  end

  def two
    "2"
  end
end

class Foo
  def hi
    puts "hi"
  end

  def howdy
    Calculator.new.evaluate do
      puts two
      hi
    end
  end
end

Foo.new.howdy

Attachments:
http://www.ruby-forum.com/attachment/920/w.rb

···

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

Can anyone tell me why this wouldn't/shouldn't/should work? I don't
want to pull out all of my hair trying to figure this out if it isn't
even possible.

When you call instance_eval in Calculator#evaluate, all method calls
without an explicit receiver are called relative to the calculator
instance. The call to two() works because two() is defined as an
instance method of Calculator. The call to hi() fails because hi()
is an instance method of Foo not of Calculator.

It is important to realize that method invocation with an instance_eval
block is resolved dynamically based on self and not lexically based
on where the block is declared (in this case within an instance method
of Foo). By calling instance_eval you are explicitly overriding the
normal binding of self within the block.

Try changing howdy() as follows:

  def howdy
    Calculator.new.evaluate do
      puts two
      puts self.inspect # <== find out what self is
      hi
    end

You'll see that self is an instance of calculator and hi() isn't
defined for for Calculator objects, only for Foo objects.

Gary Wright

···

On Nov 6, 2007, at 8:46 PM, Adam Anderson wrote:

You can either do this:
class Calculator
  def evaluate(&script)
    self.instance_eval(&script)
  end
  def two
    "2"
  end
end

class Foo
  def hi
    puts "hi"
  end
  def howdy
    me = self
    Calculator.new.evaluate do
      puts two
      me.hi
    end
  end
end

Foo.new.howdy

...or this...

class Calculator
  def evaluate(&script)
    yield self
  end
  def two
    "2"
  end
end

class Foo
  def hi
    puts "hi"
  end
  def howdy
    Calculator.new.evaluate do |cal|
      puts cal.two
      hi
    end
  end
end

Foo.new.howdy

···

On Nov 6, 6:46 pm, Adam Anderson <adamanderso...@gmail.com> wrote:

http://pastie.caboo.se/114839

Can anyone tell me why this wouldn't/shouldn't/should work? I don't
want to pull out all of my hair trying to figure this out if it isn't
even possible.

So I guess I understand now why this can't be done, but I want to do
something like this. Basically I wish that I could call a block and
every method inside that block would be called against the instance of
the class in which the block is being evaluated and if it gets a
NameError or method_missing or whatever then it moves outside the scope
of that block.

From what I am reading it would seem that this is just not a
possibility, however.

Thanks for your help!

···

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

You could use method_missing in the implicit object to delegate
the message to another object:

class A
   def foo
     warn "foo in #{self.inspect}"
   end
   def backup=(b); @backup = b; end
   def method_missing(*args, &block)
     if @backup
       @backup.send(*args, &block)
     else
       super
     end
   end
end

class B
   def bar
     warn "bar in #{self.inspect}"
   end
   def with(x, &b)
     x.backup = self
     x.instance_eval &b
   end
end

a = A.new
b = B.new.with(a) {
       foo # foo in instance of A
       bar # bar in instance of B
}

···

On Nov 7, 2007, at 3:58 AM, Adam Anderson wrote:

From what I am reading it would seem that this is just not a
possibility, however.

This isn't really what I'm trying to do, however. Your example would
mean that I would have to pass a backup to a block everytime. What I
want to do is basically impossible unless I can change scoping rules, I
think.

my_method do
  another_instance_method("foo")
  method_outside_of_instance("bar")
end

which would just be equivalent to

my_method do |lol|
  lol.another_instance_method("foo")
  method_outside_of_instance("bar")
end

where the lol object is just an instance of the class which has defined
both my_method and another_instance_method.

I want any method to implicitly check the scope of the class in which
the method is being called and *THEN* check the scope outside the class.
This is just an exercise in making code more readable and require less
typing, as far I am concerned.

If I use instance_eval then all the methods inside then only those
instance methods will work. I am pretty much resigned to believing this
is impossible. I'm always taking suggestions, though!

Thanks,
Adam

Gary Wright wrote:

···

On Nov 7, 2007, at 3:58 AM, Adam Anderson wrote:

From what I am reading it would seem that this is just not a
possibility, however.

You could use method_missing in the implicit object to delegate
the message to another object:

class A
   def foo
     warn "foo in #{self.inspect}"
   end
   def backup=(b); @backup = b; end
   def method_missing(*args, &block)
     if @backup
       @backup.send(*args, &block)
     else
       super
     end
   end
end

class B
   def bar
     warn "bar in #{self.inspect}"
   end
   def with(x, &b)
     x.backup = self
     x.instance_eval &b
   end
end

a = A.new
b = B.new.with(a) {
       foo # foo in instance of A
       bar # bar in instance of B
}

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

I think what you are suggesting is the ability to queue up a number
of objects to be used as 'self' while a block is being evaluated. If
none of the objects can handle a message, then the process would
be repeated this time looking for method_missing().

Something like:

     also_eval(a,b,c) { #code }

So the code would be executed with the 'normal' self. If a method
was not found it would be searched for via object a then b then c.

The main problem with implementing this is that you really want to
insert a step between the normal method lookup and method_missing and
I don't think that can be done without using method_missing itself.

Anyway, here is a solution that uses method_missing to link the
objects. The last object in the chain is responsible for doing
something via its own method_missing. This solution will not
properly handle nested blocks due the limitations on define_method().
I'm sure this could be improved. I'm not sure how useful this is
but it was a fun problem to think through.

module Kernel
   def also_eval(*args, &block)
     block_self = eval "self", block.binding
     args.inject(block_self) { |op1, op2|
       (class <<op1; self; end).send(:define_method, :method_missing) { |*args|
         begin
           op2.send(*args)
         rescue NoMethodError
          begin
            super
          rescue NoMethodError
            op2.send(:method_missing, *args)
          end
         end
       }
       op2
     }
     block.call
   end
end

class A
   also_eval({:a=>1}, [100,200]) {
     p self # A
     p keys # the hash
     p first # the array
     p bogus # not found
   }
end

···

On Nov 7, 2007, at 3:04 PM, Adam Anderson wrote:

I want any method to implicitly check the scope of the class in which
the method is being called and *THEN* check the scope outside the class.
This is just an exercise in making code more readable and require less
typing, as far I am concerned.