Friday, May 02, 2008

this ใน javascript กับ dojo.hitch

ใน javascript, bug ที่พบกันบ่อยๆ ก็คือ scope ของ this ที่มักไม่ได้เป็นไปตามที่ programmer คิด
ยกตัวอย่าง
<a id='linkNode' href="#">link</a>
<script>
foo = {

greeting: 'hi',

debug: function() {
alert(this.greeting);
}
}

document.getElementById('linkNode').onclick = foo.debug;
</script>

ถ้าลอง run code ข้างบนนี้ดู ก็จะพบว่า ถ้าเรา click link นั้นเมื่อไร
message ที่แสดงใน alert มันจะแสดงคำว่า "undefined" แทนที่จะเป็น 'hi'
ที่เป็นเช่นนี้ก็เพราะว่า this object ใน function debug ณ ขณะที่เกิด event
function มันจะเปลี่ยน scope ทำให้ this กลายเป็น DOM element ของ anchor แทนที่จะเป็น object foo

แล้ว dojo.hitch หล่ะคืออะไร
function นี้ในแง่ของ functional language ถือว่ามันเป็น higher order function
เนื่องจากมันเป็น function ที่ return function

หน้าที่หลักของ dojo.hitch ก็คือการ bind scope ของ function เข้ากับ object ที่เราต้องการ
อย่างตัวอย่างข้างบน ถ้าเรานำ dojo.hitch มาช่วย ก็จะเขียนเป็นแบบนี้แทน
document.getElementById('linkNode').onclick = dojo.hitch(foo, "debug");

ถ้าลอง run ดู ก็จะได้ message 'hi' ขึ้นมา

จะเห็นว่า hitch ช่วยเราเปลี่ยน scope ของ this จากเดิมที่เป็น DOM element (ที่ trig event นั้น) ให้กลายเป็น foo object แทน โดยใช้ technique การ binding
ลองมาดู code ของ function hitch กัน

dojo.hitch = function(scope, method){
if(arguments.length > 2){
return dojo._hitchArgs.apply(dojo, arguments); // Function
}
if(!method){
method = scope;
scope = null;
}
if(dojo.isString(method)){
scope = scope || dojo.global;
if(!scope[method]){ throw(['dojo.hitch: scope["', method, '"] is null (scope="', scope, '")'].join('')); }
return function(){ return scope[method].apply(scope, arguments || []); };
}
return !scope ? method : function(){ return method.apply(scope, arguments || []); };
}

เริ่มต้นด้วย การข้าม case กรณี arguments​ > 2 ไปก่อนเลย
อันนั้นเป็นเงื่อนไขพิเศษ กรณีที่ต้องการให้มี fixed(prefix) arguments.

ส่วนถัดมาก็คือส่วน ตรวจสอบว่า มีการ pass parameter มาตัวเดียวหรือเปล่า
ถ้ามาตัวเดียว ก็จะถือว่า parameter นั้นเป็น method

สุดท้ายก็ตรวจสอบอีกว่า method เป็น string
หรือเป็น string ก็จะ return closure ที่ห่อคำสั่ง scope[method].apply
ถ้าไม่ใช่ script ก็จะ return closure ที่ใช้คำสั่ง method.apply แทน

อืมม์พอเริ่มเข้าใจ ก็เริ่มเห็นความงาม

ถ้าใครสนใจอยากอ่านให้เข้าใจ(หรืองง)เข้าไปอีก อ่านเพิ่มเติมที่นี่
Jammastergoat - dojo.hitch

Related link from Roti

Tuesday, April 29, 2008

What is programming.

ลอกมาจาก Programming 's Stone บทที่ 2

Ada is sitting in a room. In the evening the room becomes dark. Ada turns on the light.

That is the fundamental act of programming. There is a problem domain (the room), which is dynamic (gets dark). There is order to the dynamic problem domain (it will be dark until morning), permitting analysis. There is a system that can operate within the problem domain (the light), and it has semantics (the switch state).

There is a desire (that the room shall remain bright), and there is an insight (that the operation of the switch will fulfill the desire).
Dynamic problem domains, systems and semantics are covered in detail elsewhere.

Related link from Roti

Monday, April 28, 2008

Override service.

เมื่อวันก่อน ผมพึ่งพบปัญหา java process กิน cpu ไป 99% ตลอดเวลา
ในขั้นแรกก็จัดการ "kill -3" เสีย เพื่อที่มันจะได้ให้มัน dump stack trace ออกมาให้เราดู
"ajp-8009-2" daemon prio=1 tid=0x08c76ef0 nid=0x3d52 runnable [0x6aeb6000..0x6aeb8040]
at java.lang.ClassLoader.loadClass(ClassLoader.java:299)
- waiting to lock <0x7e734d70> (a sun.misc.Launcher$AppClassLoader)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:268)
- locked <0x7e734d70> (a sun.misc.Launcher$AppClassLoader)
at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1267)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1198)
at java.beans.Introspector.instantiate(Introspector.java:1453)
at java.beans.Introspector.findExplicitBeanInfo(Introspector.java:410)
- locked <0x6ebe1848> (a java.lang.Class)
at java.beans.Introspector.<init>(Introspector.java:359)
at java.beans.Introspector.getBeanInfo(Introspector.java:222)
at java.beans.Introspector.<init>(Introspector.java:368)
at java.beans.Introspector.getBeanInfo(Introspector.java:222)
...
at org.apache.tapestry.util.exception.ExceptionAnalyzer.buildDescription(ExceptionAnalyzer.java:129)
at org.apache.tapestry.util.exception.ExceptionAnalyzer.analyze(ExceptionAnalyzer.java:86)
at org.apache.tapestry.util.exception.ExceptionAnalyzer.reportException(ExceptionAnalyzer.java:378)
at org.apache.tapestry.error.RequestExceptionReporterImpl.reportRequestException(RequestExceptionReporterImpl.java:59)

จากการไล่ stack trace ก็พบว่า มันมีปัญหาในส่วนของ introspector
ซึ่งเกิดจากหน้าจอรายงาน Error ของ Tapestry, พยายามที่จะ dump ข้อมูล nested Exception ออกมาให้เรา
โดยมันพยายามจะใช้กลไก Introspector ในการอธิบายปัญหาที่เกิดขึ้น, แต่ไม่รู้ด้วยเหตุใด เจ้า introspector มันดันหา class บางตัวไม่พบ
มันก็เลย lock, waiting to lock classloader อยู่

สาเหตุของปัญหาจริงๆ ยังจับไม่ได้คาหนังคาเขา แต่คาดว่าน่าจะเกิดจากการ stop webapp บางตัวลงไป กลางอากาศ ณ ขณะที่ user กำลังใช้งานอยู่
เมื่อยังจับต้นตอไม่ได้ แต่ก็ไม่อยากให้เกิดเหตุการณ์ cpu 99% อีก
ผมก็เลยต้องหาวิธีการแก้ปัญหาชั่วคราวก่อน

สิ่งที่เราต้องการก็คือ เราต้องการ replace function ในส่วนของการพยายามอธิบายรายละเอียด exception ที่เกิดขึ้น
(ให้มันลดการอธิบายรายละเอียดลง)
ในการแก้ปัญหานี้ ถ้าเจ้า framework ไม่ได้ออกแบบมาดีพอ เรามักจะต้องใช้วิธี download source code ของ framework มาแก้ไขแล้วก็็ build custom version เอาเอง
(หรือไม่ก็ใช้ AOP ซึ่งก็ยุ่งไปอีกแบบ)

โชคดีที่ tapestry ไม่ได้อยู่ในกลุ่มนี้
Howard ออกแบบ tapestry มาอย่างยอดเยี่ยม
มีความเป็น modular สูง สามารถถอด,เพิ่มเปลี่ยน function ได้ง่าย

ในกรณีนี้ สิ่งที่เราต้องก็คือไล่หาว่า service ตัวไหน รับผิดชอบงานนั้นอยู่
เมื่อเจอแล้ว ก็ทำตามคู่มือ ว่าด้วยการ Overriding a Service
(production ผมยังใช้ tapestry 4 อยู่)

Related link from Roti