Madogiwa Blog

主に技術系の学習メモに使っていきます。

Ruby: ActiveSupportコア拡張を使って任意の定数を持たない特定のクラスを継承したクラスを見つける

ライブラリのバージョンアップ等で親クラスが子クラスが持つ特定の定数に依存するようになりエラーが発生することがあり。。。

特定のクラスを継承したクラスの中で特定の定数を持たないクラスを探すコードを書いたのメモしておきます📝

サンプルコード

例えば、以下のような子クラスに定義されたATTRIBUTESを参照して、hashを作るようなメソッドが親クラスに実装されていて、一部の子クラスにATTRIBUTESが定義されていないケースをサンプルにしています。

今回のケースではEnglishNameATTRIBUTESを持たないのでEnglishName.new.attributes実行時にuninitialized constant EnglishName::ATTRIBUTESが発生しています。

class BaseName
  def attributes
    self.class::ATTRIBUTES.inject({}) do |result, attr|
      result.tap { result[attr] = public_send(attr) }
    end
  end
end

class JapaneseName < BaseName
  ATTRIBUTES = [:first_name, :last_name]

  def first_name
    'Taro'
  end

  def last_name
    'Yamada'
  end
end

class EnglishName< BaseName
  def first_name
    'John'
  end

  def middle_name
    'Fitzgerald'
  end

  def last_name
    'Smith'
  end
end

JapaneseName.new.attributes
#=> {:first_name=>"Taro", :last_name=>"Yamada"}
EnglishName.new.attributes
#=> `attributes': uninitialized constant EnglishName::ATTRIBUTES (NameError)

任意の定数を持たない特定のクラスを継承したクラスを見つけるコード

今回はActiveSupportのコア拡張を使ってコードを作成します🚃

railsguides.jp

以下がエラーが発生する、BaseNameを継承していて定数ATTRIBUTESを持たないクラスを取得するスクリプトです。

parent_class = Base
target_const = :ATTRIBUTES

puts parent_class.subclasses.select do |sub|
  sub.constants.exclude?(target_const)
end
#=> EnglishName

subclassesで子クラスの配列を取得してconstantsでそれぞれの子クラスの持つ定数を取得して、ATTRIBUTESを持たないクラスを取得しています💻

おわりに

RubyActiveSupportのコア拡張を使うと割と自由にクラスを探索出来て便利ですね✨