keyword ของ barcamp ก็คือ "เนื้อหา เกิดจาก ผู้เข้าร่วม"
คนพูดไม่จำเป็นต้องเป็น expert ในสิ่งที่พูดหรอก แค่มีประสบการณ์ดีๆที่อยากแบ่งปัน ก็เพียงพอแล้ว
มาร่วมกันสร้างอนาคตใหม่ให้กับวงการ IT เมืองไทย ด้วยการเปลี่ยนจาก ผู้รับ(passive) เป็น ผู้ให้(active) กันเถอะ
credits
เด็กตีลังกา - เด็กชายปัณณ์
เด็กห้อยหัว - เด็กชายปุณณ์
เด็กที่คนหัวโล้นอุ้ม - เด็กหญิงนมนต์ (สะกดถูกหรือเปล่าเนี่ย)
ผู้ชายกางเกงลาย - ผมเอง
ผู้ชายหัวโล้น - roofimon
คนถ่าย - keng
สถานที่ - opendream.org
Friday, August 15, 2008
Monday, August 04, 2008
code ของ grails 's plugin
คราวก่อนที่เจอปัญหา xml-rpc บน grails, ผมได้นั่งแกะโครงสร้าง plugins ของ grails ไปแวบหนึ่ง
รู้สึกติดใจว่า ถ้ามีเวลาต้องมานั่งแกะให้เป็นเรื่องเป็นราว
วันนี้ได้ที ใน mailing list ของ th-grails-user มีปัญหาซึ่งพาดพิงถึง mail-plugin
ก็เลยจัดการ install แล้วก็นั่งแกะ code ว่ามันทำอะไรบ้าง
function หลักๆที่ mail plugin ทำก็คือ
code ในส่วนของ function แรกที่น่าสนใจ ก็คือการ define bean 'mailSender'
ลองดู code ที่มันใช้
method นี้จะถูกเรียกใช้ในช่วง startup ของ grails
ซึ่งข้างในจะเห็นว่ามันใช้ DSL ในการ config spring
code ข้างบน ถ้าเขียนในแบบดั้งเดิมของ spring ก็จะออกมาทำนองนี้
ข้อได้เปรียบที่เห็นได้ชัด สำหรับการใช้ DSL ในการ config spring ก็คือ
เราสามารถใส่ logic ลงไปได้ (เพราะมันเป็น code ไม่ใช่ xml)
ในส่วน function ที่สอง ที่มีการ inject method ให้กับทุก controller
ถ้าดูตาม code แล้วจะเห็นว่ามันทำแบบนี้
code กระทัดรัดมาก มันแค่ส่งต่อ closure ที่รับเข้ามาให้กับ mailService
ถ้าเป็น java code ก็ต้องยืดยาวประมาณนี้
รู้สึกติดใจว่า ถ้ามีเวลาต้องมานั่งแกะให้เป็นเรื่องเป็นราว
วันนี้ได้ที ใน mailing list ของ th-grails-user มีปัญหาซึ่งพาดพิงถึง mail-plugin
ก็เลยจัดการ install แล้วก็นั่งแกะ code ว่ามันทำอะไรบ้าง
function หลักๆที่ mail plugin ทำก็คือ
- สร้าง service ที่ชื่อ mailService เพื่อให้ controller หรือ domain เรียกใช้ตามอัธยาศัย
โดย service นี้ จะทำหน้าที่สร้าง builder ที่จะใช้แปลง closure ที่ pass เข้ามา ให้กลายเป็น mail message
ก่อนจะ delegate ต่อให้ mailSender bean เป็นคนจัดการส่งออกไป
(mailSender แอบใช้ของที่ spring มีอยู่แล้ว นั่นคือ org.springframework.mail.javamail.JavaMailSenderImpl) - สร้าง method ที่ชื่อ sendMail ให้กับ controller ทุกตัวที่มี ผ่านทางกลไกของ metdata
code ในส่วนของ function แรกที่น่าสนใจ ก็คือการ define bean 'mailSender'
ลองดู code ที่มันใช้
def doWithSpring = {
def config = application.config.grails.mail
mailSender(JavaMailSenderImpl) {
host = config.host ?: "localhost"
defaultEncoding = config.encoding ?: "utf-8"
if(config.port)
port = config.port
if(config.username)
username = config.username
if(config.password)
password = config.password
if(config.protocol)
protocol = config.protocol
if(config.props instanceof Map && config.props)
javaMailProperties = config.props
}
}
method นี้จะถูกเรียกใช้ในช่วง startup ของ grails
ซึ่งข้างในจะเห็นว่ามันใช้ DSL ในการ config spring
code ข้างบน ถ้าเขียนในแบบดั้งเดิมของ spring ก็จะออกมาทำนองนี้
<bean id="mailSender"
class="org.springframework.mail.javamail.JavaMailSenderImpl">
<host>localhost</host>
<defaultEncoding>utf-8</defaultEncoding>
...
</bean>
ข้อได้เปรียบที่เห็นได้ชัด สำหรับการใช้ DSL ในการ config spring ก็คือ
เราสามารถใส่ logic ลงไปได้ (เพราะมันเป็น code ไม่ใช่ xml)
ในส่วน function ที่สอง ที่มีการ inject method ให้กับทุก controller
ถ้าดูตาม code แล้วจะเห็นว่ามันทำแบบนี้
def doWithApplicationContext = { applicationContext ->
configureSendMail(application, applicationContext)
}
def configureSendMail(application, applicationContext) {
application.controllerClasses*.metaClass*.sendMail = {Closure callable ->
applicationContext.mailService?.sendMail(callable)
}
}
code กระทัดรัดมาก มันแค่ส่งต่อ closure ที่รับเข้ามาให้กับ mailService
ถ้าเป็น java code ก็ต้องยืดยาวประมาณนี้
MailService svr = (MailService) ctx.getBean('mailService');
if (svr != null) {
svr.sendMail(...);
}
Related link from Roti
Tuesday, July 22, 2008
ปัญหา grails กับ xml-rpc
ใน open source project ที่กำลังทำอยู่ (ใช้ grails)
มันมี feature หนึ่งต้องสามารถรับ request เป็น xml-rpc ได้
จัดการเคาะ google หนึ่งที ก็ได้ความว่ามี plugin xmlrpc ให้ใช้
จัดการสั่ง
implement service, controller อีก ~20 บรรทัด ก็ได้ xml-rpc ฝั่ง server มาเชยชม
จากนั้นก็ทดลองเขียน xml-rpc client ด้วย python
ทดลอง run ดูก็พบว่าโลกไม่สวยงามอย่างที่คิด
มี stack trace ยอดยืดเป็นหางว่าว
debug พักใหญ่ๆ ก็ได้ความว่า มันเป็น bug ตัวนี้
http://jira.codehaus.org/browse/GRAILSPLUGINS-201
http://jira.codehaus.org/browse/GRAILS-2017
อืมม์ fixed โดยการ disable feature หนึ่งทิ้งไป
แสดงว่าเราต้องเลือกว่า อยากได้ feature ไหนมากกว่ากัน
ในเบื้องต้นเนื่องจาก xmlrpc ผมยังเป็น rpc ตัวน้อยๆอยู่
ผมก็เลยเลือกว่าจะไม่ใช้ xmlrpc plugin (ซึ่งหมายความว่าต้อง implement xmlrpc แบบง่ายๆด้วยตัวเอง)
วิธีการง่ายๆแบบนี้เลย
Update: พบปัญหาว่า กรณีมี params มากกว่า 1 ตัวแล้ว ดูเหมือนมันจะ parse ให้ผิด
ตอนนี้เลย switch ไปใช้ xml-rpc plugin แล้วทำการ disable auto xml parsing ไปก่อน
มันมี feature หนึ่งต้องสามารถรับ request เป็น xml-rpc ได้
จัดการเคาะ google หนึ่งที ก็ได้ความว่ามี plugin xmlrpc ให้ใช้
จัดการสั่ง
grails install-plugin xmlrpcimplement service, controller อีก ~20 บรรทัด ก็ได้ xml-rpc ฝั่ง server มาเชยชม
จากนั้นก็ทดลองเขียน xml-rpc client ด้วย python
ทดลอง run ดูก็พบว่าโลกไม่สวยงามอย่างที่คิด
มี stack trace ยอดยืดเป็นหางว่าว
debug พักใหญ่ๆ ก็ได้ความว่า มันเป็น bug ตัวนี้
http://jira.codehaus.org/browse/GRAILSPLUGINS-201
http://jira.codehaus.org/browse/GRAILS-2017
อืมม์ fixed โดยการ disable feature หนึ่งทิ้งไป
แสดงว่าเราต้องเลือกว่า อยากได้ feature ไหนมากกว่ากัน
ในเบื้องต้นเนื่องจาก xmlrpc ผมยังเป็น rpc ตัวน้อยๆอยู่
ผมก็เลยเลือกว่าจะไม่ใช้ xmlrpc plugin (ซึ่งหมายความว่าต้อง implement xmlrpc แบบง่ายๆด้วยตัวเอง)
วิธีการง่ายๆแบบนี้เลย
class RpcController {
def pingService
def ping = {
// ตรงนี้ grails มันจัดการแปลง xml ที่ส่งมาใน request body
// ให้อยู่ใน params object
def method = params.methodCall
def methodName = method.methodName
def methodParams = method.params
def result
if (methodName == 'ping') {
result = pingService.ping(methodParams)
} else {
result = "error: mismatch methodName ${methodName}, expect 'ping'"
}
def writer = new StringWriter()
def mkb = new groovy.xml.MarkupBuilder(writer)
mkb.methodResponse {
params {
param {
value {
string(result)
}
}
}
}
render(writer.toString())
}
}
Update: พบปัญหาว่า กรณีมี params มากกว่า 1 ตัวแล้ว ดูเหมือนมันจะ parse ให้ผิด
ตอนนี้เลย switch ไปใช้ xml-rpc plugin แล้วทำการ disable auto xml parsing ไปก่อน
Related link from Roti
Wednesday, July 16, 2008
เริ่มใช้ grails
ช่วงเดือนที่ผ่านมา ผมได้มีโอกาสสัมผัสกับ Grails อย่างจริงๆจังเป็นครั้งแรก
โดยได้มีโอกาส implement open source project ตัวหนึ่งด้วย Grails
อารมณ์ในช่วงแรก ก็คือ "wow"
สาเหตุก็คือ มันใช้ stack ทุกอย่างที่ผมคุ้นเคย และใช้อยู่แล้ว
ไม่ว่าจะเป็น spring, hibernate
แต่นำมาลด noise ด้วยการทำ DSL บ้าง หรือลด noise ด้วยคุณสมบัติของตัวภาษา groovy เองบ้าง
แถมยังยึดแนว convention over configuration ของ Rails อีก
learning curve ก็เลยถือว่าน้อยมากๆ
พอผ่านอารมณ์ wow มาได้
ก็ได้เวลาขัดอกขัดใจบ้างแล้ว
เริ่มแรกสุด ก็คือ ผมจำชื่อ package ของ java class ที่ใช้บ่อยๆไม่ได้
เดิมผมผลักภาระไปให้ IDE มัน popup หรือ import ให้เรา
แต่พอมาใช้ groovy, เจ้า context assistent ของ groovy ใน eclipse มันทำงานช้าเหลือเกิน
ความขัดใจที่สองก็คือมัน compile ช้า
เวลาเขียน unit testing เพื่อทดสอบหาแนวทางการทำงานของ library ต่างๆ (เช่น lucene, nekohtml)
การ run แต่ละครั้งมันหนึืดเหลือเกิน
แต่สิ่งที่ผมยึดถือก็คือ คนเราปรับตัวได้
อย่าปล่อยให้ความลำบากเล็กๆน้อยๆ (จากความไม่เคยชินของเรา) มาเป็นอุปสรรคในการเรียนรู้
จากข้อ 1 ที่ไหนๆ groovy plugin มันไม่ได้ช่วยอะไรเรา (แถมยังขัดขาอีก)
ก็เลยเปลี่ยนไปใช้ emacs แทน (ซึ่งไม่มี context assistent แน่ๆ)
แล้วก็เปิด javadoc ไว้ข้างๆ เพื่อใช้ค้นหาชื่อ package
แต่ก็พบว่า ถ้าเราเขียนโปรแกรมแบบไม่ระบุ type มันก็จะช่วยลด import statement ไปได้เยอะเหมือนกัน
ส่วนข้อสอง ก็แก้โดย ใช้ groovy console ทดลองเขียนให้เรียบร้อยก่อน
จากนั้นค่อย copy ไปใส่ไว้ใน unit test.
โดยได้มีโอกาส implement open source project ตัวหนึ่งด้วย Grails
อารมณ์ในช่วงแรก ก็คือ "wow"
สาเหตุก็คือ มันใช้ stack ทุกอย่างที่ผมคุ้นเคย และใช้อยู่แล้ว
ไม่ว่าจะเป็น spring, hibernate
แต่นำมาลด noise ด้วยการทำ DSL บ้าง หรือลด noise ด้วยคุณสมบัติของตัวภาษา groovy เองบ้าง
แถมยังยึดแนว convention over configuration ของ Rails อีก
learning curve ก็เลยถือว่าน้อยมากๆ
พอผ่านอารมณ์ wow มาได้
ก็ได้เวลาขัดอกขัดใจบ้างแล้ว
เริ่มแรกสุด ก็คือ ผมจำชื่อ package ของ java class ที่ใช้บ่อยๆไม่ได้
เดิมผมผลักภาระไปให้ IDE มัน popup หรือ import ให้เรา
แต่พอมาใช้ groovy, เจ้า context assistent ของ groovy ใน eclipse มันทำงานช้าเหลือเกิน
ความขัดใจที่สองก็คือมัน compile ช้า
เวลาเขียน unit testing เพื่อทดสอบหาแนวทางการทำงานของ library ต่างๆ (เช่น lucene, nekohtml)
การ run แต่ละครั้งมันหนึืดเหลือเกิน
แต่สิ่งที่ผมยึดถือก็คือ คนเราปรับตัวได้
อย่าปล่อยให้ความลำบากเล็กๆน้อยๆ (จากความไม่เคยชินของเรา) มาเป็นอุปสรรคในการเรียนรู้
จากข้อ 1 ที่ไหนๆ groovy plugin มันไม่ได้ช่วยอะไรเรา (แถมยังขัดขาอีก)
ก็เลยเปลี่ยนไปใช้ emacs แทน (ซึ่งไม่มี context assistent แน่ๆ)
แล้วก็เปิด javadoc ไว้ข้างๆ เพื่อใช้ค้นหาชื่อ package
แต่ก็พบว่า ถ้าเราเขียนโปรแกรมแบบไม่ระบุ type มันก็จะช่วยลด import statement ไปได้เยอะเหมือนกัน
ส่วนข้อสอง ก็แก้โดย ใช้ groovy console ทดลองเขียนให้เรียบร้อยก่อน
จากนั้นค่อย copy ไปใส่ไว้ใน unit test.
Related link from Roti
Tuesday, July 15, 2008
kernel stat
เมื่อวานนั่งฟัง Bart Trojanowski พูดเรื่อง "Kernel walkthrough"
ฟังได้ไม่ถึงสิบนาที ภรรยาก็บอกว่า "ปิดเถอะ ได้ยินแล้วนอนไม่หลับ ไปซื้อหูฟังกันไหม?"
หน้าที่ของสามีที่ดีก็คือ งอนแต่พองาม
สบัดหน้าเม้มปากกัดกรามแล้วก็ปิดแต่โดยดี
ถึงได้ฟังนิดหน่อย แต่ก็ได้เห็น stat บางอย่างของ Linux kernel ที่น่าสนใจ
(ตัวเลขจริงๆจำไม่ได้แล้ว แต่วันนี้เจอ blog ที่พูดถึงเรื่องนี้เหมือนกัน อยู่ที่
http://cycle-gap.blogspot.com/2008/07/linux-kernel-development-stats-from.html
ก็เลยลอกมาให้นั่งดู ประหยัดแรง ไม่ต้องไปนั่งถอดเทป
)
จำนวน changes ต่อวัน
ใน code จำนวน 9 ล้านบรรทัดของ kernel เป็น core แค่ 5%
ตัวที่มีจำนวนบรรทัดมากสุดคือ driver ล่อไปซะ 55 %
ฟังได้แค่นี้แหล่ะ ไว้ได้หูฟังมาแล้ว ค่อยฟังต่อ
แต่ตัวเลขน่าสนใจมาก (ใครไม่อยากเสียเวลาฟัง ก็ไปแอบดูใน blog ข้างบนก่อนได้เลย)
ฟังได้ไม่ถึงสิบนาที ภรรยาก็บอกว่า "ปิดเถอะ ได้ยินแล้วนอนไม่หลับ ไปซื้อหูฟังกันไหม?"
หน้าที่ของสามีที่ดีก็คือ งอนแต่พองาม
สบัดหน้าเม้มปากกัดกรามแล้วก็ปิดแต่โดยดี
ถึงได้ฟังนิดหน่อย แต่ก็ได้เห็น stat บางอย่างของ Linux kernel ที่น่าสนใจ
(ตัวเลขจริงๆจำไม่ได้แล้ว แต่วันนี้เจอ blog ที่พูดถึงเรื่องนี้เหมือนกัน อยู่ที่
http://cycle-gap.blogspot.com/2008/07/linux-kernel-development-stats-from.html
ก็เลยลอกมาให้นั่งดู ประหยัดแรง ไม่ต้องไปนั่งถอดเทป
)
จำนวน changes ต่อวัน
- เพิ่มใหม่ 4300 บรรทัด
- แก้ไข 1800 บรรทัด
- ลบทิ้ง 1500 บรรทัด
ใน code จำนวน 9 ล้านบรรทัดของ kernel เป็น core แค่ 5%
ตัวที่มีจำนวนบรรทัดมากสุดคือ driver ล่อไปซะ 55 %
ฟังได้แค่นี้แหล่ะ ไว้ได้หูฟังมาแล้ว ค่อยฟังต่อ
แต่ตัวเลขน่าสนใจมาก (ใครไม่อยากเสียเวลาฟัง ก็ไปแอบดูใน blog ข้างบนก่อนได้เลย)
Related link from Roti
Labels:
linux
Tuesday, July 08, 2008
เปิดท้าย

ผมเป็นคนที่ชอบเล่นกับเด็กเล็ก เมื่อก่อนสมัยเรียนหนังสือในมหาวิทยาลัย พอได้เวลาช่วงปิดเทอมเมื่อไร ผมก็จะไปออกค่ายสอนหนังสือเด็กๆชาวเขาที่ภาคเหนือ
หลังจากจบมาก็ไม่ได้มีกิจกรรมข้องแวะกับเด็กๆอีกเลย จนมามีลูกเป็นของตัวเอง, เมื่อลูกเริ่มถึงวัยมีเพื่อน ความสนุกเก่าๆก็หวนกลับมาอีกครั้ง, ทุกวันนี้พอตกตอนเย็น ผมก็จะเปิดประตูและกางเสื่อบนท้ายรถรอเด็กๆในหมู่บ้านแวะเวียนมาเล่นร่วมกัน
(รูปนี้แม่เด็กแอบขึ้นไปถ่ายตอนไหนก็ไม่รู้)
ปัญหาที่พบก็คือ เด็กๆมีคละวัยเหลือเกิน เจ้าเตาะแตะก็อยากเล่นแบบหนึ่ง พวกปรู๊ดปร๊าดก็อยากเล่นอีกแบบหนึ่ง
Related link from Roti
Tuesday, June 24, 2008
git-rerere
เวลาเราแตก branch ที่เรารู้ว่าเป็นหนังชีวิต (branch ที่มีอายุยาวๆ)
เรามักจะต้องคอย merge ตัว master เข้ามาที่ branch เรา เพื่อที่จะตรวจสอบ conflict ต่างๆที่เกิดขึ้น
ซึ่งมีศัพท์เรียกกันว่า "test merge"
ลองดูรูปประกอบข้างล่าง
ในที่นี่ topic ก็คือ branch (ที่อายุยืน) ของเรา
เครื่องหมาย '*' ก็คือ จุดที่ทั้ง topic และ master แตะเนื้อหาที่เดียวกัน (เป็นจุดที่เวลา merge แล้วจะเกิด conflict )
เครื่องหมาย '+' ก็คือ test merge ที่เกิดขึ้น (และได้ทำการ solve conflict ไว้แล้ว)
เมื่อเรา merge topic กลับไปยัง master,
commit ของ test merge ก็จะถูกเก็บไว้ใน history ของ master ไปด้วย
Note: รูปประกอบตัดมาจาก document ของ git
สำหรับบางคนประเด็นนี้ถือเป็นประเด็นที่สำคัญ
เช่น linus เขาเรียกเจ้า "test merge" นี้ว่า "useless merge"
และเห็นว่ากันว่า เขามักจะ reject เจ้า merge branch ที่มีเจ้า "test merge" ติดเข้ามาด้วย
แล้วทำอย่างไร ถึงจะไม่ให้มี test-merge ติดเข้ามาหล่ะ
วิธีที่เขาทำกันก็คือ
ยังทำ test-merge เหมือนเดิม แต่เมื่อ merge เสร็จแล้ว ก็จัดการลบ commit นั้นทิ้งเสีย ด้วยคำสั่ง
ผลที่ได้ก็คือ เวลา merge topic กลับเข้า master
เราก็จะได้ tree หน้าตาเกลี้ยงเกลาแบบนี้
แต่ชีวิตไม่ได้โรยด้วยกลีบกุหลาบ
ปัญหาที่ตามมาก็คือ ไอ้เจ้าพวก conflict ที่เราเคย solve ตอน test merge
มันจะตามกลับมาให้เราแก้ไขอีกครั้งตอน merge topic กลับไปยัง master
ซึ่งการแก้ไขสองรอบแบบนี้ไม่สนุกแน่ๆ
โชคดีที่ git มีทางออกให้เรา
คำสั่งที่เป็นพระเอกก็คือคำสั่ง git-rerere
หลังจาก merge แล้วเกิด conflict ขึ้น
ให้สั่ง git-rerere ก่อนที่จะแก้ไข conflict
และหลังจากแก้ไข conflict เสร็จแล้ว ก็ให้สั่ง git-rerere อีกที
ผลก็คือ เจ้า git จะแอบจำวิธี solve conflict ของเราไว้
ทำให้ครั้งต่อไปที่เกิด conflict แบบนี้ขึ้นมาอีก (test merge ครั้งถัดไป หรือตอน merge topic กลับ master)
มันจะจัดการ resolve ให้โดยอัติโนมัติ
โดย default แล้ว git-rerere ไม่ได้ถูก enable ไว้
เราสามารถสั่ง enable feature นี้โดยการเข้าไปสร้าง directory rr-cache ไว้ใต้ .git directory
เรามักจะต้องคอย merge ตัว master เข้ามาที่ branch เรา เพื่อที่จะตรวจสอบ conflict ต่างๆที่เกิดขึ้น
ซึ่งมีศัพท์เรียกกันว่า "test merge"
ลองดูรูปประกอบข้างล่าง
ในที่นี่ topic ก็คือ branch (ที่อายุยืน) ของเรา
เครื่องหมาย '*' ก็คือ จุดที่ทั้ง topic และ master แตะเนื้อหาที่เดียวกัน (เป็นจุดที่เวลา merge แล้วจะเกิด conflict )
เครื่องหมาย '+' ก็คือ test merge ที่เกิดขึ้น (และได้ทำการ solve conflict ไว้แล้ว)
เมื่อเรา merge topic กลับไปยัง master,
commit ของ test merge ก็จะถูกเก็บไว้ใน history ของ master ไปด้วย
$ git checkout topic
$ git merge master
$ ... work on both topic and master branches
$ git checkout master
$ git merge topic
o---*---o---+---o---o topic
/ / \
o---o---o---*---o---o---o---o---+ master
Note: รูปประกอบตัดมาจาก document ของ git
สำหรับบางคนประเด็นนี้ถือเป็นประเด็นที่สำคัญ
เช่น linus เขาเรียกเจ้า "test merge" นี้ว่า "useless merge"
และเห็นว่ากันว่า เขามักจะ reject เจ้า merge branch ที่มีเจ้า "test merge" ติดเข้ามาด้วย
แล้วทำอย่างไร ถึงจะไม่ให้มี test-merge ติดเข้ามาหล่ะ
วิธีที่เขาทำกันก็คือ
ยังทำ test-merge เหมือนเดิม แต่เมื่อ merge เสร็จแล้ว ก็จัดการลบ commit นั้นทิ้งเสีย ด้วยคำสั่ง
git reset --hard HEAD^ผลที่ได้ก็คือ เวลา merge topic กลับเข้า master
เราก็จะได้ tree หน้าตาเกลี้ยงเกลาแบบนี้
o---*---o-------o---o topic
/ \
o---o---o---*---o---o---o---o---+ master
แต่ชีวิตไม่ได้โรยด้วยกลีบกุหลาบ
ปัญหาที่ตามมาก็คือ ไอ้เจ้าพวก conflict ที่เราเคย solve ตอน test merge
มันจะตามกลับมาให้เราแก้ไขอีกครั้งตอน merge topic กลับไปยัง master
ซึ่งการแก้ไขสองรอบแบบนี้ไม่สนุกแน่ๆ
โชคดีที่ git มีทางออกให้เรา
คำสั่งที่เป็นพระเอกก็คือคำสั่ง git-rerere
หลังจาก merge แล้วเกิด conflict ขึ้น
ให้สั่ง git-rerere ก่อนที่จะแก้ไข conflict
และหลังจากแก้ไข conflict เสร็จแล้ว ก็ให้สั่ง git-rerere อีกที
ผลก็คือ เจ้า git จะแอบจำวิธี solve conflict ของเราไว้
ทำให้ครั้งต่อไปที่เกิด conflict แบบนี้ขึ้นมาอีก (test merge ครั้งถัดไป หรือตอน merge topic กลับ master)
มันจะจัดการ resolve ให้โดยอัติโนมัติ
โดย default แล้ว git-rerere ไม่ได้ถูก enable ไว้
เราสามารถสั่ง enable feature นี้โดยการเข้าไปสร้าง directory rr-cache ไว้ใต้ .git directory
Related link from Roti
Labels:
git
Friday, June 20, 2008
Java, Parallel, Monad
เห็น code java นี้แล้ว ธาตุไฟแทรก
ใครธาตุแข็งแรงเชิญไปอ่านได้ที่นี่
Higher-Order Java Parallelism, Part 1: Parallel Strategies and the Callable Monad
public static <A, B> F<Callable<A>, Callable<B>> fmap(final F<A, B> f) {
return new F<Callable<A>, Callable<B>>() {
public Callable<B> f(final Callable<A> a) {
return bind(a, new F<A, Callable<B>>() {
public Callable<B> f(final A ab) {
return new Callable<B>() {
public B call() {
return f.f(ab);
}
};
}
});
}
};
}
ใครธาตุแข็งแรงเชิญไปอ่านได้ที่นี่
Higher-Order Java Parallelism, Part 1: Parallel Strategies and the Callable Monad
Related link from Roti
Labels:
java
Wednesday, June 11, 2008
merge อย่างไรดี
ปัญหาหนึ่งที่ Orangegears เจอก็คือ เนื่องจากเรา fork ออกมาจาก Ofbiz
แต่ก็ยังต้องการ sync กับ Ofbiz อย่างไกล้ชิด (ไม่เหมือน Opentap ที่แยกออกไปอย่างชัดเจน)
ทำให้เราต้องคอย merge code จาก Ofbiz เข้ามายัง Orangegears เป็นระยะๆ
วิธีการเดิมที่น้อง sand ใช้ ก็คือใช้ kdiff3 ทำการ merge
ซึ่งก็สะดวกดี เพราะมี UI สวยงาม
แต่ผมก็เจอปัญหาว่า changed ของผมมักจะหายไปบ่อยๆ
ผมก็เลยมองหาวิธีใหม่ๆมาเรื่อยๆ
วันก่อนหลังจากลองเล่น multiple branches ใน git ดู
ก็พบว่า git ยืดหยุ่นพอที่เราจะทำ multiple remote branch จาก svn repository มากกว่า 1 ที่ได้
ผมก็เลยทดลอง merge ด้วยวิธีนี้ดู
วิธีการก็คือ
เริ่มด้วยการสร้าง git repository ที่มี link ชี้ไปยัง project ofbiz ก่อน
หลังจากสั่งคำสั่งนี้ git จะสร้าง working directory เปล่าๆให้ (ยังไม่ fetch code มาให้)
โดย file .git/config จะมีหน้าตาแบบนี้
เราก็ทำการจัดแจงแก้ไขชื่อให้เหมาะสม จะได้ไม่งงภายหลัง
และทำการเพิ่ม repository ของ Ofbiz เข้าไป
จากนั้นก็ทำการ fetch ข้อมูลทีละ repository
เริ่มจาก ofbiz ก่อน เอา revision ล่าสุดเลย ไม่เอา history
Note1: เลข revision ของ ofbiz นี่ล่อไปหลักหกแสนแล้ว
ทั้งนี้เพราะ repo ของ apache เขาใช้ share ร่วมกันทุก project
Note2: ผมลอง fetch orangegears ก่อน ปรากฎว่า fetch ofbiz ไม่ได้ (ไม่มี error ด้วย)
ตามด้วย orangegears โดยเริ่มต้นที่ revision 142 เลย ไม่ต้องย้อนอดีตมากนัก
ลองสั่ง git branch -r จะเห็นว่ามี og_trunk กับ ofbiz_trunk เกิดขึ้น
โดย default trunk ของ ofbiz จะถูก map เข้ามาเป็น master ใน local branch
ทำการเปลี่ยนชื่อให้เรียบร้อย จะได้ไม่งงว่าใครเป็นใคร
ส่วน og_trunk นั้นยังไม่มี local branch ต้องทำการสั่ง
ขั้นตอนการ merge
fetch code จาก ofbiz ให้ up-to-date ก่อน
fetch code ของ orangegears ให้ up-to-date เช่นเดียวกัน
เตรียม branch สำหรับ merge โดยสร้าง branch แยกออกจาก og (ไม่จำเป็น แต่ก็ควรทำเป็นนิสัย)
สั่ง merge
ที่ยากก็คือเวลาเกิด conflict ก็ต้องตามแก้ไขให้เรียบร้อยก่อน
Note1: สำหรับผม ผมเลือกใช้ emacs + git-emacs mode ซึ่งมันจะเรียกใช้ ediff ในการ solve conflict
Note2: คิดว่าการ merge ครั้งแรก จะมี conflict เยอะหน่อย
แต่พอเป็นการ merge ครั้งที่ 2.. จะมี conflict น้อยลง เพราะว่า branch ทั้งสองมี history ที่เชื่อมกันแล้ว
(เป็นผลดีต่อ three-way merge)
switch กลับไปยัง og และทำการ merge
จากนั้นก็สั่ง commit กลับขึ้น svn repository
สุดท้ายก็ลบ temporary branch ทิ้ง
ไว้จะทดลองทำสักระยะหนึ่ง (โดยยังไม่ commit ผลลัพท์จากการ merge กลับขึ้น orangegears)
เพื่อดูว่ามันมีประสิทธิภาพแค่ไหนก่อน
แต่ก็ยังต้องการ sync กับ Ofbiz อย่างไกล้ชิด (ไม่เหมือน Opentap ที่แยกออกไปอย่างชัดเจน)
ทำให้เราต้องคอย merge code จาก Ofbiz เข้ามายัง Orangegears เป็นระยะๆ
วิธีการเดิมที่น้อง sand ใช้ ก็คือใช้ kdiff3 ทำการ merge
ซึ่งก็สะดวกดี เพราะมี UI สวยงาม
แต่ผมก็เจอปัญหาว่า changed ของผมมักจะหายไปบ่อยๆ
ผมก็เลยมองหาวิธีใหม่ๆมาเรื่อยๆ
วันก่อนหลังจากลองเล่น multiple branches ใน git ดู
ก็พบว่า git ยืดหยุ่นพอที่เราจะทำ multiple remote branch จาก svn repository มากกว่า 1 ที่ได้
ผมก็เลยทดลอง merge ด้วยวิธีนี้ดู
วิธีการก็คือ
เริ่มด้วยการสร้าง git repository ที่มี link ชี้ไปยัง project ofbiz ก่อน
git svn init http://svn.apache.org/repos/asf/ofbiz/trunk
หลังจากสั่งคำสั่งนี้ git จะสร้าง working directory เปล่าๆให้ (ยังไม่ fetch code มาให้)
โดย file .git/config จะมีหน้าตาแบบนี้
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
[svn-remote "svn"]
url = http://svn.apache.org/repos/asf/ofbiz/trunk
fetch = :refs/remotes/trunk
เราก็ทำการจัดแจงแก้ไขชื่อให้เหมาะสม จะได้ไม่งงภายหลัง
และทำการเพิ่ม repository ของ Ofbiz เข้าไป
[svn-remote "ofbiz"]
url = http://svn.apache.org/repos/asf/ofbiz/trunk
fetch = :refs/remotes/ofbiz_trunk
[svn-remote "orangegears"]
url = https://orangegears.svn.sourceforge.net/svnroot/orangegears
fetch = trunk:refs/remotes/og_trunk
จากนั้นก็ทำการ fetch ข้อมูลทีละ repository
เริ่มจาก ofbiz ก่อน เอา revision ล่าสุดเลย ไม่เอา history
Note1: เลข revision ของ ofbiz นี่ล่อไปหลักหกแสนแล้ว
ทั้งนี้เพราะ repo ของ apache เขาใช้ share ร่วมกันทุก project
Note2: ผมลอง fetch orangegears ก่อน ปรากฎว่า fetch ofbiz ไม่ได้ (ไม่มี error ด้วย)
git svn fetch -r 666189:HEAD ofbiz
ตามด้วย orangegears โดยเริ่มต้นที่ revision 142 เลย ไม่ต้องย้อนอดีตมากนัก
git svn fetch -r 142:HEAD orangegears
ลองสั่ง git branch -r จะเห็นว่ามี og_trunk กับ ofbiz_trunk เกิดขึ้น
pphetra@mypann:~/projects/java/t$ git branch -r
ofbiz_trunk
og_trunk
โดย default trunk ของ ofbiz จะถูก map เข้ามาเป็น master ใน local branch
ทำการเปลี่ยนชื่อให้เรียบร้อย จะได้ไม่งงว่าใครเป็นใคร
git branch -m master ofbiz
ส่วน og_trunk นั้นยังไม่มี local branch ต้องทำการสั่ง
git checkout -b og og_trunk
ขั้นตอนการ merge
fetch code จาก ofbiz ให้ up-to-date ก่อน
git checkout -f ofbiz
git svn rebase
fetch code ของ orangegears ให้ up-to-date เช่นเดียวกัน
git checkout -f og
git svn rebase
เตรียม branch สำหรับ merge โดยสร้าง branch แยกออกจาก og (ไม่จำเป็น แต่ก็ควรทำเป็นนิสัย)
git checkout -b merge-ofbiz
สั่ง merge
git merge ofbiz
ที่ยากก็คือเวลาเกิด conflict ก็ต้องตามแก้ไขให้เรียบร้อยก่อน
Note1: สำหรับผม ผมเลือกใช้ emacs + git-emacs mode ซึ่งมันจะเรียกใช้ ediff ในการ solve conflict
Note2: คิดว่าการ merge ครั้งแรก จะมี conflict เยอะหน่อย
แต่พอเป็นการ merge ครั้งที่ 2.. จะมี conflict น้อยลง เพราะว่า branch ทั้งสองมี history ที่เชื่อมกันแล้ว
(เป็นผลดีต่อ three-way merge)
switch กลับไปยัง og และทำการ merge
git checkout -f og
git merge merge-ofbiz
จากนั้นก็สั่ง commit กลับขึ้น svn repository
git svn dcommit
สุดท้ายก็ลบ temporary branch ทิ้ง
git branch -D merge-ofbiz
ไว้จะทดลองทำสักระยะหนึ่ง (โดยยังไม่ commit ผลลัพท์จากการ merge กลับขึ้น orangegears)
เพื่อดูว่ามันมีประสิทธิภาพแค่ไหนก่อน
Related link from Roti
Labels:
git,
ofbiz,
orangegears
Tuesday, June 10, 2008
ใช้ git จัดการ svn branches
ช่วงนี้ project ผมซึ่งใช้ subversion เป็น repository เริ่มมีการแตก branch เยอะขึ้น
ส่งผลให้คนที่ดูหลาย branch เกิดความลำบากในการ switch ไปมาระหว่าง branch มากขึ้น
ปกติวิธีการ switch branch เราสามารถใช้คำสั่ง
แต่ปัญหาก็คือมันมักจะมี code ที่กำลังแก้ค้างอยู่ จะ commit ไว้่ก่อน switch ก็ไม่เหมาะ เพราะยังแก้ไขไม่ทันเสร็จดี
ผมเลือกเอา git มาช่วยจัดการปัญหานี้
เริ่มแรกสุดก็คือ ในตอนที่เราสั่ง svn clone, เราต้องระบุ flag เพิ่มดังนี้
ผลที่ได้ ก็คือ master branch ของเราจะ map เข้ากับ trunk ของ svn
ส่วน branches และ tags ต่างๆที่อยู่ใน subversion repository จะมีสถานะ เป็น remote branch ใน git
สมมติว่าเราต้องการจะทำงานกับ branch j1023
เราก็จะต้องทำการสร้าง local branch ที่ map เข้ากับ remote branch ด้วยคำสั่งนี้
ถ้ามีงานด่วนเข้ามา ระหว่างทำงาน ก็เลือกได้ว่า จะ commit เข้าไปก่อน (กลับมา revert ทีหลังได้ หรือใช้ commit --amend เพื่อรวบยอด commit ภายหลังก็ได้)
หรือถ้าไม่อยาก commit เลย ก็อาจจะใช้ git-stash แทนก็ได้
พอ clear state ของ working copy เสร็จ ก็ให้สั่ง switch โดยใช้คำสั่ง
การ update branch และ trunk ให้เท่ากับ svn repository ทำได้โดยใช้คำสั่ง
ผลของมัน ก็คือ git จะ fetch changed (ของทุกๆ branches, tags) ที่เกิดขึ้นบน svn repo มาที่ git repository
แต่จะยังไม่ apply changed เข้ากับ local branch
ถ้าต้องการ apply changed เข้ากับ local branch เราต้องสั่ง
ส่งผลให้คนที่ดูหลาย branch เกิดความลำบากในการ switch ไปมาระหว่าง branch มากขึ้น
ปกติวิธีการ switch branch เราสามารถใช้คำสั่ง
svn switch ได้เลยแต่ปัญหาก็คือมันมักจะมี code ที่กำลังแก้ค้างอยู่ จะ commit ไว้่ก่อน switch ก็ไม่เหมาะ เพราะยังแก้ไขไม่ทันเสร็จดี
ผมเลือกเอา git มาช่วยจัดการปัญหานี้
เริ่มแรกสุดก็คือ ในตอนที่เราสั่ง svn clone, เราต้องระบุ flag เพิ่มดังนี้
git svn clone http://your-repo -T trunk -b branches -t tags
ผลที่ได้ ก็คือ master branch ของเราจะ map เข้ากับ trunk ของ svn
ส่วน branches และ tags ต่างๆที่อยู่ใน subversion repository จะมีสถานะ เป็น remote branch ใน git
$ git branch
* master
pphetra@pann@[~/projects/java/gitwcfweb]
$ git branch -r
c1001
j1023
j1027
j1039
j1041
trunk
สมมติว่าเราต้องการจะทำงานกับ branch j1023
เราก็จะต้องทำการสร้าง local branch ที่ map เข้ากับ remote branch ด้วยคำสั่งนี้
git checkout -b j1023-local j1023
ถ้ามีงานด่วนเข้ามา ระหว่างทำงาน ก็เลือกได้ว่า จะ commit เข้าไปก่อน (กลับมา revert ทีหลังได้ หรือใช้ commit --amend เพื่อรวบยอด commit ภายหลังก็ได้)
หรือถ้าไม่อยาก commit เลย ก็อาจจะใช้ git-stash แทนก็ได้
พอ clear state ของ working copy เสร็จ ก็ให้สั่ง switch โดยใช้คำสั่ง
git checkout -f master
การ update branch และ trunk ให้เท่ากับ svn repository ทำได้โดยใช้คำสั่ง
git svn fetch
ผลของมัน ก็คือ git จะ fetch changed (ของทุกๆ branches, tags) ที่เกิดขึ้นบน svn repo มาที่ git repository
แต่จะยังไม่ apply changed เข้ากับ local branch
ถ้าต้องการ apply changed เข้ากับ local branch เราต้องสั่ง
git svn rebase
Related link from Roti
Wednesday, June 04, 2008
Structural Type
เมื่อก่อนเวลาเขียนโปรแกรมใน Java, สิ่งแรกที่นึกขึ้นมาในหัวก็คือการกำหนด Interface
ยังขำที่โปรแกรม ruby แรกที่เขียน ก็พยายามจะ define interface ทั้งๆที่ ruby มันไม่มี interface แถมยังทำ duck typing ได้
วันก่อนอ่านเจอ feature ของ scala ที่ชื่อ Structural types ใน Scala
รู้สึกว่า งามจริงๆ
ดูตัวอย่างการใช้ Structural Type
Note: สำหรับคนที่ไม่คุ้นกับ Scala
def คือการกำหนด method, เครื่องหมาย : คือการกำหนด type
กรณี code ข้างล่างนี้, method say มี return type เป็น String
ส่วน
ยังขำที่โปรแกรม ruby แรกที่เขียน ก็พยายามจะ define interface ทั้งๆที่ ruby มันไม่มี interface แถมยังทำ duck typing ได้
วันก่อนอ่านเจอ feature ของ scala ที่ชื่อ Structural types ใน Scala
รู้สึกว่า งามจริงๆ
ดูตัวอย่างการใช้ Structural Type
class Person {
def say():String = "hi"
}
class Duck {
def say():String = "Quack"
}
object Test {
def play(element: {def say():String}) = {
System.out.println(element.say());
}
def main(args : Array[String]) : Unit = {
play(new Person())
play(new Duck())
}
}
Note: สำหรับคนที่ไม่คุ้นกับ Scala
def คือการกำหนด method, เครื่องหมาย : คือการกำหนด type
กรณี code ข้างล่างนี้, method say มี return type เป็น String
def say():String = "Quack"
ส่วน
Object {...} ก็คือ Singleton Object
Related link from Roti
Labels:
scala
Monday, June 02, 2008
Groovy in Ofbiz
น้องแซนแจ้งมาว่าขณะที่ merge source code ของ Ofbiz เข้า project Orangegears
พบว่ามี้ groovy code โผล่เข้ามาแล้วแล้ว
ว่าแล้วผมก็จัดแจง update source code ของ orangegears เสียหน่อย
แล้วก็สั่ง grep -ilR groovy ดู
ก็พบว่าเริ่มมีการ replace screen action script จากของเดิมที่เขียนด้วย bsh ไปเป็น groovy บ้างแล้ว
แล้วก็พบว่ามีการเตรียมการใช้ groovy ใน service layer อีกด้วย (แต่ยังไม่ได้มีการ implement)
ลองไล่เปรียบเทียบ syntax ของ bsh กับ groovy ดูว่า ช่วยลดรูปอะไรได้บ้าง
ใน ofbiz เวลา pass parameters มักจะใช้ Map ในการ pass arguments
พอเปลี่ยนเป็น groovy แล้วการสร้าง Map ก็เลยกระทัดรัดขึ้น
แน่นอนพวกการ iterate collections นี่ได้ประโยชน์ไปเต็มๆ
การอ้างถึง value ใน Map
ด้วย syntax sugar ของ Groovy ก็เลย สะอาดสะอ้านขึ้นแบบนี้
การ check empty หรือ null collections ก็สบายตาขึ้น
พบว่ามี้ groovy code โผล่เข้ามาแล้วแล้ว
ว่าแล้วผมก็จัดแจง update source code ของ orangegears เสียหน่อย
แล้วก็สั่ง grep -ilR groovy ดู
ก็พบว่าเริ่มมีการ replace screen action script จากของเดิมที่เขียนด้วย bsh ไปเป็น groovy บ้างแล้ว
แล้วก็พบว่ามีการเตรียมการใช้ groovy ใน service layer อีกด้วย (แต่ยังไม่ได้มีการ implement)
ลองไล่เปรียบเทียบ syntax ของ bsh กับ groovy ดูว่า ช่วยลดรูปอะไรได้บ้าง
ใน ofbiz เวลา pass parameters มักจะใช้ Map ในการ pass arguments
พอเปลี่ยนเป็น groovy แล้วการสร้าง Map ก็เลยกระทัดรัดขึ้น
// bsh
payment = delegator.findByPrimaryKey("Payment",
UtilMisc.toMap("paymentId", paymentId)));
# groovy
payment = delegator.findByPrimaryKey("Payment", [paymentId : paymentId]);
แน่นอนพวกการ iterate collections นี่ได้ประโยชน์ไปเต็มๆ
// bsh
oibIter = orderItemBillings.iterator();
while (oibIter.hasNext()) {
orderIb = oibIter.next();
orders.add(orderIb.getString("orderId"));
}
# groovy
orderItemBillings.each { orderIb ->
orders.add(orderIb.orderId);
}
การอ้างถึง value ใน Map
ด้วย syntax sugar ของ Groovy ก็เลย สะอาดสะอ้านขึ้นแบบนี้
// bsh
context.put("decimals", decimals);
context.put("rounding", rounding);
# groovy
context.decimals = decimals;
context.rounding = rounding;
การ check empty หรือ null collections ก็สบายตาขึ้น
// bsh
if (glAccounts != null && glAccounts.size() > 0) {
glAccount = glAccounts.get(0);
# groovy
if (glAccounts) {
glAccount = glAccounts[0];
Related link from Roti
Friday, May 30, 2008
My Pyhton 's process was Killed
ช่วงนี้กำลังทดสอบโปรแกรมที่เขียนด้วย python บนเครื่อง Production ที่ใช้ AIX เป็น OS
หลังจาก run program ไปได้พักหนึ่ง, โปรแกรมก็หยุดทำงานพร้อมกับมีข้อความแบบนี้ต่อท้าย
คำถามแรกก็คือ "ใคร kill กูวะ"
process ของน้องชิ้ม (น้องที่ทำงาน) ตกเป็นผู้ต้องสงสัยรายแรก
(น้องชิ้มเขียน script ที่คอย monitor ว่ามี process ไหนใช้ cpu ผิดปกติบ้าง
ถ้าเจอก็จะจัดการ kill เสีย)
หลังจากน้องชิ้มตรวจดู log file สักพัก ก็บอกว่า "script ผมไม่ได้ทำครับพี่"
ผู้ต้องสงสัยรายถัดไป ก็คือ message ข้างล่างนี้
message นี้เกิดขึ้นในระหว่างทำงาน
หลังจากค้น google ก็พบว่า kernel น่าจะเป็นคนสั่ง kill process เอง (http://tinyurl.com/59e7yw)
ส่วนสำเหตุก็คือ bug ตัวนี้ http://bugs.python.org/issue1234
นัยว่าเจ้า python มันคงเรียกใช้ sem_wait มากเกินไป จน kernel มันทนไม่ได้
ว่าแล้วก็รอแปะ patch และ compile python เย็นนี้
จันทร์หน้ามารอดูผลกันใหม่
หลังจาก run program ไปได้พักหนึ่ง, โปรแกรมก็หยุดทำงานพร้อมกับมีข้อความแบบนี้ต่อท้าย
killed
คำถามแรกก็คือ "ใคร kill กูวะ"
process ของน้องชิ้ม (น้องที่ทำงาน) ตกเป็นผู้ต้องสงสัยรายแรก
(น้องชิ้มเขียน script ที่คอย monitor ว่ามี process ไหนใช้ cpu ผิดปกติบ้าง
ถ้าเจอก็จะจัดการ kill เสีย)
หลังจากน้องชิ้มตรวจดู log file สักพัก ก็บอกว่า "script ผมไม่ได้ทำครับพี่"
ผู้ต้องสงสัยรายถัดไป ก็คือ message ข้างล่างนี้
sem_trywait: Permission denied
sem_wait: Permission denied
sem_post: Permission denied
message นี้เกิดขึ้นในระหว่างทำงาน
หลังจากค้น google ก็พบว่า kernel น่าจะเป็นคนสั่ง kill process เอง (http://tinyurl.com/59e7yw)
ส่วนสำเหตุก็คือ bug ตัวนี้ http://bugs.python.org/issue1234
นัยว่าเจ้า python มันคงเรียกใช้ sem_wait มากเกินไป จน kernel มันทนไม่ได้
ว่าแล้วก็รอแปะ patch และ compile python เย็นนี้
จันทร์หน้ามารอดูผลกันใหม่
Related link from Roti
เปิดเน็ต เปิดใจ
ที่บ้านผมไม่มีทีวี แล้วก็ไม่ได้รับหนังสือพิมพ์ ก็เลยไม่ตามข่าวการเมืองอย่างไกล้ชิด
แต่ก็รู้ว่า ประชาธิปัตย์ ปัจจุบันแม้จะผ่านไป 50-60 ปีแล้ว ยังเล่นมุขเดิมไม่เลิก
เรื่องที่ไกล้ตัวหน่อยก็มี กรณีนายเทพไท เสนพงศ์ ออกมาสาดโคลนใส่เว็บ
เห็นมีมหาวิทยาลัยเที่ยงคืนรวมอยู่ด้วยแล้วอดรนทนไม่ได้
ก็เลยต้องขอร่วมลงชื่อแสดงเสียงสักหน่อย

ปกป้องเสรีภาพของเรา
บนอินเทอร์เน็ตของเรา
ร่วมลงชื่อในแถลงการณ์
แต่ก็รู้ว่า ประชาธิปัตย์ ปัจจุบันแม้จะผ่านไป 50-60 ปีแล้ว ยังเล่นมุขเดิมไม่เลิก
เรื่องที่ไกล้ตัวหน่อยก็มี กรณีนายเทพไท เสนพงศ์ ออกมาสาดโคลนใส่เว็บ
เห็นมีมหาวิทยาลัยเที่ยงคืนรวมอยู่ด้วยแล้วอดรนทนไม่ได้
ก็เลยต้องขอร่วมลงชื่อแสดงเสียงสักหน่อย
ปกป้องเสรีภาพของเรา
บนอินเทอร์เน็ตของเรา
ร่วมลงชื่อในแถลงการณ์
Related link from Roti
Tuesday, May 27, 2008
Pyro
ช่วงนี้มีความจำเป็นต้องใช้ remote call บน python, ค้น google ดูก็พบเจ้า Pyro
หลังจากอ่านหลักการแล้วก็อ๋อ เพราะทำงานแนวเดิียวกับ java RMI
หลักการทำงาน
สิ่งที่ต้องระวังในการ implement remote object ของเราก็คือ
มันต้องเป็น thread safe เนื่องจาก daemon จะใช้วิธีแตก thread เมื่อมี incoming connection วิ่งเข้ามา
feature ที่น่าสนใจในประเด็นนี้ ก็คือ TLS (Thread Local Storage)
ซึ่งช่วยให้เราเก็บข้อมูลโดยการ bind local data เข้ากับ thread แทน
feature ที่น่าสนใจอีกอย่างก็คือ Mobile code
case นี้เป็นกรณีที่ client call ไปยัง remote object โดยส่ง argument เป็น object ที่ไม่มี code อยู่บน server มาด้วย
ซึ่งโดยปกติ มันจะเกิด error ประเภท NoModuleError
แต่ถ้าเรากำหนด configuration ให้ Pyro ใช้ feature Mobile code
เวลาที่ server เจอ code ที่ไม่รู้จัก มันก็จะ request code มายัง client
กรณี mobile code นี้ support 2-way
นั่นคือ support กรณี server return object ที่ไม่มี code บน client ด้วย
หลังจากอ่านหลักการแล้วก็อ๋อ เพราะทำงานแนวเดิียวกับ java RMI
หลักการทำงาน
- Name Server
เพื่อให้ client สามารถ lookup service ได้ง่ายๆ, Pyro ก็เลย provide Name Server มาให้ด้วย
เจ้า name server ที่ pyro ให้มา มันแบ่งออกเป็น 2 ประเภทคือ- Regular, non-persistent
ตัวนี้พอ name server process ตาย, ค่าต่างๆใน registry ก็หายไปด้วย - Persistent
ค่าต่างๆใน registry จะเก็บไว้บน disk
เพื่อเพิ่ม availability, เจ้าตัว name server นี้ ยังสามารถ start ในแบบ Paired mode ได้อีกด้วย โดยทั้งคู่จะทำ replicate registry ซึ่งกันและกันไว้ - Regular, non-persistent
- Client
การที่ client จะติดต่อ server ได้นั้น ขั้นแรกก็ต้องหา Name Server ให้เจอเสียก่อน
วิธีการหา Name Server ก็คือlocator = Pyro.naming.NameServerLocator()
ns = locator.getNS()
ขั้นตอนการทำงานภายในก็คือ มันจะทำการ broadcast message ออกไป
เจ้า Name server (ซึ่ง implement broadcast listener) ก็จะตอบกลับมา
Note: กรณีของผม ผมลองใช้ boardcast บน production server แล้ว, message มันหายต๋อมไป (ด้วยความขี้เกียจไปไล่หาสาเหตุ)
ผมก็เลย config ให้ NameServerLocator มันต่อตรงไปที่ Name Server โดยตรงเลย แทนที่จะ broadcast
การ config ให้ต่อตรงทำได้โดยPyro.config.PYRO_NS_HOSTNAME='YOUR_HOSTNAME'
หลังจากหา Name Server เจอแล้ว ขั้นถัดไปก็คือ ทำการถามหา URI ของ server ที่ต้องการuri = ns.resolve('my_service')
เมื่อได้ uri แล้ว สุดท้ายก็คือทำการสร้าง proxy object ซึ่งเจ้า Pyro แบ่งประเภท Proxy ออกเป็น 2 แบบคือ- Proxy ธรรมดา
- Proxy ที่สามารถ access attribute ของ remote object ได้ด้วย
วิธีการสร้าง proxy ก็คือobj = uri.getProxy()
# or
obj = uri.getAttrProxy()
ที่เหลือ ก็เป็นการเรียกใช้ method ต่างๆบน remote object ที่เราได้มา - Proxy ธรรมดา
- Server
การทำงานฝั่ง server เริ่มต้นด้วยการสร้าง daemon object
(มี parameter เป็น name server object ซึ่ง lookup มาด้วยวิธีเดียวกันกับ client)daemon = Pyro.core.Daemon()
daemon.useNameServer(ns)
ข้อควรระวังก็คือ ต้องระวังเรื่อง reference ถึง object daemon ของเรา
มิเช่นนั้น garbage collection อาจกวาด daemon เราทิ้งไปได้
หลังจากได้ daemon มา ก็ทำการสร้าง Object instance ที่ต้องการให้ client ต่อใช้
เนื่องจาก pyro ต้องหลอก object ของเราว่ามันทำงานเหมือนกับอยู่ในสภาพแวดล้อมแบบ stand-alone
ดังนั้น Remote Object ของเราก็เลยต้องถูกห่อไว้ใน Pyro.core.ObjBase
ซึ่งเราสามารถทำได้ 3 วิธีคือ- ให้ Remote class ของเรา extend Pyro.core.ObjBase ตรงๆเลย
- delegate pattern
บางครั้งเราอยากเขียน Remote Class โดยไม่อยากให้มี dependency ถึง Pyro เลยobj = Pyro.core.ObjBase()
myservice = MyService()
obj.delegateTo(myservice) - สร้าง class ใหม่ที่ extend ทั้ง Pyro.core.ObjBase และ Service class ของเรา
class ServiceImpl(Pyro.core.ObjBase, MyService):
def __init__(self):
Pyro.core.ObjBase.__init__(self)
MyService.__init__(self)
ขั้นถัดไปก็คือทำการ binding
โดยสั่ง connect daemon กับ Object instance
ซึ่งเจ้า daemon จะจัดการไปคุยกับ Name Server ให้เราเองdaemon.connect(obj, 'my_service')
สุดท้ายก็คือการสั่งให้ daemon วน loop รับ request จาก clientdaemon.requestLoop()
- ให้ Remote class ของเรา extend Pyro.core.ObjBase ตรงๆเลย
สิ่งที่ต้องระวังในการ implement remote object ของเราก็คือ
มันต้องเป็น thread safe เนื่องจาก daemon จะใช้วิธีแตก thread เมื่อมี incoming connection วิ่งเข้ามา
feature ที่น่าสนใจในประเด็นนี้ ก็คือ TLS (Thread Local Storage)
ซึ่งช่วยให้เราเก็บข้อมูลโดยการ bind local data เข้ากับ thread แทน
feature ที่น่าสนใจอีกอย่างก็คือ Mobile code
case นี้เป็นกรณีที่ client call ไปยัง remote object โดยส่ง argument เป็น object ที่ไม่มี code อยู่บน server มาด้วย
ซึ่งโดยปกติ มันจะเกิด error ประเภท NoModuleError
แต่ถ้าเรากำหนด configuration ให้ Pyro ใช้ feature Mobile code
เวลาที่ server เจอ code ที่ไม่รู้จัก มันก็จะ request code มายัง client
กรณี mobile code นี้ support 2-way
นั่นคือ support กรณี server return object ที่ไม่มี code บน client ด้วย
Related link from Roti
Labels:
python
Sunday, May 25, 2008
ป้ายแดง
ช่วงนี้โรงเรียนลูกเปิดเทอมแล้ว
ผมก็เลยถอยรถคันใหม่ออกมาส่งลูกโดยเฉพาะ
เลือกรุ่น entry level (ราคาถูกกว่าคันของ ipats) พร้อมด้วยเบาะนั่งของเด็ก
ขี่ไปส่งลูกแล้วรู้สึกมั่นใจในความปลอดภัยขึ้นเยอะ

ส่วนคันเก่าก็ไม่ได้ทิ้งไปไหน พอส่งลูกเสร็จก็เปลี่ยนเป็นขี่เสือหมอบไปทำงาน
เพราะเจ้าเสือหมอบมันเร่งแซงได้ทันใจกว่ากันเยอะ
ผมก็เลยถอยรถคันใหม่ออกมาส่งลูกโดยเฉพาะ
เลือกรุ่น entry level (ราคาถูกกว่าคันของ ipats) พร้อมด้วยเบาะนั่งของเด็ก
ขี่ไปส่งลูกแล้วรู้สึกมั่นใจในความปลอดภัยขึ้นเยอะ

ส่วนคันเก่าก็ไม่ได้ทิ้งไปไหน พอส่งลูกเสร็จก็เปลี่ยนเป็นขี่เสือหมอบไปทำงาน
เพราะเจ้าเสือหมอบมันเร่งแซงได้ทันใจกว่ากันเยอะ
Related link from Roti
Friday, May 23, 2008
Case Class in Scala
ใครที่เคยเห็น code ของ Scala, คงต้องเคยเห็นคำว่า
ครั้งแรกที่ผมเห็นมัน ผมรู้สึกมึนตึบ แล้วก็สงสัยว่ามันคืออะไร
ทำไม case statement มาเกี่ยวอะไรกับ class ด้วย
หลังจากนั่งอ่าน Programming in Scala ของ Martin Odersky, Lex Spoon, Bill Venners
ก็เลยเกิดความกระจ่างขึ้น
เจ้า modifier "case" ข้างหน้า class นั้น มันทำให้ compiler ช่วย generate method บางอย่างเพิ่มขึ้นมา
เพื่อทำให้เราสามารถใช้ feature pattern matching กับ Object ได้ง่ายขึ้น
ในหนังสือเขายกตัวอย่าง กรณี expression
สมัยแรกๆที่ผมเห็น statement ข้างบน ผมก็จะนึกเดาไปว่า
ตัว case class มันต้องเขียนเรียงติดๆกันแบบเดียวกับพวก case statement
แต่จริงๆแล้วไม่ใช่ "case" เป็นแค่ modifiler (เหมือนพวก public, private, static ทำนองนั้น)
ประโยคข้างบนจริงๆแล้วก็คือ การประกาศ class ขึ้นมา 5 ตัว
ที่ดูแปลกๆ ก็คือใน scala กรณี empty body, เราไม่ต้องใส่ class body
สิ่งที่ case modifier ทำก็คือ
ดูเหมือนว่าสิ่งที่มัน gen ก็ไม่มีอะไรมาก
แต่ผลที่ได้ทำให้เราสามารถทำ pattern matching แบบนี้ได้
case classครั้งแรกที่ผมเห็นมัน ผมรู้สึกมึนตึบ แล้วก็สงสัยว่ามันคืออะไร
ทำไม case statement มาเกี่ยวอะไรกับ class ด้วย
หลังจากนั่งอ่าน Programming in Scala ของ Martin Odersky, Lex Spoon, Bill Venners
ก็เลยเกิดความกระจ่างขึ้น
เจ้า modifier "case" ข้างหน้า class นั้น มันทำให้ compiler ช่วย generate method บางอย่างเพิ่มขึ้นมา
เพื่อทำให้เราสามารถใช้ feature pattern matching กับ Object ได้ง่ายขึ้น
ในหนังสือเขายกตัวอย่าง กรณี expression
abstract class Expr
case class Var(name: String) extends Expr
case class Number(num: Double) extends Expr
case class UnOp(operator: String, arg: Expr) extends Expr
case class BinOp(operator: String, left: Expr, right: Expr) extends Expr
สมัยแรกๆที่ผมเห็น statement ข้างบน ผมก็จะนึกเดาไปว่า
ตัว case class มันต้องเขียนเรียงติดๆกันแบบเดียวกับพวก case statement
แต่จริงๆแล้วไม่ใช่ "case" เป็นแค่ modifiler (เหมือนพวก public, private, static ทำนองนั้น)
ประโยคข้างบนจริงๆแล้วก็คือ การประกาศ class ขึ้นมา 5 ตัว
ที่ดูแปลกๆ ก็คือใน scala กรณี empty body, เราไม่ต้องใส่ class body
{ } ให้มันก็ได้ (มันจะแอบใส่ให้เราเอง)สิ่งที่ case modifier ทำก็คือ
- compiler จะช่วย generate Factory method ที่ชื่อเดียวกับ class ให้เรา
ผลก็คือ เราสามารถใช้คำสั่งแบบนี้ได้val v = Number(10)
แทนที่จะเป็น
val v = new Number(10)
ซึ่งมีประโยชน์มากเวลาที่เรา nested แบบนี้
BinOp("+", Var("x"), Number(7)) - compiler จะช่วยทำให้ argument ทุกตัวกลายเป็น read only property ของ class นั้น
เช่น
scala> val b = BinOp("+", Var("x"), Var("x"))
b: BinOp = BinOp(+,Var(x),Var(x))
scala> b.operator
res23: String = + - compiler จะช่วย generate พวก method equals, hashcode ให้เรา
โดยตัว equals method มันจะ implement ในลักษณะที่จะเปรียบเทียบทุกๆ property ของ object ให้เรา
ทำให้เราสามารถเปรียบเทียบแบบนี้ได้
scala> Var("x") == Var("x")
res21: Boolean = true
scala> BinOp("+", Number(7), Number(8)) == BinOp("+", Number(7), Number(8))
res22: Boolean = true
ดูเหมือนว่าสิ่งที่มัน gen ก็ไม่มีอะไรมาก
แต่ผลที่ได้ทำให้เราสามารถทำ pattern matching แบบนี้ได้
def simplifyTop(expr: Expr): Expr = expr match {
case UnOp("-", UnOp("-", e)) => e // Double negation
case BinOp("+", e, Number(0)) => e // Adding zero
case BinOp("*", e, Number(1)) => e // Multiplying by one
case _ => expr
}
Related link from Roti
Labels:
scala
Wednesday, May 14, 2008
delegate ใน groovy closure
งาน NJUG ที่ผ่านมาตอนที่ฟังคุณ cblue พูดเรื่อง groovy
มีตัวแปรใน closure ตัวหนึ่งที่ผมติดใจ ก็คือ delegate
กลับมาแล้วก็เลยต้องนั่งเปิด groovy document หาความรู้เพิ่มเติม
ก็เลยเจอ snippet code ที่น่าสนใจตัวนี้เข้า
เทคนิคที่น่าสนใจก็คือการเปลี่ยน reference ของ delegate ให้ชี้ไปยัง object ที่เราต้องการ
ลองดูตัวอย่างการ run code ข้างบน
จะเห็นว่ามีการใช้ closure ซ้อนๆกัน
ทุกๆครั้งที่ มีการ execute inner closure ก็จะมีการ switch delegate ให้ชี้ไปที่
XmlBuilder Object แทนที่จะเป็น default parent object
มีตัวแปรใน closure ตัวหนึ่งที่ผมติดใจ ก็คือ delegate
กลับมาแล้วก็เลยต้องนั่งเปิด groovy document หาความรู้เพิ่มเติม
ก็เลยเจอ snippet code ที่น่าสนใจตัวนี้เข้า
เทคนิคที่น่าสนใจก็คือการเปลี่ยน reference ของ delegate ให้ชี้ไปยัง object ที่เราต้องการ
class XmlBuilder {
def out
XmlBuilder(out) { this.out = out }
def invokeMethod(String name, args) {
out << "<$name>"
if(args[0] instanceof Closure) {
args[0].delegate = this
args[0].call()
}
else {
out << args[0].toString()
}
out << "</$name>"
}
}
ลองดูตัวอย่างการ run code ข้างบน
จะเห็นว่ามีการใช้ closure ซ้อนๆกัน
def xml = new XmlBuilder()
xml.html {
head {
title "Hello World"
}
body {
p "Welcome!"
}
}
ทุกๆครั้งที่ มีการ execute inner closure ก็จะมีการ switch delegate ให้ชี้ไปที่
XmlBuilder Object แทนที่จะเป็น default parent object
Related link from Roti
Monday, May 12, 2008
Script bowl
หลังจากไป NJUG กลับมาผมพึ่งพบว่าใน java one มันมี event ที่ชื่อ Script Bowl
event นี้เป็นการจัดประชันระหว่าง script language 4 ตัวก็คือ JRuby, Groovy, Jython, Scala
โดยแบ่งการประชันออกเป็น 3 รอบ
รอบที่ 1 ให้ทำ app ที่เป็น Desktop GUI
รอบที่ 2 ให้ทำ app ที่เป็น Web Application
ส่วนรอบที่ 3 ให้ demo app อะไรก็ได้ที่คิดว่ามัน show feature ได้ถึงใจแฟนๆ
จากนั้นก็ให้ผู้ชมทำการ vote เข้ามา
ผลลัทพ์ที่ได้ ลองดูกันเองแล้วกัน
A คือ Groovy, B คือ JRuby, C คือ Jython, D คือ scala
Script Bowl Result
seapegasus เขาเขียนบรรยายเหตุการณ์โดยคร่าวๆไว้ที่นี่
Groovy, JRuby, Jython, Scala: Who Wins the Script Bowl?
event นี้เป็นการจัดประชันระหว่าง script language 4 ตัวก็คือ JRuby, Groovy, Jython, Scala
โดยแบ่งการประชันออกเป็น 3 รอบ
รอบที่ 1 ให้ทำ app ที่เป็น Desktop GUI
รอบที่ 2 ให้ทำ app ที่เป็น Web Application
ส่วนรอบที่ 3 ให้ demo app อะไรก็ได้ที่คิดว่ามัน show feature ได้ถึงใจแฟนๆ
จากนั้นก็ให้ผู้ชมทำการ vote เข้ามา
ผลลัทพ์ที่ได้ ลองดูกันเองแล้วกัน
A คือ Groovy, B คือ JRuby, C คือ Jython, D คือ scala
Script Bowl Result
seapegasus เขาเขียนบรรยายเหตุการณ์โดยคร่าวๆไว้ที่นี่
Groovy, JRuby, Jython, Scala: Who Wins the Script Bowl?
Related link from Roti
Sunday, May 04, 2008
ยืน-ไม่ยืน
สืบเนื่องจาก เรื่องสิทธิการไม่ยืนในโรงหนัง
ลองดูว่าที่อื่นๆเขามีประเด็นเกี่ยวกับเรื่องนี้อย่างไรบ้าง
แต่ที่ผมชอบคืออันนี้ (ซึ่งไม่เกี่ยวกับยืนไม่ยืน)
เพลงชาติอินเดีย สรรเสริญ King George V?
ส่วนตัวผมก็คิดว่าเราจะหา "สมดุล" ในเรื่องนี้อย่างไร
คือประโยชน์ของมันก็มี ไม่ใช่ไม่มี
แต่จะนำมันออกจากกรอบ "อำนาจนิยม" หรือ ความ"คลั่งไคล้" ได้อย่างไร
ลองดูว่าที่อื่นๆเขามีประเด็นเกี่ยวกับเรื่องนี้อย่างไรบ้าง
- เริ่มจากอเมริกา ดินแดนที่ประเทศไทยนับถือ
มีประเด็นเรื่องครูบังคับให้นักเรียนยืนเคารพเพลงชาติ - ต่อด้วยญี่ปุ่น (ซึ่งประเทศไทยนับถืออีกเช่นกัน)
ศาลตัดสินให้คุณครูไม่ยืนเคารพเพลงชาติได้ - ข้างบนนั้นเป็นกรณีชาติเดียวกัน แต่ถ้าเป็นคนละชาติหล่ะ ก็เป็นเรื่องสิ
เด็กนักเรียนอาหรับไม่ยืนเคารพเพลงชาติยิว
แต่ที่ผมชอบคืออันนี้ (ซึ่งไม่เกี่ยวกับยืนไม่ยืน)
เพลงชาติอินเดีย สรรเสริญ King George V?
ส่วนตัวผมก็คิดว่าเราจะหา "สมดุล" ในเรื่องนี้อย่างไร
คือประโยชน์ของมันก็มี ไม่ใช่ไม่มี
แต่จะนำมันออกจากกรอบ "อำนาจนิยม" หรือ ความ"คลั่งไคล้" ได้อย่างไร
Related link from Roti
Subscribe to:
Posts (Atom)