Ruby alias_method_chain 的一个悲剧

yakjuly · 2012年11月29日 · 最后由 yakjuly 回复于 2012年11月29日 · 4563 次阅读

众所周知 alias_method_chain 是用来继承方法的

class Apple
  def eat
    puts "eat"
  end

  def eat_with_duck
    puts "eat with duck"
    eat_without_duck
  end
  alias_method_chain :eat, :duck

  def buy
    puts "buy"
  end
end

我们平常打补丁 可以这样写。

Apple.class_eval <<-EOF
  def buy_with_me
     puts "buy with me"
      buy_without_me
  end
  alias_method_chain :buy, :me
EOF

最近改造一个数据库在 SQL Server 上的项目,用 1.9.3-p327, Rails3.2.9.rc3 因为 SQL Server2008 不能自动识别 date 类型还是 datetime。自己想利用 alias_method_chain 方法做自动转换日期类型。

class CustomModel < ActiveRecord::Base
  self.abstract_class = true

  ##此处省略一些代码

  def self.correct_sqlserver_date(*names)
    names.each do |column_name|
      self.class_eval <<-EOF
        pp self
        def #{column_name}_with_format_date
          self.#{column_name}_without_format_date && self.class.correct_date(self.#{column_name}_without_format_date)
        end
        alias_method_chain "#{column_name}", :format_date

        def #{column_name}_with_format_date=(val)
          self.#{column_name}_without_format_date = case val
          when String
            Time.zone.parse(val)
          when Date
            val.to_time_in_current_zone
          else
            val
          end
        end
        alias_method_chain :"#{column_name}=".to_sym, :format_date
      EOF
    end
  end

end

使用方法

class Car < CustomModel
   correct_sqlserver_date "InactivationDate"
end

在 console 里执行的时候,奇迹般的出错了。提示 undefined method 'InactivationDate' forCar' `。Car 对象表里肯定是有 InactivationDate 这个字段的,这是为什么呢?

这个问题查了一天,曾怀疑自己的经验,甚至怀疑 ruby1.9.3-p327 改了 alias_method_chain 用法。

最后发现一个事实:alias_method_chain 原方法名是 active_record 字段的时候,必须是规范的小写字母开头属性。

  1. 如果字段是 inactivation_date 就不会报错
  2. 如果你在 Car 中定义一个 InactivationDate 方法 也不会报错

悲剧的是 SQL Server 的数据库字段 都是大写的,例如 InactivationDate, ExpirationDate. 目前用 super 解决了。希望大家遇到类似情况 能分享下自己调试分析的过程。

ruby2.0 新加的那个东东就是替代 alias_method_chain 的

好坑,收藏备用。。

@hooopo 你是说 prepend? 相当于确定了方法内 super 的顺序。对于 method_missing 来说是个好消息啊。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号