分享 在 module 自定义 method_missing 方法

flingjie · May 10, 2014 · Last by huacnlee replied at May 12, 2014 · 2909 hits

今天在 github 上看到别人的一段代码,觉得挺有意思,大致结构如下:

module M
  module ClassMethods
    def method_missing(name, *args)
      puts 'Hello world!'
    end
  end

  class << self
    include ClassMethods

    def included(klass)
      klass.extend(ClassMethods)  if klass.is_a? Module
    end
  end
end

使用例子

class C
  include M
end

C.nomethod()  #=> "Hello world!"

这样其他的类或模块只要 include 这个模块,就会调用这个自定义的 method_missing 方法。

这么做有什么意义吗?

2.1.2 :001 > def self.method_missing(*args); args.join " " end
 => :method_missing 
2.1.2 :002 > puts oh my God!
oh my God!
 => nil 

😄

主要是 Ruby 的钩子方法,让父类(或 module)对子类(或 include module 的对象)的控制能力异常的强大。

嗯,就是元编程里讲的“幽灵方法”.

rails3 里面的 find_by_xxx 就是这种思路,对于常用的指令最好 define_method,据说幽灵方法效能很差,对了,还有要记得处理 respond_to?

#1 楼 @karmue 很多时候 method_missing 方法是非常有用的,比如:

https://github.com/ruby-china/ruby-china/blob/7becefa06ee24bbb3c3969dd26e831789b762e80/app/models/site_config.rb

# coding: utf-8
# 在数据库中的配置信息
# 这里有存放首页,Wiki 等页面 HTML
# 使用方法
# SiteConfig.foo
# SiteConfig.foo = "asdkglaksdg"
class SiteConfig
  include Mongoid::Document

  field :key
  field :value

  index :key => 1

  validates_presence_of :key
  validates_uniqueness_of :key

  def self.method_missing(method, *args)
    method_name = method.to_s
    super(method, *args)
  rescue NoMethodError
    if method_name =~ /=$/
      var_name = method_name.gsub('=', '')
      value = args.first.to_s
      # save
      if item = find_by_key(var_name)
        item.update_attribute(:value, value)
      else
        SiteConfig.create(:key => var_name, :value => value)
      end
    else
      Rails.cache.fetch("site_config:#{method}") do
        if item = find_by_key(method)
          item.value
        else
          nil
        end
      end
    end
  end

  after_save :update_cache
  def update_cache
    Rails.cache.write("site_config:#{self.key}", self.value)
  end

  def self.find_by_key(key)
    where(:key => key.to_s).first
  end

  def self.save_default(key, value)
    create(:key => key, :value => value.to_s) unless find_by_key(key)
  end
end

#6 楼 @huacnlee 我从来没用过 meteprogramming 和 method_missing 请问这样的目的是写出 SitConfig.foo = 'value' 这种方法吗?

You need to Sign in before reply, if you don't have an account, please Sign up first.