Wednesday, July 16, 2008

เริ่มใช้ grails

ช่วงเดือนที่ผ่านมา ผมได้มีโอกาสสัมผัสกับ Grails อย่างจริงๆจังเป็นครั้งแรก
โดยได้มีโอกาส implement open source project ตัวหนึ่งด้วย Grails

อารมณ์ในช่วงแรก ก็คือ "wow"
สาเหตุก็คือ มันใช้ stack ทุกอย่างที่ผมคุ้นเคย และใช้อยู่แล้ว
ไม่ว่าจะเป็น spring, hibernate
แต่นำมาลด noise ด้วยการทำ DSL บ้าง หรือลด noise ด้วยคุณสมบัติของตัวภาษา groovy เองบ้าง
แถมยังยึดแนว convention over configuration ของ Rails อีก
learning curve ก็เลยถือว่าน้อยมากๆ

พอผ่านอารมณ์ wow มาได้
ก็ได้เวลาขัดอกขัดใจบ้างแล้ว
เริ่มแรกสุด ก็คือ ผมจำชื่อ package ของ java class ที่ใช้บ่อยๆไม่ได้
เดิมผมผลักภาระไปให้ IDE มัน popup หรือ import ให้เรา
แต่พอมาใช้ groovy, เจ้า context assistent ของ groovy ใน eclipse มันทำงานช้าเหลือเกิน

ความขัดใจที่สองก็คือมัน compile ช้า
เวลาเขียน unit testing เพื่อทดสอบหาแนวทางการทำงานของ library ต่างๆ (เช่น lucene, nekohtml)
การ run แต่ละครั้งมันหนึืดเหลือเกิน

แต่สิ่งที่ผมยึดถือก็คือ คนเราปรับตัวได้
อย่าปล่อยให้ความลำบากเล็กๆน้อยๆ (จากความไม่เคยชินของเรา) มาเป็นอุปสรรคในการเรียนรู้

จากข้อ 1 ที่ไหนๆ groovy plugin มันไม่ได้ช่วยอะไรเรา (แถมยังขัดขาอีก)
ก็เลยเปลี่ยนไปใช้ emacs แทน (ซึ่งไม่มี context assistent แน่ๆ)
แล้วก็เปิด javadoc ไว้ข้างๆ เพื่อใช้ค้นหาชื่อ package
แต่ก็พบว่า ถ้าเราเขียนโปรแกรมแบบไม่ระบุ type มันก็จะช่วยลด import statement ไปได้เยอะเหมือนกัน

ส่วนข้อสอง ก็แก้โดย ใช้ groovy console ทดลองเขียนให้เรียบร้อยก่อน
จากนั้นค่อย copy ไปใส่ไว้ใน unit test.

Tuesday, July 15, 2008

kernel stat

เมื่อวานนั่งฟัง Bart Trojanowski พูดเรื่อง "Kernel walkthrough"
ฟังได้ไม่ถึงสิบนาที ภรรยาก็บอกว่า "ปิดเถอะ ได้ยินแล้วนอนไม่หลับ ไปซื้อหูฟังกันไหม?"
หน้าที่ของสามีที่ดีก็คือ งอนแต่พองาม
สบัดหน้าเม้มปากกัดกรามแล้วก็ปิดแต่โดยดี

ถึงได้ฟังนิดหน่อย แต่ก็ได้เห็น stat บางอย่างของ Linux kernel ที่น่าสนใจ
(ตัวเลขจริงๆจำไม่ได้แล้ว แต่วันนี้เจอ blog ที่พูดถึงเรื่องนี้เหมือนกัน อยู่ที่
http://cycle-gap.blogspot.com/2008/07/linux-kernel-development-stats-from.html
ก็เลยลอกมาให้นั่งดู ประหยัดแรง ไม่ต้องไปนั่งถอดเทป
)

จำนวน changes ต่อวัน
  • เพิ่มใหม่ 4300 บรรทัด
  • แก้ไข 1800 บรรทัด
  • ลบทิ้ง 1500 บรรทัด


ใน code จำนวน 9 ล้านบรรทัดของ kernel เป็น core แค่ 5%
ตัวที่มีจำนวนบรรทัดมากสุดคือ driver ล่อไปซะ 55 %

ฟังได้แค่นี้แหล่ะ ไว้ได้หูฟังมาแล้ว ค่อยฟังต่อ
แต่ตัวเลขน่าสนใจมาก (ใครไม่อยากเสียเวลาฟัง ก็ไปแอบดูใน blog ข้างบนก่อนได้เลย)

Tuesday, July 08, 2008

เปิดท้าย



ผมเป็นคนที่ชอบเล่นกับเด็กเล็ก เมื่อก่อนสมัยเรียนหนังสือในมหาวิทยาลัย พอได้เวลาช่วงปิดเทอมเมื่อไร ผมก็จะไปออกค่ายสอนหนังสือเด็กๆชาวเขาที่ภาคเหนือ
หลังจากจบมาก็ไม่ได้มีกิจกรรมข้องแวะกับเด็กๆอีกเลย จนมามีลูกเป็นของตัวเอง, เมื่อลูกเริ่มถึงวัยมีเพื่อน ความสนุกเก่าๆก็หวนกลับมาอีกครั้ง, ทุกวันนี้พอตกตอนเย็น ผมก็จะเปิดประตูและกางเสื่อบนท้ายรถรอเด็กๆในหมู่บ้านแวะเวียนมาเล่นร่วมกัน
(รูปนี้แม่เด็กแอบขึ้นไปถ่ายตอนไหนก็ไม่รู้)

ปัญหาที่พบก็คือ เด็กๆมีคละวัยเหลือเกิน เจ้าเตาะแตะก็อยากเล่นแบบหนึ่ง พวกปรู๊ดปร๊าดก็อยากเล่นอีกแบบหนึ่ง

Tuesday, June 24, 2008

git-rerere

เวลาเราแตก branch ที่เรารู้ว่าเป็นหนังชีวิต (branch ที่มีอายุยาวๆ)
เรามักจะต้องคอย merge ตัว master เข้ามาที่ branch เรา เพื่อที่จะตรวจสอบ conflict ต่างๆที่เกิดขึ้น
ซึ่งมีศัพท์เรียกกันว่า "test merge"

ลองดูรูปประกอบข้างล่าง
ในที่นี่ topic ก็คือ branch (ที่อายุยืน) ของเรา
เครื่องหมาย '*' ก็คือ จุดที่ทั้ง topic และ master แตะเนื้อหาที่เดียวกัน (เป็นจุดที่เวลา merge แล้วจะเกิด conflict )
เครื่องหมาย '+' ก็คือ test merge ที่เกิดขึ้น (และได้ทำการ solve conflict ไว้แล้ว)
เมื่อเรา merge topic กลับไปยัง master,
commit ของ test merge ก็จะถูกเก็บไว้ใน history ของ master ไปด้วย

$ git checkout topic
$ git merge master
$ ... work on both topic and master branches
$ git checkout master
$ git merge topic

o---*---o---+---o---o topic
/ / \
o---o---o---*---o---o---o---o---+ master

Note: รูปประกอบตัดมาจาก document ของ git

สำหรับบางคนประเด็นนี้ถือเป็นประเด็นที่สำคัญ
เช่น linus เขาเรียกเจ้า "test merge" นี้ว่า "useless merge"
และเห็นว่ากันว่า เขามักจะ reject เจ้า merge branch ที่มีเจ้า "test merge" ติดเข้ามาด้วย

แล้วทำอย่างไร ถึงจะไม่ให้มี test-merge ติดเข้ามาหล่ะ
วิธีที่เขาทำกันก็คือ
ยังทำ test-merge เหมือนเดิม แต่เมื่อ merge เสร็จแล้ว ก็จัดการลบ commit นั้นทิ้งเสีย ด้วยคำสั่ง git reset --hard HEAD^
ผลที่ได้ก็คือ เวลา merge topic กลับเข้า master
เราก็จะได้ tree หน้าตาเกลี้ยงเกลาแบบนี้

o---*---o-------o---o topic
/ \
o---o---o---*---o---o---o---o---+ master

แต่ชีวิตไม่ได้โรยด้วยกลีบกุหลาบ
ปัญหาที่ตามมาก็คือ ไอ้เจ้าพวก conflict ที่เราเคย solve ตอน test merge
มันจะตามกลับมาให้เราแก้ไขอีกครั้งตอน merge topic กลับไปยัง master
ซึ่งการแก้ไขสองรอบแบบนี้ไม่สนุกแน่ๆ

โชคดีที่ git มีทางออกให้เรา
คำสั่งที่เป็นพระเอกก็คือคำสั่ง git-rerere

หลังจาก merge แล้วเกิด conflict ขึ้น
ให้สั่ง git-rerere ก่อนที่จะแก้ไข conflict
และหลังจากแก้ไข conflict เสร็จแล้ว ก็ให้สั่ง git-rerere อีกที
ผลก็คือ เจ้า git จะแอบจำวิธี solve conflict ของเราไว้
ทำให้ครั้งต่อไปที่เกิด conflict แบบนี้ขึ้นมาอีก (test merge ครั้งถัดไป หรือตอน merge topic กลับ master)
มันจะจัดการ resolve ให้โดยอัติโนมัติ

โดย default แล้ว git-rerere ไม่ได้ถูก enable ไว้
เราสามารถสั่ง enable feature นี้โดยการเข้าไปสร้าง directory rr-cache ไว้ใต้ .git directory

Friday, June 20, 2008

Java, Parallel, Monad

เห็น code java นี้แล้ว ธาตุไฟแทรก

public static <A, B> F<Callable<A>, Callable<B>> fmap(final F<A, B> f) {
return new F<Callable<A>, Callable<B>>() {
public Callable<B> f(final Callable<A> a) {
return bind(a, new F<A, Callable<B>>() {
public Callable<B> f(final A ab) {
return new Callable<B>() {
public B call() {
return f.f(ab);
}
};
}
});
}
};
}


ใครธาตุแข็งแรงเชิญไปอ่านได้ที่นี่

Higher-Order Java Parallelism, Part 1: Parallel Strategies and the Callable Monad

Wednesday, June 11, 2008

merge อย่างไรดี

ปัญหาหนึ่งที่ Orangegears เจอก็คือ เนื่องจากเรา fork ออกมาจาก Ofbiz
แต่ก็ยังต้องการ sync กับ Ofbiz อย่างไกล้ชิด (ไม่เหมือน Opentap ที่แยกออกไปอย่างชัดเจน)
ทำให้เราต้องคอย merge code จาก Ofbiz เข้ามายัง Orangegears เป็นระยะๆ

วิธีการเดิมที่น้อง sand ใช้ ก็คือใช้ kdiff3 ทำการ merge
ซึ่งก็สะดวกดี เพราะมี UI สวยงาม
แต่ผมก็เจอปัญหาว่า changed ของผมมักจะหายไปบ่อยๆ
ผมก็เลยมองหาวิธีใหม่ๆมาเรื่อยๆ

วันก่อนหลังจากลองเล่น multiple branches ใน git ดู
ก็พบว่า git ยืดหยุ่นพอที่เราจะทำ multiple remote branch จาก svn repository มากกว่า 1 ที่ได้
ผมก็เลยทดลอง merge ด้วยวิธีนี้ดู

วิธีการก็คือ
เริ่มด้วยการสร้าง git repository ที่มี link ชี้ไปยัง project ofbiz ก่อน

git svn init http://svn.apache.org/repos/asf/ofbiz/trunk

หลังจากสั่งคำสั่งนี้ git จะสร้าง working directory เปล่าๆให้ (ยังไม่ fetch code มาให้)
โดย file .git/config จะมีหน้าตาแบบนี้

[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
[svn-remote "svn"]
url = http://svn.apache.org/repos/asf/ofbiz/trunk
fetch = :refs/remotes/trunk

เราก็ทำการจัดแจงแก้ไขชื่อให้เหมาะสม จะได้ไม่งงภายหลัง
และทำการเพิ่ม repository ของ Ofbiz เข้าไป

[svn-remote "ofbiz"]
url = http://svn.apache.org/repos/asf/ofbiz/trunk
fetch = :refs/remotes/ofbiz_trunk
[svn-remote "orangegears"]
url = https://orangegears.svn.sourceforge.net/svnroot/orangegears
fetch = trunk:refs/remotes/og_trunk

จากนั้นก็ทำการ fetch ข้อมูลทีละ repository
เริ่มจาก ofbiz ก่อน เอา revision ล่าสุดเลย ไม่เอา history
Note1: เลข revision ของ ofbiz นี่ล่อไปหลักหกแสนแล้ว
ทั้งนี้เพราะ repo ของ apache เขาใช้ share ร่วมกันทุก project
Note2: ผมลอง fetch orangegears ก่อน ปรากฎว่า fetch ofbiz ไม่ได้ (ไม่มี error ด้วย)

git svn fetch -r 666189:HEAD ofbiz

ตามด้วย orangegears โดยเริ่มต้นที่ revision 142 เลย ไม่ต้องย้อนอดีตมากนัก

git svn fetch -r 142:HEAD orangegears

ลองสั่ง git branch -r จะเห็นว่ามี og_trunk กับ ofbiz_trunk เกิดขึ้น

pphetra@mypann:~/projects/java/t$ git branch -r
ofbiz_trunk
og_trunk

โดย default trunk ของ ofbiz จะถูก map เข้ามาเป็น master ใน local branch
ทำการเปลี่ยนชื่อให้เรียบร้อย จะได้ไม่งงว่าใครเป็นใคร

git branch -m master ofbiz

ส่วน og_trunk นั้นยังไม่มี local branch ต้องทำการสั่ง

git checkout -b og og_trunk


ขั้นตอนการ merge
fetch code จาก ofbiz ให้ up-to-date ก่อน

git checkout -f ofbiz
git svn rebase

fetch code ของ orangegears ให้ up-to-date เช่นเดียวกัน

git checkout -f og
git svn rebase

เตรียม branch สำหรับ merge โดยสร้าง branch แยกออกจาก og (ไม่จำเป็น แต่ก็ควรทำเป็นนิสัย)

git checkout -b merge-ofbiz

สั่ง merge

git merge ofbiz

ที่ยากก็คือเวลาเกิด conflict ก็ต้องตามแก้ไขให้เรียบร้อยก่อน
Note1: สำหรับผม ผมเลือกใช้ emacs + git-emacs mode ซึ่งมันจะเรียกใช้ ediff ในการ solve conflict
Note2: คิดว่าการ merge ครั้งแรก จะมี conflict เยอะหน่อย
แต่พอเป็นการ merge ครั้งที่ 2.. จะมี conflict น้อยลง เพราะว่า branch ทั้งสองมี history ที่เชื่อมกันแล้ว
(เป็นผลดีต่อ three-way merge)

switch กลับไปยัง og และทำการ merge

git checkout -f og
git merge merge-ofbiz

จากนั้นก็สั่ง commit กลับขึ้น svn repository

git svn dcommit

สุดท้ายก็ลบ temporary branch ทิ้ง

git branch -D merge-ofbiz


ไว้จะทดลองทำสักระยะหนึ่ง (โดยยังไม่ commit ผลลัพท์จากการ merge กลับขึ้น orangegears)
เพื่อดูว่ามันมีประสิทธิภาพแค่ไหนก่อน

Tuesday, June 10, 2008

ใช้ git จัดการ svn branches

ช่วงนี้ project ผมซึ่งใช้ subversion เป็น repository เริ่มมีการแตก branch เยอะขึ้น
ส่งผลให้คนที่ดูหลาย branch เกิดความลำบากในการ switch ไปมาระหว่าง branch มากขึ้น
ปกติวิธีการ switch branch เราสามารถใช้คำสั่ง svn switch ได้เลย
แต่ปัญหาก็คือมันมักจะมี code ที่กำลังแก้ค้างอยู่ จะ commit ไว้่ก่อน switch ก็ไม่เหมาะ เพราะยังแก้ไขไม่ทันเสร็จดี

ผมเลือกเอา git มาช่วยจัดการปัญหานี้

เริ่มแรกสุดก็คือ ในตอนที่เราสั่ง svn clone, เราต้องระบุ flag เพิ่มดังนี้
git svn clone http://your-repo -T trunk -b branches -t tags


ผลที่ได้ ก็คือ master branch ของเราจะ map เข้ากับ trunk ของ svn
ส่วน branches และ tags ต่างๆที่อยู่ใน subversion repository จะมีสถานะ เป็น remote branch ใน git
$ git branch
* master
pphetra@pann@[~/projects/java/gitwcfweb]
$ git branch -r
c1001
j1023
j1027
j1039
j1041
trunk

สมมติว่าเราต้องการจะทำงานกับ branch j1023
เราก็จะต้องทำการสร้าง local branch ที่ map เข้ากับ remote branch ด้วยคำสั่งนี้
git checkout -b j1023-local j1023


ถ้ามีงานด่วนเข้ามา ระหว่างทำงาน ก็เลือกได้ว่า จะ commit เข้าไปก่อน (กลับมา revert ทีหลังได้ หรือใช้ commit --amend เพื่อรวบยอด commit ภายหลังก็ได้)
หรือถ้าไม่อยาก commit เลย ก็อาจจะใช้ git-stash แทนก็ได้
พอ clear state ของ working copy เสร็จ ก็ให้สั่ง switch โดยใช้คำสั่ง
git checkout -f master


การ update branch และ trunk ให้เท่ากับ svn repository ทำได้โดยใช้คำสั่ง
git svn fetch

ผลของมัน ก็คือ git จะ fetch changed (ของทุกๆ branches, tags) ที่เกิดขึ้นบน svn repo มาที่ git repository
แต่จะยังไม่ apply changed เข้ากับ local branch
ถ้าต้องการ apply changed เข้ากับ local branch เราต้องสั่ง
git svn rebase