カクカクしかじか

技術的なアレコレ

gem内部のクラス名とアプリケーション内のクラス名が衝突した際の名前付けの教訓

経緯

とある機能の改修で連携先のシステムが公式に提供しているgemを使わねばならず、そのgemを追加したところ変なところでテストが落ちるようになりました🔥
そのgemを追加したことが原因であることは明白だったのですが、どこが悪さをしているかの全くわからない事態に...

実装のマズかった部分

gem内の特定の処理で System.new(uri.host) といったような形でinitializeされるクラス名が lib 配下にアプリケーション側で定義しているクラス名とたまたま被っており、gem内部の処理がそちらのクラスを参照してしまい、引数の異なるinitiazlieを呼び出して例外が発生していたのがCIが落ちていた原因でした📝

gemとアプリ内のコード例(うまいこと抽象化出来てないが...)

gems/hoge-fuga-gem-1.0.0/lib/core/hoge_fuga.rb

class HogeFuga
  def initialize(options = nil)
    super
    # 何かの処理
  end
  
  def each(uri = nil)
    system = System.new(uri.host)
  end

  # 何かの処理
end

lib/system.rb

class System
  # 何かの処理
end

今回じぶんが経験したパターンで言うと System.new(uri.host) の部分で lib 直下の同名クラスを参照してしまい、 initialize の引数の数が異なっていることが原因でエラーになってました...

所感

今回の件で、たとえば System などの一般名詞で名前空間を切らずにクラスを定義してしまうと意図せずにライブラリのコードとぶつかってしまう場合があることを知りました 🙈
広い名前付けは、それが何でも含んでしまうことから責務の境界が曖昧になってしまうなどの弊害は日頃から認識していたものの、 こんな形で表出するとは全く思わず...
改めてコードレビューや自身の実装でもこういった部分に留意したいと思った次第です。