Skip to content

Instantly share code, notes, and snippets.

@feliperaul
Created Jan 7, 2022
Embed
What would you like to do?
Example
class Greeter
def greet(name)
"Hello #{name}"
end
end
class User
def first_name
"Foo"
end
def greet_with(&block)
Greeter.new.instance_exec(&block)
end
def greet_first_name
greet_with { greet(first_name) }
end
end
User.new.greet_first_name
@shnikola
Copy link

shnikola commented Jan 12, 2022

The problem here is that greet(first_name) is actually calling greet(self.first_name), and inside the instance_exec block, the self is no longer User. The block scope still sees local variables - it just can't run the method. So one solution is just putting the result of the method in a local var:

def greet_first_name
  name = first_name()
  greet_with { greet(name) }
end

instance_exec actually has a built-in pattern for this. It takes arguments which it will pass to the block. This is useful if you want to use methods or instance variables, without needing to put them into local variables. We just need to adjust the greet_with method a little:

  def greet_with(*args, &block)
    Greeter.new.instance_exec(*args, &block)
  end

  def greet_first_name
    greet_with(first_name) { |name| greet(name) }
  end

This way you can be more explicit about the original lexical scope you're using.
Neither is particularly pretty, but it's not a particularly common scenario either.

@feliperaul
Copy link
Author

feliperaul commented Jan 19, 2022

@shnikola Thank you

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment