DEV Community

Miguel Jimenez
Miguel Jimenez

Posted on

Tracking global variables in Ruby with #trace_var

An not-so-well-known feature of Ruby is #trace_var, which allows to listen to changes in a variable and execute a callback.

$counter = 0

trace_var :$counter do |number|
    puts "the counter is on #{number}"
end

10.times { $counter +=  1 }

#=> the counter is on 1
#=> the counter is on 2
#=> the counter is on 3
# ...
Enter fullscreen mode Exit fullscreen mode

The concept of tracing changes in a variable is interesting - Different objects can listen to these changes, and coordinate their work without stepping in each other's toes. Let's build a simplified Fizz! and Buzz! yellers using #trace_var

class FizzBuzzer
  def initialize(*predecessors)
    trace_var :$counter, ->(number) do
      yell if yelling_condition
    end
  end

end

class Fizz < FizzBuzzer
  def yell
    puts "Fizz!"
  end

  private

  def yelling_condition
    $counter % 3 == 0
  end

end

class Buzz < FizzBuzzer
  def yell
    puts "Buzz!"
  end

  private

  def yelling_condition
    $counter % 5 == 0
  end

end

class NoFizzBuzz < FizzBuzzer
  def yell
    puts "#{$counter}"
  end

  private

  def yelling_condition
    $counter % 3 != 0 && $counter % 5 != 0
  end

end

$counter = 0

fizz = Fizz.new
buzz = Buzz.new
no_fizz_buzz = NoFizzBuzz.new

15.times { $counter += 1 }
Enter fullscreen mode Exit fullscreen mode

While the idea of listening to global variables to drive business logic does not seem sound for production code, you can certainly use #trace_var during debugging to see what could be messing with global variables.

trace_var :$some_global_variable do |value|
  puts "$some_global_variable = #{value}"
  puts caller
end
Enter fullscreen mode Exit fullscreen mode

But if you are certainly in need of tracing variables and set callbacks in depth, Ruby takes this concept further with its TracePoint API.

Top comments (0)