Как оценить методы другого класса в текущем контексте? - PullRequest
2 голосов
/ 05 августа 2010

У меня есть 2 класса, чьи объекты должны выступать в качестве «партнеров». Первый - мой класс Thing, экземпляры которого должны действовать как Tree::TreeNode из драгоценного камня RubyTree.

По сути, это делегирование может быть реализовано с использованием Forwardable:

class Thing < NoClassInheritancePlease
  extend Forwardable

  def initialize(title = "node")
    @node = Tree::TreeNode.new title

    # Collect node methods that should be delegated
    node_methods = @node.public_methods(false)
    node_methods += @node.protected_methods
    node_methods -= (public_methods(false) + protected_methods(false) + private_methods) # own methods should not been delegated

    # Set up delegation of specified node methods as singleton methods
    for method in node_methods
      Base.def_delegator :@node, method
    end
  end
end

Проблема: Ряд TreeNode методов относится к self. Например:

def each(&block)             # :yields: node
  yield self
  children { |child| child.each(&block) }
end

Таким образом, my_thing.each {...} дает self, то есть объект Tree::TreeNode, который принадлежит my_thing, но не сам объект Thing.

Другой пример:

siblings = []
parent.children {|my_sibling| siblings << my_sibling if my_sibling != self}
siblings

parent.children возвращает массив из Thing с, и поэтому условие никогда не оценивается как ложное, поскольку my_sibling является Thing (что хорошо), но self является Tree::TreeNode

Вопрос : Как оценить методы экземпляра класса (например, Tree::TreeNode) в контексте другого класса (например, Thing)? («перезаписать себя»)

Я пытался с UnboundMethods, но вы можете привязать только экземпляр исходного класса получения к несвязанному методу.

Ответы [ 3 ]

1 голос
/ 11 августа 2010

Если вы действительно хотите, , вы можете использовать зло-рубин , чтобы решить эту проблему.

require 'evil'
class A; def m; self; end; end
class B; end
A.instance_method(:m).force_bind(B.new).call
1 голос
/ 22 февраля 2014

Вы, вероятно, хотите использовать instance_exec.

Из документов:

Выполняет данный блок в контексте получателя (obj). Чтобы установить контекст, переменная self устанавливается в obj во время выполнения кода, предоставляя коду доступ к переменным экземпляра obj Аргументы передаются как параметры блока.

class KlassWithSecret
  def initialize
    @secret = 99
  end
end

k = KlassWithSecret.new
k.instance_exec(5) {|x| @secret+x }   #=> 104

http://ruby -doc.org / ядро-1.8.7 / object.html # метод-я-instance_exec

В вашем случае вы можете использовать instance_exec, чтобы получить self.

def each(&block)
  instance_exec{ yield self }
  children { |child| child.each(&block) }
end
0 голосов
/ 05 августа 2010

Я не уверен, если вы можете.Может быть с instance_eval {unboundmethod.to_proc} или чем-то еще?

...