Question

I want to call instance_eval on this class:

class A
  attr_reader :att
end

passing this method b:

class B
  def b(*args)
    att
  end
end

but this is happening:

a = A.new
bb = B.new
a.instance_eval(&bb.method(:b)) # NameError: undefined local variable or method `att' for #<B:0x007fb39ad0d568>

When b is a block it works, but b as a method isn't working. How can I make it work?

Was it helpful?

Solution 2

This answer does not use a real method as asked, but I didn't need to return a Proc or change A. This is a DSL, def_b should have a meaningful name to the domain, like configure, and it is more likely to be defined in a module or base class.

class B
  class << self
    def def_b(&block)
      (@b_blocks ||= []) << block
    end

    def run
      return if @b_blocks.nil?
      a = A.new
      @b_blocks.each { |block| a.instance_eval(&block) }
    end
  end

  def_b do
    a
  end
end

And it accepts multiple definitions. It could be made accept only a single definition like this:

class B
  class << self
    def def_b(&block)
      raise "b defined twice!" unless @b_block.nil?
      @b_block = block
    end

    def run
      A.new.instance_eval(&@b_block) unless @b_block.nil?
    end
  end

  def_b do
    a
  end
end

OTHER TIPS

It's not clear exactly what you goal is. You can easily share methods between classes by defining them in a module and including the module in each class

module ABCommon
  def a
    'a'
  end
end

class A
  include ABCommon
end

Anything = Hash
class B < Anything
  include ABCommon

  def b(*args)
    a
  end

  def run
    puts b
  end
end
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top