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]

Wednesday, July 28, 2010

Lazy evaluating with Enumerable#any?

Most of us know this:
1
2
3
4
5
6
7
8
class Awesome
def self.m1 ; true ; end
def self.m2 ; raise Exception ; end
end

# Case 1:
Awesome.m1 or Awesome.m2
# >> no error thrown

For some of us who likes Enumerable#any?, this won't work:
1
2
3
# Case 2:
[Awesome.m1, Awesome.m2].any?
# >> error thrown

That is because :m1 & :m2 are already evaluated before calling Enumerable#any?, to have lazy evaluation, we can do this:
1
2
3
# Case 3:
[:m1, :m2].any?{|m| Awesome.send(m) }
# >> no error thrown

For the perversive ones:
1
2
3
# Case 4:
Awesome.instance_exec { [:m1, :m2].any?{|m| send(m) } }
# >> no error thrown

Thursday, July 22, 2010

Everyone should start writing macros

Did some cleaning & catching up of specs for railsbestpractices.com yesterday, added quite a number of spec macros to dry up stuff, as well as cutting down the complexity within the spec files. Personally, i really really & really like macros, because it is fun to write & easy to write, it has many beneficial side effects:

#1. Since it is so simple to use macros, everyone in the team is more willing to participate in spec writing. Personal experience has proven that if specs/tests are hard to write, some developers tend to skip it.

#2. Since it is so easy to read, maintenance becomes much easier

#3. Developers are smart people (and should be paid well, but that's another story), by replacing repeated copy & paste & minor edit-here-and-there with macros-writing-and-usage, they feel smarter, a huge morale boast ~> happier team, & when people are happy, they tend to show more love towards the project, take ownership ~> project in a better state.

Btw, macro writing is no rocket-science, let's get started:

#1. Macro for 3rd party declarative
1
2
3
4
class User < ActiveRecord::Base
acts_as_authentic
# (blah blah)
end

Ok, we all know that authlogic is well tested, so there is really no point in writing specs for it. Yet, how do we know the model is pulling in authlogic support ?? Here's what i've done:
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
27
28
29
module RailsBestPractices
module Macros

def self.included(base)
base.extend(ClassMethods)
end

module ClassMethods

def should_act_as_authentic
# Get most basic modules included by ActiveRecord::Base
basic_included_modules = Class.new(ActiveRecord::Base).included_modules

# Grab the model class
model = description.split('::').inject(Object){|klass,const| klass.const_get(const) }

# Get the extra modules included by the model
model_included_modules = model.included_modules
extra_modules = (model_included_modules - basic_included_modules).map(&:to_s)

# As long as we have any extra module matching the regexp, we can conclude that
# :acts_as_authentic has been declared for the model
extra_modules.any?{|mod| mod =~ /Authlogic::ActsAsAuthentic::/ }.should be_true
end

end

end
end

The usage in the spec file is:
1
2
3
4
5
describe User do
include RailsBestPractices::Macros
should_act_as_authentic
# (blah blah)
end

Of course, i can probably remove the include statement altogether & do the auto-including elsewhere, but taking this route, other developers may throw WTF-is-should_act_as_authentic error ~> BAD !!

#2. Macro for project-specific declarative
1
2
3
4
class Post < ActiveRecord::Base
include Markdownable
# (blah blah)
end

I wanna make sure the support from the home-baked Markdownable is pulled in. Since it is home-baked, just making sure the module is mixed in is not good enough, i need to ensure the functionality is there as well. Thus (nested within the above mentioned RailsBestPractices::Macros::ClassMethods module):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def should_be_markdownable

# Determine which factory to call
factory_id = description.split('::').inject(Object){|klass, const| klass.const_get(const) }.
to_s.tableize.singularize.to_sym

# Generate an example group
describe "being markdownable" do

it "should generate simple markdown html" do
raw = "subject\n=======\ntitle\n-----"
formatted = "<h1>subject</h1>\n\n<h2>title</h2>\n"
Factory(factory_id, :body => raw).formatted_html.should == formatted
end

it "should generate markdown html with <pre><code>" do
raw = "subject\n=======\ntitle\n-----\n def test\n puts 'test'\n end"
formatted = "<h1>subject</h1>\n\n<h2>title</h2>\n\n<pre><code>def test\n puts 'test'\nend\n</code></pre>\n"
Factory(factory_id, :body => raw).formatted_html.should == formatted
end

end
end

And the usage:
1
2
3
4
5
describe Post do
include RailsBestPractices::Macros
should_be_markdownable
# (blah blah)
end

#3. Macro for any other vanilla functionality
1
2
3
4
5
class Implementation < ActiveRecord::Base
def belongs_to?(user)
user && user_id == user.id
end
end

The macro definition:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def should_have_user_ownership
factory_id = description.split('::').inject(Object){|klass,const| klass.const_get(const) }.
to_s.tableize.singularize.to_sym

describe 'having user ownership' do

it 'should belong to someone if he is the owner of it' do
someone = Factory(:user)
Factory(factory_id, :user => someone).belongs_to?(someone).should be_true
end

it 'should not belong to someone if he is not the owner of it' do
someone = Factory(:user)
Factory(factory_id).belongs_to?(someone).should be_false
end

end
end

Finally, the usage:
1
2
3
4
describe Implementation do
include RailsBestPractices::Macros
should_have_user_ownership
end

Of course, it doesn't make sense to define macro for every functionality available. The general thumb of rule is that when similar code appears more than once, u can consider defining a macro for it. However, if similar-code is to appear more than once, u will probably start by extracting the code & placing it inside a module (eg. Markdownable), so this probably falls under #2. Macro for project-specific declarative.

Last but not least, the above macro definition can probably benefit from some refactoring, but i guess i'll leave this exercise to u :]

Wednesday, July 21, 2010

Relatively unknown Object#instance_exec

Do u happen to know Object#instance_eval's brother, Object#instance_exec ?? Here's a uninteresting way of coding wo Object#instance_exec:
1
2
3
4
5
6
7
8
9
10
11
12
Someone = Struct.new(:name, :password)

greet = lambda do |someone|
puts "Hey %s, no worries, i won't tell anyone ur password is '%s' !!" % [
someone.name, someone.password
]
end

greet[Someone.new('Peter', 'aabbcc')]
# >> Hey Peter, no worries, i won't tell anyone ur password is 'aabbcc' !!
greet[Someone.new('Jane', 'bbccdd')]
# >> Hey Jane, no worries, i won't tell anyone ur password is 'bbccdd' !!

And here's a more interesting way to do it, w Object#instance_exec:
1
2
3
4
5
6
7
8
9
10
11
12
Someone = Struct.new(:name, :password)

greet = lambda do
puts "Hey %s, no worries, i won't tell anyone ur password is '%s' !!" % [
name, password
]
end

Someone.new('Peter', 'aabbcc').instance_exec(&greet)
# >> Hey Peter, no worries, i won't tell anyone ur password is 'aabbcc' !!
Someone.new('Jane', 'bbccdd').instance_exec(&greet)
# >> Hey Jane, no worries, i won't tell anyone ur password is 'bbccdd' !!

Yet another way to do it can be:
1
2
3
4
5
6
7
8
9
10
Someone = Struct.new(:name, :password)
greet = lambda{|msg| puts msg % [name, password] }

Someone.new('Peter', 'aabbcc').
instance_exec("Hey %s, no worries, i won't tell anyone ur password is '%s' !!", &greet)
# >> Hey Peter, no worries, i won't tell anyone ur password is 'aabbcc' !!

Someone.new('Jane', 'bbccdd').
instance_exec("Hey %s, i'm glad that u entrust me with ur password '%s' !!", &greet)
# >> Hey Jane, i'm glad that u entrust me with ur password 'bbccdd' !!

Enjoy !!

Labels