Today's the day

向软件大牛炫耀我会焊单片机,向硬件大牛炫耀我会写 Rails,向软硬件大牛炫耀我生物,向软硬件生物大牛炫耀我会折腾期货 -_-bbb

rake rails:update

今天才发现有这个 rake 任务……

rake rails:update 自动帮你升级项目中的配置文件、脚本、还有默认的 javascript 文件,如果你的项目是在旧版的 rails 之上构建的,只要一个命令,就跟在新版上构建的无异啦~   原来我还傻乎乎的建个空项目然后把新的文件拷过来……

你还可以升级更详细的部分:

localhost chito # rake -D rails:update
(in /root/work/chito)
rake rails:update
    Update both configs, scripts and public/javascripts from Rails

rake rails:update:configs
    Update config/boot.rb from your current rails install

rake rails:update:javascripts
    Update your javascripts from your current rails install

rake rails:update:scripts
    Add new scripts to the application script/ directory

不过因为用了新版 prototype 和 scriptaculus 的缘故,新版 rails 的默认 javascript 比旧版的尺寸大了很多,如果旧版用的好好的,就不用升级了,比如我……

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 结果,这招就不灵了。

迂回的实现了覆盖 helper 中的函数

终于搞定了 Chito 的相对时间插件,早就想实现这个插件了,不过一直没有想出好的实现方法,目前这方法虽然有点迂回,不过总体效果还是不错,也给以后的插件开发带来了很大的方便~

继续阅读

中文相对时间

不知道啥时候开始,流行用相对时间来表示当前的日期,这种方法确实是不错,同 “2007 年 1 月 24 日” 相比,“1 天前” 这种表示确实看起来舒服多了,毕竟人们在思考时间的时候,大多数也是以当前的时间作为基点。

如果是个是个人气很旺的论坛之类,还可以看到某回复发表于 “n 分钟前” 这样的效果,顿时感觉人气和交互度高了很多。

Rails已经内置了对相对时间的支持,time_ago_in_words 这个 helper 就是干这个用的,比如要显示文章发表的相对时间:

post at <%= time_ago_in_words(@article.created_at) %> ago

这样就会显示为诸如 “post at 5 days ago” 这样的效果了~

只不过是英文的,要显示中文的就要翻译一下了,开始我想,把 time_ago_in_words 返回结果中的 days 替换成“天”、month 替换成“月”之类,后来去看了下源码,发现源码很短,重新写一个就行了。

time_ago_in_words 实际上是调用的 distance_of_time_in_words 这个函数,并且以 Time.now 作为第二个参数而已:

def time_ago_in_words(from_time, include_seconds = false)
  distance_of_time_in_words(from_time, Time.now, include_seconds)
end

这样的话,我们把 distance_of_time_in_words 中的英文改成中文就 ok 了:

  1. def distance_of_time_in_words(from_time, to_time = 0, include_seconds = false)
  2.     from_time = from_time.to_time if from_time.respond_to?(:to_time)
  3.     to_time = to_time.to_time if to_time.respond_to?(:to_time)
  4.     distance_in_minutes = (((to_time - from_time).abs)/60).round
  5.     distance_in_seconds = ((to_time - from_time).abs).round
  6.  
  7.     case distance_in_minutes
  8.         when 0..1
  9.             return (distance_in_minutes == 0) ? '不到 1 分钟' : '1 分钟' unless include_seconds
  10.         case distance_in_seconds
  11.             when 0..4   then '不到 5 秒'
  12.             when 5..9   then '不到 10 秒'
  13.             when 10..19 then '不到 20 秒'
  14.             when 20..39 then '半分钟'
  15.             when 40..59 then '不到 1 分钟'
  16.             else             '1 分钟'
  17.         end
  18.  
  19.         when 2..44           then "#{distance_in_minutes} 分钟"
  20.         when 45..89          then '大约 1 小时'
  21.         when 90..1439        then "大约 #{(distance_in_minutes.to_f / 60.0).round} 小时"
  22.         when 1440..2879      then '1 天'
  23.         when 2880..43199     then "#{(distance_in_minutes / 1440).round} 天"
  24.         when 43200..86399    then '大约 1 个月'
  25.         when 86400..525599   then "#{(distance_in_minutes / 43200).round} 个月"
  26.         when 525600..1051199 then '大约 1 年'
  27.     else                      "#{(distance_in_minutes / 525600).round} 年"
  28.    end
  29. end

把上面的代码加到 application_helper.rb 中去,就可以看到中文的相对时间了~ 当然你也可以去掉 “大约” 这个繁琐的词,或者随便改成自己喜欢的中文表达方式~

过两天就做 Chito 的相对时间插件~ 

被 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)