如何正确使用Ruby Refinement自定义{now: Time.now}.inspect的输出?
如何让包含Time对象的Ruby集合输出可直接解析的代码格式?
需求是让Hash/Array的输出可以直接复制到代码中,通过Time.parse生成真实的Time实例。原代码仅对Time进行了Refinement,但调用Hash#inspect时输出的仍是Time的原生格式,无法满足需求。
解法一:使用pp替代inspect(推荐)
Ruby的pp(Pretty Print)是纯Ruby实现的打印工具,会遍历集合元素并调用其inspect方法,因此能触发Time的Refinement。只需保留Time的Refinement,改用pp输出即可:
# frozen_string_literal: true module TimeMongoInspect refine Time do def inspect # 包含纳秒的格式,和期望输出匹配 strftime("Time.parse('%Y-%m-%d %H:%M:%S.%N %z')") end end end using TimeMongoInspect a = { time: Time.now } pp a
输出示例:
{:time=>Time.parse('2023-08-29 00:39:08.546338569 +0000')}
这种方式无需修改Hash或Array的行为,代码简洁且符合Ruby的设计习惯。
解法二:Refine Hash和Array(若必须使用inspect)
如果一定要通过Hash#inspect和Array#inspect输出目标格式,需要重新实现这两个方法,确保它们调用元素的inspect(包括Refined的Time#inspect)。可以用更简洁的Ruby语法实现:
# frozen_string_literal: true module TimeMongoInspect refine Time do def inspect "Time.parse('#{strftime('%Y-%m-%d %H:%M:%S.%N %z')}')" end end refine Hash do def inspect entries.map { |k, v| "#{k.inspect}=>#{v.inspect}" } .join(', ') .then { "{#{_1}}" } end end refine Array do def inspect map(&:inspect) .join(', ') .then { "[#{_1}]" } end end end using TimeMongoInspect a = { time: Time.now } puts a.inspect
为什么原方法不生效?
Ruby的核心集合类(Hash、Array)的原生inspect方法是用C实现的,不会调用Ruby层面的元素inspect方法,因此Time的Refinement无法被触发。只有重新实现集合类的inspect,或者使用纯Ruby实现的pp工具,才能让Refined的Time#inspect生效。
内容的提问来源于stack exchange,提问作者Daniel
相关产品推荐
相关产品推荐

