views:

89

answers:

2

I have 2 classes whose object should act as "partners". The first one is my Thing class, whose instances should act as Tree::TreeNodes of the gem RubyTree.

Basically, this delegation can be implemented using 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

Problem: A number of TreeNode methods refer to self. For example:

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

Thus, my_thing.each {...} yields self, i.e. the Tree::TreeNode object that belongs to my_thing but not the Thing object itself.

Another example:

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

parent.children returns an Array of Things and therefore the condition never evaluates to false as my_sibling is a Thing (which is fine) but self is a Tree::TreeNode

Question: How to evaluate the instance methods of a class (e.g. Tree::TreeNode) in the context of another class (e.g. Thing)? ("overwrite self")

I tried with UnboundMethods, but you can only bind an instance of the original receiving class to an unbound method.

A: 

I'm not sure if you can. Maybe with instance_eval {unboundmethod.to_proc} or something?

rogerdpack
Apparently, you cannot coerce an UnboundMethod to Proc.`NoMethodError: undefined method `to_proc' for #<UnboundMethod: Tree::TreeNode#each>`
duddle
+1  A: 

If you really want, you could use evil-ruby to solve this.

require 'evil'
class A; def m; self; end; end
class B; end
A.instance_method(:m).force_bind(B.new).call
grddev
Thanks! So I know my options. What an evil library! xD Even allows for multiple inheritance.I think I will fork RubyTree one day to make it delegatable. Probably the less-evil solution.
duddle