如何在Ruby中动态改变继承

时间:2010-06-27 10:24:18

标签: ruby metaprogramming

我想在Ruby中动态指定类的父类。请考虑以下代码:

class Agent
  def self.hook_up(calling_class, desired_parent_class)
    # Do some magic here
  end
end

class Parent
  def bar
    puts "bar"
  end
end

class Child
  def foo
    puts "foo"
  end

  Agent.hook_up(self, Parent)
end

Child.new.bar

ParentChild类定义都没有指定父类,所以它们都从Object继承。我的第一个问题是:我需要在Agent.hook_up中做些什么才能使Parent成为Child的超类(因此例如Child个对象可以继承'bar'法)。

我的第二个问题是:我需要将第一个参数传递给Agent.hook_up,还是某种方式hook_up方法可以通过编程方式确定它的类被称为?

7 个答案:

答案 0 :(得分:23)

也许你正在寻找这个

Child = Class.new Parent do
  def foo
    "foo"
  end
end

Child.ancestors   # => [Child, Parent, Object, Kernel]
Child.new.bar     # => "bar"
Child.new.foo     # => "foo"

由于parent是Class.new的参数,因此可以将其与其他类交换。

在编写某些类型的测试之前,我已经使用过这种技术。但我很难找到许多有利于做这件事的好借口。


我怀疑你真正想要的是一个模块。

class Agent
  def self.hook_up(calling_class, desired_parent_class)
    calling_class.send :include , desired_parent_class
  end
end

module Parent
  def bar
    "bar"
  end
end

class Child
  def foo
    "foo"
  end

  Agent.hook_up(self, Parent)
end

Child.ancestors   # => [Child, Parent, Object, Kernel]
Child.new.bar     # => "bar"
Child.new.foo     # => "foo"

但是,当然,根本不需要代理

module Parent
  def bar
    "bar"
  end
end

class Child
  def foo
    "foo"
  end

  include Parent
end

Child.ancestors   # => [Child, Parent, Object, Kernel]
Child.new.bar     # => "bar"
Child.new.foo     # => "foo"

答案 1 :(得分:6)

Joshua已经为您提供了很多替代方案,但是要回答您的问题:在ruby中创建类之后,您无法更改类的超类。这根本不可能。

答案 2 :(得分:4)

仅限Ruby 1.9 :( 1.8类似,但使用RCLASS(自我) - >超级代替)

require 'inline'
class Class
    inline do |builder|

        builder.c %{            
            VALUE set_super(VALUE sup) {
                RCLASS(self)->ptr->super = sup;
                return sup;
            }
        }

        builder.c %{
            VALUE get_super() {
                return RCLASS(self)->ptr->super;
            }
        }

    end


J = Class.new
J.set_super(Class.new)

答案 3 :(得分:3)

正如已经指出的那样,您应该查看模块或动态创建类。但是,您可以使用evil-ruby来更改超类。甚至还有fork for Ruby 1.9可用。这仅适用于MRI。应该很容易建立Rubinius(清除方法缓存将是主要问题),没有关于JRuby的线索。这是代码:

require 'evil'

class Agent
  def self.hook_up(calling_class, desired_parent_class)
    calling_class.superclass = desired_parent_class
  end
end

class Parent
  def bar
    puts "bar"
  end
end

class Child
  def foo
    puts "foo"
  end

  Agent.hook_up(self, Parent)
end

Child.new.bar

答案 4 :(得分:0)

Ruby的SimpleDelegator类(在delegate库中)可能会有所帮助,前提是对象嘎嘎像基类,而不是实际 基类的实例。

require 'delegate'

class Agent < SimpleDelegator
  def baz
    puts "baz"
  end
end

class BarParent
  def bar
    puts "bar"
  end
end

class FooParent
  def foo
    puts "foo"
  end
end

agent = Agent.new(FooParent.new)
agent.baz    # => baz
agent.foo    # => foo
agent.__setobj__(BarParent.new)
agent.baz    # => baz
agent.bar    # => bar

答案 5 :(得分:0)

看看这个

  class MyClass < inherit_orm("Adapter")
  end

类选择器:

  def inherit_orm(model="Activity", orm=nil)
    orm = Config.orm || orm
    require "orm/#{orm.to_s}"
    "ORM::#{orm.to_s.classify}::#{model}".constantize
  end

因此,当实例MyClass将根据ormmodel从动态类继承。 务必在模块中定义两者。它在public_activity gem(selector example)中运行良好。

我希望能帮助......再见!

答案 6 :(得分:0)

我知道这个问题已经很老了,已经有了一些很好的答案。但是我仍然想办法解决。

如果您不是要动态分配超类,而是要创建一个挂钩来执行有关继承的某些代码(XY Problem)。有内置的方法可以做到这一点。

  

继承(子类)

     

在创建当前类的子类时调用回调。

     

示例:

class Foo
  def self.inherited(subclass)
    puts "New subclass: #{subclass}"
  end
end

class Bar < Foo
end

class Baz < Bar
end
     

产生:

New subclass: Bar
New subclass: Baz

请参阅:Class#inherited

如果您打算动态创建类,则建议查看the answer of Joshua Cheek