Я хотел бы динамично указать родительский класс для класса в 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
определение класса указывает родительский класс, таким образом, они оба наследовались Объекту. Мой первый вопрос: в чем я должен был бы выполнить Agent.hook_up
для создания Parent
суперкласс Child
(так, например, Child
объекты могут наследовать метод 'панели').
Мой второй вопрос: сделайте я должен передать первый аргумент Agent.hook_up
, или есть ли некоторый путь hook_up
метод может программно определить класс, от которого это назвали?
Джошуа уже дал вам отличный список альтернатив, но чтобы ответить на ваш вопрос: вы не можете изменить суперкласс класса после того, как класс был создан в ruby. Это просто невозможно.
Возможно, вы ищете это
Child = Class.new Parent do
def foo
"foo"
end
end
Child.ancestors # => [Child, Parent, Object, Kernel]
Child.new.bar # => "bar"
Child.new.foo # => "foo"
Поскольку родительский элемент является аргументом 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 для изменения суперкласса. Доступен даже форк для 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