2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Rubyのevalを使ってみた

Posted at

環境

Ruby 2.6.6

発端

開発中に既存コードでこのような感じのCASE文に出会った。

# 定数(商品タイプ)
FOOD = 'FOOD'
DRINK = 'DRINK'
OTHER = 'OTHER'

# 定数(商品タイプラベル)
FOOD_LABEL = '食べ物'
DRINK_LABEL = '飲み物'
OTHER_LABEL = 'その他'

def get_label(shouhin_type)
	case shouhin_type
	when FOOD
	  FOOD_LABEL
	when DRINK
	  DRINK_LABEL
	when OTHER
	  OTHER_LABEL
	else
	  raise
	end
end

要はそれぞれの商品タイプに対応したラベルを取得するために、「商品タイプ名」+「_LABEL」という名前の定数を返したいというメソッド。この状態だと、商品タイプが増えるたびにCASEの分岐をメンテしなければならないし、ただ「_LABEL」をつけるだけなのに何とも回りくどさを感じた。そこでRubyには動的にコードを生成して実行する機能があったなと思い検索。

Ruby 3.1 リファレンスマニュアル module function Kernel.#eval を発見

eval

a = nil
b = "ああああ"
eval("a = '#{b}'")
# 実行される → a = ああああ
p a #=> "ああああ"

・引数で渡された文字列をRubyとして実行

・文字列なので変数展開できる

これを使えばできそうなので早速書き換えてみる。

def hoge(shouhin_type)
	eval("label = #{shouhin_type}_LABEL")
  p label
end

hoge('FOOD')
undefined local variable or method `label' for main:Object (NameError)

あれ、labelなんて変数名はない…?

どうやらeval内で定義した変数は、スコープの関係で外からは呼び出せないみたい。

def hoge(shouhin_type)
	label = nil
	eval("label = #{shouhin_type}_LABEL")
	p label
end

hoge('FOOD')
#=> '食べ物'

修正して無事に動作するようになった。

結果

# CASE文の場合
def get_label(shouhin_type)
	case shouhin_type
	when FOOD
	  FOOD_LABEL
	when DRINK
	  DRINK_LABEL
	when OTHER
	  OTHER_LABEL
	else
	  raise
	end
end

# evalの場合
def get_label(shouhin_type)
	label = nil
	eval("label = #{shouhin_type}_LABEL")
	label
end

結果、CASE文なら12+α行だったところをevalを使うことで4行にでき、

今後、商品タイプが変更されてもメンテ不要にすることができた。

2
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?