Qiita初投稿です。よろしくお願いします。
はじめに
Rubyのsendメソッドを仕事中に使用する機会があり、便利!と思ったものの、慎重に使う必要があることがわかったのでまとめました。
参考文献
sendメソッドとは
レシーバーが持っているメソッドを、文字列(またはシンボル)で指定して呼び出します。
つまり、メソッドを動的に呼び出すことができます。
どんなときに使えるか
以下のように、caseの値によって呼び出すメソッドが異なる場合、caseが増えれば増えるほどコードが膨らんでいき、可読性が下がります。
# 値によって実行するメソッドを分岐させる
class Shape
def create_shape(shape_type)
case shape_type
when "circle" then
create_shape_circle
when "square" then
create_shape_square
when "triangle" then
create_shape_triangle
end
end
def create_shape_circle
...
end
def create_shape_square
...
end
def create_shape_triangle
...
end
end
shape = Shape.new
shape.create_shape("circle")
上記のコードをsendメソッドを使って書き直すとこうなります。
class Shape
attr_accessor :shape_type
def initialize(shape_type)
@shape_type = shape_type
end
def create_shape_circle
...
end
def create_shape_square
...
end
def create_shape_triangle
...
end
end
shape = Shape.new("circle")
shape.send("create_shape_#{shape.shape_type}")
メソッドを動的に呼び出せるため、caseでの分岐が不要になりました!
ただし、sendに渡す値によっては、sendを使用せずにメソッドを固定的にコーディングした方が良い場合があります。
sendメソッドに渡してはいけない値
sendメソッドは動的にプログラムの動作を変えられる反面、使い方を誤ると深刻なセキュリティ問題を引き起こす恐れがあります。
特に、ユーザーからの入力をそのままsendに引き渡すと、意図せぬ範囲のデータやプライベートメソッドにまで影響が及ぶ可能性があります。
例)悪意のあるユーザーが「exit」と入力した場合、それがsendに渡されるとアプリケーションが終了してしまう。
※exitはKernelモジュールに定義されているメソッド。KernelモジュールはBasicObject以外のすべてのクラスで使用可能。
そのため、外部から入力された値によって呼び出すメソッドを分岐させる場合は、最初のcase文の例のように、ユーザーの入力を切り分けて個々のメソッドの呼び出しを固定的にコーディングした方が安全です。
まとめ
sendメソッドは動的なメソッド呼び出しができて便利だが、セキュリティ面を考慮し、外部の入力に依存する値は渡さないようにする。