Ответ 1
Джошуа уже дал вам отличный список альтернатив, но, чтобы ответить на ваш вопрос: вы не можете изменить суперкласс класса после того, как класс был создан в рубине. Это просто невозможно.
Я хотел бы динамически указать родительский класс для класса в 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
Ни определение класса Parent
, ни Child
не указывает родительский класс, поэтому они оба наследуются от Object. Мой первый вопрос: что мне нужно сделать в Agent.hook_up
, чтобы сделать Parent
суперкласс из Child
(так, например, Child
объекты могут наследовать метод "bar" ).
Мой второй вопрос: мне нужно передать первый аргумент в Agent.hook_up
, или есть способ, которым метод hook_up
может программным образом определить класс, из которого он был вызван?
Джошуа уже дал вам отличный список альтернатив, но, чтобы ответить на ваш вопрос: вы не можете изменить суперкласс класса после того, как класс был создан в рубине. Это просто невозможно.
Возможно, вы ищете это
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"
Только Ruby 1.9: (1.8 похоже, но вместо этого используйте RCLASS (self) → super)
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)
Как уже указывалось, вам следует, вероятно, изучить модули или динамически создавать классы. Тем не менее, вы можете использовать evil-ruby, чтобы изменить суперкласс. Существует даже fork для Ruby 1.9. Это работает только для МРТ. Должна быть легко построена на 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
Ruby SimpleDelegator class (в delegate) может помочь при условии, что достаточно, чтобы объект quack, как базовый класс, а не был фактически экземпляром базового класса.
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
Посмотрите на это
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
он будет наследовать от динамического класса в зависимости от orm
и model
.
Обязательно определите как в модуле. Он отлично работает в public_activity gem (пример селектора).
Я надеюсь помочь.. Пока!
Я знаю, что этот вопрос довольно старый и уже имеет несколько хороших ответов. Однако я все еще скучаю по определенному решению.
Если вы намерены не динамически назначать суперкласс, а создать ловушку для выполнения некоторого кода при наследовании (проблема XY). Существует встроенный способ сделать это.
унаследовал (подкласс)
Обратный вызов вызывается всякий раз, когда создается подкласс текущего класса.
Пример:
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
Если вы хотите динамически создавать классы, я бы порекомендовал посмотреть на ответ Джошуа Чика.