วันศุกร์, พฤศจิกายน 14, 2551

แก้ Google Earth ฟอนต์เล็กจิ๋ว

ลง Windows ใหม่ ปรากฏว่า Google Earth 4.3.7284.3916 (beta) ฟอนต์เล็กจิ๋วจนอ่านไม่ได้ หมายถึงฟอนต์ตรงเมนูนะครับ

ปัญหาคือฟอนต์ที่เลือกใช้คือ Angsana New ซึ่งรู้กันดีอยู่ว่าตัวเล็กมาก (ไม่รู้ทำไมเหมือนกัน คงต้องไปศึกษาเรื่องฟอนต์สักที) ต้องแก้ให้เป็นอะไรสักอย่าง Arial หรือ Tahoma ก็น่าจะโอเค

HKEY_CURRENT_USER\Software\Google\Google Earth Plus\Render
Key ชื่อ GuiFontFamily

วันศุกร์, สิงหาคม 15, 2551

รัฐบาลรับภาระค่าใช้จ่ายการใช้น้ำประปา

บิลค่าน้ำเดือนนี้ตีตรามาว่า "รัฐบาลรับภาระค่าใช้จ่ายการใช้น้ำประปา" ซึ่งก็ไม่ใช่เรื่องแปลกสำหรับบ้านที่อยู่กันแค่สองคน

แม่บ้านที่ช่วยมาทำงานบ้านให้สัปดาห์ละครั้งเขารับจ้างซักรีดด้วย คงไม่ได้รับสิทธิ์นี้ ทั้งๆที่เงินร้อยกว่าบาทต่อเดือนของเรากับของเขามีความสำคัญต่างกันมาก

เป็นลาภที่ไม่อยากได้รับเลยจริงๆ

เกณฑ์ในการให้ไม่ค่อยจะเหมาะนะ คนจนที่อยู่เป็นครอบครัวใหญ่เขาก็คงไม่ได้ แต่เขาก็คงไม่อิจฉาในลาภของคนอื่นกระมัง

ส่วนคนที่ได้จะเห็นเป็นบุญคุณของ "รัฐบาล" หรือเปล่าเราก็ไม่รู้

อยากให้เขารู้แค่ว่า เงินตรงนี้มาจากภาษีของเขาทั้งนั้นครับ จะเป็นเงินของท่านรัฐมนตรีทั้งหลายก็หาไม่

วันพฤหัสบดี, เมษายน 03, 2551

SCB Easy Fund ไม่ Easy อย่างที่คุย

ใครที่ใช้ Firefox และใช้บริการ SCB Easy Fund คงรู้กันดีอยู่ว่าจำต้องใช้ IE เปิดเว็บนี้

หลังจากโน้ตบุ๊คตัวเก่าร่อแร่ แถมตัวใหม่่ก็มีปัญหาให้เดือดร้อนใจมาสองเดือนกว่า โดยที่จบปัญหาได้ด้วยการเลิกใช้ Vista หันกลับมาหา XP เหมือนเดิม (ว่างๆจะมาเล่าเรื่องนี้ว่าไปไงมาไง) ก็มาเจอปัญหาใหม่

ปัญหาใหม่ที่ว่าคือใช้ IE เปิดหน้าจัดการกองทุนปุ๊บ Crash ปั๊บ !!!

หะแรกนึกว่า MS อาจจะไป update อะไรแปลกๆใส่ IE ทำให้บั๊กใหม่มันปูดออกมา เลยถือโอกาสนี้ลองของใหม่ๆดูซะเลย

อย่างแรกสุดคือ ลง Firefox 3 Beta 4 แน่นอนครับว่าไม่เป็นมิตรกับ SCB Easy Fund
อย่างที่สองคือลง Opera เช่นเดียวกันว่า ใช้กับ SCB Easy Fund ไม่ได้ อาการเดียวกัน

ทั้งหมดทั้งสิ้น รู้สึกว่าเป็นเพราะ SCB Easy Fund จะเรียก ActiveX (ถ้าจำผิดก็ขออภัย ผ่านมาหลายวันแล้ว ขี้เกียจกลับไปดูใหม่)

อย่างที่สามคือ ลง IE8 Beta

ผลลัพธ์นั้นตะลึงตึ่งตึงมาก คือเดี้ยงตั้งแต่หน้าแรกของ www.scbeasy.com เลย กลายเป็นหน้าเปล่า เข้าใจว่าเป็นเพราะ rendering engine ใน IE8 แต่ขี้เกียจไปแคะดูว่าเป็นเพราะอะไรแน่

ตอนนี้ก็เลยใช้ IE8 ให้ Emulate เป็น IE7 ไปพลางๆก่อน

ส่วนปัญหาเรื่อง crash แก้ได้โดยการ uninstall flashget ออกไป (search เจอ solution ที่ pantip)

สรุปว่าใครจะใช้ Easy Fund ต้องใช้ IE7 เท่านั้น แถมห้ามลง flashget ด้วย

ไม่ Easy อย่างที่คุยเลย

เฮ้ออออออออออออออออออ

วันพุธ, พฤศจิกายน 21, 2550

เน็ตติดๆขัดๆ ปัญหาอยู่ที่ไหน

ตอนนี้กลายเป็นคนขี้บ่นไปแล้ว มีแต่เรื่องให้บ่นตลอดเวลา

ช่วงนี้รู้สึกว่าเน็ตติดๆขัดๆ บางเว็บบางช่วงเข้าได้บ้างไม่ได้บ้าง
ตอนแรกก็นึกว่าคงเป็นที่เว็บไซต์นั้นเอง เซิร์ฟเวอร์อาจจะมีปัญหา ทราฟฟิกอาจจะเยอะ
แต่ตอนนี้ชักจะสงสัยว่าไม่ใช่เรื่องนั้น เพราะลองเรียกไปจาก "ที่อื่น" กลับเข้าได้ฉลุยเลย

ล่าสุดเข้า wikipedia ไม่ได้ ในขณะที่ google maps เข้าได้บ้างไม่ได้บ้างตามเรื่องตามราว
search ดูก็ไม่พบว่าฝรั่งจะ complain ว่าเซิร์ฟเวอร์ของ google หนืด

ชักจะสงสัยว่ามีปัญหาที่ ISP บ้านเรานี่แหละ

transparency proxy หรือเปล่า?

อนาคตของ .th

ว่าจะเขียนต่อจากบล็อกที่แล้วว่า อยากเห็น thnic ทำอะไร ก็มาได้ข่าวใหม่ 2-3 เรื่อง

เรื่องแรก หนึ่งใน authorized reseller สำหรับ .th คือ dotarai นั้นใช้ที่อยู่เดียวกับ thnic นัยว่าจะเป็นบริษัทเีัดียวกันหรืออย่างไรไม่ทราบ แต่อีกสองเจ้าที่เหลือนั้นคงไม่เกี่ยว และยังเป็นปริศนาต่อไปว่า thnic มีนโยบายในการแต่งตั้ง dealer อย่างไร

เรื่องที่สอง เมื่อเจอ dotarai ก็พบว่า dotarai กลายเป็น .com registrar รายแรกของไทยอย่างเป็นทางการไปแล้ว

สมมติไว้ก่อนว่า dotarai กับ thnic คือองค์กรเดียวกัน เพราะถ้าเป็นคนละองค์กรเรื่องนี้คงไม่งามถึงไม่งามมากที่สุด ขอเอาไว้คิดทีหลังละกัน

สิ่งที่ dotarai ทำก็คือสิ่งที่ผมฝันอยากให้มีนานแล้ว เพราะเท่ากับว่าเราดึงงานส่วน registrar ของ .com มาไว้ในไทยได้ ถ้าคนไทย (ที่ใช้ .com อยู่แล้ว) หันมาใช้กับ dotarai ก็คงดี

แต่อย่าลืมนะครับว่าภารกิจแรกคือ ให้คนไทยหันมาใช่ .th มากกว่า .com เพราะประหยัดได้มากกว่าอีก เพราะเราได้งาน registry ด้วย

เรื่องนี้ thnic (และ dotarai ด้วย?) ต้องกำหนดนโยบายให้ดี อย่าให้หลงทางประชาสัมพันธ์ผิดๆ กลายเป็นว่าชักจูงคนไทยให้สิ้นเปลืองกับโดเมนเนมมากเกินความจำเป็น

ดูจะหน้าเว็บของ dotarai แล้วยังเป็นห่วง เพราะราคา .com ถูกกว่าราคา .th เท่าตัว เทียบกันให้เห็นจะๆ ไม่รู้จะกลายเป็นการโฆษณาให้คนหันไปจด .com โดยไม่ได้ตั้งใจหรือเปล่า

ที่แย่หนักคือราคาของ dotarai ไฉนมันถึงได้แพงกว่า registrar เมืองนอกอยู่พอควร ทั้งๆที่ต้นทุนค่าแรงเราน่าจะต่ำกว่าเขามาก กลายเป็นว่าราคาไปฟาดฟันกับ reseller คนไทยไปเสียฉิบ

ซึ่งถ้าคิดในแกนนี้ เท่ากับว่า dotarai มีปัญหาด้านการตลาดเสียแล้ว คือ channel conflict ขัดผลประโยชน์กับพวกที่ควรจะเป็นเครือข่ายการตลาดให้ตัวเอง

ซึ่งแปลว่าจะต้องมีแรงเสียดทานจากกลุ่มนี้อยู่ไม่น้อย ซึ่งแปลว่าภารกิจการลดต้นทุน .com ให้ประเทศไทยคงยากที่จะประสบผลดังตั้งใจ

วิจารณ์มามากแล้วขอเสนอทางออกให้บ้าง ตามวถีทางประชาธิปไตยที่ดีงาม ไม่ใช่ด่าอย่างเดียว
- ลดราคาขายปลีก .th ลงมาหน่อยได้ไหม หรืออย่างน้อยก็แสดงข้อดีของการตรวจสอบตัวตนผู้สมัครให้ชัดเจนหน่อย จะได้จูงใจให้คนจด .th หน่อย แล้วพวกที่ไม่ตรวจสอบอย่าง .in.th อย่าไปคิดราคาเดียวกันเลย มันน่าเกลียด แล้วควรตั้งราคาขายปลีก .com ให้สูงกว่านี้ เพื่อไม่ให้เกิดภาพว่า .th แพงกว่า .com มาก
- น่าจะลองคิดเรื่องลดบทบาทตัวเองเป็น registry หรือถ้ายังไม่พร้อมก็ลดบทบาทการขายปลีกลง สร้างระบบ reseller ให้ดี แล้วขายผ่านเครือข่าย reseller จะได้ไม่ต้องมีปัญหา channel conflict ปล่อยให้ reseller ไปทำการตลาดแข่งกันเอง ภายใต้เป้าหมายที่ว่ามาแต่แรก 2 ข้อคือ ให้คนไทยใช้ .th แทน .com และ จูงใจคนที่ยังไงก็ต้องใช้ .com ให้หันมาใช้ registrar ของเราเองแทนของนอก
- เลิกเฮอะ ไอ้การทำ hosting เองน่ะ (อิงอยู่กับเรื่องการขายโดเมนด้วยตัวเอง) เพราะมันสร้าง channel conflict แบบไม่รู้จะคิดแก้ยังไงได้

แต่ถ้า thnic เลิกนโยบายการเป็นนิติบุคคลทีี่ไม่แสวงหาผลกำไร หรือ thnic ยังเหมือนเดิม แต่แยกร่าง dotarai ออกไปหากำไร

อันนี้ก็ตัวใครตัวมันล่ะครับ

วันเสาร์, พฤศจิกายน 17, 2550

เกิดอะไรขึ้นกับ thnic

จำไม่ได้แล้วว่ารู้จัก thnic มาตั้งแต่เมื่อไหร่ รู้แต่ว่านานมาก

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

สถานะของ thnic นั้นแปลกๆ จะว่าเป็นเอกชนก็ไม่น่าใช่ เพราะ .th เป็นสมบัติของคนไทย ในขณะที่จะว่าเป็นหน่วยงานรัฐ thnic ก็จดทะเบียนเป็นบริษัทอยู่ทนโท่ คงต้องว่าตาม อ.กาญจนาว่าเป็นนิติบุคคลที่ไม่แสวงผลกำไร

เข้าใจว่าตอนต้น thnic น่าจะลำบากมาก เพราะโดเมนเนม .th ยังมีคนใช้น้อย รายได้อาจจะไม่พอเลี้ยงองค์กร แต่ thnic ก็ดิ้นรนอยู่รอดมาได้ แต่วันนี้ที่มี .th เกิน 25,000 ชื่อ รายได้ปีละ 20 ล้านบาท ก็น่าจะอยู่ได้สบายๆ

ปัญหาคือ ผมว่าหลังๆ thnic ชักจะทำอะไรแปลกๆ

เรื่องแรกที่เห็นคือ ให้บริการ web hosting เอง อันนี้จะว่าเป็นแนวทางในการหารายได้ก็เรื่องหนึ่ง แต่มันอดทำให้เข้าใจว่า thnic กำลังจะหลงทางไม่ได้ เพราะไม่ใช่หน้าที่ขององค์กรที่มีหน้าที่ดูแล .th เลย

ยิ่งมาเห็นการแต่งตั้ง authorized dealers จำนวนหนึ่ง และมีเป้าหมายให้มีการใช้งาน .th เพิ่มจาก 25,000 เป็น 300,000 ภายใน 3 ปี อันนี้มึนเลยครับ

ความจริงเรื่องนี้เป็นเรื่องที่ผมเชียร์ให้มีมานานแล้วด้วยซ้ำ เพราะเห็นว่าเราอุดหนุนให้ .th อยู่ได้ เงินทองไม่ต้องรั่วไหล แต่ต้องจ่ายแพงกว่า .com ถ้าแยกส่วนให้เป็น registry, registrar แบบสากลก็จบ ไม่ต้องกังวลเรื่องผลประโยชน์ทับซ้อน และมีคนทำการตลาดหลายมือไม้ ย่อมแพร่หลายได้เร็ว และการแข่งขันจะทำให้ราคาถูกลง และมีการแข่งขันด้านบริการด้วย ถึงแม้ว่าที่ผ่านมา thnic ก็ให้บริการได้เป็นอย่างดีมาตลอด

แต่ที่ว่ามึนนี่ มึนเพราะไม่เคยได้ยินเรื่องการคัดเรื่อง dealer มาก่อน และก็ไม่มีการชี้แจงว่าเลือก dealer ยังไง และทำไมต้องมีจำนวนเท่านี้ แบ่งผลประโยชน์กันยังไง

แล้วทำไมต้อง 300,000 เป้าหมายควรจะเป็นการให้คนไทยหันมาใช้ .th แทน .com ไม่ใช่หรือ ไม่ใช่การใช้ฟุ่มเฟือยเปล่าประโยชน์

อย่าลืมนะครับว่า thnic ดูแล .th อยู่ก็จริง แต่ .th เป็นของคนไทย ไม่ใช่ของ thnic

thnic ทำงานดูแล .th ให้คนไทยมานานแล้ว ไม่อยากให้จบแบบไม่สวยครับ

ได้แต่หวังว่า thnic จะมีคำชี้แจงดีๆสำหรับเรื่องนี้

วันพุธ, พฤศจิกายน 07, 2550

เมล์สแปม กับ DATE_IN_FUTURE

เมล์จากบางคนถูก spamassassin ตีกลับบ่อยๆเพราะเข้าใจว่าเป็นสแปม

พอไปดูล็อก พบว่าเมล์พวกนั้นถูกประเมินคะแนน DATE_IN_FUTURE ไว้ค่อนข้างสูง น่าจะเป็นสาเหตุ

ถามไถ่ได้ความว่า เครื่องต้นทาง ดันไปตั้ง Time Zone ไว้ผิด พิลึกจริงๆ

ตั้ง Time Zone ผิด เวลาท้องถิ่นตรงเวลาในไทย นั่นแปลว่า GMT มันเก๊าะต้องไม่ตรง เจ้า spamassassin เลยงง

ยังงี้ก็มีด้วยแฮะ

วันพุธ, กันยายน 19, 2550

ประชาไท เสรีหรือแอบแฝง?

อ่านข่าวนี้ http://www.prachatai.com/ireport/view.php?id=69 ในประชาไทย แล้วหงุดหงิดใจ

คนรายงานอ้างว่าเป็นบทความ แต่เนื้อหาที่แค่ 2-3 ประโยค ที่เหลือลอกรายงานของ Forbes มาทั้งดุ้น และไม่ได้บอกที่มาที่ไป

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

คงต้องถามประชาไทว่า เป็น ไท จริงหรือ? หรือเป็นทาสใคร? หรือความคิดอะไร?

แล้วต่างจากสื่อเลวอื่นๆอย่างไร?


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

ส่วนที่เป็นทรัพย์สินเฉพาะบุคคลนั้น แยกเป็นอีกส่วนหนึ่งที่เรียกว่าทรัพย์สินส่วนพระองค์ ซึ่งต้องเสียภาษีเหมือนคนอื่นๆใต้รัฐธรรมนูญเดียวกัน

ส่วนจะมีมูลค่าเท่าใดนั้นไม่ทราบ

อ่านรายละเอียดเกี่ยวกับกฎหมายเรื่องนี้ได้ที่ http://www.crownproperty.or.th/history.php

หวังว่าปราชาไทได้อ่านแล้วจะเกิดความละอายแก่ใจบ้างนะครับ คนไทยไม่ได้กินหญ้าไปซะทุกคน คิดเองได้ จะคิดอ่านอย่างไร เอาความจริงมาคุยให้หมดดีกว่า

พูดความจริงครึ่งเดียวก็เท่ากับโกหกครับ

วันศุกร์, สิงหาคม 10, 2550

๕๐ หรือ ๔๐ กับ คมช. หรือ ทักษิณ

ดูรัฐธรรมนูญ ๕๐ ที่ออกมาแล้วรู้สึกผิดหวัง ส่วนที่เป็นจุดอ่อนของ ๔๐ ไม่ได้แก้ แต่ไปแก้อะไรที่ไม่เข้าท่า และไม่มีเหตุมีผลสนับสนุนเพียงพอ

โดยภาพรวมแล้วต้องบอกว่าฉบับ ๔๐ ยังดีกว่า ขอให้แก้ส่วนที่เปิดช่องให้เกิดการยึดอำนาจอธิปไตยผ่านสภาอย่างที่เคยเกิดขึ้นโดยรัฐบาลทักษิณ และเพิ่มช่องทางการมีส่วนร่วมของภาคประชาชนอย่างเป็นรูปธรรม

แค่สองเรื่องหลักๆก็น่าจะพอ

ปัญหาคือคมช.ไปตั้งธง(ผิดๆ)เอาไว้ว่า ๔๐ คือ ทักษิณ และ ๕๐ คือ คมช. ทำให้เกิดสภาพแบบ "ถอยไม่ได้" และเปิดช่องให้ทักษิณรุกผ่านช่องทางนี้

๔๐ คือ ทักษิณ ทั้งๆที่ ๔๐ โดนชำเราโดยทักษิณ

เศร้านะครับ

ส่วนภาคประชาชน น่าเสียใจเหมือนกันว่าขบวนการประชาธิปไตยบริสุทธิ์ไม่ประกาศตัวเองให้ชัดเจนว่าไม่ยืนข้างเผด็จการรัฐสภา กลับไป "แสวงจุดร่วม" ผิดที่ผิดทาง ทำให้การรณรงค์ต้านการยึดอำนาจไม่สามารถเดินไปได้สะดวกนัก

เพราะถึงคนไม่ชอบเผด็จการทหาร แต่ก็รังเกียจเผด็จการรัฐสภายิ่งกว่า

ไม่ว่าผลจะเป็นอย่างไร เชื่อว่าผู้แพ้ก็เหมือนเดิม

"ประเทศไทย" ครับ

วันจันทร์, กรกฎาคม 30, 2550

ลุงบุญมีได้ควายคืนก็เลิกได้

แต่ควายข้ายังอยู่เมืองนอกนี่หว่า จะให้ข้าเลิกได้ไง

อิอิ

คนเขียนบทนี่เหลือเกินจริงๆ

วันพฤหัสบดี, กรกฎาคม 05, 2550

อ่าน Agile Web Development with Rails ( 11 )

ทีนี้มาดูว่าเขาใช้ AJAX ใน Rails กันอย่างไร

ปกติเราใส่ปุ่มเข้าไปใน Rails โดยแทรกโค้ดง่ายๆดังนี้

<%= button_to "Add to Cart" , :action => :add_to_cart, :id => product %>

ซึ่งในเวลาทำงานจะสร้างโค้ด html อย่างนี้

<form method="post" action="/store/add_to_cart/1" class="button-to" >
<input type="submit" value="Add to Cart" />
</form>

เราเอา AJAX มาใช้ตรงนี้โดยการแทนที่แท็ก button_to โดยโค้ดนี้

<% form_remote_tag :url => { :action => :add_to_cart, :id => product } do %>
<%= submit_tag "Add to Cart" %>
<% end %>

คำสั่ง form_remote_tag ใช้สร้างฟอร์ม โดยโค้ดระหว่างบล็อก do - end จะเป็น elements ในฟอร์มที่สร้างขึ้น

นอกจากนี้ เพื่อให้ Rails รับรู้การใช้งาน AJAX เราต้องไปเพิ่มคำสั่ง javascript_include_tag ที่ head ใน app/views/layouts/store.rhtml ดังนี้

<head>
<title>Pragprog Books Online Store</title>
<%= stylesheet_link_tag "depot" , :media => "all" %>
<%= javascript_include_tag :defaults %>
</head>

โค้ดที่เขียนขึ้นจะถูกเรียกโดย add_to_cart แต่เราจะไม่ใช้ app/views/store/add_to_cart.rhtml อันเก่า (ลบทิ้งซะ) แต่จะใช้ app/views/store/add_to_cart.rjs แทน ดังนี้

page.replace_html("cart" , :partial => "cart" , :object => @cart)

เช่นนี้เอง

วันพุธ, กรกฎาคม 04, 2550

อ่าน Agile Web Development with Rails ( 10 )

ทีนี้เรื่อง partial templates หรือที่เรียกย่อๆว่า partials

ลองดูโค้ดนี้

<table>
<% for cart_item in @cart.items %>
<tr>
<td><%= cart_item.quantity %>×</td>
<td><%= h(cart_item.title) %></td>
<td class="item-price" ><%= number_to_currency(cart_item.price) %></td>
</tr>
<% end %>
<tr class="total-line" >
<td colspan="2" >Total</td>
<td class="total-cell" ><%= number_to_currency(@cart.total_price) %></td>
</tr>
</table>


เขียนใหม่โดยใช้ partials ได้อย่างนี้

<table>
<%= render(:partial => "cart_item" , :collection => @cart.items) %>
<tr class="total-line" >
<td colspan="2" >Total</td>
<td class="total-cell" ><%= number_to_currency(@cart.total_price) %></td>
</tr>
</table>

ส่วน cart_item จะแยกไว้อีกไฟล์หนึ่ง app/views/store/_cart_item.rhtml

<tr>
<td><%= cart_item.quantity %>×</td>
<td><%= h(cart_item.title) %></td>
<td class="item-price" ><%= number_to_currency(cart_item.price) %></td>
</tr>

คำสั่ง render ในไฟล์แรกจะแสดงผล app/views/store/_cart_item.rhtml (โปรดสังเกตว่ามี _ นำหน้า) โดยจะวนลูปให้อัตโนมัติครับ

วิธีนี้ทำให้เราสามารถเขียน partials ไปแปะแทรกไว้ที่ไหนก็ได้ที่ต้องการได้ทุกเมื่อครับ

วันอังคาร, กรกฎาคม 03, 2550

อ่าน Agile Web Development with Rails ( 9 )

เก็บหนังสือไปนาน กลับมาอ่านกันต่อครับ

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

app/controllers/store_controller.rb

def add_to_cart
begin
product = Product.find(params[:id])
rescue ActiveRecord::RecordNotFound
logger.error("Attempt to access invalid product #{params[:id]}" )
flash[:notice] = "Invalid product"
redirect_to :action => :index
else
@cart = find_cart
@cart.add_product(product)
end
end

คราวนี้ถ้าเรียก http://localhost:3000/store/add_to_cart/wibble แทนที่จะเจอ error เจ้า error ก็จะไปอยู่ใน log/development.log แล้ว อย่างงว่า wibble คืออะไร สำหรับ Rails แล้ว สิ่งที่ใส่ต่อท้ายก็คือ parameter ID นั่นไงครับ

ปัญหาอีกอย่าคือ error message ที่เราคาดหวังยังไม่โผล่ ต้องไปใส่โค้ดนี้ที่ app/views/layouts/store.rhtml ด้วย

<% if flash[:notice] -%>
<div id="notice" ><%= flash[:notice] %></div>
<% end -%>

โดยทั่วไปจะไว้บบบนสุดก่อนข้อความอื่นใดนะครับ

วันเสาร์, มิถุนายน 23, 2550

Britain's Got Talent

สลับฉากด้วยรายการ TV การแข่งขัน Britain's got telent
Connie Talbot น่าประทับใจมากกับเด็กอายุ 6 ขวบคนนี้
Paul Potts เป็นหนังดราม่าเลยครับ

ร้องไห้เลย

วันศุกร์, มิถุนายน 22, 2550

อ่าน Agile Web Development with Rails ( 8 )

ทีนี้ลองดัดแปลง cart ให้มีส่วนของจำนวนสินค้าด้วย

เพิ่ม model ดังนี้ app/models/cart_item.rb

class CartItem
attr_reader :product, :quantity
def initialize(product)
@product = product
@quantity = 1
end
def increment_quantity
@quantity += 1
end
def title
@product.title
end
def price
@product.price * @quantity
end
end


แก้ไฟล์ app/models/cart.rb เพิ่ม mothod นี้

def add_product(product)
current_item = @items.find {|item| item.product == product}
if current_item
current_item.increment_quantity
else
@items << CartItem.new(product)
end
end

แล้วแก้ app/views/store/add_to_cart.rhtml ให้เป็นดังนี้

Your Pragmatic Cart
<ul>
<% for cart_item in @cart.items %>
<li><%= cart_item.quantity %> × <%= h(cart_item.title) %></li>
<% end %>
</ul>

แก้เสร็จแล้วต้อง
rake db:sessions:clear
เพื่อล้างข้อมูล sessions เก่าที่เก็บไว้ใน database

ตอนนี้สั้นๆแค่นี้เอง ถือว่าพักผ่อน

อ่าน Agile Web Development with Rails ( 7 )

คราวนี้เราจะมาดูเรื่อง cart กันครับ โดยจะเก็บข้อมูล cart ไว้ใน session

Rails เตรียมเรื่องนี้ไว้ให้แล้ว เราสามารถเพิ่ม session เข้าไปใน data model ดังนี้
rake db:sessions:create
จะได้ไฟล์ db/migrate/004_add_sessions.rb ขึ้นมา แล้วก็
rake db:migrate
table ชื่อ sessions จะถูกสร้างขึ้นมาครับ

สิ่งที่เราต้องทำต่อคือ เปิดไฟล์ config/environment.rb เข้าไปปลด # ให้บรรทัดนี้ทำงาน
config.action_controller.session_store = :active_record_store
เพราะ default จะบันทึกข้อมูลใน session ลงไฟล์ครับ

ต้อง restart เว็บเซิร์ฟเวอร์ใหม่ session ถึงจะเริ่มทำงานครับ

ขั้นต่อไปคือการสร้าง data model ของ cart โดยสร้างไฟล์ชื่อ app/model/cart.rb ดังนี้
class Cart
attr_reader :items

def initialize
@items = []
end

def add_product(product)
@items << product
end
end
โปรดสังเกตว่าเราไม่สร้างไฟล์นี้โดย script/generate เพราะอันนั้นใช้สร้างเฉพาะ model ที่เชื่อมต่อกับ table ในฐานข้อมูลโดยตรงครับ กรณีนี้ไม่ใช่ คราวนี้ไปที่ app/controllers/store_controller.rb เพิ่ม 2 method นี้เข้าไป

private
def find_cart
session[:cart] ||= Cart.new
end

def add_to_cart
@cart = find_cart
product = Product.find(params[:id])
@cart.add_product(product)
end

ยังจำได้ไหมครับ ตอนที่แล้ว ใน app/views/store/index.rhtml มีบรรทัดนี้อยู่
<%= button_to "Add to Cart" , :action => :add_to_cart, :id => product %>

ถ้าเราเรียกตอนนี้ก็จะขึ้น error เพราะยังไม่ได้กำหนด view ให้เลย
ก็ต้องสร้างไฟล์ app/views/store/add_to_cart.rhtml

<ul>
<% for item in @cart.items %>
<li><%= h(item.title) %></li>
<% end %>
</ul>

ทีนี้ cart ก็เริ่มใช้งานได้แล้วครับ

วันพฤหัสบดี, มิถุนายน 21, 2550

อ่าน Agile Web Development with Rails ( 6 )

ตอนที่แล้วเรายุ่งอยู่กับหลังบ้านของ depot คราวนี้มาจัดหน้าบ้านบ้างครับ

เราจะสร้าง controller ใหม่ขึ้นมาอีกอันหนึ่ง ให้ชื่อว่า store
ruby script/generate controller store index
index ที่แปะท้ายเพิ่มมาจากที่เราคุ้นเคยนั้นมีเพื่อบอกให้ Rails สร้าง action ชื่อ index ใส่ไว้ใน controller นี้ด้วยครับ

การทำอย่างนี้ นอกจาก Rails จะสร้าง app/controllers/store_controller.rb แล้ว ยังสร้าง app/views/store/index.rhtml ให้เราด้วยเลย

ที่ app/controllers/store_controller.rb เราจัดการแก้โค้ดให้เป็นดังนี้
class StoreController < ApplicationController
def index
@products = Product.find_products_for_sale
end
end

แล้วเราก็ไปแก้ product model เพิ่ม mothod ชื่อ find_products_for_sale เข้าไปดังนี้
def self.find_products_for_sale
find(:all, :order => "title" )
end

คำสั่ง find จะคืนค่ากลับมาเป็น array ของข้อมูลจาก product ที่มีเงื่อนไข :all (เอาหมด) และเรียงลำดับตามฟิลด์ "title" ที่ระบุโดย :order => "title"

คราวนี้ไปจัดการกับ app/views/store/index.rhtml ให้เป็นอย่างนี้

Your Pragmatic Catalog
<% for product in @products -%>
<div class="entry" >
<img src="<%= product.image_url %>" />
<%= h(product.title) %>
<%= product.description %>
<%= number_to_currency(product.price) %>
<%= button_to "Add to Cart" , :action => :add_to_cart, :id => product %>
</div>
<% end %>


คำสั่ง h() ใช้ถอด HTML Elements ออกจากข้อความ
- ที่อยู่นำหน้า %> ใช้ บอกให้ไม่ต้องขึ้นบรรทัดใหม่หลังคำสั่งนี้

ส่วนเจ้า add_to_cart นี่เราจะไปคุยกันตอนหน้าครับ

อ่าน Agile Web Development with Rails ( 5 )

จากตอนที่แล้ว คำสั่ง scaffold ใน controller มีอิทธิฤทธิ์ยิ่งนัก สามารถสร้างสิ่งมหัศจรรย์จากสูญญากาศ
แต่สุดยอดวิชาย่อมต้องมีจุดอ่อน อะไรที่ดีมากๆ จุดอ่อนก็คือ มันดีเกินไป

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

แต่ทางออกย่อมมีเสมอ เมื่อมี dynamic scaffold ก็ย่อมต้องมี static scaffold อยู่คู่กัน เป็นอีกทางเลือกหนึ่งซึ่งยุ่งยากกว่านิดหน่อย แต่ก็สามารถพลิกแพลงได้หลากหลายกว่ามากครับ

ruby script/generate scaffold product admin
product บอก model ที่ใช้ ในขณะที่
admin ระบุ controller ครับ

สิ่งสำคัญที่เราได้จากการนี้คือ rhtml หนึ่งพวงใหญ่อยู่ใน /app/views/admin/
ทีนี้อยากแก้ตรงไหนก็ตามสบายเลยครับ

เมื่อส่วนที่ rhtml พวกนี้จัดการคือ html ย่อมต้องมีส่วนของ css อยู่ที่ไหนสักที่
นั่นคือ public/stylesheets/ ครับ โดยปกติถ้าไม่ระบุ css file ที่ใช้ public/stylesheets/scaffold.css จะถูกเรียกใช้ by default ครับ

หากเราต้องการเปลี่ยนไปใช้ css file อื่น ก็ควรสร้างและเก็บไว้ที่ public/stylesheets/ นี้เช่นเดียวกันครับ

ส่วนที่ระบุเรื่อง css นี้จะเก็บแยกอยู่ใน app/views/layouts/con_name.rhtml ครับ ไฟล์นี้จะถูกสร้างตอนที่เราใช้คำสั่ง ruby script/generate scaffold model_name con_name ครับ

เปิดไฟล์ layout นี้ดูแล้วมองหาส่วนที่กำหนด css ครับ
<%= stylesheet_link_tag 'scaffold' %>
สมมติว่าเราอยากให้ใช้ depot.css ด้วยก็แก้เป็น
<%= stylesheet_link_tag 'scaffold' , 'depot' %>

แค่นี้เองครับ

อ่าน Agile Web Development with Rails ( 4 )

ทีนี้ลองปรับเปลี่ยน product ดู
ruby script/generate migration add_price
จะได้ไฟล์สำคัญที่ชื่อ db/migrate/002_add_price.rb
ยังจำได้ไหมว่าครั้งที่แล้วเรา generate model product แล้วได้ db/migrate/001_create_products.rb มา

เลข 001, 002 นี้ Rails สร้างขึ้นเพื่อควมคุมลำดับของการเปลี่ยนแปลง data model

ทีนี้แก้ไข db/migrate/002_add_price.rb ให้เป็นอย่างนี้
class AddPrice < precision =""> 8, :scale => 2, :default => 0
end
def self.down
remove_column :products, :price
end
end

แล้ว rake db:migrate

เรียก http://host_name/admin คงพอเดาได้ไม่ยากนะครับว่าจะเกิดอะไรขึ้น

ทีนี้ลองไปที่ app/models/product.rb แล้วใส่โค้ดเพิ่มเติมเข้าไป

class Product < ActiveRecord::Base
validates_presence_of :title, :description, :image_url
validates_numericality_of :price
validates_uniqueness_of :title
validates_format_of :image_url,
:with => %r{\.(gif|jpg|png)$}i,
:message => "must be a URL for a GIF, JPG, or PNG image"
protected
def validate
errors.add(:price, "should be at least 0.01" ) if price.nil? || price < 0.01
end
end

พระเจ้าจ๊อดมันยอดมากเลยใช่ไหมครับ model มันมีไว้ทำงี้นี่เอง

อ่าน Agile Web Development with Rails ( 3 )

ตอนที่แล้วทำให้เห็นภาพของสององค์ประกอบหลักในสถาปัตยกรรม MVC ใน Rails ยังขาดอยู่อีกตัวหนึ่งคือ M - Model ซึ่งเราจะได้เห็นกันในระหว่างการทดลองสร้าง application ตัวอย่างที่ชื่อ depot นี้ครับ

Rails รองรับการใช้งานฐานข้อมูลหลายตัว เราสามารถกำหนดได้ว่าจะใช้ตัวไหนตั้งแต่ตอนสร้าง application เช่น
rails app_name --database=oracle
หากไม่กำหนดดังนี้ rails จะเลือกใช้ค่า default คือ mysql ครับ

นอกจากนี้เรายังสามารถแก้ไขโดยตรงในไฟล์ config/database.yml พร้อมกับการกำหนดชื่อฐานข้อมูล และ username/password ก็ได้
Rails ไม่ได้บังคับเรื่องชื่อของฐานข้อมูลที่ใช้ แต่ตามข้อตกลงแล้ว ควรจะเป็นชื่อเดียวกับ application_*
เช่น application ชื่อ depot ฐานข้อมูลที่ใช้ระหว่างพัฒนาก็ควรเป็น depot_development

เมื่อกำหนดข้อมูลใน config/database.yml แล้ว สามารถทดสอบ config ได้โดย
rake db:migrate
เจ้า rake นี่ก็คือ make ของ ruby นั่นเองครับ

อ้อ... อย่าลืม create database และ user พร้อมกำหนดสิทธิ์ให้เรียบร้อยก่อน rake นะครับ ไม่งั้นไม่ผ่านแน่ๆครับ

เอาเป็นว่าตอนนี้เราสร้าง app ชื่อ depot และอยู่ใน working directory แล้ว โดยมีฐานข้อมูลพร้อมแล้วด้วยนะครับ

ทีนี้ก็กำหนด data model สำหรับสินค้าใน depot ชื่อโมเดล (และตาราง) คือ product ครับ
ruby script/generate model product
จะได้ไฟล์มากองนึง แต่ที่น่าสนใจมี 2 ไฟล์คือ app/models/product.rb และ db/migrate/001_create_products.rb (โปรดสังเกตว่าอันหลังมี s แถมท้ายชื่อ model มาให้ด้วย)

ไฟล์ app/models/product.rb คือตัว model ซึ่งเราจะมาดูทีหลังครับ
ตอนนี้ดูที่ db/migrate/001_create_products.rb กันก่อน เราจะแก้ไขไฟล์นี้ให้เป็นอย่างนี้ครับ

class CreateProducts < ActiveRecord::Migration
def self.up
create_table :products do |t|
t.column :title, :string
t.column :description, :text
t.column :image_url, :string
end
end

def self.down
drop_table :products
end
end

method ชื่อ up จะถูกเรียกให้ทำงานเมื่อเราสั่ง rake db:migrate
จะจัดการสร้าง table ชื่อ productsให้เราครับ

วิธีนี้ทำให้การพัฒนา application สะดวกขึ้น เพราะสามารถควบคุม version ได้
กรณีนี้ ถ้าเรียก rake db:migrate -VERSION=0 เจ้า method ชื่อ down ก็จะถูกเรียก tabel ชื่อ products ก็จะหายไป
การ rake db:migrate โดยไม่ระบุ VERSION ก็จะสร้าง VERSION ปัจจุบัน ทั้งนี้ Rails จะรู้ได้ว่า VERSION ในฐานข้อมูลคือ VERSION ไหนได้โดยสร้าง table ชื่อ schema info มาเก็บค่า VERSION ไว้ครับ

ทีนี้มาสร้าง controller ที่ใช้จัดการ products
ruby script/generate controller admin
เช่นเดิม เราจะสนใจเฉพาะ app/controllers/admin_controller.rb โดยเข้าไปเพิ่มเพียงหนึ่งบรรทัดให้เป็นอย่างนี้
class AdminController < ApplicationController
scaffold :product
end
คำสั่ง scaffold ใช้กำหนดให้ Rails สร้างโค้ดสำหรับจัดการ ข้อมูลใน model product ระหว่าง runtime

ไม่เชื่อก็ต้องเชื่อ เรียก http://host_name/admin จะเห็นได้ว่าเราสามารถจัดการกับข้อมูลใน product ได้แล้ว ทั้ง add, edit, remove ได้หมดเลย

คำสั่งเดียวแท้ๆ