“Personal” method in ruby

∥☆過路亽.° 提交于 2020-01-24 07:44:56

问题


I'm looking for a way of making a method "personal" - note NOT PRIVATE to a class

here is an example - by "personal" I mean the behaviour of method "foo"

class A
  def foo
     "foo"
  end
end

class B < A
  def foo
      "bar"
  end
end

class C < B
end

a=A.new; b=B.new;c=C.new

I'm looking for a way of producing the following behaviour

a.foo #=> "foo"

b.foo #=> "bar"

c.foo #=> "foo" (ultimate base class method called)

回答1:


Instead of creating 'personal' methods, change your inheritance structure.

It appears that you want the C class to have only some of the same functionality of the B class while not making changes to the A class.

class A
  def foo
     "foo"
  end
end

class BnC < A
end

class B < BnC
  def foo
      "bar"
  end
end

class C < BnC
end

a=A.new; b=B.new;c=C.new



回答2:


There's no standard way of doing this. It circumvents how inheritance works. You could implement B's method to do the logic like this:

def foo
  instance_of?(B) ? "bar" : super
end

And you could of course define a method on Class that would do this for you similar to public and private.

class Class
  def personal(*syms)
    special_class = self
    syms.each do |sym|
      orig = instance_method(sym)
      define_method(sym) {|*args| instance_of?(special_class) ? orig.bind(self).call(*args) : super}
    end
  end
end

Then you can personal :foo in B just like you'd private :foo.

(This isn't at all optimized and I didn't implement the zero-argument behavior that public and private have because frankly it's a huge PITA to do right and even then it's a hack.)




回答3:


Seems like it could be confusing, but here's one option:

class A
  def foo
     "foo"
  end
end

class B < A
 def initialize #when constructing, add the new foo method to each instance
    def self.foo
      "bar"
    end 
 end
end

class C < B
 def initialize #when constructing, do nothing
 end
end

More generally, using a similar approach, you can always add a method to a given instance, which of course has no effect on inherited classes or indeed on other instances of the same class.

If you give us specifics of what you're ultimately trying to accomplish we can probably be more helpful.




回答4:


Answering this is a bit tricky since I don't really see what you want to accomplish in practice, but you could try something like

class C < B
  def foo
    self.class.ancestors[-3].instance_method(:foo).bind(self).call
  end
end

(The ancestors[-3] assumes that A inherits from Object and Kernel and your intent was to access the method from the topmost non-builtin class. Of course you could substitute self.class.ancestors[-3] with just A, or figure out the class from the Array ancestors yourself, etc.)

In practice it would be simpler to alias the original in class B if you can modify it (i.e. alias :foo_from_A :foo in class B < A before the new def foo, then you can call foo_from_A in C). Or just redefine what you want in C. Or design the whole class hierarchy differently.




回答5:


You can write a shortcut function to handle personalizing methods.

def personalize(*methodNames)
  old_init = instance_method(:initialize)
  klass = self
  modul = Module.new {
    methodNames.each { |m|
      define_method(m, klass.instance_method(m)) if klass.method_defined?(m)
    }
  }
  methodNames.each { |m| 
    remove_method(m) if method_defined?(m)
  }
  define_method(:initialize) { |*args|
    # I don't like having to check instance_of?, but this is the only way I 
    # could thing of preventing the extension of child classes. At least it only
    # has to happen once, during initialization.
    extend modul if instance_of?(klass)
    old_init.bind(self).call(*args)
  }
  self
end

class A
  def foo
     "foo"
  end
end

class B < A
  def foo
      "bar"
  end
  def bam
    'bug-AWWK!'
  end
  personalize :foo, :bam, :nometh
end

class C < B
end

a=A.new; b=B.new; c=C.new
a.foo #=> "foo"
b.foo #=> "bar"
b.bam #=> "bug-AWWK!"
c.foo #=> "foo"
C.instance_method(:foo) # => #<UnboundMethod: C(A)#foo>
c.bam #throws NoMethodError



回答6:


Sometimes you don't really want an "is a" (inheritance) relationship. Sometimes what you want is "quacks like a." Sharing code among "quacks like a" classes is easily done by using modules to "mix in" methods:

#!/usr/bin/ruby1.8

module BasicFoo
  def foo
     "foo"
  end
end

class A
  include BasicFoo
end

class B
  def foo
      "bar"
  end
end

class C
  include BasicFoo
end

p A.new.foo    # => "foo"
p B.new.foo    # => "bar"
p C.new.foo    # => "foo"


来源:https://stackoverflow.com/questions/2556118/personal-method-in-ruby

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!