Ruby 2.7 - Singleton class yield syntax throws a warning


Singleton classes are classes which can be instantiated only once.

There are various ways to create a singleton class or methods in Ruby as below:

class Logger
  def self.log(msg)
    @@log ||= File.open("log.txt", "a")
    @@log.puts(msg)
  end
end

Logger.log('message 5')

module Logger
  def self.log(msg)
    @@log ||= File.open("log.txt", "a")
    @@log.puts(msg)
  end
end

Singleton class can also contain yield, which when called with a block will execute the block. As shown below

def foo
  class << Object.new
    yield
  end
end

foo { puts "Hello Ruby!" }
  => "Hello Ruby!"

Before Ruby 2.7 this function gets executed without any warnings or errors.

In Ruby 2.7

In Ruby 2.7 a warning will be issued when the above function is executed.

def foo
  class << Object.new
    yield
  end
end

warning: `yield' in class syntax will not be supported from Ruby 3.0. [Feature #15575]

foo { puts "Hello Ruby!" }
  => "Hello Ruby!"

This will be deprecated since yield in singleton class syntax was inconsistent with local variables accessibility.

Consider below example

def foo
  x = 1
  class << Object.new
    p x   # NameError (undefined local variable or method) -- enclosing scope NOT accessible
    yield # calls block passed to foo, implying enclosing scope IS accessible
    # In Ruby 2.7: warning: `yield' in class syntax will not be supported from Ruby 3.0.
  end
end

foo { p "Hello Ruby!" }

The above code raises error as shown below

NameError (undefined local variable or method `x' for #<Class...>

NOTE: The variables declared in the block are accessible in the enclosing scope of the singleton class.

def foo
  class << Object.new
    yield
  end
end

foo { x = 1; p x }
 => 1