Friday, November 14, 2008

fast-forward ใน git-merge

เห็นคำว่า Fast forward เวลาสั่ง git pull มานานแล้ว, มาอ่าน git-doc ก็เลยเข้าใจขึ้น

เวลาเราสั่ง git merge, การทำงานของมันสามารถแบ่งได้เป็น 3 ประเภท

  • ถ้า merged commit ที่เราดึงมา อยู่ใน Head(current tree ของเรา) ของเราแล้ว, ก็จะแสดงผลลัพท์ "Already up-to-date." แล้วก็จบการทำงาน

  • ถ้า Head ของเราอยู่ใน commits ที่ดึงมา, case นี้มักเกิดจากคำสั่ง "git pull" เพื่อดึง code จากต้นน้ำมา update code(ที่ไม่มีการเปลี่ยนแปลง) ของเรา, สิ่งที่เกิดขึ้นก็คือ git จะ update HEAD ของเราให้ตรงตาม HEAD ของ merged commit (โดยไม่มีการสร้าง commit object ใหม่ขึ้นมา) มีศัพท์เฉพาะสำหรับกรณีนี้ว่า "Fast-forward"

  • สุดท้ายเป็นกรณีที่เกิดการ merge จริงๆ นั่นคือ ตัว HEAD ของเรา independent กับ merged commit, ดังนั้นกรณนี้จะเกิดการ merge จริง และมีการสร้าง commmit object ใหม่ขึ้นมา

Related link from Roti

Thursday, November 13, 2008

config syntax ใน grails

ผมสงสัยเรื่อง configuration ใน grails มาได้พักใหญ่แล้ว
เพราะเห็น syntax มันแปลกประหลาดเหลือเกิน ลองดูตัวอย่างข้างล่างนี้
log4j.appender.stdout = "org.apache.log4j.ConsoleAppender"
log4j.appender."stdout.layout"="org.apache.log4j.PatternLayout"

ทำไมบรรทัดที่สองต้องมี quote ครอบด้วยหล่ะ?

class ที่ทำหน้าที่อ่าน file Config.groovy ของ grails ก็คือ ConfigSlurper
โดยวิธีการใช้งานเจ้า ConfigSlurper ก็คือแบบนี้
def config = new ConfigSlurper().parse(new File('/tmp/x.groovy').toURL())

สิ่งที่เจ้า ConfigSlurper return มาก็คือ Map ตัวหนึ่ง (มันคือ class ConfigObject ที่ extends จาก LinkedHashMap)

ที่นี้มาลองดู nature ของมันบ้าง
เริ่มจาก config ง่ายๆ บรรทัดเดียว
topic1.subtopic1=1

ผลลัพท์ที่ได้จากการ parse จะเป็น map หน้าตาแบบนี้
["topic1":["subtopic1":1]]


ที่นี้ถ้าลองเพิ่ม sub node เข้าไปใต้ subtopic แบบนี้
topic1.subtopic1=1
topic1.subtopic1.subofsubtopic1="foo"

เมื่อลอง parse ดู แทนที่จะได้ผลลัพท์ เรากลับได้ exception แบบนี้แทน
groovy.lang.MissingPropertyException: No such property: subofsubtopic1 for class: java.lang.Integer
at script1226551599145.run(script1226551599145.groovy:2)
at Script9.run(Script9:1)

ดูเหมือนว่าการทำงานภายในของมันก็คือ เมื่อมันต้องการ assign ค่า topic1.subtopic1.subofsubtopic1 มันจะไป get value โดยใช้ key topic1.subtopic1 จากนั้นก็พยายามจะ set property subofsubtopic1ลงใน value ที่ได้มาก ซึ่งไม่สำเร็จแน่ๆเพราะ value ที่ได้มันไม่ใช่ map อย่างที่ควรจะเป็น

ที่นี้ลอง config แบบนี้บ้าง
topic1.subtopic1.subofsubtopic1="foo"
topic1.subtopic1=1

ผลลัพท์ที่ได้คือ
["topic1":["subtopic1":1]]

อ้าว แล้ว subofsubtopic1 ของเราหายไปไหนหล่ะ
กลายเป็นว่า subofsubtopic1 ของเราถูก เจ้า topic1.subtopic1 override ค่าไปเรียบร้อยแล้ว

ลองให้เหลือ บรรทัดเดียวแบบนี้ ดูบ้าง
topic1.subtopic1.subofsubtopic1="foo"

ผลลัพท์ที่ได้ จะเป็นแบบนี้
["topic1":["subtopic1":["subofsubtopic1":"foo"]]]


สรุปได้ว่า เนื่องจาก ConfigSlurperใช้ nested map เป็น internal structure ในการเก็บผลลัพท์จากการ parse
ทำให้เกิดข้อจำกัดในการเก็บขึ้นมา ดังนั้นถ้าเราต้องการเก็บค่าแบบบ้างบนให้ได้ เราก็เลยต้อง hack มัน โดยการห่อค่าให้เป็น string แบบนี้แทน
topic1.subtopic1=1
topic1."subtopic1.subofsubtopic1"="foo"

ผลลัพท์ที่ได้จากการ map ก็จะเป็นแบบนี้
["topic1":["subtopic1":1, "subtopic1.subofsubtopic1":"foo"]]

อืมม์ไม่สวยเป็นอย่างยิ่ง น่าจะมีคน refactor นะ

สรุปได้ว่า ถ้าเรานำไปใช้งาน configuration ของเรา (ถ้าใช้ grails ก็ต้องเจอแน่ๆ)
เราก็ควรจะออกแบบ tree ของเราดีๆ อย่าให้เกิดกรณีที่ parent node มีค่า value attach อยู่
(ให้มี valueได้เฉพาะ leaf node)
ไม่งั้นอาจจะปวดหัวว่า ทำไมค่า config ถึงหายไป หรือไม่ก็ ทำไมถึง get config ที่ต้องการไม่เจอ

Related link from Roti