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

No comments: