Thursday, August 19, 2010

Back to Basics

Enough is enough, i seriously don’t think i’m built to use any online blogging engines. Simply because i’m built to prefer typing in my terminal, using vim, and prefer playing with terminal-interfaced stuff, instead of so called idiot-proof online tools … i guess i’m probably more idiot than idiots.

Moreover, blogspot is blocked by the great wall of china (at least this is what i’ve encountered in shanghai), and though i can continue to access it via some customized proxy settings, it is terribly slow, slow & slow. In fact, i find it amazing that i am able to bear with it for so long …

Anyway, enough is enough, here i am, back to basics, typing my blog in vim, on the engine side, i’m using the git-based blogging engine shinmun, and deploying it at heroku

Ha, it cannot be any easier than what i have now … what a wonderful world !!

(i'm gradually do the migration to the newer blog, as and when i find time)

Friday, August 13, 2010

Benchmarking any rake task

While writing serializable_proc, i wanted to find out how the RubyParser-based implementation fair against ParseTree-based one, here's a rake task i've written to support the following use cases:

# yield benchmark results by running :spec task x5 (default)
$ rake benchmark[spec]

And:

# yield benchmark results by running :spec task x2
$ rake benchmark[spec,2]

And here's the code to acheive the above:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# Benchmarking
task :benchmark, :task, :times do |t, args|
times, task = (args.times || 5).to_i.method(:times), args.task
title = " ~ Benchmark Results for Task :#{task} ~ "
results = [%w{nth}, %w{user}, %w{system}, %w{total}, %w{real}]

# Running benchmarking & collecting results
require 'benchmark'
times.call do |i|
result = Benchmark.measure{ Rake::Task[task].execute }.to_s
user, system, total, real =
result.match(/^\s*(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+\(\s*(\d+\.\d+)\)$/)[1..-1]
["##{i.succ}", user, system, total, real].each_with_index{|val, j| results[j] << val }
end

# Formatting benchmarking results
formatted_results = results.map do |rs|
width = rs.map(&:to_s).map(&:size).max
rs.map{|r| ' ' + r.ljust(width, ' ') }
end.transpose.map{|row| row.join }

# Showdown .. printout
line = '=' * ([title.size, formatted_results.map(&:size).max].max + 2)
puts [line, title, formatted_results.join("\n"), line].join("\n\n")

end

And here's the output i get for the x2 run:

...
(blah blah, the output of running :spec task x2)
...
===============================================

~ Benchmark Results for Task :spec ~

nth user system total real
#1 0.000000 0.000000 1.030000 1.052567
#2 0.000000 0.000000 1.020000 1.040352

===============================================

Friday, August 6, 2010

Reliably getting a formatted time string in ur desired lang

Today, i'm spent quite abit of time playing with the very simple Time#strftime:
1
2
puts Time.now.strftime("%B %d, %Y %H:%M")
# >> August 06, 2010 17:17

What i want to acheive is to reliably get the chinese version:

八月 06, 2010 17:24

I've tried playing with the environment variable $LANG:

$ export LANG=zh_CN.utf8
$ locale
LANG=zh_CN.utf8
LC_CTYPE="zh_CN.utf8"
LC_NUMERIC="zh_CN.utf8"
LC_TIME="zh_CN.utf8"
LC_COLLATE="zh_CN.utf8"
LC_MONETARY="zh_CN.utf8"
LC_MESSAGES="zh_CN.utf8"
LC_PAPER="zh_CN.utf8"
LC_NAME="zh_CN.utf8"
LC_ADDRESS="zh_CN.utf8"
LC_TELEPHONE="zh_CN.utf8"
LC_MEASUREMENT="zh_CN.utf8"
LC_IDENTIFICATION="zh_CN.utf8"
LC_ALL=

Seems good, but nope, Time#strftime still gives me the same english output. I've trying recompiling a different ruby using rvm with the exported $LANG ... nope, doesn't work either. It seems that not matter how $LANG is set (thus affecting LC_TIME), ruby just doesn't care abt it. Yet, i swear that on flyhzm's machine, he has been getting the chinese version, no matter how hard he has tried to get the english version.

Battled, beaten & worn out, i've concluded that the most reliable way (and definitely what most people do), is to use the i18n. Given i already have this /path/to/i18n.yml:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
en:
date:
month_names: [~, January, February, March, April, May, June, July, August, September, October, November, December]

time:
formats:
default: "%B %d, %Y %H:%M"

zh:
date:
month_names: [~, 一月, 二月, 三月, 四月, 五月, 六月, 七月, 八月, 九月, 十月, 十一月, 十二月]

time:
formats:
default: "%B %d, %Y %H:%M"

The following works for mri 1.9.1:
1
2
3
4
5
6
7
8
9
10
11
require 'rubygems'
require 'i18n'

I18n.load_path << '/tmp/i18n.yml'
p I18n.available_locales # >> [:en, :zh]

I18n.locale = :en
p I18n.localize(Time.now) # >> "August 06, 2010 17:48"

I18n.locale = :zh
p I18n.localize(Time.now) # >> "八月 06, 2010 17:48"

Without modication, i got the following unfriendly output for the chinese port under mri 1.8.7, jruby & ree-1.8.7:

\345\205\253\346\234\210 06, 2010 20:27

After much googling, stumbled across this post abt $KCODE. Here's the final modified code for the 1.8.7 equivalents:
1
2
3
4
5
6
7
8
9
10
require 'rubygems'
require 'i18n'

$KCODE = 'UTF-8' # Hey, this matters !!

I18n.locale = :en
p I18n.localize(Time.now) # >> "August 06, 2010 17:48"

I18n.locale = :zh
p I18n.localize(Time.now) # >> "八月 06, 2010 17:48"

For people doing rails, here are some useful resources:
* http://guides.rubyonrails.org/i18n.html
* http://github.com/svenfuchs/rails-i18n

Wednesday, August 4, 2010

Bitten by Array#*

Have u tried Array#* ? Its a pretty cool way to return an array, built by concatenating the original array by n times:
1
2
3
4
5
a = [1] * 2
p a # >> [1, 1]

a = %w{a} * 2
p a # >> ["a", "a"]

However, u should be aware that all elements of the new array actually points to the same instance, as proven by their object_id:
1
2
3
4
5
x = 'a'
p x.object_id # >> 70212363949780

a = [x] * 2
p a.map(&:object_id) # >> [70212363949780, 70212363949780]

This means that:
1
2
3
4
5
6
7
8
9
10
11
12
13
# Reassigning one element does not affect another
a = %w{a} * 2
p a.map(&:object_id) # >> [70058328018360, 70058328018360]
a[0] = a[0].sub('a','b')
p a # >> ["b", "a"]
p a.map(&:object_id) # >> [69915340242120, 69915340242360]

# In-place manipulating one element affects another
a = %w{a} * 2
p a.map(&:object_id) # >> [70058328018360, 70058328018360]
a[0].sub!('a','b')
p a # >> ["b", "b"]
p a.map(&:object_id) # >> [70058328018360, 70058328018360]

Labels