Friday, March 21, 2008

ไม่ไว้วางใจ ปรเมศวร์

ในกรณีของกระปุก ซึ่งกลายเป็นข่าวร้อนในตอนนี้
มีเรื่องหนึ่งที่ผมรู้สึกติดใจ ก็คือ
เนื้อหาที่อยู่ใน (อดีต) xxx.kapook.com
ผมรู้สึกว่า เนื้อหาในนั้นมันไม่ได้แค่ สุ่มๆตัดถ้อยคำมาจาก web โป๊ ทั่วๆไปมาแปะ
แต่มันเป็นการ จงใจ และ ตั้งใจ แต่งขึ้น

ปกติเวลาคนเราเขียนอะไรออกมา
สิ่งที่เขียน มันต้องมีเม็ดพันธุ์บางอย่างอยู่ในใจ
กรณีของคนที่เขียนเนื้อหาของ xxx.kapook.com
ผมก็เชื่อว่า เนื้อหาที่เขียน มันสะท้อนความหมกมุ่น ของเขา (ซึ่งดูจะเกินเลยขีดปกติของคนธรรมดาไป)
และก็เชื่อด้วยว่า ขณะที่แต่ง เขาคงรู้สึกสนุกและเมามันไปกับมัน
แถมยังเชื่อต่อไปอีกด้วยอีกว่า ในระหว่างที่ทำ
ก็คงมีการแจกจ่ายกันอ่าน สนุกกับการวิจารณ์ และช่วยกันเสริม ในกลุ่มของคนที่ทำงานด้วยกัน

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

Related link from Roti

Thursday, March 20, 2008

การใช้ Git กับโครงการ Orangegears

ช่วงนี้ผมกำลังเริ่ม contribute ให้กับ project Orangegears ซึ่งเป็นระบบ ERP ที่ fork มาจาก Ofbiz
โดยมีเหล่าน้องๆฝึกงาน (สองคน) มาช่วยกันรุมยำ source code

ประเด็นปัญหาแรกที่ต้อง solve ก็คือจะจัดการกับ version control อย่างไร
โดยมีประเด็นที่ต้องคำนึงถึงดังนี้
  • code ของ orangegears อยู่บน sourceforge.net และมีขนาดใหญ่
  • เนื่องจากยังเป็นมือใหม่ กับ ERP framework ตัวนี้, รูปแบบการแก้ไข orangegears จะออกไปในทางลองผิดลองถูก ซึ่งหมายความว่าจะมี throw away code จำนวนมาก
  • ต่อเนื่องจากข้อข้างบน การลองผิดลองถูกจะทำให้เกิดการ branch จำนวนมาก
  • code ที่น้องๆช่วยกันเขียน อาจจะมีปัญหาเรื่อง quality ของ code เช่นมี noise ปนอยู่จำนวนหนึ่ง (น้องใหม่ส่วนใหญ่ มักจะเขียน code แบบ แปะๆ อันไหนใช้ไม่ได้ก็ comment เอาแทนที่จะลบทิ้งไปเลย)
  • เนื่องจากเป็นระบบที่ไม่ชำนาญ ก็จะมีประเด็นเรื่องแก้ไขบ่อย, commit ถี่และบ่อย ซึ่งการ commit บ่อยๆ(แบบไม่ควรเกิด) ก็ถือเป็น noise ใน repository log อย่างหนึ่ง

เมื่อคิดสะระตะแล้ว solution ที่ผมเลือกใช้ก็คือ Git เนื่องจากมันตอบโจทย์เหล่านี้ได้ดี
  • distributed development ทำให้ผมเป็นอิสระจาก sourceforge.net, ไม่กิน bandwidth และไม่เสียเวลารอ
  • large file sets, ถึงจะไม่เท่ากับ linux kernel แต่ด้วยจำนวน 8000 กว่า file ก็ถือว่าไม่เบาเหมือนกัน
  • trial branches, git มีจุดเด่นด้านนี้เป็นพิเศษ
  • ผมสามารถยุบ patch หรือกรอง patch ได้ ทำให้ผมสามารถตัด patch ที่ไม่สำคัญทิ้งได้ หรือรวมทั้งยุบหลายๆ commit เป็น commit เดียวได้ (ลด noise)

แนวทางที่วางแผนไว้ก็คือ



  • มี working copy เพียงอันเดียวที่ไว้คอย sync กับ sourceforge subversion repository
  • developer ที่เหลือจะ clone ออกจาก master code ชุดนี้
  • การแก้ไข code ใดๆ จะต้องทำผ่าน branch ทั้งหมด
  • เมื่อน้องแก้ไข branch ใดๆจนพอใจแล้ว ก็จะแจ้งมาที่ผม ให้ทำการ pull มาที่ working copy หลัก เพื่อที่จะทดสอบ
  • เมื่อผลการทดสอบเป็นที่น่าพอใจ ก็จะทำการ merge เข้า master และ commit ขึ้น subversion

สถานะปัจจุบัน ตอนนี้ก็ทดลองทำอยู่ 2 branch และกำลังสนุกกับการลองผิดลองถูกกับ workflow และขั้นตอนการ merge อยู่

เท่าที่สัมผัส Git มาระยะหนึ่ง ในตอนแรกเนื่องเราเห็นภาพเพียงบางส่วน ก็จะรู้สึกว่ามัน fragile มาก
การเรียกใช้คำสั่งแต่ละที ดูเหมือนว่า โอกาสจะเกิด error มีสูงมาก
(สมัยทดลองที่ codefest นั้นสุดๆ เกิด error ในอัตรา 80 %)
หลังจากเริ่มใช้มันมากขึ้น มากขึ้น ก็เริ่มสัมผัสถึงความงามบางอย่างของมันแล้ว
ถึงตอนนี้ก็เลยรู้สึกติดและสนุกกับการใช้งาน Git เสียแล้ว

Related link from Roti

Wednesday, March 19, 2008

Recursive streams (Haskell)

หลายคนคงเคยเห็นตัวอย่าง function ที่ทำหน้าที่หาค่า Fibonacci แบบ recursive ธรรมดามาแล้ว

fib 0 = 1
fib 1 = 1
fib n = fib(n-1) + fib(n-2)


ใน haskell มันมีเทคนิคตัวหนึ่งที่เรียกว่า recursvie stream
ซึ่งเราสามารถนำมา apply หาค่า Fibonacci ได้

fibs = 1:1:zipWith (+) fibs (tail fibs)

หลักการก็คือ ถ้าเราสังเกตุค่า Fib ดู

1 1 2 3 5 8 13 21 ... ค่า fib sequence
1 2 3 5 8 13 21 34 ... tail fib (function tail จะทำการตัดตัวหน้าสุดทิ้ง)

ทั้งสองบรรทัดข้างบนบวกกันได้
2 3 5 8 13 21 34 55
ซึ่งก็คือ tail of tail of fib sequence

เมื่อนำหลักการข้างบนมาเขียน function fibs
fibs ก็เลยเท่ากับ 1 ตาม ด้วย 1 และตามด้วย ค่า fib บวกด้วย tail ของ fib

แต่ Diagram ที่ช่วยให้ผมเห็นภาพได้ดียิ่งขึ้น ก็คือ diagram นี้



เป็น diagram ที่อยู่ในหนังสือ The Haskell School of Expression
ของ Paul Hudak

Related link from Roti

Tuesday, March 18, 2008

Logger ใน ActiveRecord

จากโจทย์ที่ apirak ถามที่ codenone, ผมจะลองเล่าให้ฟังว่า ถ้าผมไม่ลักไก่ค้นด้วย google แล้ว
ผมจะมีวิธีไล่หามันใน rails code ได้อย่างไร

เรามาลองไล่ code ของ ActiveRecord กัน
เริ่มจากบรรทัดแรก

ActiveRecord::Base.establish_connection(
:adapter => 'mysql',
:host => 'localhost',
:username => 'root',
:password => '',
:database => 'test'
)

จะเห็นว่ามันเรียก class method จาก class ActiveRecord::Base
แต่ถ้าเราไปลองเปิด file "base.rb" ไล่หา method นี้ดู ก็จะหาไม่เจอ
ที่เป็นเช่นนี้ ก็เพราะเจ้า ruby มัน open class ได้ คนที่เขียน rails ก็เลยอาศัยคุณสมบัตินี้ มา implement
code ในลักษณะ Partition class ออกเป็นส่วนๆ ตามความรับผิดชอบ
ดังนั้น เจ้าเนื้อหาใน ActiveRecord::Base ก็เลยกระจายอยู่ในหลายๆ file

ลอง grep หาดู

$ grep -Rl establish_connection *
abstract/connection_specification.rb

ลองเปิด connection_specification.rb มาดู ก็จะพบ method หน้าตาแบบนี้
    def self.establish_connection(spec = nil)
case spec
when nil
raise AdapterNotSpecified unless defined? RAILS_ENV
establish_connection(RAILS_ENV)
when ConnectionSpecification
clear_active_connection_name
@active_connection_name = name
@@defined_connections[name] = spec
when Symbol, String
if configuration = configurations[spec.to_s]
else
spec = spec.symbolize_keys
unless spec.key?(:adapter) then raise AdapterNotSpecified, "database configuration does not specify adapter" end
adapter_method = "#{spec[:adapter]}_connection"
unless respond_to?(adapter_method) then raise AdapterNotFound, "database configuration specifies nonexistent #{spec[:adapter]} adapter" end
remove_connection
establish_connection(ConnectionSpecification.new(spec, adapter_method))
end
end

เห็น switch เยอะๆแล้วตาลายนิดหน่อย case ของเราจะตกลงตรงช่อง else
  # เริ่มด้วยการแปลง key ของ Hash table เราให้เป็น symbol ก่อน
spec = spec.symbolize_keys
# parameter ที่บังคับใส่ก็คือ :adapter
unless spec.key?(:adapter) then raise AdapterNotSpecified, "database configuration does not specify adapter" end
# หา adapter_method ที่รับผิดชอบ connection ของ database นี้
# กรณี mysql ก็จะเป็น mysql_connection
adapter_method = "#{spec[:adapter]}_connection"
# check ว่ามี class method นี้อยู่จริงหรือไม่
unless respond_to?(adapter_method) then raise AdapterNotFound, "database configuration specifies nonexistent #{spec[:adapter]} adapter" end
remove_connection
# ตรงนี้แหล่ะที่ทำผมแปลกใจ มัน recursive call ตัวเองอีกครั้งด้วย parameter ที่ห่อไว้ใน ConnectionSpecification object
establish_connection(ConnectionSpecification.new(spec, adapter_method))

แกะแล้วมี surprise เล็กน้อย เนื่องจากมัน recursive call ตัวเองด้วย parameter ที่ถูกแปลงไปแล้ว
(เจ้า ruby มันเป็น dynamic type ดังนั้น code ในลักษณะ Polymorphism ก็เลยต้องเขียนออกมาอย่างนี้)

ในการ recursive ครั้งที่สอง มันก็จะมาตก code ส่วนนี้

clear_active_connection_name
@active_connection_name = name
# spec ก็คือ ConnectionSpecification object
@@defined_connections[name] = spec

จะเห็นว่าหลังจากจบคำสั่ง establish_connection, เจ้า activerecord มันไม่ได้รีบร้อนจะเปิด connection ต่อ database ให้เรา
แต่อย่างไร, มันจะรอให้เราต้องการ access ข้อมูลจริงๆก่อน จึงจะเริ่มต้นเปิด connection ให้เรา

ตัว key ที่สำคัญใน code ข้างบน ก็คือ adapter_method ที่ชื่อ mysql_connection
ลอง grep หาดู ก็จะพบว่ามัน define ไว้ใน file "mysql_adapter.rb"
ลองเปิดดู
module ActiveRecord
class Base

...
...

def self.mysql_connection(config) # :nodoc:
config = config.symbolize_keys
host = config[:host]
port = config[:port]
socket = config[:socket]
username = config[:username] ? config[:username].to_s : 'root'
password = config[:password].to_s

if config.has_key?(:database)
database = config[:database]
else
raise ArgumentError, "No database specified. Missing argument: database."
end

require_mysql
mysql = Mysql.init
mysql.ssl_set(config[:sslkey], config[:sslcert], config[:sslca], config[:sslcapath], config[:sslcipher]) if config[:sslkey]

ConnectionAdapters::MysqlAdapter.new(mysql, logger, [host, username, password, database, port, socket], config)
end

...
end
end

จะเห็นว่ามี keyword ตัวหนึ่งที่ชื่อ logger
แล้ว เจ้า logger หล่ะมาจากไหน
เนื่องจากเจ้า method mysql_connection มันอยู่ใน scope ของ class ActiveRecord::Base
ก็ลองไปเปิด file "base.rb" ดู และหาคำว่า logger ก็จะพบบรรทัดนี้
  class Base    
# Accepts a logger conforming to the interface of Log4r or
# the default Ruby 1.8+ Logger class, which is then passed
# on to any new database connections made and which can be
# retrieved on both a class and instance level by calling +logger+.
cattr_accessor :logger, :instance_writer => false

Bingo!!
ถ้าเราต้องการ Logger ก็เพียงแต่ supply มันให้ เจ้า ActiveRecord::Base แบบนี้
require 'logger'
ActiveRecord::Base.logger = Logger.new(STDOUT)

Related link from Roti

Monday, March 17, 2008

ทัวร์ Dinosaur

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



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

จากการสืบค้นข้อมูลเชิงลึก ก็พบว่า
สาเหตุการปิดก็คือ
1. ทางกรมทรัพยากรธรณี มีโครงการจะเชิญให้พระเทพฯเสด็จในช่วงเดือนเมษายน ก็เลยต้องซ่อมแซมปรับปรุง
2. พิพิธภัณฑ์มีความขัดแย้งกับชาวบ้านในพื้นที่ ถึงขั้นชาวบ้านประท้วงปิดถนนไม่ยอมให้รถนักท่องเที่ยววิ่งเข้าไปจอดในพิพิธภัณฑ์ได้
ผลลัพท์ก็คือ "เมื่อเอ็งปิดถนน ข้าก็ปิดพิพิธภัณฑ์" โดยใช้เหตุผลในข้อ 1 มาอ้าง แถมไม่มีกำหนดด้วยว่าจะเปิดเมื่อไร

เมื่อได้ทราบข้อมูลเชิงลึกในข้อ 2 บวกกับรู้ว่า ทางวังยังไม่ได้ตอบรับเรื่องการเสด็จแต่อย่างไร
ผมก็เลยตัดสินใจใช้ "เส้นสาย" (ตัวอย่างที่ไม่ดี ขอนำมาสารภาพผิดในที่สาธารณะ)
ขอให้ลูกชายเข้าไปดูเฉพาะห้องโถง ซึ่งเป็นห้องที่มีโครงกระดูก dinosaur ตั้งอยู่เต็มไปหมด
ซึ่งก็ happy ending ดี เพราะเจ้าลูกชายสามารถระบุชื่อของ dinosaur หลักๆได้ทั้งหมด
ทำให้คนที่พาเข้าชม เขารู้สึกคุ้มค่าที่เสียเวลามา support เจ้าตัวเล็ก


จบจากพิพิธภัณฑ์ เราก็พากันไปเดินป่าเพื่อดูรอยเท้าไดโนเสาร์กัน
ซึ่งก็โชคดีอีกที่มีไฟไหม้ป่าก่อนหน้าที่เราจะไปกันพอดี
ทำให้ทางเดินชัดเจนมา เพราะตรงไหนที่เป็นทางเดินก็จะเป็นสีขาว
ตรงไหนไม่ใช่ทางเดิน ก็จะเป็นขี้เถ้าสีดำ
เริ่องเดินป่านี่ เจ้าลูกชายคนโตผมเขาชอบอยู่แล้ว
ผมเคยพาเขาไปเดินป่าที่อุทยานแห่งชาติเขาแหลม ตอนนั้นพาไปกับลูกพี่ลูกน้องเขา
ซึ่งอยู่ ป.5 กับ ป.6 ผลก็คือ เจ้าพวก ป.5 กับ ป.6 นี่ถอดใจอ้อนให้พากลับตั้งแต่กิโลเมตรที่ 2 (เส้นนั้นไปกลับ 6 กิโล)
ส่วนเจ้าลูกชายผม (ชั้นเตรียมอนุบาล) มันยืนยันอย่างเดียวว่าจะไปต่อ แถมยังเป็นคนเดินนำอีกด้วย

เสร็จจากดูรอยเท้าไดโนเสาร์ ก็มุ่งหน้าต่อไปยังขอนแก่น
เพื่อจะแวะเที่ยวชมไดโนเสาร์ที่ภูเวียง


สรุป trip นี้ 5 วัน นอน โคราช,กาฟสินธุ์,ขอนแก่น,ชัยภูมิ
เหนื่อยสุดๆ พาลูกเล็ก 2 คนเที่ยว

Related link from Roti