ブログ

読んで思い出す。忘れるために書く

文字列によるメソッド呼び出しとループ処理による絞り込み

書いたところで 誰も しあわせにしないコード

まとめ

(この例では有効ではなかったけれど) if...elsif... が続くコードは、case...when やループ処理に変換できないか検討してみるといいかもしれない

下の質問に書かれてたコードが気になったので、読んでギョッとする方向性に書き換えてみた

teratail.com

コードの書き換え

質問に書かれてたコード:

if(windows.model1_id.present?)
  model = Model1.find(windows.model1_id)
elsif(windows.model2_id.present?)
  model = Model2.find(windows.model2_id)
elsif(windows.model3_id.present?)
  model = Model3.find(windows.model3_id)
end

書き直してみたコード:

#
# モデルとして動作するようテキトーに定義
#
module Model1; def self.find(_id); 1; end; end
module Model2; def self.find(_id); 1; end; end
module Model3; def self.find(_id); 1; end; end

class Windows
  attr_accessor :model1_id, :model2_id, :model3_id
end

# 検証用にインスタンスを用意
windows = Windows.new
windows.model2_id = 12_345

#
# -------------------------------
#

#
# 最終的には Windows インスタンスのプロパティ(名)に対応する、「モデル名で検索」をしたい
# ので、対象モデルの検索と呼び出しを可能にするデータ形式を生成する
#
target_models_range = (1..3)
# 各処理については後述
p target_models_range.map { |i| { "Model#{i}": windows.method("model#{i}_id").call.to_s } }
  .find { |x| x.values.first.size > 0 }
  .map { |k, v|  Kernel.const_get(k).find(v) }
  .first # 1つ前の map の処理で配列になるので素のデータに直す
# => 1 # 実際は検索結果として当該モデルが返ってくる
  • method() でオブジェクトが持つメソッド名を文字列ないしシンボルで可変的に指定できる
  • method().call(args) で指定メソッドの呼び出しをする(引数を含めることもできる)
  • 「最終的に呼び出したいモデル名: (それと対応する)Windows がもつプロパティ値」でハッシュを作る
  • プロパティ呼び出しに対して数値を持つ要素を探す(Rails だと present? が有用)
  • 文字列でモデル名を呼び出して その実態に対して find メソッドの呼び出し(Ruby だと const_get, Rails だと constantize を使う)

Windows モデルに get_model を生やしてそこに上の処理を置けば、見かけ上解決っていうことにできる...!!

これでModel の数が4 とか5 に増えても対応できるね!ヤッタ!!

Links