Friday, July 07, 2006

Sawfish

ช่วงนี้ใช้ notebook ที่ลง Linux ไว้บ่อยมาก
เวลาใช้ ก็ใช้เป็น routine เลย
เปิด terminal ที่ workspace 1
เปิด eclipse ที่ workspace 2
เปิด emacs ที่ workspace 2
เปิด firefox ที่ workspace 3

ทุกวันตอนเปิดเครื่อง ก็ต้องไล่ start
แล้วก็ไล่วางให้ถูก workspace
ก็เลยเกิดความคิดจะลอง customize ให้มัน start ให้เอง

โปรแกรมที่ลองใช้ก็คือ Sawfish
ซึ่งเป็น Window manager ที่เขียนด้วย Lisp
ทำให้ได้หัดเขียน lisp ไปในตัว

เริ่มด้วยความต้องการอย่างง่ายสุดก่อน
กดครั้งเดียวได้โปรแกรมทุกตัวที่ต้องการ
(defun run-all ()
(start-emacs)
(start-eclipse)
(start-firefox)
(start-term))

(defun start-emacs ()
(system "emacs &"))

...

start มาได้อย่างนี้แล้ว แต่ทุกตัวก็ยังกองกันอยู่ที่ workspace เดียว
ก็เลยเขียน function ขึ้นมาจัด workspace
(defun arrange-ws ()
(move-window-to-workspace (emacs-win) 0 1)
(move-window-to-workspace (eclipse-win) 0 1)
(move-window-to-workspace (firefox-win) 0 2))

(defun emacs-win ()
(get-window-by-name-re "^emacs"))

(defun eclipse-win ()
(get-window-by-name-re ".Eclipse"))

...


จัดการ bind function เข้ากับ short-cut key
ก็เป็นอันเรียบร้อย
(bind-keys global-keymap
"H-a"
`(run-all))


ขั้นถัดไป ก็คือทำให้โปรแกรมมันฉลาดกว่านี้
กดครั้งเดียวพอ ไม่ต้องกด 2 ครั้ง
(เนื่องจากเวลาสั่ง system เราระบุว่าให้ start process
แต่ความที่มันเป็น asyncronous ทำให้เราไม่รู้ว่า process มัน start เสร็จหรือยัง
ทำให้ไม่รู้ว่าควรจะใช้คำสั่ง switch workspace ตอนไหนดี)

Link
Sawfish code library - แหล่งรวม code ให้ลอก

Related link from Roti

Rock Balancing

เคยไปเดินเที่่ยวถ้ำแล้วเจอคนเอาก้อนหินมาวางซ้อนเรียงกัน
ดูเป็นปฏิมากรรมง่ายๆดี

แต่เรื่องวางให้ดู amazing ก็ต้องอันนี้
Rock Balancing Gallery

(ได้ link จาก Signal vs. Noise)

Related link from Roti

Thursday, July 06, 2006

dynamically add methods

เห็นวิธี add dynamic method ของ Prototype แล้วชอบใจดี
var Iterators = function() {
var tags = "div p span ul ol li span form input select textarea h1 h2 h3 h4 h5 h6 dl dt em strong";
var methods = {};
$A(tags.split(' ')).each(function(tag) {
methods["each" + tag.charAt(0).toUpperCase() + tag.substring(1)] = function(element, iterator) {
element = $(element);
element.cleanWhitespace();
$A(element.getElementsByTagName(tag)).each(iterator);
}
});

Element.addMethods(methods);
}();


Writing Custom Iterators For Prototype

Related link from Roti

Wednesday, July 05, 2006

Dojo Custom Widget

ช่วงนี้กำลังลุ่มหลงกับการหาวิธีใช้ Dojo ให้เกิดประโยชน์สูงสุดอยู่
วันนี้เลยทดลองสร้าง widget ง่ายๆเองดู
แน่นอนเริ่มต้น ก็ต้องเริ่มที่ HelloWorld

ขั้นแรก เริ่มด้วยการ define widget ก่อน
dojo.widget.defineWidget(
"pok.Test",
dojo.widget.HtmlWidget,
{ ... }
);

parameter แรกสุด ก็คือชื่อ widget ของเรา
parameter ตัวที่สอง ก็คือ widget ที่เรา extend
ส่วน parameter ตัวที่สาม เป็น object ที่เป็น properties, methods ของ widget เรา
(จริงๆมี parameter อีก 2 ตัว แต่เรา ignore ก่อน)

แน่นอนว่า เมื่อเป็น html widget
widget ของเราก็ต้อง generate html ออกมาด้วย
เราสามารถเลือกวิธี generate html ออกเป็น 2 วิธี (คงมีมากกว่านี้ แต่ตอนนี้รู้แค่นี้)
  • generate จาก template file
  • generate จาก template string


ทดลองทำวิธี template string
โดยต้องการ output แบบนี้ <input type='text' value=''/>
dojo.widget.defineWidget(
"pok.Test",
dojo.widget.HtmlWidget,
{
templateString: "<input type='text' value=''/>"
}
);

เวลานำไปใช้ก็แค่
<body>
<div dojoType="Test"/>
</body>


ทีนี้ ถ้าเราอยากเพิ่ม parameter ให้เรา customize widget เราได้
ก็สามารถทำได้ดังนี้
กรณีนี้เราจะยกตัวอย่างการ customize textbox value
dojo.widget.defineWidget(
"pok.Test",
dojo.widget.HtmlWidget,
{
templateString: "<input type='text' value='${title}'/>",
// default value
title: "hello world",
postMixInProperties: function() {
this.strings = {
title: this.title
}
}
}
);

เห็นได้ว่า เราทำการ map title ที่แสดงใน template ผ่านทาง
method postMixInProperties

ที่นี้เวลานำไปใช้ เราก็สามารถ customize title ได้ดังนี้
<div dojoType="Test" title='hi'/>


concept อันถัดไปก็คือ dojoAttachPoint
ลองเปลี่ยน template เราให้เป็นหน้าตาแบบนี้แทน
    templateString: "<span> <input type='text' value='${title}'/> " +
" <span dojoAttachPoint='labelNode'>xx</span></span>",

dojoAttachPoint ก็คือจุดที่เราสามารถ manipulate DOM ในระหว่างที่ render widget ได้
โดย implement ผ่านทาง hook method ที่ชื่อ fillInTemplate
dojo.widget.defineWidget(
"pok.Test",
dojo.widget.HtmlWidget,
{
templateString: "<span> <input type='text' value='${title}'/> " +
" <span dojoAttachPoint='labelNode'>xx</span></span>",
title: "hello world",
label: "your name here",
postMixInProperties: function() {
this.strings = {
title: this.title
}
},
fillInTemplate: function() {
this.labelNode.innerHTML = this.label;
}

});


ไม่เลยเลย พอเริ่มเข้าใจก็ดูไม่ค่อยยากแล้ว

Related link from Roti

Tuesday, July 04, 2006

Dojo ScriptSrcIO

สืบเนื่องมาจากงาน codefest ที่ติดใจว่ายังทำ Cross Domain javascript ไม่สำเร็จ

ผมเคยเขียนเรื่อง cross domain javascript ไปทีแล้ว
Yahoo Web Services + JSON + Cross domain javascript

วิธีที่เคยเขียนถึงนั้น จริงๆ ก็ไม่ยากเย็นอะไร
แต่มีวิธีที่คิดว่าดีกว่า นั่นก็คือใช้ Dojo
Dojo เตรียม feature dojo.io.ScriptSrcIO ให้เราเรียบร้อยแล้ว
(ซึ่งเดาว่า คงใช้วิธีแบบเดียวกับที่เคยเขียนไปนั่นแหล่ะ เพียงแต่ฉลาดกว่า และทำได้หลายหลายแบบกว่า)
เหลือแต่ว่าหาวิธีใช้มันให้ได้เท่านั้นเอง
วันนี้ผมก็เลยนั่ง งม หาวิธีใช้

หลังจากมั่วอยู่นาน ไม่เกิดผลสักที จนเกือบยอมแพ้ไปแล้ว
google ก็เป็นใจ ส่งตัวอย่างที่ชัดๆมาให้ดูจนได้
หลังจากทดลอง run ตัวอย่าง ก็เลยรู้ว่าท่ีทดสอบไม่สำเร็จ
เป็นเพราะผมใช้ browser ไปเปิด html file จาก filesystem ตรงๆ
โดยไม่ผ่าน web server,
การเปิด file ตรงๆ จะทำให้ dojo ไม่สามารถ solve หา src ที่มันต้องการได้
(เนื่องจากเวลา dojo solve หา src มันจะใช้ xmlhttprequest ในการค้นหา)
UPDATE:
ผิดที่วิธี config ของผมเอง ไม่ได้ผิดที่การเรียกใช้โดยตรงจาก filesystem


ลองดูตัวอย่างเต็มๆ ได้ที่ site ของหนุ่มญี่ปุ่นคนนี้
http://d.hatena.ne.jp/shinichitomita/20060522/1148276164


ที่นี้ลองมาทำความเข้าใจดูบ้าง
เริ่มแรกสุด ก็คือวิธีที่ใช้ dojo.io.bind
dojo.io.bind({
url: 'http://del.icio.us/feeds/json/'+deliciousId,
transport: "ScriptSrcTransport",
jsonParamName: "callback",
load: function(type, data, event, kwArgs) {
renderPosts(data);
}
});

จะเห็นว่า เราเลือกใช้ transport เป็น "ScriptSrcTransport"
เจ้า dojo.io.bind ออกแบบมาให้เราสามารถ switch วิธีการที่ใช้
ติดต่อกับ server ได้
โดยค่า default ของมันก็คือ XMLHTTPTransport
ซึ่งก็คือการใช้ XmlHttpRequest นั่นเอง

ประเด็นถัดมา ก็คือค่า result ที่ได้
พอได้มาแล้ว จะนำมาแสดงอย่างไร
ลองทดสอบนำ url "http://del.icio.us/feeds/json/pokpok" มา run ดู
จะพบว่ามัน return code มาหน้าตาแบบนี้

if(typeof(Delicious) == 'undefined')
Delicious = {};
Delicious.posts = [{"u":"http://sawmill.sourceforge.net/","d":"sawfish: an extensib .... }];

จะเห็นว่าถ้า call ได้สำเร็จ เราจะได้ ตัวแปร Delicious.posts มาใช้
แต่ปัญหาก็คือ เนื่องจาก nature ของการ call แบบนี้ มันเป็น Asyncronous
เราจะรู้ได้อย่างไรว่า Delicious.posts ถูก assign เรียบร้อยแล้ว

พวก json service เห็นปัญหานี้ ก็เลยออกแบบ service ของตนให้ pass ค่า callback ได้ด้วย
ทดลองเรียก
"http://del.icio.us/feeds/json/pokpok?callback=show"
ผลลัพท์ที่ได้จะมีหน้าตาแบบนี้

show([{"u":"http://sawmill.sourceforge.net/","d":"sawfish: an extensib ...."}];);

Note: มีศัพท์ที่เขาใช้เรียกวิธีนี้ว่า JSONP
อ่านรายละเอียดได้ที่นี่ Remote JSON - JSONP


ในการใช้ Dojo ScriptSrcIo
เราสามารถเลือกวิธี callback ได้เหมือนกัน
โดยการใส่ parameter jsonParamName: "callback"
ค่า callback ที่ใส่นี้เป็นชื่อ param ที่ remote service รับนะ
ไม่ใช่ชื่อของ javascript function ที่ต้องการให้ call
ผลลัพท์ที่ได้จาก remote service ก็จะถูกส่งต่อให้
function ที่ระบุไว้ที่ parameter load: function(type, data, event, kwArgs) ทำงาน
โดย data ก็คือ javascript object ที่ service ตอบกลับมา

เพื่อให้แม่นยิ่งขึ้น ผมเลยทดลองเขียน service hello world ด้วย rails ดู
ตัว view หน้าตาแบบนี้
​​
<%= params[:callback] %>({result: "helloworld"})

ส่วน client ที่ call ก็หน้าตาแบบนี้
function test() {
dojo.io.bind({
url: 'http://erp:3000/test',
transport: "ScriptSrcTransport",
jsonParamName: "callback",
load: function(type, data, event, kwArgs) {
alert(data.result);
}
});
}

Related link from Roti

Monday, July 03, 2006

เริ่มเห็นภาพ Monads

วันนี้อ่านเจอ Tutorial ที่พูดถึง IO ของ haskell (ที่เต็มไปด้วย Monads)
แม้จะทนอ่านจนจบไม่ได้ แต่ก็ทำให้เริ่มเห็นภาพ Monads มากยิ่งขึ้น
เริ่มรู้แล้วว่า เหตุผลที่ต้องมี monad ก็เพราะ
  • Haskell is a pure language, which means that the result of any function call is fully determined by its arguments.
    ตามกฎนี้ เราไม่สามารถเขียน function พวก rand() หรือ getchar() ได้
    เพราะกฎของ haskell จะบังคับไว้ว่า ถ้า argument เหมือนเดิม (กรณีนี้คือ void)
    return ที่ได้ ต้องเหมือนกันทุกครั้ง
  • Haskell functions can't have side effects, which means that they can't effect any changes to the "real world"
    ถ้าตามกฎนี้ haskell จะไม่สามารถเปลี่ยนแปลง file content ภายนอก
    หรือไม่สามารถ writing to screen, ฯลฯ

Related link from Roti

ActiveResources

ActiveResources คือ
Feature ใน rails 1.2 ที่ผมชอบมากเป็นพิเศษ
เพราะมันเปิดโอกาศให้ผมใช้ Rails กับ Legacy project ปัจจุบันของผมได้

โดย ActiveResource ก็คือ rest client ที่ช่วยให้เราต่อ
เข้ากับ rest service ที่อยู่บน server ตัวอื่นๆได้ง่ายๆ


Person = ActiveResource::Struct.new do |p|
p.uri "http://www.myhost.com/people"
p.credentials :username => "xxx", :password => "pass"
end
person = Person.find(1)
person.name = "pphetra"
person.save!


เห็นทางสว่าง สำหรับโปรเจค ที่จะ deploy ปลายปีนี้แล้ว
บางส่วนเป็น rails บางส่วนเป็น java

Related link from Roti

จบงาน codefest

เหนื่อยมาก 2 วันกับ project Vehicle Tracking System
ในงาน codefest ครั้งที่ 1
แต่ก็ชื่นใจที่ implement ได้ทััน แม้จะไม่ได้ feature ครบตามที่ตั้งใจไว้
กลับมาบ้าน แทนที่จะได้นอนเร็ว ปรากฎว่าแม่เด็ก ดันติดธุระ
ต้องนั่งถ่างตาเล่านิทาน, ระบายสี, เล่นกับลูก
พอแม่เด็กกลับมา ก็สลบไปเลย

ในส่วนของ erlang, ยิ่งใช้ก็ยิ่งชอบใจ
syntax ที่ simple
บวกกับ standard library ที่มีครบครัน
ทำให้เขียนแล้วรู้สึกสนุก
(จริงๆ ผมไม่ได้เขียนหรอก
เป็น mentor ให้รุ่นน้องเขียน เสียมากกว่า)

จำนวน line of code ที่ได้ ก็น่าสนใจมาก
(อย่าไปนับทั้งหมดที่อยู่ใน cvs นะครับ
เพราะมีพวก experimental อยู่หลายแบบ)
source code ของ server =~ 100 บรรทัด
source code ของ controller (web app) =~ 110 บรรทัด
ตัว html+javascript =~ 300 บรรทัด
test code กับพวกที่ run ครั้งเดียว(setup database) ไม่นับ

Performance ก็ไม่เลยเลย
ลองใช้ jmeter ทำ load testing ดู
โดยใช้ Notebook pentium4 2.4 GHz เป็น server
ยิ่งเข้าไป 500 thread แต่ละ thread ส่งข้อมูล 5 request
ทั้งหมดส่งไปภายใน 1 วินาที โดยกระจายตัวแบบ Gaussian random
ตังค่า assert ไว้ 500 ms.
ปรากฎว่า มีจำนวน request ที่ใช้เวลาเกิน 500 ms.
ทั้งหมด 189 request
คิดเป็น 0.075 %

บรรยากาศในงาน ก็สนุกดี
มีกล้องค่อยถ่ายไปเรื่อยๆ
ได้บรรยากาศเหมือนอยู่ใน reality show

ช่วงแรกๆที่เริ่ทำงานยังรู้สึกเสียวอยู่ กลัวทำไม่ทัน
คิดในใจ "รู้งี้แอบเตรียมเขียน code มาก่อนดีกว่า"
แต่พอตกเย็น ส่วน server เริ่มลงตัว
ก็รู้สึกผ่อนคลาย เพราะรู้แล้วว่าเสร็จทันแน่

Related link from Roti