猫の魔法

主にruby系の技術メモを記載

再々考:classとそれが読み込んでいるmoduleの一覧を表示する方法(bugfix)

nekomaho.hatenablog.jp

前に上記の記事を書いたが、改めて見直して見た所、新メソッドと旧メソッドの比較が全く意味の無い物となっている事が分かった。

※記事のアップ時間が夜中の2時な事を差し引いてもちょっと酷いと思う。

コードを再掲するので、モノ好きな方は何が間違っているのか見て見て欲しい。
ちなみに出力は間違っていない。
答えは下に掲載している。

def display_class_modules_old(class_name)
  print "class:#{class_name},modules:#{class_name.included_modules}","\n" if class_name.respond_to?(:included_modules)
  display_class_modules(class_name.superclass) if class_name.respond_to?(:superclass)
end


def display_class_modules(class_name)
  class_name.ancestors.each do |klass|
     puts "class:#{klass},modules:#{klass.included_modules}" if klass.is_a?(Class)
  end
end

puts 'same' if display_class_modules(1.class) == display_class_modules(1.class)


#=> class:Integer,modules:[Comparable, Kernel]
#=> class:Numeric,modules:[Comparable, Kernel]
#=> class:Object,modules:[Kernel]
#=> class:BasicObject,modules:[]
#=> class:Integer,modules:[Comparable, Kernel]
#=> class:Numeric,modules:[Comparable, Kernel]
#=> class:Object,modules:[Kernel]
#=> class:BasicObject,modules:[]
#=> same












間違い

間違いは以下の3つ。

1: 同じかどうかの比較をしている物がおかしい

puts 'same' if display_class_modules(1.class) == display_class_modules(1.class)

上記、ifの左辺値と右辺値がスペルミスで同じメソッドを比較している。それは同じ結果になるだろう。

2: oldがちゃんとoldのメソッドを呼んでいない

仮に1を直すと、display_class_modulesとdisplay_class_modules_oldは異なる値となる。 何故なら、これもタイプミスだがdisplay_class_modules_oldが再帰的に呼んでいるメソッドがdisplay_class_modulesだからだ。
※こう言うときはaliasを使うのが正しいのでしょう。。。

3: そもそも比較出来ていない

仮に1と2を直したとしても、1で行っているようなメソッド同時を==で比較する方法では正しく比較が出来ない。
なぜなら、この2つのメソッドは返している値が異なるからだ。
新しい方はancestorsの戻りが返るが、古い方はputsしているだけなのでnilが返る。
本来なら、何かしらの戻りを比較するか、putsで出している内容を比較する必要がある。

1〜3を統合して、直したのが以下のロジックになる。

def display_class_modules_old(class_name)
  calls ||= []
  calls = [{class: class_name, modules: class_name.included_modules}] if class_name.respond_to?(:included_modules)
  calls << display_class_modules_old(class_name.superclass) if class_name.respond_to?(:superclass)
  calls.flatten
end

def display_class_modules(class_name)
  calls ||= []
  class_name.ancestors.each do |klass|
     calls << {class: klass,modules: klass.included_modules} if klass.is_a?(Class)
  end
  calls
end

old=display_class_modules_old(1.class)
new=display_class_modules(1.class)

old == new ? puts("same") : puts("different")

#=> same

これで本当に正しいのか。。。何か気がついた方居ましたら教えて下さい。

ちなみに自分は両方のメソッドの中身を書き換えたが、
恐らくメソッドの中身を直接書き換える事なく比較する方法もある。
これは時間あるときにチャレンジしてみたい。