使用undef_method, 遇到的一个问题,想不明白rails是如何处理的。最后在rubyChina上发帖求助,没成想得到了很耐心的解答,贴上,以期可飨同惑者。
正文
先看一段调用undef_method的代码:
module MyModule |
执行,很明显,会报错,抛出NoMethodError
undefined method `my_value' for #<MySubClass:0x007fbdc4065818 @my_value=20> |
没毛病,正常,ruby 2.5中对undef_method 的定义就是这样的,prevents the current class from responding to calls to the named methods。
But,你会发现rails中有一个神奇的地方,以一个简单的user model为例:
user model含有字段name, 同时include了一个module UserAddon,使用undef_method来禁止调用引入的name方法,按照undef_method的定义, 此时User的对象调用name时,应该会报错,但是进入终端后,发现没有,反而返回了正确的结果。
代码如下:
## app/models/user.rb |
这时,rails c 进入console:
user = User.first |
使用pry的$ ,找不到user.name的定义,但是执行user.name却返回了正确的结果,此时一定调用了method_missing。但是,如何查看rails是如何实现的呢?
ruby元编程中在有关 rails的属性方法中曾经提到,当第一次访问一个属性时,这个属性是一个幽灵方法,ActiveRecord::Base # method_missing() 会把它转换成一个真实的方法,同时创建出读,写,查询方法,比如上面的name,会创建name, name=, name?,这样下次就可以直接调用。
但是,这里很显然,user.methods.include?(:name)返回了false,这里并没有创建name这个方法,那么rails是如何让user.name 返回了”admin”的呢?
这里附上来自IChou 大神的解答:
使用undef_method :name后,调用user.name, 它会落入下面的 method_missing 方法中:
# lib/active_model/attribute_methods.rb:425 |
「也就是最后调用@attributes.fetch_value(attr_name.to_s) ,user.name的返回了正确的结果,而不是如预期所想的那样抛出NoMethodError。」