instance_exec

Executes the given block within the context of the receiver (obj). In order to set the context, the variable self is set to obj while the code is executing, giving the code access to obj's instance variables. Arguments are passed as block parameters.
BasicObject#instance_exec

We can use instance_exec to implement FactoryBot-like factory method. Given the following

class User
  attr_accessor :name, :email
end

FactoryMaker.factory(User) do
  name { "Juanito" }
  email { "[email protected]" }
end

The above example shows we want to do something like this given this User class:

user = User.new
user.name = "Me"
user.email = "[email protected]"

But instead we want to do it in the above block-style. Let’s see how to implement this.

FactoryMaker.factory(User) do
  ...
end

Here we pass in a User and a block to FactoryMaker.factory. We want to use the given class to instantiate a new user instance and set the instance variables accordingly.

class FactoryMaker
  def self.factory(klass, &block)
    ObjectMaker.new(klass).instance_exec(&block)
  end

  class ObjectMaker
    def initialize(klass)
      @object = klass.new
    end

    def method_missing(name, *args, &block)
      self.object.instance_variable_set(
        "@#{name}",
        block.call,
      )
      object
    end

    private
    attr_accessor :object
  end
end

Hope this helps :)