In Progress
Unit 1, Lesson 21
In Progress

Warn

Video transcript & code

Let's say we have two methods which each do something similar. One of them is the old, legacy version. The other is a new improved method that we'd like everyone to start using, so we can stop supporting the old one.

def old_and_busted
  puts "Hello, world"
end

def new_hotness
  puts "Salutations, universe!"
end

We can put in a comment about this deprecation. But we have no guarantee existing users will see these warnings.

# DEPRECATED: Use #new_hotness instead
def old_and_busted
  puts "Hello, world"
end

def new_hotness
  puts "Salutations, universe!"
end

For a better chance at letting users know about the deprecation, we can output a warning with puts.

def old_and_busted
  puts "#old_and_busted is deprecated. Use #new_hotness instead."
  puts "Hello, world"
end

def new_hotness
  puts "Salutations, universe!"
end

But this introduces a problem. The whole purpose of these methods is to print a cheerful greeting to standard out. Consider this very fine and worthwhile client code, which makes use of the #old_and_busted method to print three greetings.

require "./greet"
3.times do
  old_and_busted
end

Now let's say someone uses this program from the command line, and redirects the output to a file.

$ ruby hello.rb > hello.txt

Contrary to our intent, the user is not alerted to the need to update their code. What's worse, the warnings have ended up in the output file, where they certainly won't do any good, and might well interfere with the intended usage of that file.

Of course, this problem is precisely why processes on all major operating systems get both standard output and standard error streams. Our deprecation warnings shouldn't be going to standard output; they should be going to the standard error stream. We can easily change their destination by sending the puts message to the $stderr global.

def old_and_busted
  $stderr.puts "#old_and_busted is deprecated. Use #new_hotness instead."
  puts "Hello, world"
end

def new_hotness
  puts "Salutations, universe!"
end

There is another reason to prefer this version. Not only will it keep normal output separate from warning output; but it will also ensure that warnings are seen more quickly. By default, the system standard error stream is set up to be synchronized, which means that output will be written to the console immediately instead of potentially waiting in a buffer.

Given that writing to standard error is doubly preferable in this situation, it would be unfortunate if we had to type out $stderr.puts every time we wanted to do so. Happily, Ruby has a shortcut for us. The warn method is like puts, only instead of defaulting to standard output, it uses $stderr.

def old_and_busted
  warn "#old_and_busted is deprecated. Use #new_hotness instead."
  puts "Hello, world"
end

def new_hotness
  puts "Salutations, universe!"
end

As its name suggests, the warn method is the idiomatic way to output warnings in Ruby programs.

warn also respects Ruby's verbosity settings. Let's see what this means. When we run our client script normally, we see both normal and warning output.

$ ruby hello2.rb
#old_and_busted is deprecated. Use #new_hotness instead.
Hello, world
#old_and_busted is deprecated. Use #new_hotness instead.
Hello, world
#old_and_busted is deprecated. Use #new_hotness instead.
Hello, world

But if we run our program using a ruby flag of -W0 to disable all warnings, it will suppress our deprecation messages.

$ ruby -W0 hello2.rb
Hello, world
Hello, world
Hello, world

We'll talk more about Ruby's warning and verbosity flags in another episode. For now, the important thing to remember is that when we want to output a warning in Ruby, we should use the tool intended for that job: the warn method.

Happy hacking!

Responses