Techioz Blog

Ruby セーフ ナビゲーション オペレーターは、レシーバーが nil の場合にそのパラメーターを評価しますか?

概要

Ruby のセーフ ナビゲーション演算子 (&.) は、レシーバーが nil の場合にパラメーターを評価しますか?

例えば:

logger&.log("Something important happened...")

前もって感謝します。

コードベース全体に次のようなコードがあります。

logger.log("Something important happened. (#{Time.current})") if verbose

私の主な目標は、log メソッドを呼び出すたびに行われる if 詳細チェックの繰り返しを削除することです。これは忘れられやすく、誤用についてはまったく通知されないためです。

「Tell、Don’t ask」の原則に触発されて、

ログメソッド実装内の if 冗長チェックを移動しました。

class Logger
  # ...
  
  def log(message)
    return unless verbose

    # ...
  end
end

def logger
  @logger ||= Logger.new
end

logger.log("Something important happened. (#{Time.current})")

このアプローチにより、主な問題が解決されたため、コードが簡素化されました。log メソッドを呼び出すたびに if冗長を配置することを覚えておく必要がなくなりました。

しかし、別の問題を受け取りました。

「Something important…」文字列は、verbose が true か false かに関係なく、常に評価されます。

したがって、ソリューションを完全に変更しました。

def logger
  @logger ||= Logger.new if verbose
end

logger&.log("Something important happened. (#{Time.current})")

その結果、冗長チェックの場合を思い出すという最初の問題を、& を思い出すことに置き換えました。呼び出します。

しかし、とにかく、安全なナビゲーション演算子を使用するのを忘れると NoMethodError が発生する、つまり、ログ メソッドの誤使用について通知されるため、これは改善であると考えています。

ここで、「安全なナビゲーション オペレーターのアプローチ」が実際に私の問題に対して「より良い」選択肢であることを確認するために、次のようにします。

Ruby のセーフ ナビゲーション オペレーターがレシーバーが nil のときにパラメーターを評価するかどうかを正確に知る必要があります。

解決策

セーフ ナビゲーション演算子の構文ドキュメントから引用するには、次のようにします。

そのため、次のように呼び出したときにロガーが nil の場合、log メソッドの引数は評価されません。

logger&.log("something happened at #{Time.now}")

そうは言っても、Ruby コア ロガーは、まさに問題に対して別の解決策を提供することに注意してください。つまり、ログ レベルが高すぎる場合に、潜在的に高価な引数を評価する必要がなくなるのです。

Ruby コア ロガーは、次のような add メソッドを実装します (簡略化)。

class Logger
  attr_accessor :level

  def initialize(level)
    @level = level.to_i
  end

  def add(severity, message = nil)
    return unless severity >= level
    
    message ||= yield
    log_device.write(message)
  end

  def info(message = nil, &block)
    add(1, message, &block)
  end
end

これを次のように使用できます

logger = Logger.new(1)
logger.info { "something happened at #{Time.now}" }

ここで、ブロックは、メッセージが実際に使用されるほどログ レベルが高い場合にのみ評価されます。