Today's the day

思考,自知,耐心

Ruby 1.8.6 p230 p238 有内存泄漏 Bug

这两天有些奇怪,服务器上 Rails 进程的内存占用总是不断上涨,只要一天时间,内存就暴满,然后开始啃虚拟内存,网站也变得很慢。

开始以为是 ruby-gettext 的问题,因为 1.90  的 ruby-gettext 确实有这么个内存泄漏的 bug,不过我当初打过了补丁,应该不会出现问题。尝试升级到 1.91,问题依旧……

接着怀疑是代码本身的问题,不过前些日子一直正常,这两天又没有改过网站代码,所以排除。

后来想起来,上个星期把服务器上的 ruby 升级为最新的 1.8.6_p230,难道问题出在这里? Google 了一下,果然:

[Ruby 1.8 - Bug #216] (Open) Memory leaks in 1.8.6p230 and p238

看来 production 下不能使用 p230 和 p238 了,即使是 is-Programmer 这样流量很小的网站,问题都很严重。

降级回 1.8.6_p114,问题解决~

带有默认值的 to_i

Ruby 中的 to_i 方法可以把对象转换成数字,"123".to_i 就返回 123,用来将用户输入的文本转换成数字非常实用。

不过如果对象无法转换成数字,to_i 就返回 0 了,假如我想在用户的输入无意义的情况下,取一个默认值 ( 比如 50 ),而不是 0,就要:

  1. if input.to_i == 0
  2.    return 50
  3. else
  4.    input.to_i
  5. end

这样的代码很让人郁闷……  更好的办法是利用 Ruby 方便的语法特性,定义一个 to_num 方法:

  1. module Kernel
  2.     def to_num(default=0)
  3.         self.to_i == 0 ? default : self.to_i
  4.     end
  5. end

这样,所有的对象都可以应用这个带有默认值的 to_num 方法了~

  1. irb > "123".to_num
  2. => 123
  3. irb > "abc".to_num
  4. => 0
  5. irb > "abc".to_num(50)
  6. => 50

完全可以取代 to_i 方法。

Ruby 数组转换成 Javascript 参数

有的时候数据存储在 Ruby 的数组里,如果你想把这个数组当作 Javascript 的函数的参数,比如:

@names = ['Zhangsan','Lisi']

你想把他作为参数传给 Javascript 代码,达到这样的效果:

a_js_function(['Zhangsan','Lisi']);

需要把 @names 转换成字符串的表达形式,你可能想到迭代数组中的每项,拼出最终的字符串,其实简单的方法是 inspect:

a_js_function(<%= @name.inspect %>);

因为在 Ruby 和 Javascript 中数组的表达是基本一样的,所以这样刚好达到了要求。

不过如果数组中有不期望的数据,比如 nil 或者一个复杂的对象什么的,结果可能就不是你希望的了。那么可以试试 array_or_string_for_javascript:

a_js_function(<%= array_or_string_for_javascript(@names) %>);

不过,array_or_string_for_javascript 会把数组中所有的项目都转换成字符串,所以如果数组是 [100, 200, 300] 就会被转换成 ['100', '200', '300'] 如果你想传入数值的话就用前面的 inspect 了。

另外还有一个 options_for_javascript,把 Ruby 的 Hash 转成 Javascript 的格式:

<%= options_for_javascript(:a => 1, :b => 2, :c => 3) %>
=>  {a:1, b:2, c:3}

Rails 中的 Helper 也用这俩函数生成 Javascript 的参数。

inspect 只适合偷懒用,毕竟 inspect 的格式并不保证不会变,也许 Ruby 下一般换个更漂亮的方式显示 inspect 结果,这招就不灵了。

被 Ruby 耍了一下

Fixnum 有个实例方法 [ ],返回数字二进制表示的第 n 位,比如:

  1. > 8[3]
  2.  1

  1. > 10.downto(0){|n| print 255[n]}
  2. 00011111111

如果传入个符号会怎么样呢? 本以为会出错,发现运行正常,返回 0:

  1. > 5[:asdasdasd]
  2. 0

果不其然,[ ] 对传入的符号做了隐式转换,符号的 to_int 方法返回一个唯一代表本身的整数,一般这个整数很大,也就是返回数字的很高位,自然是 0。

这样的话,像 val[:something] 这样的形式,有可能 val 是个 hash,拿符号当作键,也有可能像上面那样 val 只是个数字。

前两天写个东西,类似下面的代码:

  1. @user.data ||= {:a => 1, :b => 2, :xxx => xxx ....... }
  2. ....
  3. val = @user.data[:someting]

运行发现,不管我怎么取,val 的值都是 0 ……百思不得其解

后来发现 users 表的 data 这项,不小心在数据库中指定默认值为 0 了,所以 @user.data 就不是 nil,根本没有被赋值为后面的 hash,val 就是 0[:something],怪不得取什么都是 0。

符号可以响应 to_int,字符串无法响应 to_int,所以传入个字符串就报错了,不过数字的 [ ] 方法传入非字符的变量有啥意义呢?检测参数不是 Fixnum 就 raise,这样就好了……


Update: 值得庆幸的是,最新的 Ruby 1.9.0 中已经拿掉了 Symbol 的 to_int 方法~

RUBY_VERSION                                       # => "1.9.0"
:foo.to_int                                       
# ~> -:2: undefined method `to_int' for :foo:Symbol (NoMethodError)

Ruby中的数组过滤

如何在ruby中用简洁的方法过滤数组中的元素

Ruby中的字符串与符号

简单介绍Ruby中的字符串与符号两种变量类型的区别。

Ruby中 respond_to? 和 send 的用法

介绍了下 Ruby 中 respond_to? 和 send 两个方法的应用。

Ruby 特殊的分隔输入

ruby中有很多特殊的分隔输入方法,提供了另一种方式来输入字符串、数组、正则表达式和shell命令,在某些情况下,比使用常规的方法要方便,而且语义更加一目了然。