Friday, December 16, 2005

การใช้งาน Rails Plugin command

วันนี้เรียนรู้วิธีการใช้งาน Rails Plugin ขึ้นมาอีกหนึ่งอย่าง
คำสั่งแรก ก็คือ

script/plugin discover

คำสั่งนี้ rails จะ scan หา plugins repository site
และ add plugin source ที่พบไว้ใน
file ~/.rails-plugin-sources ของเรา

ตอนแรกที่เห็นคำสั่งนี้ ก็นึกว่าเขาคงมี web service
ที่ให้บริการเรื่อง query เพื่อที่จะสอบถามได้ว่า
มี repositories อะไรที่ลงทะเบียนไว้บ้าง
แต่เปล่าเลย หลังจากเปิดดู source code
ก็พบว่า เขาใช้วิธีง่ายกว่านั้น
กล่าวคือ
ปกติ Rails จะมี page Plugins
ที่ลงรายการ plugin ที่มีคนทำแล้ว พร้อมคำอธิบายสั้นๆ
สิ่งที่คำสั่ง discover ทำ ก็คือ
load page ที่ว่ามา
แล้วก็ใช้ pattern matching หา link ที่มีคำว่า "plugin"

|- class Discover
||+ def initialize(base_command)
||
||+ def options
||
||+ def parse!(args)
||
2- def scrape(uri)
23 require 'open-uri'
23 puts "Scraping #{uri}" if $verbose
23 dupes = []
3- content = open(uri).each do |line|
4- if line =~ /<a[^>]*href=['"]([^'"]*)['"]/
45 uri = $1
5- if uri =~ /\/plugins\// and
uri !~ /\/browser\//
56 uri = extract_repository_uri(uri)
56 yield uri unless dupes.include?(uri) or
Repositories.instance.exist?(uri)
56 dupes << uri
56 end
45 end
34 end
23 end
||
||+ def extract_repository_uri(uri)
|| end


หลังจากได้ listing ของ plugin source มาแล้ว
คราวนี้เราก็สามารถใช้คำสั่ง

script/plugin install PLUGIN_NAME

เพื่อ install plugin ที่เราต้องการ

Note:
ผมชอบใช้วิธี svn export มากกว่าแฮะ

Related link from Roti

คนเดินถนน

เมื่อก่อนบ้านผมอยู่ในซอยที่เงียบสงบ
ต่อมามีโรงหนังดังมาตั้งอยู่ปากซอย
ซอยก็เลยกลายเป็นที่ระบายรถ

ถนนซึ่งไม่มีฟุตบาตให้เดิน ก็เลยกลายเป็นที่วัดใจกัน ระหว่างรถกับคน
ผมพบว่าการเดินหันหลังให้รถ
(เดินทางซ้ายของถนน)
ดูปลอดภัยกว่า
เพราะเดินทางขวาทีไร หมิ่นเหม่ต่อการ
ถูกชนเหลือเกิน

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

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

แต่ที่เหมือนกันหมด ก็คือแววตา
หรือสีหน้า ที่ไม่รับรู้การมีตัวตนของคนเดินถนน

Related link from Roti

Thursday, December 15, 2005

ผจญภัยกับ Emacs

Note:
post นี้
  • อาจจะช่วยสร้างกำลังใจให้กันคนที่หวาดหวั่นกับ Emacs
  • สนับสนุนสิ่งที่คุณ poonlap post ไว้ใน Bad Knowhow and Good Wrapper


ช่วงนี้ผมกำลังสนใจ Constraint Programming
ก็เลยไป load Mozart Programming System มาลองใช้ดู
Environment การพัฒนาของตัวนี้จะอยู่บน Emacs เป็นหลัก
ผมก็ไม่ถนัด Emacs เท่าไร
พึ่งหัดใช้มาได้ไม่เกิน 2 เดือน
จำคำสั่งอะไรก็ไม่ได้
การใช้ Emacs ก็เลยใช้ mouse เป็นหลัก (XEmacs)
(key-binding มันเยอะแยะไปหมด ใครจะจำได้วะ)

ตอนแรกที่ลองใช้ Mozart ก็ทดลองบน Ubuntu ก็พบว่าใช้ได้ดี
แต่พอทดลองลงบน Mac OS X บ้าง
กลับพบว่ามี error ขึ้นมาดังนี้

Buffer is read-only: #<buffer *Oz Compiler*>

ลอง search หาใน google ดู ก็เห็นมีคนถามเหมือนกัน
แต่ไม่มีคนตอบ

ก็เลยเปลี่ยนไปอ่านคู่มือ Emacs แทน
หาอะไรที่เกี่ยวกับ read-only
ก็พบว่ามีบท 27.7 Read-only Buffers
ก็เลยเข้าไปอ่าน
อ่านไปอ่านมา ก็เห็นว่ามีคำสั่ง toggle-read-only ให้ใช้
ก็เลยไปทดสอบสั่งใน buffer ที่ error ดู
M-x toggle-read-only
"เฮ้ย มหัศจรรย์ มันใช้ได้" !!
Error หายไปแล้ว
เหลือแต่ข้อสงสัยว่า ทำไมมันเปิด buffer มาแบบ read-only วะ

ด้วยความมันส์ในอารมณ์
ก็เข้าไปเปิด script file ของ mozart ที่ใช้ทำงานใน emacs
สำหรับคนที่ไม่เคยใช้ Emacs, script file พวกนี้
จะเขียนด้วยภาษา eLisp
โชคดีที่ผมหัดเรียน scheme มาได้พักใหญ่แล้ว
(ใช้ scheme เป็น ก็จะอ่าน code lisp ออก
เพราะ concept มันคล้ายๆกัน)

หลังจากตาลายอยู่พักใหญ่ ก็เริ่มอ่าน code รู้เรื่องขึ้น
จะว่าไปแล้ว code lisp ที่ดูเหมือนอ่านยาก
ถ้าจับหลักได้ มันก็อ่านง่ายดีเหมือนกันนะ
ในที่สุด ก็เจอ code ส่วนที่เป็นตัว open buffer

(let ((buffer (generate-new-buffer "*Oz Compiler*")))
(setq oz-compiler-buffers (cons buffer oz-compiler-buffers))
(save-excursion
(set-buffer buffer)
(compilation-mode)
(set (make-local-variable 'compilation-parse-errors-function)
'oz-compilation-parse-errors))

หลักการมั่วของผม ก็คือดูแต่โครงๆ
อย่างอันนี้ ก็ให้รู้ว่ามันกำลังสร้าง buffer ใหม่ด้วยคำสั่ง generate-new-buffer
และก็มีการ switch ไปที่ buffer นั้นด้วยคำสั่ง set-buffer
แล้วก็มีเรียกใช้คำสั่ง compiler-mode (ซึ่งก็ไม่รู้เหมือนกันว่ามันคืออะไร)
ผมก็เลยทดลองแทรกคำสั่ง ลงไปแบบนี้

(let ((buffer (generate-new-buffer "*Oz Compiler*")))
(setq oz-compiler-buffers (cons buffer oz-compiler-buffers))
(save-excursion
(set-buffer buffer)
(compilation-mode)
(toggle-read-only)
(set (make-local-variable 'compilation-parse-errors-function)
'oz-compilation-parse-errors))

ทดลอง restart emacs ใหม่
Error ยังอยู่เหมือนเดิม
เกือบจะถอดใจแล้ว
เผอิญเหลือบไปเห็นว่า file ที่ผมแก้อยู่ มันมีนามสกุลอยู่ 2 แบบ (ชื่อเดียวกัน)
อันหนึ่งเป็น el (อันที่ผมแก้อยู่) อีกอันเป็น elc
ก็เลยลองค้น google ดู จึงรู้ว่า elc ก็คือ script ที่ compile ไว้แล้ว

คราวนี้ก็ต้อง search หาอีกว่า จะ compile el script นี้ต้องสั่งอย่างไร
ในที่สุดก็เจอในคุ่มือ elisp บท 16.2 The Compilation Functions

หลังจาก compile เสร็จ
error ก็อัตรธานหายไป
ฮา ฮา, ของไชโย 3 ที

(ส่วนสาเหตุจริงๆ ที่ทำให้เกิด read-only นั้น
ไม่อยากหาต่อแล้ว แค่นี้ก็หมดไปเกือบ 2 ชั่วโมงแล้ว)

สรุป
อย่างที่ post ของคุณ poonlap บอกแหล่ะครับ ยิ่งเล่นเยอะ bad knowhow ก็เยอะขึ้น
การเล่นครั้งนี้ จริงๆก็ไม่ได้มีประโยชน์อะไรที่เป็นชิ้นเป็นอัน
แต่มันจะช่วยสะสม degree ของ bad knowhow ของผมขึ้นมาอีก

Related link from Roti

Wednesday, December 14, 2005

ทดลอง Rails Plugin

Rails version ปัจจุบัน (>=0.14) มีการเพิ่ม architecture
ที่เรียกว่า plugin เข้ามาใหม่
เพื่อให้ใช้เป็นมาตรฐานสำหรับการ customize rails
หรือการเพิ่ม extension ใหม่ๆลงไป

เดิมที่ยังไม่มีมาตรฐานนี้
developer ต่างคนก็ต่างใช้วิธีการของตัวเองในการ add Mixin ลงไป
เช่นบางคนก็ติดตั้ง library ใน gems
แล้วค่อยไปแก้ environment.rb ให้ require เข้ามา
หรือไม่ก็เขียน code ไว้ใน $RAILS_PROJECT/lib directory แล้ว require
ใน code ส่วนที่ต้องการใช้

ส่วนของใหม่ กำหนดให้เก็บ code นี้ไว้ใน
direcotry $RAILS_PROJECT/vendor/plugins
โดยเราต้องสร้าง directory $PLUGIN_NAME/lib ขึ้นมา
และภายในนั้น Rails จะเรียก require file ที่ชื่อ $PLUGIN_NAME.rb
ให้โดยอัตโนมัติ (อันนี้เดาเอานะ เพราะหา source code หรือเอกสารยืนยัน
ยังไม่เจอ แต่ได้ทดลองเปลี่ยนชื่อ หรือสร้าง file ชื่ออื่นๆไว้ มันก็ไม่ได้เรียกใช้แต่อย่างไร)

ส่วนการ install plugin ที่คนอื่นๆเขียนไว้
นิยมใช้ svn export ออกมาจาก repository
โดยเราต้อง cd ไปที่ $RAILS_PROJECT/vendor ก่อน

สำหรับ plugin ที่ผมทดลองใช้แล้วก็คือ Asset Timestamping
โดย install โดยใช้คำสั่ง

$ svn export http://svn.aviditybytes.com/rails/plugins/asset_timestamping

plugin นี้จะช่วยแปะ timestamp ลงไปใน path ของพวก javascript หรือ css
เช่น
เดิมใน view ที่สร้างโดย scaffold มันจะ require css โดยใช้คำสั่งนี้

<%= stylesheet_link_tag 'scaffold' %>

ซึ่งผลลัพท์ html ที่ได้จะได้ link หน้าตาแบบนี้

<link href="/stylesheets/scaffold.css" media="screen" rel="Stylesheet" type="text/css" />

เมื่อ install plugin แล้ว มันจะเปลี่ยนเป็นแบบนี้

<link href="/stylesheets/scaffold.css?1134445305" media="screen" rel="Stylesheet" type="text/css" />


Note: ที่ต้องทำเช่นนี้ ก็เพราะว่า browser มักจะทำ
cache ในส่วนของ javascript กับ css ไว้
เวลาที่เรามีการเปลี่ยนแปลง file พวกนี้
browser มันไม่ยอมรับรู้ตามไปด้วย
เดือดร้อนต้องเปลี่ยนชื่อ หรือเพิ่ม file ใหม่
หรือไม่ก็โทรไปบอกว่า ช่วยลบ cache ทิ้งด้วยครับ

ลองมาดู source code ของ Asset Timestamping กัน
จะได้รู้ว่า Mixin เขียนอย่างไร

12 module ActionView #:nodoc:
13 module Helpers #:nodoc:
14 module AssetTagHelper
15 private
16 def timestamped_compute_public_path(source, dir, ext)
17 public_path = compute_public_path_without_timestamp(source,dir
,ext)
18 if not source.include?('?')
19 file_paths = ["#{RAILS_ROOT}/public/#{dir}/#{source}",
20 "#{RAILS_ROOT}/public/#{dir}/#{source}.#{ext}"
]
21 for path in file_paths
22 if File.exists?(path)
23 return public_path << '?' + File.stat(path).mtime.to_i.t
o_s
24 end
25 end
26 end
27 public_path
28 end
29 alias :compute_public_path_without_timestamp :compute_public_pat
h
30 alias :compute_public_path :timestamped_compute_public_path
31 end
32 end
33 end

หลักการก็คือ มันทำการ mixin เข้าไปที่ module ActionView::Helpers::AssetTagHelper
ทำการตัดต่อเปลี่ยนชื่อ method ก่อน
โดยของเดิมให้เปลี่ยนไปใช้ชื่อใหม่ว่า compute_public_path_without_timestamp (บรรทัดที่ 29)
และให้เอาชื่อ method ที่พึ่งเพิ่มเข้าไปใหม่ ให้ไปแทนที่ของเดิม (บรรทัดที่ 30)

method timestamped_compute_public_path
เนื้อหาก็ไม่มีอะไรมาก
เริ่มแรก ก็ให้ gen url ขึ้นมาโดย method เดิม (บรรทัดที่ 17)
จากนั้นก็หา file และแปะ mtime ของ file นั้นตามหลังไป (บรรทัดที่ 23)

ดูแล้ว ก็รู้สึกสนุกกับ ruby
ที่มี class แบบเปิด
ใครใคร่แก้ แก้ (mixin)
ไม่เหมือน java ที่
ใครไคร่แก้ ก็ต้อง extends เอา (อันนี้สำหรับกรณีที่เขาออกแบบมาเผื่อ)
แต่ถ้าไม่ได้ออกแบบมาเผื่อ
ใครใครแก้ ก็ต้องcglib เอา

Related link from Roti

Tuesday, December 13, 2005

เรื่องทางสาธารณะสุขบ้าง

PracticallyKM
อันนี้อ่านเจอใน GotoKnow
เป็นเรื่องที่คุณหมอ phichet เล่าถึงประสบการณ์การดูงาน
ที่ออสเตรเลีย น่าสนใจทีเดียว

อ่านไปอ่านมาก็เจอที่คุณหมอพูดถึง
นพ.สุภัทร ฮาสุวรรณกิจ
จำชื่อได้ว่า เป็นเพื่อนร่วมกิจกรรม (สมัยเรียน)
ก็เลยตามไปดูใน google
ได้บทความที่เขาเขียนมาจำนวนหนึ่ง
เลยเอามาลง link ไว้



อ่านแล้วก็รู้สึกว่าเราชักจะหมกมุ่นกับ โลกของ technical มากเกินไปแล้ว

Related link from Roti

Monday, December 12, 2005

Javascript in ruby style

นึกแล้วว่า Prototype มันต้องมีอะไรดีๆซ่อนอยู่
ด้วยความที่ว่ามันไม่มีเอกสารให้ดู
เราก็เลยไม่รู้ว่ามันทำอะไรได้บ้าง

วันนี้อ่านเจอ article เรื่อง Prototype Meets Ruby: A Look at Enumerable, Array and Hash
อ่านแล้วต้องร้อง "ว้าว" ดังๆ

Related link from Roti

ทดลองใช้ OpenLaszlo

ผมเคยทดลอง evaluate OpenLaszlo ตั้งแต่สมัยที่มันยังไม่มี คำว่า open นำหน้า
จากการทดลองเล่น ยอมรับเลยว่าติดใจใน interface ที่ดู หรูหรา มาก
แต่ตอนนั้นมีปัญหาว่า compile-time มันสูงมาก
อีกอย่าง swf file ที่ได้ก็มีขนาดใหญ่เกินไป (1 MB นิดๆ)

วันก่อนเจอที่ mk เขียนถึง OpenLaszlo ก็เกิดอาการสนใจอยากลองเล่นอีกครั้ง

เริ่มแรกทดสอบขนาดก่อนเลย
เอาที่ helloworld ก่อนเลย เพราะมันเป็น app ที่เล็กที่สุดที่เป็นไปได้แล้ว

<canvas>
<text>Hello Laszlo!</text>
</canvas>

ขนาดที่ได้ ก็คือ 102,599 Byte
ลองมาดูว่า ตัวเลข 100 กว่า KB นี้มันแถมอะไรมาให้บ้าง
(openlaszlo มี feature ที่ช่วย describe ว่า
app ของเราหลังจาก compile แล้วจะมีอะไรแปะอยู่ข้างในบ้าง)
ใน hello app ของเรา จะแบ่งออกเป็น 2 ส่วนคือ
LFC (laszlo foundation class) กับ Instances
โดย LFC ของ flash version 7 มีขนาดก่อน zip 209 KB
ส่วน instance ของเรามีขนาด 168 Byte
หลังจาก zip แล้วจะได้ขนาด 69 KB
ซึ่งไม่เท่ากับ swf file ที่เราได้กลับมา
แสดงว่ามี overhead ในการแปลงเป็น swf อีก 30 KB
(ตรงนี้ไม่รู้เหมือนกันว่า ขนาด 30 KB นี้
เป็น fix size หรือ variable size
เมื่อโปรแกรมใหญ่ขึ้น)

สรุปขนาดของ App
เล็กสุด ก็น่าจะประมาณ 100 KB

ที่นี่ด้านเวลาบ้าง
เวลาใน version นี้คงไม่เป็นปัญหาแล้ว
เพราะเราสามารถ pre-compile เป็น format lzo ได้
(จริงๆแล้ว binary ข้างในคือ swf format)

ที่นี้ลองมาดู factor สุดท้ายบ้าง
ก็คือเรื่องของ runtime environment
(พัฒนาเสร็จแล้ว จะเอาไปใช้แล้ว)
กรณี static web app
อันนี้หมายถึง file laszlo ของเราไม่ได้ dynamic เปลี่ยนแปลงไปมา
(หมายถึงโปรแกรมนะ ไม่ใช่ data)
การเอาไปใช้ วิธีที่ง่ายที่สุด ก็คือ copy file lzo (ที่ได้จากการ pre-compile)
ไปไว้ใน web application ที่เราต้องการ
โดยแค่เปลี่ยนนามสกุลเป็น swf ก็ใช้ได้แล้ว

ส่วนกรณีที่ lzx (โปรแกรม) ของเราเปลี่ยนไปมาได้ (dynamic)
อันนี้ไม่ขอแนะนำ เพราะ compile time
สูงมาก (>15 sec)

Related link from Roti