Thursday, June 15, 2006

Dabbawallahs สุดยอด logistic

วันนี้นั่งดูรายการ Discovery channel เจอสารคดี
เกีี่ยวกับ "dabbawallahs"

"dabbawallash" เป็นชื่อของระบบการขนส่ง
ที่ใช้ขนส่งอาหารกลางวัน
โดยเรื่องของเรื่องก็คือ มีคน india ใน Mumbai จำนวนหนึ่ง
ที่ต้องกินข้าวกลางวันที่ทำจากบ้านตัวเองเท่านั้น
(อาจจะเป็นเหตุผลทางศาสนา หรือความเชื่อ)
ก็เลยเกิดเครือข่ายในการขนส่งขึ้น

ด้วยการไปรับปิ่นโตตามบ้านที่เหล่าภรรยาเตรียมไว้
จากนั้นก็รวบรวมแล้วนำส่งไปยังที่ทำงานของเหล่าบรรดาสามี
โดยใช้ทั้งการเดิน, ขี่จักรยาน, รถไฟ, รถเข็น
ระยะทางที่ขนส่ง ไม่มากเลย
a typical customer commutes about 100 kms a day between his home and work place.

(อย่าลืมหาร 2 หล่ะ)
น้ำหนักของการแบกเดิน ก็ไม่มาก
carrying a load of 100 kgs. mannually on their head and walk 2.5 kms.


น่าทึ่ง ที่ระบบนี้มีมา 120 ปีแล้ว
ในปี 2005 มีปริมาณอาหารที่ต้องส่ง 150,000 box, 300,000 transaction ต่อวัน
(บริการเก็บกลับบ้านให้ด้วย)
ใช้พนักงาน 5000 คน
ความผิดผลาดน้อยมาก
reported less than six errors in 13 million transaction


การจัดทืม จะแบ่งให้แต่ละคนรับผิดชอบอาหาร 30 ชุด
ทืมหนึ่งมี 8 คน
8 ทืมรวมเป็น 1 group
มี group ทั้งหมด 120 group

เริ่ม process ด้วยการไปรับอาหารจากบ้านต่างๆ ประมาณ 8.30-9.00
จากนั้นก็จะขนอาหารมารวมกันที่สถานีรถไฟ
ถึงตอนนี้ ก็เป็นเวลา Sorting แล้ว
ตรงนี้ต้องใช้ระบบรหัสมาช่วย
code ของแต่ละปิ่นโต จะประกอบด้วย
  • origin of the lunch box
  • assocated collection team member
  • destination
  • corresponding delivery team member
  • location building identification and floor number

อาหารไปถึงลูกค้าไม่เกินบ่ายโมง
จากนั้น 3.30pm ก็ได้เวลาขนปิ่นโตกลับบ้าน

paper อ่านได้ที่นี่
World class logistics operations: The case of Bombay Dabbawallash
รูป ดูได้ที่นี่
http://perso.orange.fr/laurentallard/Les_Enfants_de_Gandhi/dabbawallahs.html

Related link from Roti

เปรียบเทียบ Dojo กับ Prototype

เดิมที่ผมใช้ ruby on rails อยู่นั้น
Prototype ถือเป็นพระเอกในฝั่ง javascript
ช่วงนี้หันมาทำงานด้วย Tapestry4
Tacos ซึ่งเป็น Ajax component ของ Tapestry
ดันใช้ Dojo เป็นหลัก
ก็เลยหันมาทดลองใช้ Dojo ดูบ้าง

วันนี้ทำหน้าจอหนึ่งซึ่งมีการใช้ event กับ dom
ลองเปรียบเทียบกันดูว่า คำสั่งหน้าตาแตกต่างกันแค่ไหน

เริ่มจาก getElementById ก่อน
ใน Prototype เราใช้
$('foo')

ส่วนใน Dojo
dojo.byId('foo')

ใน Prototype เราสามารถ get แบบนี้ได้
var element_array = $('foo','bar','xxx')

ใน Dojo
var element_array = dojo.byIdArray('foo','bar','xxx')


การ attach event เข้ากับ element
ใน prototype
function myFunction() { ... }
Event.observe('foo', 'click', myFunction);
หรือ
Event.observe($('foo'), 'click', myFunction);

ใน Dojo
function myFunction() { ... }
dojo.event.connect(dojo.byId('foo'), 'onclick', myFunction);

จะเห็นว่า prototype จะแปะ "on" ให้กับ event เอง

กรณี detact event
prototype ใช้

Event.stopObserving('foo', 'click', myFunction);

Dojo
dojo.event.disconnect(dojo.byId('foo'))


กรณี Effect
Prototype

new Effect.Fade('foo', {duration: .5});

Dojo
dojo.fx.html.fadeHide(dojo.byId('foo'), 500);


กรณีที่ combine effect โดยมีลำดับก่อนหลัง
Prototype
new Effect.Fade('foo', {queue:'front', duration: .5});
new Effect.Fade('bar', {duration: .5});

ใน Dojo ไม่แน่ใจว่ามีเรื่อง combine effect หรือเปล่า
เพราะเอกสารมันน้อย
แต่สามารถเรียงลำดับโดยใช้ hook function เข้ามาช่วย
dojo.fx.html.fadeHide(dojo.byId('foo'), 500,
function() {
dojo.fx.html.fadeHide(dojo.byId('bar'), 500);
});


feature ที่เกี่ยวกับ Enumeration
Prototype
var ary = ['foo', 'bar'];
var total = 0.0;
ary.each(function(elm) {
total += getValue(elm);
});

Dojo
var ary = ['foo', 'bar'];
var total = 0.0;
dojo.lang.forEach(ary,
function(elm) {
total += getValue(elm);
});


add, remove css class
prototype
Element.addClassName('foo', 'bar');
Element.removeClassName('foo', 'bar');

dojo
dojo.html.addClass(dojo.byId('foo'), 'bar');
dojo.html.removeClass(dojo.byId('foo'), 'bar');


เปรียบเทียบแค่นี้แหล่ะ เพราะหน้าจอแรก ใช้อยู่เท่านี้

Update ไปนั่งไล่ดู code ใหม่แล้ว พบว่า function ส่วนใหญ่รับค่า node เป้น string name ได้
แต่ตัว event.connect นี่ไล่ดู code แล้วมันแปลกๆอยู่ ดูเหมือนว่าต้อง pass เป็น object

Related link from Roti

Wednesday, June 14, 2006

Sue Rubira ชอบงาน painting ของเขา(เธอ) จริงๆ

Related link from Roti

Tuesday, June 13, 2006

retract ใน Drool

อ้างถึงที่เคย post เรื่อง drool ไป

โง่อยู่ตั้งนาน ไม่ยอมอ่านคู่มือให้ดี
feature หนึ่งใน Drool ที่ผมพลาดไปก็คือ
ก็คือ retract(object) ซึ่งตรงข้ามกับ assert(object)

assert ก็คือการส่ง object เข้าไปใน working memory ของ rule engine
ซึ่ง rule engine จะตรวจเงื่อนไขว่าควรจะมีการ trig ให้ rule อะไรทำงานบ้าง
ส่วน retract ก็ตรงกันข้าม ใช้สำหรับล้าง object ออกจาก working memory

ที่นี้ retract เอามาใช้ทำอะไรบ้าง
กรณีแรกเลย ก็คือ กรณีที่ rule conflict กัน
(assert ครั้งเดียว แล้วมีหลาย rule ที่ trig พร้อมๆกัน
และพยายาม update resource ตัวเดียวกัน)
ซึ่งเราต้องใช้ salience เข้ามาช่วย
เพื่อเรียงลำดับว่าอะไรมี priority สูงกว่ากัน

ที่ผมทำผิดไป ก็คือ ผมเรียงลำดับให้ rule ผิด
(ได้ผลลัพท์ถูก แต่ในแง่ของ performance แล้ว
ไม่ถูกต้องอย่างยิ่ง)
ที่ถูกต้อง คือต้องนำเอา retract มาช่วยตัดวงจรไม่ให้ rule ที่เหลือทำงาน
โดยเรียงให้ rule ที่มีความสำคัญสูงสุด มีค่า salience สูงสุด
และเมื่อ rule ทำงานเสร็จ ก็ให้ retract object ออกเสีย

rule "ทุกคนได้ส่วนลด 5%"
salience 0
when
o : Order()
then
o.setPercentDiscount(5.0);
retract(o);
end

rule "ลูกค้าภาคเหนือได้ส่วนลด 7 %"
salience 80
when
c : Customer(region == Customer.NORTH)
o : Order(customer == c)
then
o.setPercentDiscount(7.0);
retract(o);
retract(c);
end

rule "ซื้อเกิน 1500 บาท ด้วยเงินสด ได้ส่วนลด 10 %"
salience 90
when
o : Order(amount >= 1500, payType == Order.CASH)
then
o.setPercentDiscount(10.0);
retract(o);
end

rule "สินค้า p-1 จะไม่ให้ส่วนลด"
when
item : OrderItem(p : product)
eval(p.getProductCode().equals("p-1"))
then
item.setIncludeInTotalDiscount(false);
retract(item);
end


ลำดับของการ assert ก็ต้องเรียงให้ดีด้วย
ไม่งั้น retract อาจจะทำให้ case บาง case ไม่ fire ได้
for (OrderItem item : order.getItems()) {
workingMemory.assertObject(item);
}
workingMemory.assertObject(order.getCustomer());
workingMemory.assertObject(order);

Related link from Roti

Monday, June 12, 2006

Slide with Scheme

ลืมเรื่องนี้ไปเลย
slide presentation ที่ผมแสดงในงาน blognone techday นั้น
เขียนขึ้นด้วย programming language ที่ชื่อ scheme
ซึ่งเป็น lisp dialect หนึ่ง

ว่าจะเขียนอธิบายวิธีใช้ไปหลายทีแล้ว แต่ก็ลืมไปเรื่อย
วันนี้เผอิญอ่านเจอใน Minority Blog
เขามี presentation วิธีเขียน slide ด้วย scheme

สิ่งที่น่าสนใจสำหรับการเขียน slide ด้วย scheme
ก็คือ ด้วยขอบเขตที่ slide พึ่งทำได้ มันไม่เยอะจนเกินไป
ทำให้เราหัดเขีียน scheme ได้โดยไม่หมดแรงไปเสียก่อน

ข้อดีของการหัดเขียน scheme ก็คือมันทำให้เราหัดคิดแบบ functional language,
รู้ว่าเขียนโปรแกรมแบบไม่มี side effect เป็นอย่างไร
ส่วนที่สนุกสุด ก็คงเป็นการ present ออกมาเป็น diagram
เพราะมันไม่ได้เขียนตรงไปตรงมา แบบ Imperative Language

ลองดูตัวอย่าง code

Related link from Roti

Sunday, June 11, 2006

Tapestry4 customize component

วันนี้นั่งเขียน custom component ทั้งวัน
ช่วงแรกๆนี่ฝันร้ายเลย
(ฝันร้ายจริงๆ ไม่ใช่คำเปรียบเปรย
เพราะงีบหลับกลางวันไป โดยในฝันมีแต่
source code ของ tapestry เต็มไปหมด
จนงงเลยว่าอันไหนเป็น source ที่เราเห็นจริงๆ
อันไหนเป็นอันที่จิตนาการเห็นในฝัน
)
โจทย์มีอยู่ว่า ต้องการให้ทำ ajax กรณีที่มี
การเปลี่ยนแปลง ข้อมูลใน textfield
ดูเหมือนง่ายๆ ถ้าใน rails ก็กระดิกนิ้วไม่กี่บรรทัด ก็ได้ ajax แล้ว
  <tr>
<td width="25%">เลขที่บัญชี :</td>
<td colspan='3'><%= text_field 'rec_no', 'firm_acct', :size => 10 %><span id="firm_name"></span>
</td>
</tr>
<%= observe_field('rec_no_firm_acct', :url => {:action => 'firm_entry'}) %>


ส่วน tapestry ก็มี Ajax Library component ที่ชื่อ Tacos
ซึ่งใช้ได้ดีเลยแหล่ะ
แต่ไม่ตรงกับ use case ที่จะใช้ั
ก็เลยเดือนร้อนต้องหาทางเขียน component ขึ้นมาเอง
โดยพยายาม reuse code เก่าของ Tacos ให้มากที่สุด

ความยากของ tapestry component อยู่ที่ต้องเข้าใจ model การทำงานภายใน
ซึ่งมี learning curve ที่สูงใช้ได้เลย
ต้อง debug ไล่ code กันหลายรอบ กว่าจะเห็นภาพว่า
เกิดอะไรขึ้นข้างใน tapestry บ้าง

เอารูป prototype มาให้ดูเล่นๆแล้วกัน
อันนี้ยังไม่ลงเส้นทางที่ต้องไปผ่าน javascript,
ไม่รวมเส้นทางภายในของ tapestry เอง,
กับยังมองไม่เห็นการเชื่อมโยงกับ html ที่เกิดขึ้น
อธิบายไม่ไหว รายละเอียดมันเยอะ

Related link from Roti

Saturday, June 10, 2006

Drools version 3

เคยเขียนตัวอย่างการใช้ Drools ไปแล้ว
ใน "ทดลอง Implement Business Rule ด้วย Drools"
ตอนนั้นยังเป็น version 2 อยู่

มา version ใหม่นี้้, drools ย้ายไปสังกัด jboss แล้ว
ก็เลยได้ชื่อใหม่เป็น JBossRules

feature ที่เปลี่ยนไปอย่างเห็นได้ชัด ก็คือ
เพิ่มวิธีเขียน rule ที่เดิมต้องเขียนอยู่ในรูป xml
มาอยู่ในรูป syntax ที่อ่านง่ายสบายตา
แถมยังมี Eclipse Plugin ที่ช่วยในการพัฒนา rules ให้อีกด้วย

ลองเขียน syntax ใหม่ โดยใช้โจทย์เดิมที่เคยเขียนด้วย xml



หน้าตาดีขึ้นอย่างเห็นได้ชัด อ่านง่ายอีกต่างหาก

นอกจากนี้เรายังสามารถ define DSL ของเราเข้าไปเองได้ด้วย
ลองดู rule อันนี้ เทียบกับข้างบน



ส่วน dsl ก็ define แบบนี้



ใครสนใจก็ลอง load มาเล่นดูได้
โดยตัว plugin ใช้ได้กับ eclipse 3.2 up
(ผมใช้ 3.2RC7)

Related link from Roti

Tuesday, June 06, 2006

ขั้นตอนการทำงานของ CAS

CAS -> Central Authentication Service เป็น opensource ที่ช่วยทำเรื่อง
Single Sign-On
ซึ่งใน Architecture ของมัน สามารถทำได้ทั้งที่ระดับ
browser -> web app (user login ครั้งเดียว ใช้ได้หลาย web app)
web app -> web app (user login ใน web app A, แล้ว web app A call ไปยัง web app B)

ในที่นี้ขออธิบายเฉพาะกรณีแรก

1. เริ่มด้วย user request ไปยัง Web app A.


2. web app ตรวจพบว่า user ยังไม่ login ก็เลยส่ง redirect กลับมา
พร้อมทั้งระบุ parameter ด้วยว่า หลังจาก login แล้วให้กลับมาที่ url ที่ระบุ
3. request วิ่งไปที่ CAS


4. CAS ส่งคืนหน้าจอ login มาให้ user กรอก


5. user submit ข้อมูล user, password


6. กรณีที่ password ถูกต้อง, CAS จะส่ง redirect กลับมาให้ browser
ว่าให้ต่อ request ไปยัง web app A โดยมี parameter ชื่อ ST (service ticket) มาด้วย
7. สิ่งที่ส่งกลับมาพร้อมกับ redirect ก็คือ cookie TGC ที่ใช้แสดงว่า user login แล้ว
8. browser ส่ง request ไปที่ Web app A.
9. web app A ตรวจพบ parameter ST ก็เลยส่งไป validate ที่ CAS
ถ้า validate ผ่าน ก็อนุญาติให้ user เข้ามาใช้งานได้


ที่นี้ ถ้า user เกิดไปเรียกใช้ url ใน Web App B.
10. browser request ไปยัง Web app B.
11. Web app B. ตรวจพบว่ายังไม่เคย login ก็เลย redirect ข้อมูลไปยัง CAS
12. browser ส่ง request ไปยังหน้าจอ login ของ CAS
13. cookie TGC ที่รับไว้ตอนขั้นตอนที่ 7. ถูกส่งตามไปด้วย


14. CAS ตรวจว่า TGC valid หรือไม่ ถ้ายัง valid อยู่ ก็ถือว่าเคย login แล้ว
ก็จะทำการออก ST (service ticket) ให้
และส่ง redirect กลับมายัง browser
15. browser ส่ง request ไปที่ Web app B. พร้อมด้วย ST ที่ได้จากขั้น 14
16. Web app B. ทำการ validate ST ที่ได้กับ CAS
ซึ่งถ้าผ่าน ก็จะถือว่า user login แล้ว

Related link from Roti

Sunday, June 04, 2006

CodeFest

วันที่ 24-25 มิถุนายนนี้ Nectec มีกำหนดจัด CodeFest ครั้งแรกของไทย (เข้าใจว่างั้นนะ)
มีชื่อเป็นทางการว่า การเขียนโปรแกรมมาราธอน
วัตถุประสงค์ก็เพื่อส่งเสริมให้เกิดนักพัฒนาโปรแกรมโอเพนซอร์สมากขึ้น
ฟังชื่องานแล้วไม่ต้องกลัวเหนื่อย งานจัดแค่ 2 วัน 1 คืน
เริ่มเขียนสัก 11 โมง ไปจนถึง เที่ยงของอีกวันหนึ่ง

อยากไปเหมือนกัน แต่ยังนึกไม่ออกเลยว่าจะทำอะไรดี
จะไปช่วยทำ linux หรือ infrastructure, ความรู้ก็ไม่มี

ไว้ไปหยั่งเสียงน้องๆในบริษัทฯก่อน มีใครสนใจบ้าง

Related link from Roti

Saturday, June 03, 2006

CellMode

CellMode เป็น emacs mode ที่หน้าตาดูเหมือน spreadsheet



concept การทำงานของมัน ก็คือ value ที่อยู่ใน cell จะ flow ไปในทิศทาง ขวา และ ล่าง
และมี cell พิเศษที่เป็น processing ซึ่งรับ input จาก cell ที่อยู่บน หรือ ซ้าย ของมัน
ผลลัพท์ที่ได้ ก็ feed ให้กับ cell ด้านล่าง และ ด้านขวา ของตน

ผมก็เลยลองเขียน hello world ฉบับ cellmode ขึ้นมา



โดยถ้าเรา click ที่ cell click จะเกิดการ
run คำสั่ง echo 'helloworld' > /tmp/pok.txt
ให้สังเกตว่ามีการส่งข้อมูลผ่านตัวแปร greeting ด้วย
Note: ส่วนที่เป็นสีเทา ก็คือการ print cell value ออกมาดู,
ส่วนคำสั่ง cons ก็คือการ append item กับ array

สนุกดี จากการพยายามทำความเข้าใจว่ามันคืออะไร
และใช้อย่างไร (มั่วอยู่นาน)

Related link from Roti

TestNG in Maven2

ช่วงนี้อยู่ในช่วง kickoff project ตัวใหม่อยู่
โปรเจคนี้ผมตัดสินใจใช้ Framework, Library Release ใหม่ทั้งหมด
รวมทั้งขยับขึ้นไปใช้ jdk1.5

ศึกแรกที่ต้องเจอ ก็คือ Maven2 กับ TestNG
ตัว TestNG เคยเขียนไปแล้วที่ post นี้ Link
(ผ่านมาปีหนึ่งแล้ว พึ่งได้ใช้จริงๆจังๆ)

ใน maven2 เราสามารถใช้ TestNG ได้เลย (Out of a box)
โดยมีสิ่งที่ต้องทำดังนี้
  • เพิ่ม dependency เข้าไปดังนี้
        <dependency>
    <groupId>org.testng</groupId>
    <artifactId>testng</artifactId>
    <version>4.7</version>
    <scope>test</scope>
    <classifier>jdk15</classifier>
    </dependency>

  • ระบุว่าต้องการใช้ Jdk1.5 ในการ compile
     <build> 
    <plugins>
    <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>2.0</version>
    <configuration>
    <source>1.5</source>
    <target>1.5</target>
    </configuration>
    </plugin>
    </plugins>
    </build>



จากนั้นเมื่อต้องการ run test ก็แค่
mvn test


ดูง่ายๆเลย
ทลลอง run ดู ปรากฎว่า run test ไม่ผ่าน
เปลี่ยนไป run ใน eclipse ด้วย plugin ของ TestNG
อ้าว ผ่านแฮะ

error ที่ปรากฎ

-------------------------------------------------------
T E S T S
-------------------------------------------------------
[surefire] Running pok.MyTest
[surefire] Tests run: 1, Failures: 1, Errors: 0, Time elapsed: 0.072 sec <<<<<<<< FAILURE !!

Results :
[surefire] Tests run: 1, Failures: 1, Errors: 0

ที่หนักว่านั้นก็คือ เมื่อทดลอง copy ไป run บน Linux box บ้าง
(เดิม run บน Mac)
ผลที่ได้

-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running pok.MyTest
Tests run: 0, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.056 sec

Results :
Tests run: 0, Failures: 0, Errors: 0, Skipped: 0

ไม่ error แต่ ไม่ run
อย่างนี้ก็แสดงว่า Surefire
(plugin ของ Maven ที่รับผิดชอบในเรื่่อง Testing)
ต้องการให้เราระบุ สิ่งที่ต้องการจะ run ด้วย
ซึ่งทำได้โดยการเพิ่ม Suite file เข้าไปอีก 1 file
เพื่อระบุสิ่งที่ต้องการ run
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >

<suite name="Suite1" verbose="1" >
<test name="Domain" >
<packages>
<package name="pok.*" />
</packages>
</test>
</suite>

และระบุเพิ่มใน pom ดังนี้
      <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<suiteXmlFiles>
<suiteXmlFile>testng.xml</suiteXmlFile>
</suiteXmlFiles>
</configuration>
</plugin>


ส่วนใน Mac ผมลองลบ repository ของ maven ทิ้ง
สั่งทำงานใหม่ (มันก็จะ load repository มาให้ใหม่)
อาหารหายเป็นปลิดท้ิง
แสดงว่ามีปัญหาเรื่อง version ของ jar file ที่ download มา

เฮ้อ เกือบถอดใจหันกลับไปใช้ JUnit กับ maven1.x เหมือนเดิมแล้วสิ

Related link from Roti

Thursday, June 01, 2006

ปัญหาการเปลี่ยน user ใน subclipse

Subclipse เป็น Plugin ที่ integrate Subversion เข้ากับ Eclipse
วันนี้น้องใหม่ที่บริษัทดันไปใช้ user กับ password ของคนอื่น commit งานเข้าไป
แล้วดันเลือก choice "remember password" ไว้
แต่ปัญหาก็คือ Subclipse ไม่มี menu หรือ dialog ที่ไว้ใช้เปลี่ยน user

ผมก็เริ่มด้วยการมองหา file ที่เก็บ password ก่อน
หลังจากเดาว่า file .keyring นี่แหล่ะ ชื่อมันเหมาะดี ลบทิ้งซะ
ไม่หายแฮะ
เปลี่ยน workspace ใหม่, checkout ออกมาใหม่
มันก็ยังจำ password ตัวเก่าอยู่
ลบ configuration ทั้งหมดใน eclipse installation ทิ้ง
เอ๊ะ มันก็ยังดันจำได้อีก

เข้าไปค้นใน window registry ก็หาไม่เจอ

หลังจากค้นใน net อยู่พักใหญ่
ก็เลยได้ความมาว่า Subclipse มัน delegate งานไปให้
Client Adapter ทำงาน ซึ่ง Adapter มีอยู่ 2 แบบคือ
  • JavaSVN อันนี้เป็น pure Java
  • JavaHL อันนี้ใช้ Native Library ทำงาน

ตามไปอ่านเอกสารของทั้งสองอันแล้ว ก็ยังไม่ได้เรื่อง
เกือบจะ download source code มานั่งไล่แล้ว
แต่นึกวิธีง่ายสุดๆขึ้นมาได้เสียก่อน

วิธีที่ง่ายที่สุดก็คือไปเปลี่ยน password บน server เสีย
พอ subclipse เจอว่ามีการเปลี่ยน password
มันก็จะ prompt ขึ้นมาให้ใส่ user, password ใหม่

เฮ้อ..

Related link from Roti

Wednesday, May 31, 2006

Yaws vs. Apache

วันก่อนที่พูดถึง Erlang ไป
ซึ่งได้ยกตัวอย่างการ implement server push
โดยใช้ Yaws เป็น Web Server
และได้บอกไว้ว่า performance ของ Yaws สูสี กับ apache

ที่ตอนนั้นบอกเช่นนั้น ก็เพราะได้ทดลองทำ load testing ง่ายๆ เทียบกันระหว่าง apache กับ yaws
แต่เนื่องจากมีเครื่องเดียว ก็เลย simulate load ได้ไม่เยอะ
ก็เลยไม่เห็นความแตกต่าง

วันนี้อ่านเจอ บทความที่เขา test Apache vs. Yaws
แบบจริงๆจังๆ ใช้เครื่อง 16 เครื่อง simulate load
ก็เลยรู้ว่า Yaws รับ load ได้มากกว่า apache เยอะเลย

ตรง comment เขาบอกไว้ว่า
The problem with Apache is not related to the Apache code per se but is due to the manner in which the underlying operating system (Linux) implements concurrency. We believe that any system implemented using operating system threads and processes would exhibit similar performance. Erlang does not make use of the underlying OS's threads and processes for managing its own process pool and thus does not suffer from these limitations.

Related link from Roti

Ice Lens

Matthew Wheeler
photographing the natural beauty of his surroundings through the ice lenses he made.


Related link from Roti

Operator And, Or -- Haskell vs. Java

ผมชอบความรู้สึกประหลาดใจ เวลาเรียนรู้ Haskell
หรือพวก Functional Language ทั้งหลาย
ก็คือเราจะเจอแนวคิดบางอย่างที่เราไม่เคยคิดมาก่อน
อย่างวันนี้เจอ วิธีการ define operator && (and)
กับ operator || (or) ของ Haskell
-- define Type --
-- operator นี้รับ parameter เป็น boolean, boolean
-- และ return เป็น type boolean
(&&), (||) :: Bool -> Bool -> Bool

False && x = False
True && x = x
False || x = x
True || x = True

จะเห็นว่ามีการใช้ Pattern Matching เข้ามาใช้
x ก็คือ Any Value

เทียบกับพวก imperative language
ในที่นี้จะยกตัวอย่าง Java แล้วกัน
เวลานึกถึง operator พวกนี้ (and ,or) ว่า compiler จะแปลงมันอย่างไร
มันจะลงเอยไปที่ระดับ assembly
ที่จะมี compare กับการ jump
ลองดูประโยคนี้ข้อง java
return x && y;

เมื่อแปลงเป็น byte code
   L0 (0)
LINENUMBER 5 L0
ILOAD 1: x # load ค่าใน local var ขึ้น operand stack
IFEQ L1 # ถ้าเป็น 0 กระโดดไป L1
ILOAD 2: y
IFEQ L1
ICONST_1 # return true
IRETURN
L1 (7)
ICONST_0 # return false
IRETURN

Related link from Roti

Monday, May 29, 2006

Conkeror

Conkeror คือ Firefox extension
ที่จะช่วยให้เราใช้ Firefox โดยไม่ต้องแตะ mouse เลย
ลองดู www.google.com ที่ load ขึ้นมาด้วย Conkeror



อยากกด link ไหน ก็ใส่ตัวเลขลงไปได้เลย

keyboard binding ของ Conkeror เดินตามรอย Emacs ทุกอย่าง
ไม่ว่าจะเป็น C-x,C-f เพื่อเปิด url
C-x-b เพื่อ switch buffer

ใครเบื่อ mouse ก็ลองเอาไปเล่นดู

Related link from Roti

Sunday, May 28, 2006

Rails Memo #3

ความเดิม


วันนี้จะว่าด้วยเรื่อง Ruby บ้าง
(เนื่องจาก Rails ใช้ Ruby ดังนั้นจึงหลีกไม่พ้น ที่เราต้องเรียนรู้ syntax ของ ruby)

หัวข้อที่หลายคนสับสนในตอนเรียน Rails
ก็คือ การเรียกใช้ method ของ Model จาก controller
เช่น สมมติเรามีหน้าจอที่แสดงสินค้าทั้งหมดที่เรามี

ในฝั่ง controller ก็จะเขียนประมาณนี้
def index
@products = Product.find(:all)
end

Product ที่เห็นใน code ก็คือ ชื่อของ Class ที่เราประกาศเป็น model ไว้
ส่วน find ก็คือ class method ของ Product

class method ก็คือ method ที่เรียกใด้จาก Class ตรงๆ
ส่วน instance method ก็คือ method ที่เรียกจาก object
ลองดูตัวอย่าง
class Dog  
def initialize(name)
@name = name
end

def say
"hong"
end

def Dog.myDog()
Dog.new("bogy")
end
end

ในตัวอย่างข้างบน method say ก็คือ instance method
ส่วน method myDog ก็คือ class method
เวลา run ก็จะเป็นแบบนี้
irb(main):033:0> d = Dog.myDog
#<Dog:0x58f14 @name="bogy">
irb(main):034:0> d.say
"hong"


Syntax ของ ruby กำหนดไว้ว่า
ชื่อ class ต้องขึ้นต้นด้วยตัวใหญ่
ส่วนชื่อ method ไม่ได้บังคับ แต่ส่วนใหญ่นิยมใช้ตัวเล็ก

ส่วน Syntax ของการประกาศ class method
มีทั้งหมด 4 แบบใหญ่ๆ
อ่านได้ในเอกสาร ClassMethods
(อ่านถึงตรงนี้ แฟนๆ python คงโห่กันใหญ่แล้ว
อะไรกัน simple แค่นี้ ทำไมต้องเขียนได้ตั้ง 4 วิธี)

Related link from Roti

Erlang

หนึ่งในคำแนะนำของบทความ Teach Yourself Programming in Ten Years
ก็คือ
Learn at least a half dozen programming languages. Include one language that supports class abstractions (like Java or C++), one that supports functional abstraction (like Lisp or ML), one that supports syntactic abstraction (like Lisp) ...

เนื่องจากผมยังรู้ไม่ครบ 6 ภาษา
Erlang ก็เลยเป็นหนึ่งในภาษาที่ผมสนใจ
เพราะคิดว่าน่าจะสามารถนำมาประยุกต์กับงานที่ทำอยู่ได้ (server side)
เนื่องจาก Erlang มีชื่อเสียงในด้าน Concurrency, Distribution, Soft real-time
(อ่านโฆษณาของเขาได้ใน White Paper)

วิธีที่ดีที่สุดในการเรียนรู้ภาษาใหม่ก็คือ
การหาตัวอย่างการใช้งานจริง ที่เรารู้วิธี implement อยู่ก่อนแล้ว
ตัวอย่างที่ผมใช้สำหรับการเรียน Erlang ครั้งแรกก็คือ
Pushing events to the browser via Ajax
จาก blog BLUISH CODER
ซึ่งเป็นตัวอย่างการของใช้ long-lived ajax connection
มา implement server push

ตัวอย่างของเขา เขาใช้ yaws เป็น server
yaws ย่อมาจาก "Yet Another WebServer"
เป็น server ที่เขียนด้วย Erlang
โดยออกแบบมา เพื่อให้ serve dynamic page ที่เขียนด้วย Erlang
แต่การตอบสนองต่อ static page ก็ทำได้ดีมากเลย
(ไม่แพ้ apache)

มาลองดูตัว code กัน
(ขอแนะนำว่าให้ใช้ เอกสาร document-part-1 อ่านประกอบไปด้วย)
ในตัวอย่างที่ download มา เขาจะเริ่มต้นด้วยการ start process ขึ้นมา 2 process
process แรกทำหน้าที่ generate random page id เพื่อใช้จำแนก page ที่ต่อเข้ามายัง server
ส่วน process ที่สอง ทำหน้าที่เป็น register process
ที่ใช้ลงทะเบียนว่า ตอนนี้มี page ไหนกำลังต่อมาที่ server อยู่บ้าง
%% Start all processes used by this module.
start() ->
register(page_registry, spawn(html_rpc, page_registry, [[]])),
register(random_name, spawn(html_rpc, random_name, [])).

Note: คำสั่ง spawn ก็คือการแตก process ออกมา
ผลลัพท์ที่ได้จากคำสั่ง spawn ก็คือ pid (process id)
จะถูก map เข้ากับ naming ผ่านทางคำสั่ง register
การที่เราต้อง map เลข pid เข้ากับ name ก็เพื่อให้สะดวกต่อการ
ติดต่อกับ process นั้นๆ


การทำงานเริ่มจาก user request ขอหน้าจอหลักเข้ามา
เราก็จะทำการ generate page_id แปะกลับไปด้วย
<html>
<head>
<title>html_rpc Test1</title>
<script type="text/javascript" src="html_rpc.js"></script>
</head>
<body>
<script type="text/javascript">
<erl>
# out เป็นชื่อ function ที่ใช้ output ผลลัพท์ (เหมือนพวก System.out, Writer, หรือพวก output stream ใน java)
# แต่อันนี้เขียนใน style ของ callback
# ก็คือ yaws จะเรียกใช้ out เมื่อต้องการผลลัพท์
out(A) ->
Name = html_rpc:get_random_name(),
# เก็บตัวแปรไว้ใน scope ของ Page
# เพื่อที่ dynamic block อื่นๆ จะสามารถใช้ ตัวแปรนี้ได้
put(page_name, Name),
{html, "var page = '" ++ Name ++ "';"}.
</erl>
function process_message(message) {
message()
}
html_rpc_receive("ajax1.yaws?page="+page, process_message);
</script>

Note: วิธีการเขียน dynamic html page
ของ yaws ก็เหมือนด้วย jsp หรือ rhtml
นั่นคือมีลักษณะเป็น template


เมื่อ browser ได้รับ page แล้ว ก็จะเกิดการ evaluate javascript
ซึ่งจะทำการ call กลับมายัง browser ผ่านทาง XmlHttpRequest
โดยมีการ pass ค่า page id มาด้วย

ที่ฝั่ง server เมื่อได้รับ request ของ ajax มา
ก็จะแตก process ออกมาอีก เพื่อรองรับ request นั้นๆ

<erl>
out(A) ->
# เริ่มด้วยการ get request parameter ที่ pass มาจาก browser
# เก็บไว้ในตัวแปรชื่อ Page
{ok,Page} = queryvar(A, "page"),
# เรียกใช้ function start_ajax_handler ใน module html_rpc
# โดย pass function handler ไปด้วย (callback function)
html_rpc:start_ajax_handler(Page, fun(A,B,C) -> handler(A,B,C) end).

# function นี้จะทำการ generate javascript ส่งกลับไปยัง browser
handler(Yaws_Pid, Page, Message) ->
case Message of
{alert, Value} ->
"function() { alert('" ++ Value ++ "'); }"
end.

</erl>

ตามไปดู function start_ajax_handler
start_ajax_handler(Page, Callback) ->
# เก็บค่า pid ของตัวเองไว้ใน variable Yaws_Pid
Yaws_Pid = self(),
# แตก process อีกแล้ว โดยเรียกใช้ anonymous function
spawn(fun() ->
# ส่ง message ไปยัง process ที่ register ไว้ในชื่อ page_registry
# โดย pass tuple ที่มี name 'add' , process id ของตัวเอง
# เครื่องหมาย ! เป็น syntax ที่ใช้ในการส่ง message ไปยัง process
page_registry ! {add, Page, self()},
# เรียกใช้ function ที่ทำหน้าที่รับ message ที่ส่งมาให้ process นี้
ajax_handler(Yaws_Pid, Page, Callback)
end),
# อันนี้คือ ผลลัพท์ที่ตอบกลับไปยัง browser เลยทันที (เนื่องจากคำสั่ง spawn มันจะ return กลับมาทันที
[{header, {cache_control, "no-cache"}},
{streamcontent, "text/html", "[ "}].

หน้าตาของ function ajax_handler ที่ทำหน้ารอรับ message ที่จะ push กลับไปยัง browser
ajax_handler(Yaws_Pid, Page, Callback) ->
# receive เป็นการ lookup message queue ของตัวเอง
receive
# ถ้ามี message ที่ชื่อ stop ก็ให้หยุดการทำงาน
# โดยการ close stream ที่เปิดมาจาก browser
%% Stop the iframe handler and inform the HTML page we are
%% finished streaming.
stop ->
yaws_api:stream_chunk_deliver(Yaws_Pid, "true, true ]"),
yaws_api:stream_chunk_end(Yaws_Pid),
page_registry ! {remove, {pid, self()}};
# X แทน any message
# ลักษณะการทำงาน จะเป็นการ delegate message ต่อไปยัง Callback function
# เสร็จแล้วก็ให้จบ process และ unregister page id
X ->
Result = Callback(Yaws_Pid, Page, X),
yaws_api:stream_chunk_deliver(Yaws_Pid, "false, " ++ Result ++ " ]"),
yaws_api:stream_chunk_end(Yaws_Pid),
page_registry ! {remove, {pid, self()}}
# อันนี้สิ สุดยอด
# เพื่อให้รักษา long-lived connection ไว้
# ถ้าครบ 5 วินาที แล้วยังไม่มี message มา
# ก็ให้ส่ง newline กลับไปยัง browser
# after เป็น syntax ที่ใช้ระบุพวก timeout
after 5000 ->
case yaws_api:stream_chunk_deliver_blocking(Yaws_Pid, "\n") of
ok -> ajax_handler(Yaws_Pid, Page, Callback);
{error,{ypid_crash,_}} -> page_registry ! {remove, {pid, self()}}
end
end.

หลังจากที่ browser load page ไป แล้วทำการเปิด connection รอ server push แล้ว
ถ้า server ต้องการ push ข้อมูลกลับ ก็จะเรียกใช้คำสั่งนี้
html_rpc:send("443584617445720273715201", {alert, "Hello World"}).

ตัวเลขยาวๆนั่นคือ page id
การทำงานของ send ก็คือ

%% Send a message to the page.
send(Page, Message) ->
# เรียกขอ process id ที่รอรับ messagae จาก page_registry
# ให้สังเกตุว่า ไม่มีการ return ค่ากลับ แต่ใช้วิธี รอรับ message ที่ตอบกลับมาจาก page_registry
page_registry ! {self(), get, Page},
# รอรับ messagae จาก page_registry
receive
{result, {_, Pid}} ->
# หลังจากได้ pid จาก page_registry
# ก็จะ dispatch ส่ง message ไปยัง process นั้น
Pid ! Message
end.


โอยอธิบายยากจริงๆ
10 ปากว่า ไม่เท่าลงมือทำ
ลอง run ดูแล้วจะเห็นการทำงานของมันครับ

Related link from Roti

Friday, May 26, 2006

Climbing

สาวน้อยน่ารักคนนี้ อายุแค่ 16
แต่ปีน overhang เหมือนเป็นของเล่นๆ
ภาพจากการแข่งขัน bouldering เมื่อวันอาทิตย์ที่ผ่านมา



ผมลองเล่นกีฬานี้ครั้งแรก เมื่อนานมาแล้ว อาจจะ 8-9 ปีมาแล้ว
เริ่มจากไปเที่ยว Railay กระบี่ แหล่งปีนเขาที่ติด 10 อับดับแรกของโลก
ไปทดลองเล่น โดยมีอาจารย์เจ้าถิ่น สาวสวย หุ่นดี ชื่อรำพึง เป็นคนสอนให้

ผมเป็นคนกลัวความสูง
ยิ่งปีนสูงก็ยิ่งเสียว
ยังจำการปีนแบบ multi-pitch ซึ่งเป็นการปีนระยะที่สูงเกินกว่าความยาวเชือก
เลยต้องมีการสลับปลายเชือกกันกลางทาง
เหงื่อนี่ไหลเป็นน้ำเลย กลัวผิด
clip ผิด แกะผิด มันจะล่วงลงมาตายเอาได้

Related link from Roti

ติดตั้ง Maven-Proxy

วันนี้นั่งติดตั้ง Maven-Proxy
link เอกสารการ config ก็ดัน error 404 อีก
ก็เลยต้องมองหาตัวอย่างจาก testcase ของเขาแทน

สรุปขั้นตอนการติดตั้ง
1. download Maven-Proxy ซึ่งเลือกได้ 2 แบบคือ
เป็น War file กับ Stand-alone application
ผมเลือกใช้แบบ Stand-alone
2. สร้าง configuration file หน้าตาประมาณนี้
Link
3. สั่ง start server ด้วยคำสั่ง
java -jar maven-proxy-standalone-0.2-app.jar maven-proxy.properties


ในฝั่ง client ก็ให้ไป set ใน file
~/.m2/settings.xml
เพิ่มส่วน Mirrors เข้าไป
<settings>
.
.
<mirrors>
<mirror>
<id>maven-proxy</id>
<name>maven proxy</name>
<url>http://myserver:8888/repository</url>
<mirrorOf>central</mirrorOf>
</mirror>
</mirrors>
.
.
</settings>


Note: maven1 กับ maven2 มี repository structure คนละแบบกัน
เวลา config server ก็ต้องเลือก repository ให้ถูก version ด้วย

Related link from Roti