Tuesday, May 31, 2011

การติดตั้ง RVM บน Ubuntu (แถมการติดตั้ง Passenger)

RVM คือคำสั่ง/สภาพแวดล้อม ที่ทำให้เราสามารถติดตั้งและเรียกใช้ Ruby หลายเวอร์ชั่นได้อย่างสะดวกสบาย ลำบากแค่กดคีย์บอร์ดไม่กี่ที

คราวก่อน คุณ Neokain เขียนวิธีการติดตั้ง RVM บน Mac ไปแล้ว คราวนี้ผมจะเขียนในส่วนของ Ubuntu ดูบ้าง ซึ่งขั้นตอนต่างๆ เกือบจะเหมือนกันทั้งหมด มีรายละเอียดที่แตกต่างไปบ้างเล็กน้อย

จุดเด่นของ Ubuntu (และลินุกซ์ดิสโทรตระกูล Debian) คือเรื่องระบบการจัดการแพ็คเกจ APT ไม่ว่าจะเป็น apt-get หรือ aptitude ก็ตาม ที่สามารถติดตั้งไบนารีแพ็คเกจได้อย่างรวดเร็วและประสิทธิภาพก็ไม่ได้ต่างไปจากการคอมไพล์ซอร์สโค้ดเองเท่าไหร่นัก โดยเฉพาะเครื่องสำหรับใช้พัฒนา การใช้ *nix ที่ต้องคอมไพล์แพ็คเกจจากซอร์สโค้ดเอง ไม่ว่าจะเป็น *BSD, Gentoo หรือ MacPorts ดูจะเป็นการเสียเวลาและเปลืองไฟฟ้าโดยใช่เหตุ

ขั้นตอนแรกที่สำคัญมาก คือต้องติดตั้งไลบรารีพื้นฐานให้ครบก่อน ไม่อย่างนั้นจะต้องคอมไพล์ซ้ำหลายรอบ (ครั้งแรกผมโดนไป 5)

$ sudo aptitude install build-essential libssl-dev libreadline6-dev zlib1g-dev


ต่อไปเป็นการติดตั้งสคริปท์ RVM โดยเลือกติดตั้งได้สองแบบคือ single-user กับ multi-user โดยทั่วไปแนะนำให้ติดตั้งแบบ single-user ดีกว่า เนื่องจากซอร์สโค้ดทั้งหมดจะถูกเก็บไว้อยู่ในพาธ $HOME ของเราเอง ทำให้การติดตั้งแพ็คเกจผ่าน RubyGems ในอนาคตไม่ต้องใช้สิทธิ์ root แต่หากต้องการติดตั้งแบบ multi-user ก็ให้เติม sudo ลงไปข้างหน้าคำสั่ง แล้ว RVM ก็จะถูกนำไปติดตั้งอยู่ที่ /usr/local/rvm ให้แทน

$ bash < <(curl -s https://rvm.beginrescueend.com/install/rvm)


จากนั้นให้นำโค้ดข้างล่างนี้ไปแปะไว้ในไฟล์ ~/.bashrc สำหรับ single-user และ /etc/profile สำหรับ multi-user ตามลำดับ

[[ -s "$HOME/.rvm/scripts/rvm" ]] && . "$HOME/.rvm/scripts/rvm"

[[ -s "/home/YOURNAME/.rvm/scripts/rvm" ]] && source "/home/YOURNAME/.rvm/scripts/rvm"


เสร็จแล้วให้สั่งเริ่มการทำงานของเชลล์ใหม่ เพื่อให้คำสั่ง rvm ไปอยู่ใน $PATH ของระบบให้เราเรียกใช้ได้สะดวกๆ

$ source ~/.bashrc


ดู Ruby เวอร์ชั่นต่างๆ ที่สามารถติดตั้งได้

$ rvm list known | less


ปกติผมใช้ Matz's Ruby แต่ถ้าจะใช้ JRuby ก็ให้ติดตั้ง JDK (openjdk-6-jdk) ก่อน

$ rvm install 1.9.2


ดูรายการ Ruby ที่ติดตั้งไว้แล้ว

$ rvm list


เลือก Ruby ที่จะใช้เป็นค่าปริยาย (default)

$ rvm use 1.9.2 --default


จากนั้นจะมีคำสั่ง "ruby" ให้เราเรียกใช้ได้

$ ruby -v


ส่วนการติดตั้ง Passenger ผ่าน RubyGems ก็ไม่มีอะไรยาก

$ gem install passenger --no-ri --no-rdoc


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

$ passenger-install-apache2-module


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

$ sudo aptitude install libcurl4-openssl-dev apache2-prefork-dev libapr1-dev libaprutil1-dev


เสร็จแล้วก็ทำตามคำแนะนำในขั้นตอนสุดท้าย โดยเอาโค้ดไปใส่ไว้ในไฟล์ /etc/apache2/mods-available/passenger.load แล้วสั่งให้ Apache เริ่มทำงานใหม่

$ sudo a2enmod passenger
 $ sudo apache2ctl restart


ส่วนการติดตั้งใช้กับ Nginx นั้นแตกต่างออกไป เนื่องจากจะต้องผูก Passenger เข้าไปตั้งแต่ตอนคอมไพล์ ซึ่งต้องใช้สิทธิ์ root โดยเราจะทำการติดตั้งผ่านคำสั่ง rvmsudo จากนั้น Passenger จะให้เราเลือกว่าจะคอมไพล์ Nginx วิธีไหน ระหว่างให้มันไปดาวน์โหลดซอร์สโค้ดมาให้ กับให้เราไปเลือกดาวน์โหลดเอง โดยทั่วไปวิธีแรกจะได้ Nginx เวอร์ชั่นที่เก่ากว่านิดหน่อย

$ rvmsudo passenger-install-nginx-module


ที่เหลือเป็นวิธีการตั้งค่า virtual host ศึกษาได้คู่มือของ Apache หรือ Nginx

ในกรณีปกติ Nginx จะใช้หน่วยความจำน้อยกว่า Apache และจากผลการทดสอบของนักพัฒนาท่านอื่นๆ ได้ผลลัพธ์ว่า Apache ทำงานได้ดีกับ dynamic content ส่วน Nginx ทำงานได้ดีกับ static content แต่ก็เห็นหลายๆ ท่านใช้ Nginx เป็น reverse proxy รับ request แล้วส่งต่อไปให้ Apache ทำงาน ซึ่งก็ได้ผลลัพธ์เป็นที่น่าพอใจ

ขอพลังแห่ง Ruby จงสถิตย์แก่ท่าน

Saturday, May 28, 2011

มาเล่น Cloud Foundry กันเถอะ

บทความนี้เป็นบทความแรกของผม ถ้ามีอะไรผิดพลาดก็ขออภัยด้วยครับ :)

Cloud Foundry คือแพลตฟอร์มสำหรับการประมวลผลแบบกลุ่มเมฆซึ่งให้บริการโดยVMware Cloud Foundry มีความแตกต่างจากGoogle App Engineตรงที่รองรับภาษาและเฟรมเวิร์คได้หลากหลายกว่า(Spring, Rails and Node.js) อีกทั้งยังเป็นโอเพ่นซอร์สที่ทำให้เราสามารถเข้าถึงซอร์สโค้ดของCloud Foundry ด้วยสัญญาอนุญาติแบบMITครับ รายละเอียดภาษาไทยเพิ่มเติมอ่านได้จาก Blognone ครับ

ก่อนอื่นเราต้องลงทะเบียนเพื่อขอinviteที่ http://cloudfoundry.com/signup หลังจากนั้นก็รอให้ทีมงานapproved ขั้นตอนนี้อาจจะใช้เวลาหน่อยน่ะครับ(ของผมใช้เวลาประมาณสัปดาห์)

สำหรับคนที่มีaccountแล้วก็มาเริ่มกันเลยครับ เริ่มจากการติดตั้ง vmc command line tool ด้วยคำสั่ง
$ sudo gem install vmc

สำหรับท่านใดที่ใช้บริการอินเตอร์เน็ตของ3BB อาจจะเจอerrorแบบนี้
ERROR:  Could not find a valid gem 'vmc' (>= 0) in any repository
ERROR: While executing gem ... (Gem::RemoteFetcher::FetchError)
too many connection resets (http://rubygems.org/latest_specs.4.8.gz)

ให้แก้ไขโดยการติดตั้งผ่านproxyครับ
$ sudo gem install --http-proxy http://123.456.7.89:1234 vmc

เมื่อติดตั้งเสร็จแล้วให้ทำการทดสอบว่าใช้งานได้หรือไม่ด้วยคำสั่ง
$ vmc target api.cloudfoundry.com

ทำการloginด้วยอีเมล์และรหัสผ่านชั่วคราวที่ได้มา
$ vmc login

อย่าลืมเปลี่ยนรหัสผ่านให้เป็นของเราเองตามคำแนะนำในอีเมล์ครับ
$ vmc passwd

ผมเริ่มต้นด้วยการสร้างแอพใหม่ชื่อว่า esper-blog
$ rails new esper-blog

สำหรับการใช้railsบนCloud Foundry ยังมีข้อจำกัดและสิ่งที่ต้องระวังอยู่ครับ สามารถอ่านได้จาก Ruby and CloudFoundry: Things to know

ต่อไปให้เราเพิ่มthin, rakeและbundlerเข้าไปใน Gemfile ครับ (ข้อควรระวัง: ให้ใช้rake 0.8.7น่ะครับ เพราะ0.9.0ไม่สามารถใช้งานได้ครับ)
source 'http://rubygems.org'
gem 'rails', '3.0.7'
gem 'bundler'
gem 'sqlite3'
gem 'rake', '0.8.7'
gem 'thin'

ก่อนจะdeploy อย่าลืมสั่ง bundle package, bundle install ก่อนน่ะครับ สำหรับการdeployก็ง่ายแสนง่าย เพียงสั่ง
$ vmc push
Would you like to deploy from the current directory? [Yn]: กดEnter
Application Name: esper-blog ใส่ชื่อแอพพลิเคชั่น
Application Deployed URL: 'esper-blog.cloudfoundry.com'? กดEnter
Detected a Rails Application, is this correct? [Yn]: กดEnter
Memory Reservation [Default:256M] (64M, 128M, 256M, 512M, 1G or 2G) ผมกดEnterเพื่อใช้แค่ 256M
Creating Application: OK
Would you like to bind any services to 'esper-blog'? [yN]: กดEnter
Uploading Application:
Checking for available resources: OK
Processing resources: OK
Packing application: OK
Uploading (13K): OK
Push Status: OK
Staging Application: OK
Starting Application: OK

สุดท้ายลองเปิดWeb Browserขึ้นมาแล้วใส่URLของเราลงไปครับ

หลังจากมีการแก้ไขแอพพลิเคชั่น สามารถdeployโดยใช้คำสั่ง
$ vmc update

สามารถอ่านข้อมูลเพิ่มเติมได้ที่นี่ครับ support.cloudfoundry.com บทความอาจจะสั้นไปนิดน่ะครับ ขอให้สนุกน่ะครับ

Saturday, May 14, 2011

ปัญหา RubyGems Warning หลังจากอัพเกรด

หลังจากที่อัพเกรด ruby gem เป็นเวอร์ชั่นล่าสุด (1.8.2) ปรากฎว่ารันโปรเจ็กต์จะมีแจ้งเตือนว่า

NOTE: Gem::Specification#default_executable= is deprecated with no replacement. It will be removed on or after 2011-10-01.
Gem::Specification#default_executable= called from /Users/phuwanart/.rvm/gems/ruby-1.9.2-p180@global/specifications/rake-0.8.7.gemspec:10.

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

$ gem update --system 1.7.2

แต่เราไม่ต้องตกใจไป เราสามารถอัพเกรด gem ของเราได้ แต่เพียงว่าตอนนี้ยังไม่แนะนำ

ที่มา: RubyGems Warningitis Outbreak

Tuesday, May 3, 2011

ลง RVM บน Mac OS X Snow Leopard

เนื่องด้วยว่า rails2 และ rails3 ใช้ ruby คนละ version และถึงแม้ว่า rails3 จะยังใช้ได้กับ ruby 1.8.x แต่ก็มี gem บางตัวที่ต้องการ ruby 1.9.x เป็นผลว่าทำให้ต้องมี ruby หลาย version ในเครื่องเดียวกัน ซึ่งในที่นี้เราจะใช้ RMV ช่วยจัดการ ruby ที่อยู่ในเครื่องของเรา

ติดตั้ง RVM

เปิด Terminal แล้วพิมพ์คำสั่งนี้

$ bash < <(curl -s https://rvm.beginrescueend.com/install/rvm)

จากนั้นให้เพิ่มบรรทัดนี้ลงไปใน .bash_profile

[[ -s "$HOME/.rvm/scripts/rvm" ]] && source "$HOME/.rvm/scripts/rvm" # This loads RVM into a shell session.

ทดสอบว่าลง RVM สำเร็จโดยสั่ง

$ type rvm | head -1

ซึ่งจะได้ผลออกมาดังนี้

rvm is a function

ติดตั้ง Ruby

ติดตั้ง ruby 1.8.7 โดยพิมพ์คำสั่งนี้ลงบน terminal

$ rvm install 1.8.7

ติดตั้ง ruby 1.9.2 ก็ทำเช่นเดียวกัน

$rvm install 1.9.2

ใช้งาน Ruby

เมื่อต้องการจะใช้ ruby 1.8.7 ก็ให้พิมพ์คำสั่งนี้ลง terminal

$ rvm use 1.8.7

หากต้องการใช้อีก ruby อีก version ก็ทำเช่นเดิม

$ rvm use 1.9.2

หากต้องการให้ version เป็น default ก็ใช้ --default ตามลงไป

$ rvm use 1.9.2 --default

เมื่อสลับ ruby ในแต่ละ version แล้ว gem ก็จะเปลี่ยนไปด้วย ทั้งหมดจะถูกแยกตามการลงในแต่ละ version

Monday, April 4, 2011

[PR] ประชาสัมพันธ์ชาว Thai Ruby On Rails

My name is Parinaya, I work as a Project Manager at MGB Software Company (www.mgb-software.com) which work for software industry, mobile, web/windows applications, and consultancy, and we are leading software house in Bangkok, with an international clients base. We are a partner of Nuxos (http://www.nuxos.fr) Company in Asia. I'm searching for Rails developers to work full time on Rails project for our European partner.

ผมชื่อปริญญาทำงานตำแน่ง Project Manager อยู่ที่บริษัท MGB Software (www.mgb-software.com) ซึ่งธุรกิจบริษัทเกี่ยวกับซอฟท์แวร์ หรือ software house โดยบริษัทเราพัฒนาด้าน mobile, web หรือ windows applications และเป็นที่ปรึกษาทางด้านซอฟท์แวร์ รายละเอียดเพิ่มเติมอ่านได้ในเวปบริษัทนะครับ (www.mgb-software.com). และบริษัทเรายังเป็นเป็น partner ของบริษัท Nuxos (http://www.nuxos.fr) ทางโซนเอเชีย ตอนนี้ผมกำหลังหา Rails Programmer หลายคนเพื่อมาร่วมงานกันพัฒนาซอฟท์แวร์ให้กับ Partner ของบริษัทผมครับด้วย Rails

Introduce my partner, His name is Richard Piacentini, he is a Associate Director at Nuxos company (http://www.nuxos.fr) which is one of the most known company doing Ruby & Rails in France. He has been one of the evangelist of Ruby and Rails in France since 2004, and his partner Laurent Julliard are considered as European's Ruby & Rails experts, that He has created the Railsfrance.org (http://www.railsfrance.org) portal, and He is the organiser of the biggest Rails event in France, called Paris on Rails Rails (http://paris.onrails.info/) where the creator of Rails (DHH aka David Heinemeier Hansson) has been speaking in video since the first edition.

ก่อนอื่นผมต้องขอแนะนำ partner ของบริษัทผมก่อนคือ Richard Piacentini เป็น Associate Director ที่บริษัท Nuxos (http://www.nuxos.fr) เขาเป็นคนสอนและเผยแพร่ Ruby & Rails ในฝรั่งเศสตั้งแต่ปี 2004 และมีหุ้นส่วนอีกคนชื่อ Laurent Julliard โดยทั้งสองคนเป็นผู้เชี่ยวชาญ European's Ruby & Rails Richard เคยเขียนโปรแกรมสร้าง Railfrance Portal (http://www.railsfrance.org) และยังเป็นคนจัด event ที่ใหญ่ที่สุดสำหรับ Rails ในฝรั่งเศสเรียกว่า Paris on Rails Rails (http://paris.onrails.info/) และทำ VDO เกี่ยวกับการสอน Rail ตั้งแต่เวอร์ชันแรกชื่อ DHH aka David Heinemeier Hansson This is his profile on the working with Rails website : http://workingwithrails.com/person/4817-richard-piacentini. And they have translated and adapted 3 of the major Rails & Ruby Book: - Dave Thomas's "Agile Web Development with Rails" - Russ Olsen's "Design Patterns in Ruby" - Chad Fowler's "Rails Recipes"

เพื่อนๆใน Blog สามารถเข้าไปดูผลงานของ Richard ได้ที่ http://workingwithrails.com/person/4817-richard-piacentini. และผลงานทางด้านหนังสืออีก 3 เล่ม Dave Thomas's "Agile Web Development with Rails", Russ Olsen's "Design Patterns in Ruby", Chad Fowler's "Rails Recipes" I have looked through "Thai Ruby On Rails" Blog that make me interested in your articles, topic, and online community.

ผมได้อ่านบทความและหัวข้อที่คุยกันใน community นี้แล้วรู้สึกสนใจครับ

This is a opportunity to know everybody in this community Blog, Richard will be in BKK until the end of next week, He and I are happy to meet some people in "Thai Ruby On Rails", have a drink and exchange about Ruby, Rails and how it's perceived in Thailand.

ตอนนี้เป็นโอกาสดีที่เพื่อนทุกคนใน Blog จะได้พูดคุยแลกเปลี่ยนความคิดเกี่ยวกับ Ruby on Rails กับ Richard เพราะคนจะกลับฝรั่งเศสวันที่ 22 เดือนนี้ และเค้าสนใจมากที่เจอทุกคนที่อยู่ใน Thai Ruby on Rails นี้ทุกคนเพื่อ หาโปรแกรมเมอเพื่อมาทำงาน Full Time และ แลกเปลี่ยนความรู้กับ Rails ในประเทศไทย

โดยกลุ่ม Thai Ruby on Rails อาจจะรวบรวมคนที่สนใจและส่ง รายชื่อ, เบอร์โทร, และอีเมล์ ให้ผม หรือติดต่อผมโดยตรงตาม contact ด้านล่างครับ

ใครสนใจติดต่อผมด่วนนะครับ

If there is any questions you can contact me below: email : parinya.k@mgb-software.com

ปล. ผมต้องขออภัยคุณปริญญาด้วยนะครับ ที่ลงประกาศให้ช้าไป

Sunday, March 13, 2011

สร้างไฟล์ PDF แบบชิวๆ จาก HTML และ CSS ด้วยภาษา Ruby

การสร้างไฟล์ PDF ด้วยภาษา Ruby ในวันนี้เป็นเรื่องง่ายดายมาก เนื่องจากมีไลบรารีให้เลือกใช้มากถึง 2 ตัว (เป็นอย่างน้อย) คือ Prawn และ PDFKit

  • Prawn สร้าง PDF ด้วยท่ายาก อยากได้อะไรก็เขียนโค้ดเอา ความสามารถล้นเหลือ เหมาะกับ PDF ที่มีความซับซ้อน แถมมีตัวอย่างให้ดูเยอะมาก ซอร์สโค้ดอยู่ในโฟลเดอร์ examples ส่วน PDF ผลลัพธ์อยู่ในโฟลเดอร์ reference_pdfs
  • PDFKit สร้าง PDF ง่ายๆ จากวัสดุที่หาได้ตามธรรมชาติ คือ HTML+CSS ธรรมดาๆ โดยตัวมันทำหน้าที่เป็นเพียง Ruby binding ของไลบรารี่ wkhtmltopdf อีกที


งานของผมเหมาะกับ PDFKit มากกว่า ส่วนการได้มาซึ่ง HTML และ CSS นั้นหรูหราเล็กน้อย กล่าวคือ สร้าง HTML จาก Slim และสร้าง CSS จาก Sass (SCSS)

  • Slim คือเครื่องมือสำหรับสร้าง HTML โดยไม่ต้องเขียนโค้ด HTML แต่เขียนด้วยโค้ดที่สั้นกระชับและสะอาดตากว่ามาก ได้แรงบันดาลใจมาจาก Haml + Jade แต่ทำงานได้เร็วกว่า
  • Sass คือเครื่องมือสำหรับสร้าง CSS ที่เพิ่มความสามารถเข้าไปในโค้ด CSS ธรรมดา เช่น มีตัวแปร มีฟังก์ชัน สามารถ import/include ส่วนของโค้ดได้ เรียกว่า DRY - Don't Repeat Yourself กันสุดลิ่มทิ่มประตู โดยผมใช้มันผ่าน Compass อีกที

 

สร้าง HTML จาก Slim


โดยปกติเราใช้ Slim กันในฐานะเป็น Views ของ Rails ซึ่งไม่มีปัญหาอะไรน่าตื่นเต้น แต่กรณีนี้เราเรียกใช้ Slim template โดยตรง ซึ่งในเอกสารเขาเขียนวิธีเรียกใช้สั้นๆ แค่ว่า

Tilt.new['template.slim'].render(scope)
Slim::Template.new(filename, optional_option_hash).render(scope)
Slim::Template.new(optional_option_hash) { source }.render(scope)


คำที่ไม่เข้าใจคือ "scope" - what's a scope? เดาได้เบื้องต้นว่ามันน่าจะเป็นออปเจ็คอะไรสักอย่าง แต่การทดลองเบื้องต้นพบว่า ส่งอะไรไปมันก็ไม่รู้จักท่าเดียว ไม่ว่าจะเป็น Model object หรือ Hash

เนื่องจากมัน based on Tilt เราก็ไปเปิดเอกสารของ Tilt อ่านต่อ จากตัวอย่างจะเห็นชัดเจนว่า scope เป็นได้ทั้ง Model object และ Hash ซึ่งเราก็ทำเหมือนมันเป๊ะๆ แต่ทำไมยังคอมไพล์ไม่ผ่านสักที บอกแต่ว่าไม่รู้จักตัวแปรที่เราเรียกใช้ใน template

โชคดีได้พี่ป๊อก (@pphetra) มาช่วยดู ท่าไม้ตายของพี่แกคือไปแกะซอร์สโค้ดแม่งเลย แล้วก็พบว่า error อยู่ตรงคำสั่ง instance_evl() ซึ่งเมธอดนี้จะทำงานได้ก็ต่อเมื่อถูกเรียกใช้ในรูปแบบนี้

{:say => 'Hello'}.instance_eval('puts fetch(:say)', '', 0)


สรุปแบบง่ายๆ คือถ้าเราส่ง object อะไรเข้าใน template ตอนเรียกใช้ก็ให้เสมือนว่าเราเป็น object นั้นเสียเอง ตัวอย่าง

person = Person.find(:name => 'Jane')
Slim::Template.new('template.slim').render(person, :say => 'Hello')


#template.slim

p = '%s says %s' % [name, fetch(:say)]

 

สร้าง PDF จาก HTML+CSS


ก่อนอื่นต้องติดตั้ง wkhtmltopdf ก่อน จะทำผ่าน APT หรือ build source ก็ตามแต่ลำบาก ส่วนวิธีใช้ PDFKit ก็ลอกจากตัวอย่างเลย

pdf = PDFKit.new('template.html')
pdf.stylesheets << 'template.css'
pdf.to_file('output.pdf')

ปัญหาเดียวที่พบคือ แสดงภาษาไทยไม่ได้ !! โฮลี่ ชิท นี่มันเมกะโพรเบล็มเลยนะเว้ย สันนิษฐานว่าน่าจะเป็นที่ encoding ของไลบรารี่สักตัว ไม่ PDFKit ก็ wkhtmltopdf งมโข่งอยู่นานมาก ทั้งอ่าน manual และค้นหาจาก Google ซึ่งก็ไม่เจอเบาะแสใดๆ ในเอกสารของ PDFKit บอกเพียงว่า encoding เป็น UTF-8 โดยปริยายอยู่แล้ว ... แล้วปัญหาอยู่ตรงไหนล่ะ?

โชคยังดีอยู่ ตรงที่เราไปลองสร้าง PDF ด้วยการใส่ URL ของเว็บไซต์เข้าไป แล้วมันดันแสดงภาษาไทยได้สวยงาม

PDFKit.new('http://google.co.th').to_file('output.pdf')


แล้วปัญญาก็เกิดปิ๊งทันที ปัญหานี้แก้ได้ด้วยโค้ดบรรทัดเดียว ... ไม่เฉลยด้วย ปล่อยให้เป็นเส้นผมบังภูเขาของท่านอื่นต่อไป ฮ่าๆ

 

Monday, February 28, 2011

ปัญหาการแสดงผล form error message บน Rails 3

เป็นที่ทราบกันดีอยู่แล้วว่า Rails 3 มีความแตกต่างจาก Rails 2 อยู่มากพอสมควร ฟังก์ชันหลายๆ อย่างถูกตัดออกไป โดยเฉพาะส่วนที่เกี่ยวข้องกับการแสดงผล เนื่องจากทีมพัฒนาเห็นว่าโค้ดส่วนนี้ไม่ควรจะอยู่ใน Rails core ควรจะปล่อยให้เป็นเรื่องของ application ไปจัดการกันเอง ตัวแรกที่ผมเพิ่งเจอคือ form.error_messages ปัญหามันเกิดแบบ combo ดังรายละเอียดต่อไปนี้

อย่างแรกคือ form.error_messages ถูกตัดออกจาก core ไปแล้ว ทำให้เราไม่สามารถแสดง error ด้วยวิธีเดิมได้ Rails developer ทุกคนเจอปัญหานี้เหมือนกันหมด จึงมีคนสร้าง dynamic_form ขึ้นมาทดแทน (copyrighted by DHH) คุณ @sikachu ก็เป็น contributor กับโปรเจ็คนี้ด้วย ใช้งานได้ค่อนข้างดีทีเดียว แต่ยังไม่ถูกใจกับ HTML ที่มันสร้างมาให้ นอกจากนี้ยังมีคนเสนอแนวทางอื่นด้วย เช่น คนนี้ และ คนนี้

ปัญหาต่อมาคือผมใช้ routing แบบ resources (RESTful)

def create
  @project = Project.new(params[:project])
  if @project.save
    redirect_to @project, :notice => 'The project has been saved.'
  else
    flash[:alert] = "Can't save the project."
    redirect_to :action => :new
    # render :action => :new
  end
end

def new
  @project = Project.new
end
โดยปกติเมื่อ @project.save ไม่ผ่านการ validate มันควรจะ redirect ไปหา :action => :new แต่จะมีปัญหาว่า error message ที่ผูกอยู่กับ model จะหายไป เนื่องจาก object ถูกสร้างใหม่โดยเมธอด new แล้ว ครั้นจะเปลี่ยน redirect เป็น render ซึ่งสามารถแสดง error message ได้ แต่มันก็จะแปลกๆ ตรงที่ URL จะยังเป็นของ create ('/projects' - path นี้ใช้ร่วมกัน 2 เมธอด คือ index#GET กับ create#POST) ทั้งๆ ที่มันควรจะเป็นของ new มากกว่า ('/projects/new' - new#GET) คนที่ไม่เคยใช้ routing แบบ resources อาจจะงงๆ หน่อยนะครับ : )

ตอนนี้สรุปว่าแก้ปัญหาเรื่อง form.error_messages ได้แล้วล่ะ ปัญหาที่เหลือคือจะจัดการกับ state ของ object อย่างไรดี (หรือผมเข้าใจอะไรผิดไปวะ)