Thursday, January 28, 2010

code <- พิกัด -> diagram

วันก่อนนั่งแกะ code ของ Webmachine เห็น code ส่วนนี้น่าสนใจดี

อันนี้เป็น diagram แสดง flow decision ของ webmachine


ถ้าดูที่ขอบของ diagram เขาจะแสดงเลขพิกัดด้วย


ดู code ที่เขา implement จริง จะเห็นว่าเขาตั้งชื่อ function อ้างอิงเลขพิกัดและ version ของเอกสารด้วย
%% "Forbidden?"
decision(v3b7) ->
decision_test(resource_call(forbidden), true, 403, v3b6);
%% "Okay Content-* Headers?"
decision(v3b6) ->
decision_test(resource_call(valid_content_headers), true, v3b5, 501);
%% "Known Content-Type?"
decision(v3b5) ->
decision_test(resource_call(known_content_type), true, v3b4, 415);
%% "Req Entity Too Large?"
decision(v3b4) ->
decision_test(resource_call(valid_entity_length), true, v3b3, 413);
%% "OPTIONS?"
decision(v3b3) ->
case method() of
'OPTIONS' ->
Hdrs = resource_call(options),
respond(200, Hdrs);
_ ->
d(v3c3)
end;

Related link from Roti

Helloworld with Mochiweb

ใน erlang ถ้าคิดว่าจะใช้อะไรเป็น web server ดี ก็ต้องคิดถึง Yaws ก่อนเพื่อน
แต่ในระยะหลังเริ่มได้ยิน mochiweb และเห็นมี project ใหม่ๆที่นิยมใช้มากขึ้นเรื่อยๆ ก็เลยได้ฤกษ์ทำการทดสอบแบบง่ายๆ โดยจะ implement simple helloworld

เริ่มด้วยการ checkout source code ของ mochiweb ก่อน

svn checkout http://mochiweb.googlecode.com/svn/trunk/ mochiweb
cd mochiweb

จากนั้นก็สั่ง make เพื่อ compile code (แน่นอนว่าต้องติดตั้ง erlang รอไว้ก่อนแล้ว)

เริ่มต้นสร้างโปรเจค โดยสั่ง

escript /PATH_TO/mochiweb/scripts/new_mochiweb.erl helloworld

จะได้ directory หน้าตาประมาณนี้

./helloworld/
./helloworld/support/
run_tests.escript
include.mk
start.sh
start-dev.sh
./helloworld/src/
skel_web.erl
skel_sup.erl
skel_deps.erl
skel_app.erl
skel.hrl
skel.erl
skel.app
Makefile
./helloworld/priv/
./helloworld/priv/www/
index.html
Makefile
./helloworld/include/
./helloworld/doc/
./helloworld/deps/

file ที่เป็นหัวใจหลักก็คือ helloword_web.erl ทำหน้าที่เป็น process ที่ handle incoming request
ให้แก้ไข function loop ดังนี้
loop(Req, DocRoot) ->
"/" ++ Path = Req:get(path),
case Req:get(method) of
Method when Method =:= 'GET'; Method =:= 'HEAD' ->
case Path of
"helloworld" ->
Req:ok({"text/html", "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">
<html>
<head><title >Welcome to mochiweb</title></head>
<body>
<h1>Helloworld</h1>
</body>
</html>"
});
_ ->
Req:serve_file(Path, DocRoot)
end;
'POST' ->
case Path of
_ ->
Req:not_found()
end;
_ ->
Req:respond({501, [], []})
end.


สั่ง run server โดยใช้คำสั่ง start-dev.sh
แล้วเรียก http://localhost:8000/helloworld

ถ้าเห็น crash report แบบนี้ อย่าตกใจ

=CRASH REPORT==== 28-Jan-2010::09:31:23 ===
crasher:
initial call: mochiweb_socket_server:acceptor_loop/1
pid: <0.57.0>
registered_name: []
exception error: bad argument
in function erlang:universaltime_to_localtime/1
called as erlang:universaltime_to_localtime({{1969,12,31},
{23,59,59}})

มันเป็น bug ใช้วิธี export TZ=GMT-7 แก้ปัญหาไปพลางๆก่อน

ทดลองแก้ไข code แล้วสั่ง refresh browser จะเห็นว่ามันไม่ reload ให้
ไม่ต้องเศร้าใจ ให้เปิด command prompt สั่ง make , ใน development mode, mochiweb จะตรวจดูว่า binary code เปลี่ยนหรือไม่ ถ้าเปลี่ยนมันจะ reload ให้เอง

ลองเปลี่ยน code ให้ รับ url /helloworld/ANYNAME แล้วแสดง message Hello ANYNAME ดูบ้าง
                case Path of
"helloworld/" ++ ID ->
Msg = io_lib:format("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">
<html>
<head><title >Welcome to mochiweb</title></head>
<body>
Hello ~s
</body>
</html>"
, [ID]),
Req:ok({"text/html", Msg});

Related link from Roti

Tuesday, January 26, 2010

Git กับ Remote Branch

สรุปไว้กันลืม

สมมติว่าเรา clone git มาจาก Remote
โดย default เราจะได้ local branch ที่ชื่อ "master" ที่ track กับ remote branch ที่ชื่อ "master"

$ git branch
* master

แต่ถ้าเราสั่งให้ show branch ทั้งหมด

$ git branch -a
* master
origin/HEAD
origin/activity-calendar
origin/master
origin/pilot
origin/pilot_20100128
origin/user-admin

จะเห็นว่า git ทำการ fetch branch ทั้งหมดมาให้เรา เพียงแต่มัน map ขึ้นมาเป็น local branch ให้เราเฉพาะแค่ master

ถ้าเราสั่ง git fetch สิ่งที่เกิดก็คือ git จะทำการดึงข้อมูลการเปลี่ยนแปลงทุกอย่างของ server มาที่เครื่องเรา แต่จะยังไม่ update Working copy ให้เรา

ส่วน git pull ก็เทียบได้กับการสั่ง git fetch แล้วตามด้วย git merge โดยจะ merge จะเฉพาะ current HEAD ของเรา (ถ้า HEAD คือ master ก็จะ merge เฉพาะ master)

ที่นี้ถ้าเราต้องการ track remote branch บนเครื่องเรา สิ่งที่ต้องทำก็คือ

$ git checkout -b pilot origin/pilot
Branch pilot set up to track remote branch refs/remotes/origin/pilot.
Switched to a new branch "pilot"


ปัญหาที่เกิดบ่อยๆก็คือ เราสั่ง track remote branch โดยตั้งชื่อ local branch ไม่ตรงกับ remote branch เช่น

$ git checkout -b myb1 origin/b1
Branch myb1 set up to track remote branch refs/remotes/origin/b1.
Switched to a new branch "myb1"

เวลาเราแก้ไข file ใน branch myb1 เสร็จแล้วอยาก push ไปที่ server

$ vi MYFILE

$ git add MYFILE
$ git commit -m 'Add MYFILE'

$ git push
Everything up-to-date

จะเห็นว่ามันไม่ยอม push commit ใหม่ของเราไปที่ remote repository
ที่เป็นเช่นนี้เพราะถ้าเราสั่ง git push โดยไม่มี parameter สิ่งที่ git ทำก็คือ มันจะ push เฉพาะชื่อ branch ที่ตรงกันของทั้งสองฝั่ง

การแก้ไขก็คือ เปลี่ยนตัวแปร configuration push.defaut ให้เป็น tracking เพื่อที่จะให้มัน push เฉพาะ branch ที่มี tracking กับ remote branch โดยไม่สนใจเรื่องชื่อตรงกัน

$ git push
Everything up-to-date
pphetra@[~/t2]
$ git config push.default tracking
pphetra@[~/t2]
$ git push
Counting objects: 5, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 248 bytes, done.
Total 3 (delta 1), reused 0 (delta 0)
To git@github.com:pphetra/test.git
7d96d34..0dd66fa myb1 -> b1


ที่นี้ถ้าเราต้องการสร้าง branch ใหม่บน remote หล่ะ
เริ่มจากการสร้าง branch บน server

$ git push origin origin:refs/heads/b4
Total 0 (delta 0), reused 0 (delta 0)
To git@github.com:pphetra/test.git
* [new branch] origin/HEAD -> b4
$ gitx

$ git branch -a
* b1
b2
master
myb1
remotes/origin/HEAD -> origin/master
remotes/origin/b1
remotes/origin/b2
remotes/origin/b4
remotes/origin/master

จากนั้นก็สั่ง checkout ตามปกติได้เลย

$ git checkout -b b4 origin/b4
Branch b4 set up to track remote branch b4 from origin.
Switched to a new branch 'b4'

Related link from Roti