views:

318

answers:

2

In Ruby, I'd like to acheive something like this Java sample:

class A {        
    private void f() {  System.out.println("Hello world"); }
    public  void g() { f(); }
}
class B extends A {
    public void f() { throw new RuntimeException("bad guy");}
}
public class Try {
    public static void main(String[] args) { new B().g();}
}

This will print "Hello world" in Java, but the straight Ruby transcript:

class A   
    def g; f; end 
    private
    def f; puts "Hello world"; end
end
class B < A
    def f; raise "bad guy"; end
end
B.new.g    # want greet

will of course raise a bad guy - due to differences in method lookup mechanism (I realise the meaning of 'private' is very different between these languages)

Is there any way to achieve a similar effect? I don't really care about visibility, would actually prefer all public methods here. My goal is simply to isolate a method in the superclass from overriding in subclasses (which would break the other base methods).

I guess if there is a solution, that would work with modules/includes, too?

module BaseAPI 
   def f; puts "Hello world"; end
   def g; f; end;
end
module ExtAPI 
  include BaseAPI
  # some magic here to isolate base method :f from the following one?
  def f; raise "bad guy"; end # could actually be something useful, but interfering with base 'g'
end
include ExtAPI
g # want greet


Follow-up: this looks to be the rare case where something is possible with Java but not with Ruby :-/

+1  A: 
class A
  def g
    real_f
  end 
  private
  def f
    real_f
  end
  def real_f
    puts "Hello world"
  end
end
class B < A
  def f
    raise "bad guy"
  end
end
B.new.g    # want hello world


I have a feeling these obvious answers aren't what you want. Ruby just doesn't have enforcement mechanisms. But just for the record, we could construct a replacement class AA. You could even be more clever than this, and use the method_missing() to dynamically insert alias methods.

class A
  def g
    f
  end 
  def f
    puts "Hello world"
  end
end
class AA
  def initialize
    @x = A.new
  end
  def g
    @x.g
  end
  def f
    @x.f
  end
end
class B < AA
  def f
    raise "bad guy"
  end
end
B.new.g    # want hello world
DigitalRoss
Thanks, but I actually didn't want to change the base class to care about 'real_f'.I mean, I'd like to override/replace any methods in the extending API, without the base API noticing it.
inger
Ok, sorry. Sometimes the "obvious" answer is exactly why what was wanted, sometimes it isn't. :-) Here is another idea, perhaps you really need two separate types? Perhaps the real implementation of A should be a nested class? Or, class AA; class x < A; **. . .**
DigitalRoss
So it seems like straight subclassing, simple module includes won't help here(although I heard 2.0 might change the override story). My use case actually has modules(rather than class inheritance - just brought that up to demonstrate the expectation in Java).I guess the essence of the workaround is that you have to have another object(sort delegate) under the hood, with it's own method table.. Maybe that wouldn't be too bad if I could automatically generate that delegate. I'll give it a thought.Thanks again.
inger
A: 
class A;   def g;f;end ; private ;def f;puts "Hello world";end;end
class B < A 

  def self.method_added(name)
    if superclass.method_defined? name  or superclass.private_method_defined? name
      raise "A trial to add a new instance method: #{name}, this is not allowed." 
    end
  end 

  def f;raise "bad guy";end;

end
#=>`method_added': A trial to add a new instance method: f,
#   this is not allowed.(RuntimeError)
khelll
Thanks, but I guess I was misunderstable, B#f might not necessary be a bad guy - it could be a useful method from B. The only way it's bad is that it breaks A#g.I just added some clarification above - see the module example. I don't want to disallow overrides - I just want to isolate them, so that they don't interfere with the base methods. Cheers for that trick, anyway - will be useful elsewhere.
inger
Yea got what you mean now. However I don't think that's doable in Ruby, as method dispatching is totally different from Java, in addition to the fact that Monkey Patching just works this way ;)
khelll
I guessed that.. Knew the direct translation wouldn't work, but thought some cool metaprogramming magic would save me (I still haven't given up on this one;)Strange to stumble into something in Ruby which is less powerful than Java:-/
inger