Moduleをincludeした先のコンテキストでメソッドを実行したい
def validate_length(def_attr_length) attr_symbol = get_attr_symbol_from_attr_length(def_attr_length) if eval(def_attr_length) < (send(attr_symbol) || "").jlength errors.add(attr_symbol,"が文字数制限を超えています。") return false end return true end
この場合、def_attr_length(実際には呼び元の定数をあらわす文字列がはいる)が
Moduleのコンテキストで評価されてしまい、こんな定数はこのモジュールにないよといわれる。
ここで、eval(def_attr_length) => self.send(***)と変えると、インスタンスメソッドで
ないのでない。self.class.sendにしても、結局定数はメソッドでないので、この形では
呼べない
この場合、2つの方法があった。
1つめは、bindingを使う。evalは第2引数にコンテキストを渡すことができる。
そのコンテキストを得るメソッドがbinding。なので、呼び元のbindingの結果をここのeval
の引数に渡せばいい。こんな感じになる。
def validate_length(def_attr_length, bind) attr_symbol = get_attr_symbol_from_attr_length(def_attr_length) if eval(def_attr_length, bind) < (send(attr_symbol) || "").jlength errors.add(attr_symbol,"が文字数制限を超えています。") return false end return true end
もう一つは、eval_classメソッドを使う。この場合は、これのレシーバのコンテキスト
を使うので次のようにする。
eval(def_attr_length) => self.class.class_eval(***)
selfはinclude先のクラスが入るので、これで、include先のクラスの定数を持ってこれる。
わかりやすくすればこうか。
def validate_length(def_attr_length) attr_symbol = get_attr_symbol_from_attr_length(def_attr_length) if self.class.class_eval(def_attr_length) < (self.send(attr_symbol) || "").jlength self.errors.add(attr_symbol,"が文字数制限を超えています。") return false end return true end
http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-list/43286
これによると、selfは取り込み先のクラスを参照できないように書いてあるが、
実際はいけるよ。