How do I change the context of lambda?

815 views Asked by At

If you run the code below you get an error.

class C
  def self.filter_clause param_1
    puts param_1
    yield # context of this param is class B
  end

  def hi
    "hello"
  end
end

class B
  def self.filter(value, lambda)
    code = lambda { filter_clause(value, &lambda) }
    C.instance_exec(&code)
  end

  filter(:name, ->{ hi })
end

The error is

NameError: undefined local variable or method `hi' for B:Class
from (pry):17:in `block in <class:B>'

From my understanding the reason for this is the lambda is running under the context of class B. So it can't find the method def hi. I can't figure out how to get it to run under the context of class C.

Essentially I want to be able to inject a method being called in another class that accepts an argument and a block.

For example:

filter_clause("value", ->{ hi })

Is this possible?

Not sure if I am making sense.

2

There are 2 answers

5
Chris Heald On BEST ANSWER

You're close. If you want the lambda(/block, really) to execute in the context of an instance of C (since hi is an instance method on C), then you'll need to instantiate it and then instance_exec the block on that new instance:

class C
  def self.filter_clause param_1, &block
    puts new.instance_exec &block
  end

  def hi
    "hello"
  end
end

class B
  def self.filter(value, lambda)
    code = lambda { filter_clause(value, &lambda) }
    C.instance_exec(&code)
  end

  filter(:name, ->{ hi })
end

# => hello
1
pdoherty926 On

Are you opposed to passing the context into the block? If not, something like this would work:

class C
  def self.filter_clause param_1
    puts param_1
    yield self
  end

  def hi
    "hello"
  end
end

class B
  def self.filter(value, lambda)
    code = lambda { filter_clause(value, &lambda) }
    C.instance_exec(&code)
  end

  filter(:name, ->(klass){ klass.new.hi }) # "hello"
end