rubymonkeypatchingrefinements

How to use a refinement to change the output of { now: Time.now }.inspect to be customized?


After years of working with ruby, I finally came across a good need for ruby refinement!

# frozen_string_literal: true

module TimeMongoInspect
  refine Time do

    def inspect
      strftime("Time.parse('%Y-%m-%d %H:%M:%S %z')")
    end

    def to_s
      strftime("Time.parse('%Y-%m-%d %H:%M:%S %z')")
    end
  end
end

using TimeMongoInspect

a = { time: Time.now }
puts a.inspect

The desired output is

{:time=>Time.parse('2023-08-29 00:39:08.546338569 +0000')}

In case someone is curious, I want to be able to cut-n-paste output into code, however, I need the date to be properly interpetted as a real Time class..

ChatGPT said to also refine Array and Hash

module TimeMongoInspect
  refine Time do
    def inspect
      "Time.parse('#{strftime('%Y-%m-%d %H:%M:%S %z')}')"
    end

    def to_s
      inspect
    end
  end
  
  refine Hash do
    def inspect
      "{" + self.map { |k, v| "#{k.inspect}: #{v.inspect}" }.join(", ") + "}"
    end
  end

  refine Array do
    def inspect
      "[" + self.map { |v| v.inspect }.join(", ") + "]"
    end
  end
end

However, I thought this was making the problem even more complicated and was the wrong way to go.

What is the correct way to do this?


Solution

  • The issue is that refinements are lexically scoped.

    Meaning they apply only to code you write in the same script.

    If you refine Time#inspect. Every call to Time#inspect from the same file/script will use your refined method.

    If you call Time.now.inspect directly you will see that.

    However, the refinement has no effect on code not in your script. For example Hash#inspect is not defined in your code. It's defined elsewhere that does not have your refinement. Therefore any Time#inspect called from there does not use your refinement.

    Your options: