Local_method_missing possible

I'm wondering how feasible it might be to implement a local_method_missing
hook in core Ruby? What I mean by that is a hook like method_missing, but one
that only is concerned with the methods defined in the current class context.

E.g.

  class A
    def a; end
  end
  class B < A
    def local_method_missing(sym, *args)
      puts "Can't find #{sym}."
    end
  end

  B.new.a
  #=> Can't find a.

Thanks,
T.

I'm not sure I understand what you're getting at. Why would you
subclass A if you didn't want to inherit any of the methods from A?

   -- Markus

···

On Sat, 2004-09-25 at 18:57, trans. (T. Onoma) wrote:

I'm wondering how feasible it might be to implement a local_method_missing
hook in core Ruby? What I mean by that is a hook like method_missing, but one
that only is concerned with the methods defined in the current class context.

E.g.

  class A
    def a; end
  end
  class B < A
    def local_method_missing(sym, *args)
      puts "Can't find #{sym}."
    end
  end

  B.new.a
  #=> Can't find a.

Thanks,
T.

Hi.

It's related to AOP. The basic idea is to be able to reroute method
invocations.

   class A
     def a; puts "here"; end
   end
   class B < A
     def b(meth)
       puts "before"
       send(meth)
       puts "after"
     end
     def local_method_missing(sym, *args)
       if /^a/ =~ meth.to_s
         b(meth)
       end
     end
   end

T.

···

On Saturday 25 September 2004 10:42 pm, Markus wrote:

I'm not sure I understand what you're getting at. Why would you
subclass A if you didn't want to inherit any of the methods from A?

--
( o _ カラチ
// trans.
/ \ transami@runbox.com

I don't give a damn for a man that can only spell a word one way.
-Mark Twain

1) Wouldn't that cause infinite recursion if you called B.new.a?
2) Is this something like CLOS (common lisp object system) around
methods? (See http://www.aiai.ed.ac.uk/~jeff/clos-guide.html\). If so,
I have an partial implementation that works in 1.8.0 (and I hope will
work in 1.8.2 final)

-- Markus

···

On Sat, 2004-09-25 at 20:10, trans. (T. Onoma) wrote:

On Saturday 25 September 2004 10:42 pm, Markus wrote:
> I'm not sure I understand what you're getting at. Why would you
> subclass A if you didn't want to inherit any of the methods from A?

Hi.

It's related to AOP. The basic idea is to be able to reroute method
invocations.

   class A
     def a; puts "here"; end
   end
   class B < A
     def b(meth)
       puts "before"
       send(meth)
       puts "after"
     end
     def local_method_missing(sym, *args)
       if /^a/ =~ meth.to_s
         b(meth)
       end
     end
   end

T.

* trans. (T. Onoma) <transami@runbox.com> [2004-09-26 12:10:59 +0900]:

It's related to AOP. The basic idea is to be able to reroute method
invocations.

   class A
     def a; puts "here"; end
   end
   class B < A
     def b(meth)
       puts "before"
       send(meth)
       puts "after"
     end
     def local_method_missing(sym, *args)
       if /^a/ =~ meth.to_s
         b(meth)
       end
     end
   end

Could you just use super?

  class A
    def a; puts "in a"; end
  end
  class B < A
    def a
      puts "before"
      super
      puts "after"
    end
  end

  B.new.a

···

--
Jim Freeze

1) Good point.

Actually, what is really needed, to make this work well, is a way to send the
method on, up to the next level of the class/module hierarchy, without
rerouting back to the bottom.

2) Thanks. I actually have many implementations of wraps already -- I take it
you are using alias?

Obviously local_method_missing is just a proto-idea, but what it intendeds to
solve is related to dynamically creating wraps that apply to collections of
methods, not just a single method. Also, it is for use in a specific context,
namely a subclass.

So the question is this: Given a class and a predefined 'wrapping' method, how
do I create a subclass and a submethod to effect multiple methods in the
class. Obviously I could just create a submethod for each class method, but
that seems rather bulky.

Hope that makes enough sense,
T.

···

On Sunday 26 September 2004 02:01 am, Markus wrote:

1) Wouldn't that cause infinite recursion if you called B.new.a?
2) Is this something like CLOS (common lisp object system) around
methods? (See http://www.aiai.ed.ac.uk/~jeff/clos-guide.html\). If so,
I have an partial implementation that works in 1.8.0 (and I hope will
work in 1.8.2 final)

The idea is to submethod multiple methods in one fell-swoop. Hence:

  if /^a/ =~ meth.to_s

If you have any ideas on other ways to do this, I would love to hear them.

Thanks,
T.

···

On Sunday 26 September 2004 10:33 pm, jim@freeze.org wrote:

> class A
> def a; puts "here"; end
> end
> class B < A
> def b(meth)
> puts "before"
> send(meth)
> puts "after"
> end
> def local_method_missing(sym, *args)
> if /^a/ =~ meth.to_s
> b(meth)
> end
> end
> end

Could you just use super?

  class A
    def a; puts "in a"; end
  end
  class B < A
    def a
      puts "before"
      super
      puts "after"
    end
  end

trans. (T. Onoma) wrote:

  class A
    def a; puts "here"; end
  end
  class B < A
    def b(meth)
      puts "before"
      send(meth)
      puts "after"
    end
    def local_method_missing(sym, *args)
      if /^a/ =~ meth.to_s
        b(meth)
      end
    end
  end

Could you just use super?

class A
   def a; puts "in a"; end
end
class B < A
   def a
     puts "before"
     super
     puts "after"
   end
end

The idea is to submethod multiple methods in one fell-swoop. Hence:

  if /^a/ =~ meth.to_s

If you have any ideas on other ways to do this, I would love to hear them.

Well, it's not very pretty, but what about:

1) aliasing all methods of the subclass to something else (mangling them, basically),

2) Using "method_missing" to intercept and redirect calls to those methods,

3) and using "self.class.instance_methods(false).include?(...)" to determine whether the method was defined in the subclass or a superclass...

If this isn't clear enough (and it may very well not be!) I could try posting some pseudo-code...

- Jamis

···

On Sunday 26 September 2004 10:33 pm, jim@freeze.org wrote:

--
Jamis Buck
jgb3@email.byu.edu
http://www.jamisbuck.org/jamis

batsman@tux-chan:/tmp$ cat gdfgrre.rb

module Magic
    def wrap_method(*names, &block)
        names.each do |name|
            old = instance_method(name)
            define_method(name) do |*a| # |*a, &b| on 1.9
                block.call(name, old.bind(self), *a) # &b on 1.9
            end
        end
    end
end

class A
    def foo; puts "A#foo" end
    def bar; puts "A#bar" end
end

class B < A
    extend Magic

    wrap_method(:foo, :bar) do |meth, old|
        puts "pre"
        puts "About to call #{meth}"
        if meth.to_s == "foo"
            puts "special action for foo!"
        end
        old.call
        puts "post"
    end
end

b = B.new
b.foo
b.bar
batsman@tux-chan:/tmp$ ruby gdfgrre.rb
pre
About to call foo
special action for foo!
A#foo
post
pre
About to call bar
A#bar
post

···

On Tue, Sep 28, 2004 at 01:28:47AM +0900, trans. (T. Onoma) wrote:

> Could you just use super?
>
> class A
> def a; puts "in a"; end
> end
> class B < A
> def a
> puts "before"
> super
> puts "after"
> end
> end

The idea is to submethod multiple methods in one fell-swoop. Hence:

  if /^a/ =~ meth.to_s

If you have any ideas on other ways to do this, I would love to hear them.

--
Running Debian GNU/Linux Sid (unstable)
batsman dot geo at yahoo dot com

Note, though, that this will fail under 1.8.1 & 1.8.2Pre (but not, I
hope, under later versions) because of the change in block/proc
semantics. See:

http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/113629

http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/113697

(and of course the follow-ons), if you haven't been following that
thread already.

-- Markus

···

On Mon, 2004-09-27 at 09:53, Mauricio Fernández wrote:

module Magic
    def wrap_method(*names, &block)
        names.each do |name|
            old = instance_method(name)
            define_method(name) do |*a| # |*a, &b| on 1.9
                block.call(name, old.bind(self), *a) # &b on 1.9
            end
        end
    end
end

Well done, albeit this the "bulky" solution I referred to. It does seem bulky
doesn't it --defining all those new methods? Or is it just me?

Nonetheless you get the idea. I'm wondering if there's a way to implement
without the bulkiness (even if it is in Ruby core, hence why I was wondering
about the fiesability of local_method_missing)

T.

···

On Monday 27 September 2004 12:53 pm, Mauricio Fernández wrote:

module Magic
def wrap_method(*names, &block)
names.each do |name|
old = instance_method(name)
define_method(name) do |*a| # |*a, &b| on 1.9
block.call(name, old.bind(self), *a) # &b on 1.9
end
end
end
end

Well, it's not very pretty, but what about:

1) aliasing all methods of the subclass to something else (mangling
them, basically),

You're right. And it's this first step that's just too ugly to be acceptable
to me.

2) Using "method_missing" to intercept and redirect calls to those methods,

3) and using "self.class.instance_methods(false).include?(...)" to
determine whether the method was defined in the subclass or a superclass...

If this isn't clear enough (and it may very well not be!) I could try
posting some pseudo-code...

It's clear and it's certainly a solution --but not the "less filling" one I'm
really after.

T.

···

On Monday 27 September 2004 12:45 pm, Jamis Buck wrote:

batsman@tux-chan:/tmp$ ruby -v gdfgrre.rb
ruby 1.8.2 (2004-09-22) [i686-linux]
#<ArgumentError: wrong number of arguments (1 for 2)>
A#foo a: 1 b: 2
#<ArgumentError: wrong number of arguments (3 for 2)>
A#foobar a: [1, 2]
#<ArgumentError: wrong number of arguments (2 for 1)>
A#bar a: [[1, 2, 3]]
A#bar a: [1, 2, 3]
batsman@tux-chan:/tmp$ cat gdfgrre.rb

module Magic
    def wrap_method(*names, &block)
        names.each do |name|
            old = instance_method(name)
            define_method(name) do |*a| # |*a, &b| on 1.9
                block.call(name, old.bind(self), *a)
            end
        end
    end
end

class A
    def foo(a,b); puts "A#foo a: #{a.inspect} b: #{b.inspect}" end
    def foobar(a); puts "A#foobar a: #{a.inspect}" end
    def bar(*a); puts "A#bar a: #{a.inspect}" end
end

class B < A
    extend Magic

    wrap_method(:foo, :bar, :foobar) do |meth, old, *args|
        old.call(*args)
    end
end

b = B.new
b.foo [1,2] rescue p $!
b.foo 1, 2
b.foo 1, 2, 3 rescue p $!
b.foobar [1,2]
b.foobar(1, 2) rescue p $!
b.bar [1,2,3]
b.bar 1, 2, 3

propagation in 1.9 maybe?

···

On Tue, Sep 28, 2004 at 02:35:58AM +0900, Markus wrote:

On Mon, 2004-09-27 at 09:53, Mauricio Fernández wrote:
> module Magic
> def wrap_method(*names, &block)
> names.each do |name|
> old = instance_method(name)
> define_method(name) do |*a| # |*a, &b| on 1.9
> block.call(name, old.bind(self), *a) # &b on 1.9
> end
> end
> end
> end

    Note, though, that this will fail under 1.8.1 & 1.8.2Pre (but not, I
hope, under later versions)

--
Running Debian GNU/Linux Sid (unstable)
batsman dot geo at yahoo dot com