Thursday, February 14, 2008

Git กับการ revert

ใน svn เวลาเราทำอะไรผิดผลาด แล้วเราอยากย้อนกลับเนื้อหา file
มันก็มี case อยู่แค่ 2 case คือ
  1. file ยังไม่ถูก commit
    อันนี้ง่ายเลย แค่ใช้ svn revert ก็จบ
  2. file ถูก commit เข้าไปแล้ว
    อันนี้ก็ง่าย ก็แค่ revert โดยระบุ revision ที่เราต้องการ

    svn update -r REVISION_ID



ส่วนใน git นั้น มันไม่ง่ายเหมือน svn เพราะว่า มันมี state มากกว่า svn
โดย git มันจะมี state ของ file ได้ดังนี้
  • สถานะปกติ ยังไม่ถูกแก้ไข
  • ถูกแก้ไขแล้ว แต่ยังไม่ได้ add เข้า index (index ของ git เปรียบเสมือน buffer area ที่รอการ commit)
  • add เข้า index แล้ว แต่ยังไม่ได้ commit


จะเห็นว่ามี state ที่ content ค้างอยู่ใน index แต่ยังไม่ถูก commit เพิ่มเข้ามา(เมื่อเทียบกับ svn)
ดังนั้นถ้าเราจะ revert มันก็ต้องคิดว่าจะ revert ขั้นไหน เช่น

  1. file นั้นยังไม่ได้ถูก add เข้า index กรณีนี้ใช้คำสั่ง
    git checkout -- FILENAME

  2. file นั้นถูก add เข้า index แล้ว แต่ต้องการเอาออกมาจาก index
    git reset

    (state จะกลับไปเหมือนในข้อ 1)
  3. file นั้นถูก commit ไปแล้ว แต่ต้องการย้อนกลับออกมาสู่สถานะที่ file นั้นอยู่ใน index
    git reset --soft COMMIT_ID

  4. file นั้นถูก commit ไปแล้ว แต่ต้องการย้อนออกมาสู่สถานะก่อนการ add เข้า index
    ให้ทำคำสั่งในขั้น 3 แล้วต่อด้วยคำสั่งในขั้น 2
  5. file นั้นถูก commit ไปแล้ว และต้องการย้อนกลับมาสู่ version ก่อนหน้านั้น โดยเก็บ track การ revert ไว้ใน history ด้วย
    git revert COMMIT_ID

  6. file นั้นถูก commit ไปแล้ว แต่ต้องการย้อนไปสู่ version(ใดๆ) ก่อนหน้านั้น โดยไม่ทิ้งร่อยรอย
    git reset --hard COMMIT_ID



Note: ผมใช้คำว่า file ซึ่งไม่ถูกต้อง จริงๆต้องใช้คำว่า content (อ่านได้ใน "Git อีกทีน่า")
แต่เพื่อให้สื่อสารได้ง่ายขึ้น ขออนุญาติใช้คำว่า file

Related link from Roti

Wednesday, February 13, 2008

Open class

Ruby

>> class Integer
>> def fact
>> (2..self).inject(1) {|acc,v| acc * v}
>> end
>> end
=> nil
>> 7.fact
=> 5040


Grovvy

groovy:000> Integer.metaClass.fact = {   
groovy:001> delegate == 1 ? 1 : delegate * (delegate-1).fact()
groovy:002> }
===> groovysh_evaluate$_run_closure1@89ff97
groovy:000> 7.fact()
===> 5040


Scala

scala> class IntExt(v:Int) {             
| def fact() = {
| (2 to v).foldLeft(1) (_ * _)
| }
| }
defined class IntExt

scala> implicit def FactExt(v:Int) = new IntExt(v)
FactExt: (Int)IntExt

scala> 7 fact
res33: Int = 5040


เจ้า scala นี่แปลกตรงมันใช้หลักการ Implicit conversions
อย่างประโยค
7 fact จริงๆคือ
7.fact()
แต่ method fact มันไม่มีใน class Int
ดังนั้น compiler จะดูว่ามี Implicit conversions rule อะไรอยู่บ้าง
ในกรณีนี้ ก็คือจะแอบแปลงให้เป็นแบบนี้
(new IntExt(7)).fact()

Related link from Roti

Friday, February 08, 2008

Here documents

พึ่งรู้ว่า Here documents ของ Ruby ทำอะไรแปลกๆแบบนี้ได้ด้วย
puts 'Produce', '-------', <<PRODUCE, 'Dairy', '-----', <<DAIRY, '* Organic'
1. Strawberries*
2. Blueberries

PRODUCE

1. Yogurt
2. Milk*
3. Cottage Cheese

DAIRY

ใน rails (postgresql adapter) ก็มีใช้เหมือนกัน Charlie เขาชี้ให้ดู

Related link from Roti

Thursday, February 07, 2008

haskell -> python -> ruby -> ...

วันนี้เจอโปรแกรม haskell เขียนโดย sigfpe


def q(a,b,c):print b+chr(10)+'q('+repr(b)+','+repr(c)+','+repr(a)+')'
q("def q(a,b,c):print b+chr(10)+'q('+repr(b)+','+repr(c)+','+repr(a)+')'","def e(x) return 34.chr+x+34.chr end;def q(a,b,c) print b+10.chr+'main=q '+e(b)+' '+e(c)+' '+e(a)+' '+10.chr end","q a b c=putStrLn $ b ++ [toEnum 10,'q','('] ++ show b ++ [','] ++ show c ++ [','] ++ show a ++ [')']")

ทายสิว่ามันทำอะไร
ลอง copy ไปใส่ file ชื่อ x.hs ดู
แล้วลอง run แบบนี้

$ runhaskell x.hs > x.py
$ python x.py > x.rb
$ ruby x.rb > y.hs

$ runhaskell y.hs > y.py
$ python y.py > y.rb
$ ruby y.rb > z.hs

... (ทำไปเรื่อยๆ จนกว่าจะเบื่อ)

Related link from Roti

Friday, February 01, 2008

วันโชคร้ายของจักรยาน

ช่วงนี้หลังจากย้ายบ้านไปอยู่แถวเกษตร-นวมินทร์แล้ว
ผมก็ขี่จักรยานไปทำงานทุกวัน
ระยะทางกำลังดี ก็คือประมาณ 5 โล
ทำให้เหงื่อออกไม่เยอะ


View Larger Map

ขาไปทำงาน ส่วนใหญ่จะขี่แบบเรื่อยๆเฉื่อยๆ เพราะพึ่งกินอิ่ม
ส่วนขากลับนี่ ทางมันตรง ก็เลยใส่เต็มที่ ลมออกหูทุกวัน (เหนื่อย)

ส่วนวันนี้ ค่อนข้างโชคร้ายหน่อย
ออกจากบ้านมาได้หน่อยเดียว ยางดันแตก
ก็เลยต้องเข็นกลับไปปะยางที่บ้าน
ดูบาดแผลแล้วเกิดจากเศษแก้วแตก

หลังจากปะเสร็จก็ออกมาอีกครั้ง
ปั่นมาถึงแถววังหิน
คราวนี้ โซ่ขาด
โชคดีได้ร้านซ่อมมอเตอร์ไซด์แถวนั้น จัดต่อโซ่ให้

ปล. เมื่อต้นอาทิตย์ ขี่จักรยานไปเจอเจ้าปั้นเหน่งกำลังข้ามถนน
สอบถามได้ความว่า codegent ตั้งอยู่แถวนี้ด้วย
เห็นชุดทำงานมันแล้ว ต้องอิจฉา บริษัทอะไรใส่เสื้อกล้ามโชว์รักแร้ไปทำงานได้ด้วย

Related link from Roti

Monday, January 28, 2008

Y combinator in BarcampBangkok

คำเตือน
post นี้อาจทำให้เกิด stack overflow ในสมองคนอ่านได้ (เหมือนที่เกิดกับคนเขียนไปแล้ว)

กลับจาก BarcampBangkok ด้้วยสมองอันหนักอื้ง
สิ่งหนึ่งที่ยังติดใจไม่หายก็คือ source code ของ Haskell หน้าตาแบบนี้
(จาก presentation เรื่อง Yoda in Haskell ของ Ben)
fix f = let x = f x in x

โชคดีที่เหลือบมองเห็น comment ที่ Ben เขาเขียนเหนือ function นี้ มันมีคำว่า Y combinator อยู่ด้วย

เรื่อง Y combinator เคยผ่านตามาหลายครั้งแล้ว แต่ไม่เคยทำความเข้าใจได้สักที
วันนี้เลยได้ฤกษ์การพยายามครั้งใหม่

จากการ search google ก็พบว่า code ข้างบนนั้น มันมีชื่อเรียกว่า
Fixed point combinator
หลักการมันประมาณนี้

เริ่มที่คำว่า Fixed point ก่อน
fix point ก็คือ ค่าที่ทำให้ผลลัพท์ของ function มีคุณสมบัติแบบนี้
f(x) = x

ในทางคณิตศาสตร์ สมมติเรามี function อยู่อันหนึ่งซึ่งมี definition ดังนี้
f(x) = x^2

ค่า fixed point คือ function นี้ก็คือ 0 และ 1
เพราะ 0^2 == 0 และ 1^2 = 1

ใน functional language ทุกอย่างเป็น function
ดังนั้น เมื่อมี function ก็แสดงว่า มันก็ต้องมี fixed point ได้เหมือนกัน
ลองนึกถึง higher-order function f ที่รับ parameter เป็น function
มันก็ควรจะมี fixed point function p ซึ่ง
f(p) = p


Y combinator ก็คือ function g ซึ่งรับ parameter เป็น function f
ผลลัพท์ที่ได้ ก็คือ function p ซึ่งเป็น fixed point ของ function f
p = g(f), f(p) = p

ลองอ่านคำอธิบาย contract ของ Y combinator เทียบกับ function ข้างบน
Give me myself and a higher order function f, and I’ll return to you a function that is a fixed point of f.


ถ้าลองแทน function ข้างบนไปมา ก็จะได้
f(g(f)) = g(f)

อ้าจ้องไปจ้องมา แล้วมันก็คือ let x = f x in x นั่นเอง

ประเด็นหลักๆของไอ้ Fix ก็คงจะอยู่ที่เรื่อง Lambda expression
เนื่องจากเราไม่สามารถเขียน recursive function ในรูป lambda expression ได้
ใครอยากรู้ลองอ่านไอ้นี่เอาแล้วกัน
http://en.wikipedia.org/wiki/Lambda_abstraction#Recursion

ลองดูตัวอย่าง่ factorial บ้าง
ปกติเราเขียน factorial อย่างนี้

fact n = if n == 0 then 1 else n * fact (n-1)


ลองปรับ form ให้เป็น lambda expression โดยสร้าง function genfact ขึ้นมา

genfact f n = if n == 0 then 1 else n * f(n-1)

ลอง apply fix เข้าไป

fix genfact 3 = fact (fix genfact) 3
= 3 * ((fix genfact) 2)
= 3 * (genfact (fix genfact) 2)
= 3 * 2 * ((fix genfact) 1)
= 3 * 2 * 1 * ((fix genfact) 0)
= 3 * 2 * 1 * 1
= 6


พอแล้ว stack ผม overflow แล้ว

Related link from Roti

Tuesday, January 22, 2008

Mixin - Thin & Think interface

เห็น Bow เขียนเรื่อง Mixin กับ python ไป
ก็เลยคิดว่าจะลองขยาย Idea ของเรื่องนี้ดู

ในหนังสือ Programming in Scala ที่ผมกำลังอ่านอยู่
เขาพูดถึง Mixin ในประเด็น
Thin versus Thick interfaces

Thick interface คือ interface ที่อุดมสมบูรณ์ เพรียบพร้อมไปด้วย method, พร้อมจะให้ developer เด็ด method ที่ต้องการไปเชยชม
ส่วน Thin interface คือ interface ที่มี minimum method
อยากได้อะไรที่เกินกว่าที่จัดหาให้ ก็ออกแรงเขียน code เอาหน่อย
เมื่อเทียบ ruby กับ java แล้ว
java จะออกไปในแนว Thin interface
ส่วน ruby จะออกไปในแนว Think interface

ในหนังสือเขายกตัวอย่างง่ายๆคือ
interface Ordered {

}

ถ้าเป็น Thin interface แล้วหล่ะก็
method ที่ควรจะมี ก็น่าจะมีแค่นี้ก็พอแล้ว
interface Ordered {
/**
* return -1 if this
< that
* return 0 if this == that
* return 1 if this
> that
*/

public int compare(Ordered that);
}

ถ้า developer อยาก check เงื่อนไข <= ก็ให้ทำแบบนี้เอาเอง
if (this.compare(that) <= 0) {
...
}

แต่ถ้ามองแบบ Think interface ก็ควรจะเป็นแบบนี้
// สมมติว่า java define operator ได้
interface Ordered {
public boolean <= (Ordered that);
public boolean < (Ordered that);
public boolean > (Ordered that);
public boolean >= (Ordered that);
}


Mixin ก็เข้ามามีบทบาทตรงนี้แหล่ะ
กรณี scala เราเขียน Mixin โดยใช้ keywork ว่า Trait ได้ดังนี้
trait Ordered[a] {
// abstract method, class ที่ include mixin นี้ ต้อง implement method นี้
def compare(that: a): Int
def < (that: a): Boolean = (this compare that) < 0
def > (that: a): Boolean = (this compare that) > 0
def <= (that: a): Boolean = (this compare that) <= 0
def >= (that: a): Boolean = (this compare that) >= 0
}

จะเห็นว่า operator 4 ตัวหลังนี้ ถือว่าเป็นของแถม

ตัวอย่างของ Mixin ยอดนิยมของ Ruby ก็คือ Enumerable
class ที่ include Enumerable ไป จะต้อง implement method บังคับ 2 method
ก็คือ each กับ <=>
แต่หลังจาก include แล้ว ก็จะมี method ให้ใช้เพิ่มขึ้นมาอีก 22 method.
(น่าจะถือเป็นตัวอย่างที่ดีของ Think interface)

Related link from Roti

Thursday, January 17, 2008

หนังสือ The Rails Way

เห็นชื่อคนเขียนแล้วสนใจ, น้อง Roof ก็เลยแวะเอาหนังสือมาให้ผมยืมอ่านดู
เปิดอ่านบทแรกก็ประทับใจเลย เพราะมันว่าด้วย process bootstrap ของ Rails
นั่งอ่านได้อย่างเพลิดเพลิน เพราะคนเขียนรู้จริงและรู้ลึก

อย่างบทที่สองที่ว่าด้วย Controller ก็มีพูดถึงการ send_file จาก controller
คนเขียนก็พูดถึงว่า method send_file ถ้าเราไปใช้ใน mongrel
มันจะ load เนื้อหาทั้งหมดเข้ามาไว้ใน memory
ทำให้มีปัญหากับ file ขนาดใหญ่ๆได้
เขาแนะนำทางออก ด้วยการเปลี่ยนไปใช้ กลไก X-Sendfile ซึ่ง support บน Apache(module mod_xsendfile), Lighttpd
ด้วยการ ส่ง response ที่มี header เป็น

response.headers['X-Sendfile'] = path_to_file

เจ้า web server ก็จะจัดการส่ง file กลับไปให้เราเอง

หลังจากอ่านจบบท ผมก็เข้าไป check feed
ก็พบว่าใน Rails edge พึ่งมีการ submit changeset 8628
ซึ่งเป็นการปรับปรุงคำสั่ง send_file ให้มี option แบบนี้

send_file '/path/file.png', :x_sendfile => true, :type => 'image/png'

นี่ถ้าไม่อ่านมาก่อน ก็คงไม่ได้สนใจเจ้า feed นี้แน่ๆเลย

ตอนนี้วางแผนคร่าวๆไว้ว่า จะพยายามอ่านให้ได้วันละ 1 บท
จะได้รีบเอาไปคืนน้อง Roof

Related link from Roti

Tuesday, January 15, 2008

แปลงเลข Roman

หลายคนคงเคยเห็นโจทย์ประเภท

"จงเขียนโปรแกรมแปลงเลขที่รับเข้ามาให้เป็นเลข Roman"

วันนี้ผมผ่านไปเห็น haskell code ที่เขียนโดย geezusfreeek ใน reddit
แล้วก็มี Bill Mill ช่วยเขียนอธิบายการทำงาน

เห็นแล้วงามจับใจ

ถัดไปอีกวันหนึ่ง David pollak ก็เขียนด้วย scala บ้าง

Related link from Roti

Monday, January 14, 2008

Server in the house

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

วันนี้เห็นนิทานเล่มนี้ใน net แล้วอดหัวเราะไม่ได้



ลองตามไปอ่านดู Link
ในหน้า 3 จะเห็นว่าเป็นหนังสือของ Microsoft, Windows Home Server

Related link from Roti

Friday, January 11, 2008

Qi4j

Qi4j ออกเสียงว่า "chee for jay"
ความพยายามใหม่ของ Rickard Öberg (co-founder of JBoss)
ที่ตั้งชื่อว่า "Composite Oriented Programming"
โดยมีฐานต่อยอดจาก Java
Qi4j is a framework for domain centric application development, including evolved concepts from AOP, DI and DDD.


แนวคิดหลักๆคือ
1. fine-grained parts, แยกส่วน model ออกเป็นหลาย part เช่น behavior part กับ state part
แล้วเวลาใช้ ค่อยนำมาประกอบกันในลักษณะของ Composite
การแยกส่วนแบบนี้เชื่อว่า จะนำมาสู่การ reuse ที่ดียิ่งขึ้น
2. Context-base behavior, composite เดียวกัน แต่ถ้าอยู่ใน context ที่ต่างกัน
ก็จะมี behavior ที่ต่างกันได้

ลองดูเฉพาะข้อ 1 กันก่อน
ถ้าใครคุ้นกับ ruby, พออ่านข้อ 1 แล้วอาจจะนึกถึง Mixin
ลองเปรียบเทียบกับตัวอย่างใน tutorial ที่ว่าด้วย HelloWorld ของ Qi4j ดู

ปกติ HelloWorld model ถ้าเขียนแบบปกติ ก็เขียนได้ดังนี้
public class HelloWorld {
String name;

public String say() {
return "hello " + getName();
}

public void setName(String name) {
this.name = name;
}

public void getName() {
return name;
}
}

ถ้าเราแยก HelloWorld ออกเป็นส่วนๆ เราจะเห็นว่า มันประกอบด้วย
behavior part ซึ่งก็คือ method say
กับ state part ซึ่งก็คือ property name

เขียนแบบ Qi4j ก็จะต้องทำดังนี้
(ใน Qi4j เขามี concept ว่า "Classes ard dead, Long live interfaces.")

เริ่มด้วย declare model เราด้วย interface
public interface HelloWorld
extends HelloWorldBehaviour, HelloWorldState
{
}

@Mixins( HelloWorldStateMixin.class )
public interface HelloWorldState
{
Property<String> name();
}

@Mixins( HelloWorldBehaviourMixin.class )
public interface HelloWorldBehaviour
{
String say();
}

implement logic ของ behavior
public class HelloWorldBehaviourMixin
implements HelloWorldBehaviour
{
@ThisCompositeAs HelloWorldState state;

public String say()
{
return state.phrase() + " " + state.name();
}
}

implement logic ของ state
public class HelloWorldStateMixin
implements HelloWorldState
{
@PropertyField Property<String> name;

public Property<String> name()
{
return name;
}
}

สุดท้ายประกอบทั้งหมดเข้าหากัน
public interface HelloWorldComposite
extends HelloWorld, Composite
{
}


ลองเปรียบเทียบกับการเขียน Mixin ด้วย ruby ดูบ้าง
module HelloState
attr_accessor :name
end

module HelloBehavior
def say
puts "hello #{@name}"
end
end

class HelloWorld
include HelloState
include HelloBehavior
end


ตอนนี้ tutorial ในส่วน context base behavior ยังไม่ออกมา
ก็ต้องรอดูกันต่อไป ว่าจะออกมาหน้าตาเป็นอย่างไร

Related link from Roti

Thursday, January 10, 2008

ปวดหัวกับ PXE

ช่วงนี้ผมกำลัง config พวก Network boot ด้วย PXE
แล้วก็เจอปัญหาที่ว่า เครื่องรุ่นใหม่ๆ สามารถ boot ได้ตามปกติ
แต่เครื่องรุ่นเก่ากลับเจอ error ฟ้องว่า
PXE-E53 No boot filename received

ท้าวความก่อนว่า เจา PXE protocol นี่มันจะอาศัย DHCP protocol เป็นพาหนะ
เนื่องด้วย DHCP protocol เขาออกแบบไว้ดี, มันออกแบบให้มี slot options เผื่อไว้
เจ้า PXE protocol ก็เลยอาศัย options นี่แหล่ะเป็นช่องทางไว้สื่อสาร

สำนึกแรกผม เมื่อเห็นคำว่า "No boot filename received"
ก็ต้องหมายความว่า DHCP server ไม่ได้ส่ง option นี้กลับมา
แต่ถ้ามันไม่ได้ส่งกลับมาแล้ว ทำไม เครื่องรุ่นใหม่ๆ boot ได้หล่ะ
หรือว่า config DHCP server ไม่ถูกหว่า ทำให้มันส่งกลับมาไม่ถูก
ก็เลยหมกมุ่นแต่กับการแก้ configuration ของ DHCP
ซึ่งไม่ว่าจะแก้อย่างไร ก็ไม่มีความคืบหน้าใดๆให้เห็น

หลังจากพายเรือวนในอ่างมาหลายวัน
สุดท้ายก็ต้องใช้อาวุธลับ นั่นก็คือ wireshark + tcpdump เข้ามาช่วย debug
(โดยมีพี่สมภพเป็นคนช่วยแกะ packet)
ได้ความว่า

ในเครื่องใหม่ๆ protocol ของ PXE (เฉพาะครึ่งแรก ตอนที่ solve หา bootfile name กับ tftp server)
จะเป็นดังนี้


  • client broadcast จาก port 68 ไปยัง port 67 อันนี้เรียกว่า DHCP Discover
  • server broadcast กลับมา พร้อมค่า configuration ต่างๆ อันนี้เรียกว่า DHCP Offer
  • client broadcast ไปอีก โดยส่งค่า configuration ที่ได้จากขั้น 2 ไปด้วย ขั้นนี้เรียก DHCP Request
  • server broadcast ยืนยันรับทราบกลับมา ขั้นนี้เรียกว่า DHCP ACK

หลังจาก 4 step นี้แล้ว เจ้า PXE client ก็จะเข้าสู่ขบวนการ download file จาก tftp server

แต่ในเครื่องเก่าๆ protocol ของ PXE มันเป็นแบบนี้
เริ่มด้วย 4 อันแรกนี่เหมือนปกติ ทุกอย่าง แต่มันเพิ่มมาอีก 1 อันคือ


  • client ส่ง DHCP Request ไปที่ server ซำ้อีกครั้ง
    แต่สิ่งที่แตกต่างจากขั้นแรก ก็คือ
    • การส่งไม่ได้ใช้กลไก broadcast เหมือนครั้งแรก (เนื่องจากมันได้เบอร์ IP ของตัวเองมาแล้ว)
    • port ที่ request ไป กลายเป็น port 4011



เมื่อ check ที่ server ก็พบว่า ที่ port 4011 นั้น ยังไม่มี process start อยู่เลย
ความคิดแรกสุด ก็คือ เราทำ port forwarding จาก port 4011 ส่งไป 67 ดีไหม
หรือ จะติดตั้ง PXE daemon ซึ่งปกติเป็นคนรับผิดชอบเจ้า port นี้อยู่

สุดท้ายเลือกทางง่าย ก็คือลง PXE daemon ไปก่อน (เพราะทำ forward ไม่เป็น)

Related link from Roti

Friday, January 04, 2008

Singleton ?

ใครใช้ Java ก็คงรู้สึกว่า Singleton เป็นส่วนหนึ่งของชีวิต (โดยเฉพาะ Lazy-loaded)
เมื่อก่อน (นานมาแล้ว)
เขานิยมใช้ pattern แบบนี้
class MyClass {
private static MyClass instance;

private MyClass() {}

public static MyClass getInstance() {
if (instance == null) {
instance = new MyClass();
}
return instance;
}
}

ซึ่งก็ใช้ได้ ยกเว้นว่า ถ้าอยู่ในสภาพแวดล้อมแบบ concurrency แล้ว ก็มีโอกาสที่ MyClass จะถูกสร้างซ้ำซ้อนได้มากกว่า 1 ครั้ง
สมัยนั้นมีคนถกเถียงเรื่องนี้อยู่วุ่นวาย แต่ผมขี้เกียจอ่านและติดตาม
(ใครสนใจว่าถกกันอย่างไร ลองอ่านตัวอย่างอันนี้ดู The double-checked locking is broken)
ถึงจะรู้ว่ามีปัญหาเรื่อง concurrency แต่ผมก็ใช้ pattern นี้มาตลอด (โดยแอบเข้าข้างตัวเองว่า เอาน่า อย่างมากก็เปลือง resource นิดๆหน่อยๆ)
จนมาภายหลังที่มี springframework ออกมา ผมก็เลยยกหน้าที่นี้ให้ spring ไป

จนมาเมื่อวาน อ่านหนังสือเล่มหนึ่ง พบว่าเขามีวิธีการเขียนแบบใหม่ที่ดีกว่ากว่าแล้ว
เรียกว่า Initialization on demand holder idiom
public class Something
{
private Something() {}

private static class LazyHolder
{
private static final Something something = new Something();
}

public static Something getInstance()
{
return LazyHolder.something;
}
}


เป็นอันว่าเรื่องนี้ผมตกข่าวไป เพราะใน history ของ wiki เห็นเขาเขียนเรื่องนี้มาตั้งแต่ July 2006 แล้ว

Related link from Roti

Thursday, January 03, 2008

CouchDB เตรียมเข้า Apache

สืบเนื่องมาจากเรื่องเก่า ที่เคยพูดถึง CouchDB
ตอนนี้มีข่าวใหม่ว่า IBM เข้าไปเป็น sponsor
และจะนำ CouchDB เข้าไปอยู่ใน Apache Software Foundation อีกด้วย

Related link from Roti

Friday, December 28, 2007

Mail.app encoding

ช่วงนี้ที่บริษัทใช้ environment ผสมกัน ระหว่าง mac, windows, linux
ก็เลยเจอปัญหาพวก encoding กันบ่อยๆ

ตัว source code ไม่มีปัญหาแล้ว เพราะหลังๆผมบังคับให้ไปใช้ utf-8 กันหมดแล้ว

ตัวที่ทำให้ปวดหัวมากหน่อยก็คือ Mail
เนื่องจากผมใช้ Mail client เป็น Mail.app ของ apple
ทำให้เวลา forward, reply กันไปมา แล้วเกิดปัญหา encoding เพี้ยนขึ้น

ปัญหาหลักๆ ตัวหนึ่งเกิดที่ Mail.app
โดยปกติ mail ที่ส่งมา มันจะมี encoding แปะมาทั้งส่วน body และส่วน subject

Subject: =?windows-874?B?UkU6ILe0ys26wNLJ0uS3wg==?=
Content-Type: text/plain;
charset="windows-874"

แต่เวลาผม reply ต่อออกไป, เจ้า Mail.app ดัน set encoding เป็นแบบนี้แทน

Content-Type: text/plain;
charset=CP874;
delsp=yes;
format=flowed
Subject: =?UTF-8?B?RndkOiDguYDguKHguKXguYzguInguJrguLHguJrguJfguLXguYg=?=
=?UTF-8?B?4Liq4Lit4LiH?=

ตัว encoding ของ subject กับ body ที่ไม่ตรงกัน ถือเป็นจุดตั้งต้นของความเพี้ยน
(ยังไม่ตามต่อไป ว่า mail client ตัวไหนไปทำให้เพี้ยน แต่คาดว่าน่าจะเป็น outlook express)

ตอนนี้ผมก็เลยแก้ไข โดยการประพฤติตนเป็นพนักงานที่ดี โดยการ set default charset
ของผมให้เหมือนประชากรส่วนใหญ่ไปก่อน (ค่านี้ไม่มีหน้าจอ UI ให้ set ต้อง command line อย่างเดียว)

$ defaults write com.apple.mail NSPreferredMailCharset "CP874"

Related link from Roti

Thursday, December 27, 2007

กลับด้าน

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

คำถามก็คือ แล้วตึกที่เขาทุบจากล่างขึ้นบนหล่ะมีไหม
พบคำตอบได้ที่นี่
http://www.dailymail.co.uk/pages/live/articles/news/news.html?in_article_id=472602&in_page_id=1770

Related link from Roti

Tuesday, December 25, 2007

vm เอ๋ย vm

ช่วงนี้ได้ server ใหม่มาใช้ เป็นพวก duo-core ด้วย ก็เลยได้ฤกษ์ทดลองเอา Xen มาใช้เสียที
(เดิมใช้แต่ vmware-server ซึ่งก็ถือว่า work มากแล้ว)

ลงแล้ว ก็เจอปัญหาอยู่หลายอย่างเหมือนกันเช่น
  • จะ enable vt ได้ ต้อง set config ใน bios ด้วย (default เป็น off)
    ทำให้หลงคิดว่าเป็นปัญหาที่ Xen อยู่พักใหญ่
  • กรณีที่เป็น Intel, แผ่นติดตั้งใหม่ๆ พวก ubuntu, opensuse ไม่สามารถติดตั้งบน xen ได้เลย
    เพราะมันใช้ isolinux ที่แสดง Logo เป็นรูป graphic
    ซึ่งจะทำให้ xen มัน hang และนิ่งไป
    ทางแก้ไข ก็คือต้องแตก แผ่นออกมา แล้วเข้าไปแก้ option ของ isolinux ไม่ให้แสดง Logo แบบ graphic
    อ่านเพิ่มเติมใน https://bugs.launchpad.net/ubuntu/+bug/83642
  • Windows 98 ติดตั้งบน Xen ไม่ได้
    ตายตั้งแต่ขั้น boot แผ่น setup เลย, โดยมีปัญหาตอน load driver ของ cdrom
  • ใช้ Xen คู่กับ vmware ไม่ได้

Related link from Roti

Friday, December 21, 2007

protected with_scope

feature หนึ่งใน rails ที่มีคนใช้บ่อยก็คือ with_scope
ใน rails 2.0, with_scope ได้เปลี่ยน visibility จากเดิมที่เป็น
public method ไปเป็น protected method แล้ว
นั่นหมายความว่า เราจะใช้ with_scope ได้เฉพาะใน model เท่านั้น (ระวังประโยคนี้ด้วย ใน ruby เราสามารถหลายๆอย่างที่ไม่น่าเป็นไปได้ได้)

ที่เป็นดังนี้ ก็เพราะว่ามีคนเอา with_scope ไปใช้ที่ระดับ controller กันเยอะ
แล้วก็เจอผล ที่ไม่ตรงตามที่คาดหวัง
เช่น เอาไปใช้ใน filter แบบนี้
แล้วไปติดปัญหาตอน save ที่มันไม่ยอม save ข้อมูล default จาก with_scope ให้
class ApplicationController  < AC::Base 
def scope_story_by_account
Story.with_scope(:find =>
{:conditions => {:account_id => current_account.id},
:create => {:account_id => current_account.id}) {
yield }
end
around_filter :scope_by_account
end

class StoryController < ApplicationController
def create
@story = Story.new(params[:story])
# do some stuff to @story here...
@story.save #...@story.account_id won't be set
end
end


แน่นอน การเปลี่ยนแปลงแบบนี้ ก็ต้องมีคนบ่น
บางคนก็บ่นว่า
I think it's a poor choice to limit something that people find useful just because it doesn't fit your idea of useful. Why not let the coder make these
decisions?


คำตอบที่ได้น่าสนใจ (David Heinemeier คนเขียน rails เป็นคนตอบเองเลย)
You must be new here :). Rails is opinionated software. We make things that we consider good style easy to do and bad style hard. Or rather, we make good style beautiful and bad style ugly. So even though with_scope is going protected, you can still use it in filters if you really, really want to. Just use send(:with_scope) -- that'll side-step the access control. Yes, that'll be ugly and it's intended to be.


อ่านตัวอย่างการใช้ที่เหมาะสมจากที่นี่ “WITH_SCOPE WITH SCOPE”

Related link from Roti

Wednesday, December 19, 2007

Caching in AssetTagHelper

ใน rails 2.0 มี feature อยู่ตัวหนึ่งที่ช่วย combine javascript หรือ CSS หลายๆ file ให้รวมกันเป็น file เดียว
ซึ่งจะช่วยเพิ่มความเร็วของการ load web ของเราได้
อย่างเช่น สมมติว่าเรามีการ include javascript ดังนี้
javascript_include_tag "prototype", "cart"

ปกติมันจะ render เป็นแบบนี้
<script type="text/javascript" src="/javascripts/prototype.js"></script>
<script type="text/javascript" src="/javascripts/cart.js"></script>

แต่ถ้า เราเปิด feature cache ด้วยการใส่ option แบบนี้
javascript_include_tag "prototype", "cart", :cache => "shop"

มันก็จะ render แบบนี้แทน
<script type="text/javascript" src="/javascripts/shop.js"></script>

โดยเจ้า shop.js เกิดจากการ concat prototype.js เข้ากับ cart.js
และ gzip ก่อนส่งให้ browser

เงื่อนไขสำคัญที่จะทำให้ cache ทำงาน ก็คือ ใน environment file ต้องมีการกำหนด
config.action_controller.perform_caching             = true

ซึ่งใน default environment ค่านี้จะถูกกำหนดเป็น true ใน production mode
และเป็น false ใน development mode

อ่านรายละเอียดใน

Related link from Roti

Tuesday, December 18, 2007

ศิษย์ - ครู

อ่านเรื่องนี้แล้วรู้สึกระทึกใจ

http://gotoknow.org/blog/phoenix-mirror/154417

อ่านแล้วนึกถึงเจ้าลูกชาย ว่าจะได้พบเจอครูแบบไหน

Related link from Roti