最近在帮我们最大的一个Rails项目(多大?想象下上万行Ruby代码的项目吧)做Rails升级,从2.3.2升级到2.3.17(别笑,这种项目升3是没有指望的),升级过程总体上顺利。升级完后跑Test Case,有一些小错误发生。一个小错误似乎是由于Rails 2.3.2存在Bug使得明明应该发生错误的Test Case竟然能通过,而在2.3.17的时候已经修复,导致这个Test Case理所当然的发生了错误。还有另外一个错误是在测试中向response header中写入了被标记为secure和httponly的cookie,但是测试发现这些cookie不存在的错误。起先我还不以为然,以为是redirect后造成cookie在测试时不能正常读取,但是后来总觉得有点奇怪,毕竟这个Test Case在2.3.2的时候是通过的,还有就是调试发现,在执行好redirect_to方法后,被写入的cookie依然是存在的,只有当请求结束后的测试时cookie才会消失。太诡异了!于是仔细跟踪代码流,并对比了下2.3.2和2.3.17的区别,发现了在2.3.17的actionpack/lib/action_controller/cookies.rb中的CookieJar类(这个是存储Cookie的最核心的数据结构了,继承自Hash)的[]=方法中,在向response header写入cookie前,调用了下一个叫做write_cookie?的方法,这个方法在2.3.2中是不存在的。代码如下:

# Sets the cookie named +name+. The second argument may be the very cookie
# value, or a hash of options as documented above.
def []=(key, options)
  if options.is_a?(Hash)
    options.symbolize_keys!
  else
    options = { :value => options }
  end

  options[:path] = "/" unless options.has_key?(:path)
  super(key.to_s, options[:value])
  @controller.response.set_cookie(key, options) if write_cookie?(options)
end

从代码中可以看到,write_cookie?方法并不阻止super的调用,但是阻止了set_cookie的调用。而通过调试可知,在测试时这个write_cookie?这个方法的的确确返回了false,造成了cookie在测试时没有正常写入!

而这个问题的始作俑者,write_cookie?的代码是这样的:

def write_cookie?(cookie)
  @secure || !cookie[:secure] || defined?(Rails.env) && Rails.env.development?
  # 其中@secure来自initialize时controller.request.ssl?的结果
end

在测试过程中,三个条件均为false,导致最终结果是false。

由于这个站点的安全级别较高,在生产环境中一定是用HTTPS协议运行的,所以在测试的时候也模拟HTTPS的环境,在cookie中写入了secure标志并在测试中有相应的assert。在2.3.2中,由于没有这个方法的存在,测试没有任何问题。但是在2.3.17中,明确要求了,要么cookie是被设置成secure的,要么必须是https请求,要么当前是development模式。但是在我们的测试中,我们并没有让所有测试都用https来做,这也是不合理的嘛。因此我认为,在这段代码中,Rails犯了三个错误,第一,应该把Rails.env.test?也加入或条件,即如果是测试环境,也总会让write_cookie?返回true。第二,如果write_cookie?返回false,super方法也不应当调用,不该造成cookie已经被写入的假象。第三,后台应该有安全警告,以说明本次cookie写入失败的原因,否则对开发者而言实在是莫名其妙。

本来想给Rails提交个patch的,但是得知Rails 2.3除了Fix严重安全漏洞以外不再接受任何Patch了(链接),因此还是写成Blog让大家看到吧。我最后在项目中加了这个一个Monkey Patch:

# The write_cookie? always returns false in our test cases because 
# we set secure in cookie and our request in test env is http 
# rather than https, so Rails will refuse to write value in cookie. 
# This hack will resolve this problem. 
class ActionController::CookieJar < Hash
  alias_method :__origin_write_cookie?, :write_cookie?
  def write_cookie?(cookie)
    __origin_write_cookie?(cookie) || defined?(Rails.env) && Rails.env.test?
  end
end

解决掉了这个问题。至于另外两点就懒得用Monkey Patch做了,还是算了吧。

就这样了。由此可见,Rails程序员熟悉Rails本身的代码是很重要的吧,仅仅局限在使用Rails框架上实在是太肤浅了,根本对不起四年大学本科的学习嘛,何况Rails这种项目由于是开源的本来就问问多多嘛,不熟悉的话稍微有点什么问题就束手无策了。我们组有些同事就是这样的,拿了个比VIM先进的多的RubyMine,叫他调试下出错的Test Case就各种震惊各种迷茫的,实在是,哎,不说了,说出来比我自己干还累啊。。

在KDE下用GVim,一直有个很怪的问题,就是最大化几乎是无效的,最大化之后可以看到右边和下边都各有一条细缝。有人说可能是GVim规定要整数的行和列,也有人说是GVim和KWin都想管理窗口,然后冲突了。

有一种比较简单的解决方法,右击GVim标题,选择Advanced,选择Fullscreen即可。

设置后效果如图所示:

这种方法效果总体较好,但是却有个很严重的缺陷。ALT+TAB看不到窗口切换的效果,这在窗口切换时是很不方便的。

这是Gvim官方的Wiki中的教程:http://vim.wikia.com/wiki/Automatically_maximizing_gvim_in_KDE

设置界面:

按照这个做法的话,也有个很大的问题,就是如果用GVim打开一个其他窗口,比如查找和替换,那个就会变成这样:

显然对GVim主窗口的设置影响到了对话框。

经过多次研究,后来发现一种办法,可以完美解决这个问题而没有这样那样的副作用。首先,和Wiki中一样,进入KWin设置界面。

选择Detach Window Properties按钮,在GVim主窗口任意位置点击一下。

看到如下界面,选择Use whole window class(specific window)。

保持Window Extra中只有Normal Window一项被选中。

这里还是和Wiki中一样的设置方法,在我电脑上,GVim大小为1280x775。

设置后效果如下,可以看到,已经最大化了。

搜索对话框还是和原来一样大小。

下面说下如何修改和删除已经设置过的KWin的规则,之前以为KWin的规则都保存在~/.kde/share/config/kwinrulesrc中,只要编辑这个文件就可以修改KWin的规则,其实是不对的,因为设置完之后再次启动GVim的话,~/.kde/share/config/kwinrulesrc会回滚到原来的设置,不知道这是什么情况。如果要修改或删除已经设置过的KWin的规则,可以启动Window Rules,启动方法如图:

在这个框口中编辑即可,编辑完后不要忘记点击Apply按钮,否则编辑还是无效的。