July 29, 2012

Git: แก้ conflict จากการ merge

ตอนก่อนจะเห็นว่า เราทำการ merge ได้อย่างสบายใจไร้กังวล เพราะ code ส่วนที่แก้นั้นอยู่คนละที่กันเลย แต่ถ้าแก้ code ในที่ๆ ซ้อนทับกัน จะเกิด conflict ซึ่งโปรแกรมไม่สามารถหยั่งรู้ได้ว่า เราต้องการ code ตอนสุดท้ายออกมาเป็นยังไงกันแน่ และเราต้องลงมือจัดการกับ conflict นั้นเองครับ

ศึกษาการแก้ conflict โดยการแตก branch แรกเพื่อเพิ่มความสามารถให้โปรแกรมรับ -v สำหรับบอกเวอร์ชั่นโปรแกรม

ที่ไฟล์ hello.py ส่วนที่เช็ค -h (แถวบรรทัดที่ 5) เพิ่ม code ให้เป็นอย่างนี้

แล้วก็ commit

เรียร้อยกับ branch แรกไปแล้ว ก็มาทำ branch ที่สอง โดยเขียนให้โปรแกรมรับ -l เพื่อบอกลิขสิทธิ์โปรแกรม

แก้ไฟล์ hello.py ที่เดิมเลย (แค่เปลี่ยนหน้าตา code นิดหน่อย)

เช่นเดิม commit มันซะ

คราวนี้กลับมาที่ master แล้วทำการ merge เหมือนตอนที่ผ่านมา

พบว่ามีฟ้อง conflict และส่งผลให้ merge ไม่ผ่าน ตอนนี้ถ้าดูสถานะจะพบว่า

หมายเหตุว่า Git จะไม่ยอมให้ย้าย branch ถ้าหากยังมี conflict เช่นนี้อยู่ ดังนั้นมาเก็บข้อผิดพลาดนี้กัน โดยเปิดไฟล์ hello.py ขึ้นมา จะเห็นดังนี้

ส่วนที่เกิด conflict จะถูกคั่นด้วยบรรทัดที่ขึ้นต้นด้วยเครื่องหมาย <<<<<<< , ======= , >>>>>>> ในตัวอย่างนี้ จัดการลบ 3 บรรทดนั้นทิ้งไปก็พอครับ (สำหรับงานจริง เปิดอ่านโปรแกรมแล้วจัดการเรียบเรียง code ใหม่ให้ทำงานถูกต้องนะครับ)

ตอนนี้อาจจะทดสอบเพิ่มกันอีกเล็กน้อย เมื่อมั่นใจว่าโปรแกรมทำงานถูกต้องแล้ว ก็สั่ง commit ความเปลี่ยนแปลงนี้ครับ (ไม่ใช่สั่ง merge ไปอีกรอบนะครับ -- ตัว Git จะรู้ได้เองว่านี่เป็นการแก้ conflict ที่เกิดจากการ merge)

ถึงตอนนี้ถ้ายังไม่มั่นใจว่าทั้ง 2 branch นั้นถูก merge แล้วจริงหรือเปล่า สามารถดูด้วย branch --no-merged ซึ่งควรจะไม่แสดงผลลัพท์ออกมา (เพราะถูก merge เรียบร้อยแล้ว) และถ้าสั่ง branch --merged ก็ควรจะเห็นครบ

เรียบร้อยแล้วก็ลบ branch ทั้ง 2 ทิ้งเลยครับ

July 28, 2012

Git: ทำการ merge หลาย branch

ในการทำงานร่วมกันหลายคน ย่อมมี branch เกิดขึ้นมากมายแน่นอน (มันคงไม่มีประโยชน์ถ้าจะรอให้คนนึงทำงานแรกเสร็จก่อน แล้วค่อยอนุญาตให้อีกคนทำงานถัดมา) ความสามารถในการ merge หลายๆ branch ที่แตกออกมาในเวลาเดียวกันนั้น จึงเป็นสิ่งที่โปรแกรม SCM ควรทำได้อย่างเป็นเรื่องปรกติธรรมดา

ตอนนี้จะทดสอบการ merge หลาย branch โดยเพิ่มความสามารถ 2 อย่างให้โปรเจค hello

ความสามารถแรกคือการแสดง help ซึ่งสามารถเรียกโดยเพิ่ม -h ทาง command line เช่นเดียวกับโปรแกรมทั่วๆ ไปครับ

แก้ code ในไฟล์ hello.py ดังนี้

(ทดสอบแล้วผ่าน) ก็ commit ตามปรกติครับ

เรียบร้อยกับความสามารถแรกไปแล้ว ทีนี้มาเพิ่มความสามารถที่สอง โดยกลับไปแตก branch มาจากตัว master นะครับ

ความสามารถนี้ จะทำการเรียงลำดับชื่อที่รับเข้ามาก่อน แล้วค่อยทักทายแต่ละชื่อที่เรียงแล้วนั้น ซึ่งสามารถเรียกโดยเพิ่ม -s เข้าไปตอนเรียกโปรแกรมครับ

(ทดสอบกันเอง) แล้วก็ commit ครับ

สลับกลับมาที่ master ถึงตอนนี้เราอาจจะลืมไปแล้วว่าได้ทำการเปลี่ยนแปลงอะไรไว้ที่ branch อื่นบ้าง เราสามารถสั่งให้แสดงความแตกต่างระหว่าง branch ดังนี้ครับ

ข้อมูล 4 บรรทัดแรกจากคำสั่ง diff จะบอกว่าทำการหาความแตกต่างระหว่าง branch ไหน ข้อมูลในบรรทัดที่ 5 (ขึ้นต้นด้วยเครื่องหมาย @@) จะบอกช่วงบรรทัดที่นำมาแสดง และข้อมูลที่เหลือจะแสดงอยู่ในรูปของ patch file คือถ้ามีเครื่องหมาย + หมายถึงมีข้อมูลเพิ่มขึ้นมา เครื่องหมาย - คือข้อมูลที่ถูกลบทิ้งครับ

เมื่อพร้อมแล้วก็ merge กันเลย

สังเกตว่าการ merge ย่อยครั้งแรก (show-help) แม้จะเป็นการ Fast-forward เช่นเดียวกับที่ผ่านมา แต่เมื่อ merge เสร็จ จะมี code ส่วนที่ตรวจว่ามีการขอ help หรือเปล่าเพิ่มขึ้น ทำให้บรรทัดต่างๆ ใน code ส่วนที่เหลือถูกเลื่อนลงมาข้างล่างด้วย เมื่อ Git พยายามจะ merge ครั้งถัดมา (sort-name) มันจะทำการเลื่อน code ส่วนที่ต้องตรวจว่าจะ sort หรือเปล่าลงไปให้เองโดยอัตโนมัติครับ (เปิดไฟล์ hello.py เพื่อศึกษาผลลัพท์)

เสร็จแล้วก็อย่าลืมลบ branch ทิ้งนะครับ

July 27, 2012

Git: รวม branch เข้าด้วยกัน

การแตก branch นั้นช่วยเก็บรักษา code รุ่นที่ยังทำงานถูกต้องไว้ก็จริง แต่มันจะไม่มีประโยชน์เลยถ้าจะแตก branch ต่อไปกันเรื่อยๆ เพราะสุดท้ายเราจะไม่รู้เลยว่า branch ไหนมีความสำคัญอย่างไร code ที่เพิ่มมาใน branch ย่อยๆ เมื่อทดสอบว่าทำงานได้ถูกต้องสมบูรณ์แล้ว ควรถูกย้ายกลับมาอยู่ใน branch master ครับ

ตอนนี้จะเขียนโปรแกรมให้รับ argument เป็นชื่อคนจาก command line แล้วเปลี่ยนไปทักทายคนเหล่านั้นแทน

สร้าง branch ใหม่เช่นเคย (สังเกตว่าเปลี่ยนไปใช้ checkout -b ซึ่งเป็นรูปย่อของการสร้าง branch พร้อม checkout) แล้วแก้ไขไฟล์ hello.py เป็นดังนี้

ทดสอบโปรแกรมกันหน่อย

โปรแกรมทำงานได้ตามที่ออกแบบไว้ ก็ได้เวลา commit เข้าระบบ

เรียบร้อยแล้วสลับกลับมาที่ branch master และสั่ง merge (รวม branch) hi-name กับ master เข้าด้วยกัน

เพียงเท่านี้ที่ branch master ก็จะได้รับ code ที่เพิ่ม feature ใหม่ๆ มาจาก branch ที่แตกไปแล้วครับ

อันที่จริง ถ้าสังเกตดูรายงาน จะเห็นว่าเป็นการ Fast-forward ซึ่งหมายถึงหมายเลข commit ของ branch master ย้ายไปใช้ตัวเดียวกับของ hi-name เท่านั้นเอง ยังไม่ใช่การ merge ที่แท้จริงแต่อย่างใด

และเนื่องจากตอนนี้ branch hi-name ก็ได้ทำหน้าที่ของมันเรียบร้อยแล้ว สั่งลบได้เลยครับ

สังเกตว่าเปลี่ยนไปใช้ -d (ตัวเล็ก) แทน ซึ่งตัวเลือกนี้จะเช็คดูให้แน่ในก่อนว่า branch นั้นถูก merge เรียบร้อยแล้วถึงจะลบทิ้งได้ครับ

July 26, 2012

Git: แตก branch

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

การแตก branch ก็เป็นหนึ่งในจุดแข็งนี้ คอนเซ็ปง่ายๆ ของมันคือทำสำเนาไฟล์ทั้งหมด แล้วก็ทำงานในไฟล์ที่สำเนามานั่นเอง ถ้าแก้ไขต่อเติมไฟล์แล้วผลลัพท์ออกมาไม่ดี ก็สามารถลบ branch นั้นๆ ทิ้งได้โดยไม่กระทบกับโปรเจคหลัก

ลองแตก branch จากโปรเจค hello โดยให้ชื่อว่า polyglot สามารถสั่งได้ดังนี้

และดู branch ที่มีด้วย

จะเห็นว่ามี branch master กับ polyglot โดยตอนนี้อยู่ที่ branch master ซึ่งเป็น branch หลักที่ Git สร้างไว้ให้ โดย branch นี้ควรจะเก็บโปรเจคช่วงที่ stable เอาไว้ครับ

ตอนนีเรารู้แล้วว่าต้องการทำงานที่ branch polyglot ก็สามารถสลับ branch ไปได้โดยสั่ง

จะทดลองลองเขียนโปรแกรมแบบรองรับหลายภาษา โดยแก้ไขไฟล์ hello.py กันนิดหน่อย ให้เป็นดังนี้

เรียบร้อยแล้วคอมไพล์เป็น C++ และทดสอบการทำงานทั้ง 2 ภาษาดู

ทำงานได้ผ่านเรียบร้อยก็เตรียมตัว commit code ซึ่งถ้าลองตรวจสถานะโปรเจคดู จะพบว่ามีไฟล์ a.out ซึ่งจะไม่ถูกจำเข้าระบบด้วย

เนื่องจากไฟล์ a.out เกิดจากการคอมไพล์ไฟล์ hello.py และยังไม่มีความจำเป็นต้องใช้ในเวลานี้ เราอาจสั่งให้ Git ลบไฟล์ทั้งหมดที่ไม่รู้จักโดย

ถึงตรงนี้ ก็พร้อม commit แล้ว ซึ่งถ้าไม่อยากเสียเวลาไล่สั่ง add ไฟล์ที่แก้ไข สามารถใส่ -a เพื่อบอกให้ add ไฟล์เหล่านั้นแบบอัตโนมัติได้ครับ



แต่เนื่องจาก branch polyglot นี้ มันค่อนข้างพัฒนาโปรแกรมต่อได้ยาก เราจะลบ branch นี้ทิ้งไปครับ โดยก่อนอื่นต้องเปลี่ยนไปอยู่ branch master ก่อน แล้วสั่งลบด้วยคำสั่งนี้

July 25, 2012

Git: จัดการไฟล์พื้นฐาน

ตอนก่อนเราได้สวัสดีชาวโลกกันไปแล้ว แต่จะเห็นว่าไวยากรณ์ผิดพลาดอยู่บ้าง ลองแก้ไฟล์ hello.py เป็น

พร้อมกับเขียนไฟล์ใหม่ชื่อ README.md เพื่อบอกข้อมูลพื้นฐานของโปรแกรมนี้ โดยมีเนื้อหาว่า

และอีกไฟล์ชื่อ MANUAL.md

ลองเช็คสถานะของโปรเจคนี้ดู จะได้ผลลัพท์ดังนี้

ซึ่งบอกว่า ทั้งไฟล์ hello.py ที่เพิ่งแก้ไขไป และไฟล์ MANUAL.md, README.md ที่เพิ่มมาใหม่นั้น จะไม่ถูกจำสำหรับการ commit ในครั้งถัดไป

ดังนั้น เราต้องเพิ่มไฟล์ทั้งสองเข้าไปในระบบก่อน โดยสั่ง add เหมือนตอนที่แล้วครับ

เช็คสถานะไฟล์อีกครั้ง

พร้อมแล้วก็สั่ง commit เลยครับ



แต่ตอนนี้เราจะเห็นว่า ไฟล์ MANUAL.md และ README.md นั้น มีเนื้อหาน้อยมาก และสามารถจับเอามารวมกันได้ แก้ไขไฟล์ README.md เป็นดังนี้

ถึงตอนนี้ เราจะไม่ต้องการไฟล์ MANUAL.md แล้ว ก็สั่งลบไฟล์ออกจากระบบโดย

จะเห็นว่าไฟล์ MANUAL.md หายไปจากไดเรคทอรี่เป็นที่เรียบร้อย ตอนนี้ก็ได้เวลาสั่ง commit งานที่เพิ่งแก้ไขกันครับ

อย่าลืมว่าก่อน commit ต้อง add ไฟล์ที่ถูกแก้ไขทุกครั้งนะครับ

PHP: Send mail

การใช้ PHP ส่งเมลนั้นทำได้ง่ายมาก อาจจะเป็นภาษาที่ส่งเมลได้ง่ายที่สุดในบรรดาภาษาคอมพิวเตอร์ที่มีอยู่ในโลก ฟังชั่นที่ใช้ส่งเมลคือ mail()
mail ( $to, $subject, $message [, $headers[ , $additional_param] ] )
พารามิเตอร์ที่จำเป็นมีอยู่ 3 ตัว และพารามิเตอร์เสริม 2 ตัว คือ
  • $to ที่อยู่อีเมลผู้รับ
  • $subject หัวข้อของอีเมล
  • $message เป็นเนื้อหาของอีเมล
  • $headers เป็นพารามิเตอร์เสริม เอาไว้กำหนดหัวเมลเพิ่มเติม
  • $additional_param เป็นพารามิเตอร์เสริม สำหรับส่งค่าให้กับโปรแกรมส่งเมล (ไม่ค่อยได้ใช้)
การขึ้นบรรทัดใหม่ในเมลนั้นจะใช้ \r\n (เหมือนการขึ้นบรรทัดใหม่ในโปรแกรมของฝั่ง Windows) แทนที่จะใช้ \n อย่างเดียว ไม่ว่าจะส่งจาก OS ที่เป็น Windows หรือ Linux แค่นี้ก็จบ

แต่ถ้าต้องการระบุอีเมลที่ใช้ส่ง รวมทั้งการใช้เนื้อหาในอีเมลเป็น HTML จะต้องใส่ $headers ลงไปด้วย ข้อจำกัดของอีเมลที่ควรรู้ไว้คือ
  • แนบ JavaScript ลงไปไม่ได้ (ที่จริงก็แนบได้ แต่โปรแกรมอ่านเมลทั้งหลายปิดการทำงานของ JavaScript ทิ้งเพื่อความปลอดภัย)
  • หากใช้ CSS จะต้องแทรกลงไปในเนื้อหาอีเมล ไม่สามารถแนบเป็นไฟล์ภายนอกไปได้
  • หากต้องการแทรกรูปภาพจะต้องใช้พาธเต็ม แต่หากไม่จำเป็นก็ไม่ควรจะใส่ลงไป

July 24, 2012

Git: มารู้จักกับโปรแกรมบริหารซอร์สกันเถอะ

1969 Linus Torvalds (คนเดียวกับผู้สร้าง Linux) เกิด
1972 กำเนิดโปรแกรม SCCS โปรแกรมจัดการซอร์สโปรแกรมแรกๆ
1990 กำเนิดโปรแกรม CVS
2000 กำเนิดโปรแกรม SVN
2005 Linus ให้กำเนิด Git



Git เป็นโปรแกรมบริหารจัดการซอร์สตัวหนึ่ง เนื่องจากในการพัฒนาโปรแกรมนั้น เราอาจมีการลองผิดลองถูกอยู่หลายทางว่าได้ผลลัพท์เป็นที่น่าพอใจหรือไม่ หรือบางครั้งฟีเจอร์ใหม่ๆ ที่เพิ่มเข้าไปอาจจะทำให้ระบบล่ม ต้องถอยกลับไปก่อนหน้าที่จะเกิดข้อผิดพลาดนั้น การใช้โปรแกรมบริหารซอร์สจะช่วยให้เราแก้ไขปัญหาต่างๆ ได้อย่างมีประสิทธิภาพมากขึ้น

โดย Git นั้นเป็นโปรแกรมบริหารซอร์สแบบกระจายศูนย์ ที่จะแจกซอร์สทั้งหมดให้ผู้ใช้ทุกคนเหมือนกัน ไม่แบ่งแยกชนชั้นครับ (ถ้าใครเป็น CVS/SVN มาก่อน ก็อาจถือได้ว่าต้องเรียนรู้ใหม่เกือบหมดเลยทีเดียว)

ผู้ใช้ Windows สามารถใช้ msysGit แทนได้ (โปรแกรมจะไปรันบน MinGW อีกที)

ส่วนผู้ใช้ Linux ก็เช่นเคย apt-get install git กันได้เลย

หลังจากติดตั้งโปรแกรมเรียบร้อยแล้ว ก็ต้องตั้งค่าเริ่มต้นที่จำเป็นกันหน่อย โดยสั่ง



ลองเริ่มใช้โปรแกรมเลยดีกว่า สมมติจะสร้างโปรเจค hello ก็สั่งดังนี้

Git จะสร้างไดเรกทอรี .git/ พร้อมไฟล์ระบบต่างๆ ของโปรเจคนี้ให้ครับ

คราวนี้มาเขียนโปรแกรมหลักของโปรเจคนี้กัน

ทดสอบแล้วรันได้ไม่มีปัญหา ก็ได้เวลาสั่งให้ Git จำ code เข้าระบบโดย

เท่านี้ก็เรียบร้อยครับ

ปล. โปรแกรมบริหารซอร์สแบบนี้ ใช้คนเดียวยังไงก็ไม่สนุก อย่างลืมชวนเพื่อนๆ มา code เล่นด้วยกันนะครับ

July 21, 2012

Python: เขียน TDD อย่างง่ายด้วย doctest

วิธีที่ง่ายที่สุดในการทำ TDD บน Python คือ ใช้ module doctest แล้วแนบคำสั่งที่ต้องการทดสอบพร้อมผลลัพท์ (เขียนรูปแบบเดียวกับการสั่งงาน Python shell) ลงไปที่ docstring ของฟังก์ชัน เช่นนี้

เซฟเป็นไฟล์ to_test.py แล้วเรียกโปรแกรมนี้ผ่าน terminal จะเห็นผลลัพท์ดังนี้

ซึ่งหมายความว่า test เราไม่ผ่านอยู่ 1 test case เมื่อส่งข้อความ 'World' ให้กับฟังก์ชั่น คราวนี้ลองปรับปรุงบรรทัดที่สั่ง print ให้เป็น

เมื่อสั่ง $ python to_test.py อีกรอบ จะไม่ได้ค่าอะไรคืนมาแล้ว ซึ่งก็คือ test ผ่านหมดนั่นเองครับ

หรือจะขอดูรายงานแบบเต็มๆ ก็ย่อมได้ โดยการเพิ่ม argument -v เข้าไปเช่นนี้




สำหรับส่วน docstring เราสามารถแนบคำอธิบายอื่นๆ ลงไปกับ test ได้ โดยต้องเว้นบรรทัดว่างเอาไว้เพื่อบอกให้รู้ว่าส่วนไหนที่หมด output จาก test แล้ว และสามารถเขียนส่วน input test หลายบรรทัดได้ สำหรับกรณี error สามารถใส่ ... ตามตัวอย่าง เพื่อบอกว่าไม่ต้องสนใจ output ส่วนที่เป็นรายงานอย่างละเอียดก็ได้ครับ

นอกจากนี้ ใน production code จริง การเรียกฟังก์ชัน doctest.testmod() ลงไปอาจไม่เหมาะสมนัก เราสามารถยกส่วนนั้นทิ้งไปได้ (เหลือแต่ code เปล่าๆ กับ docsting ที่มี test case อยู่) แล้วสั่งผ่าน shell เช่นนี้แทน



หรือถ้าส่วน test case นั้นใหญ่มากจนไม่ควรจะไปอยู่กับ docstring เราอาจยก test case มาเขียนเป็นไฟล์ใหม่ที่บรรจุเฉพาะข้อมูล test ไปเลยก็ได้ ดังนี้

ไฟล์ to_test.py


ไฟล์ info_test.txt

เรียกผ่าน shell ไม่ต่างจากเดิม เพียงแค่เปลี่ยน argument เป็นไฟล์ที่เขียน test เอาไว้เท่านั้นครับ

July 4, 2012

PHP: Session และสิ่งที่แตกต่างจาก Cookie

ในการเขียนโปรแกรมทั่ว ๆ ไป เราสามารถเก็บข้อมูลของผู้ใช้ เช่น ชื่อ ข้อมูลของแต่ละขั้นตอนที่ผู้ใช้ได้ทำลงไปในตัวแปรแบบปกติได้ แต่สำหรับการเขียนโปรแกรมสำหรับเว็บนั้นทำแบบนั้นไม่ได้ เพราะ URL ไม่สามารถบ่งบอกตัวผู้ใช้ และไม่สามารถเก็บข้อมูลตัวแปรได้ (ยกเว้นจะใช้ GET ซึ่งไม่ปลอดภัยสักเท่าไหร่ เพราะถ้ามีใครรู้ URL ก็สามารถปลอมตัวเป็นคน ๆ นั้นได้ มันคงไม่สนุก ถ้าเขาปลอมเป็นเรา แล้วโอนเงินในธนาคารออกไปจนหมด) PHP จึงสร้างตัวแปรพิเศษขึ้นมาคือ session

session ต่างจากตัวแปรปกติตรงที่ เมื่อจบการประมวลผล ค่าของมันจะไม่ถูกทำลายเหมือนตัวแปรปกติ แต่จะถูกทำลายเมื่อผู้ใช้ออกจากเว็บแทน

จะใช้ session ต้องประกาศก่อน และต้องทำก่อนที่จะส่งข้อมูลใด ๆ ออกมา ไม่ใช่นั้นจะเจอข้อความ headers already sent เวลาเรียกใช้ก็เรียกใช้เหมือนอาเรย์ปกติ ผ่านทางตัวแปร $_SESSION ซึ่งเป็นตัวแปรชนิด superglobal หากต้องการลบค่านั้นออกจาก session สามารถสั่ง unset ทีละตัวหรือทำลาย session นั้นทิ้งไปเลยก็ได้ แต่ถึงจะไม่ทำลาย ถ้าผู้ใช้ออกจากเว็บข้อมูลใน session ก็ถูกทำลายอยู่ดี โดยทั่วไปแล้ว session จะคล้าย ๆ กับ cookie คือเก็บข้อมูลของผู้ใช้ไว้เหมือน ๆ กัน ต่างกันตรงที่ว่า
  • cookie จะส่งข้อมูลนั้นกลับเป็นไฟล์ไปยังเบราว์เซอร์ และเก็บไว้ที่ฝั่งผู้ใช้ด้วย แต่ session ส่ง UID ของ session ไปอย่างเดียว ข้อมูลยังอยู่ฝั่งเซิฟเวอร์
  • cookie เก็บข้อมูลไว้เป็นไฟล์ข้อความธรรมดา สามารถเปิดขึ้นมาแก้ไขได้ หากรู้วิธี
  • cookie จะยังคงอยู่แม้ว่าจะออกจากเว็บไปแล้ว (เพราะเก็บอยู่บนเครื่องผู้ใช้) เมื่อกลับเข้ามาใหม่ ก็สามารถเรียกใช้ข้อมูลเหล่านั้นได้อยู่
  • หากต้องการเก็บข้อมูลใน session เอาไว้ ต้องเขียนลงไฟล์ หรือฐานข้อมูลเอาเอง