Wednesday, March 07, 2007

Erlang Book

3-4 วันที่ผ่านมานี้, mailing list ของ ruby กับ erlang
มีการ post announce ว่า Pragmatic Bookshelf จะออกหนังสือเล่มใหม่
เรื่อง Programming Erlang. สร้างความตื่นเต้น (วัดจาก feedback) ให้กับ mailing list ทั้งสองได้ไม่น้อย

สิ่งที่น่าสนใจก็คือ
ในกรณีนี้หัวข้อหนังสือคือ erlang
ดังนั้นไม่แปลกที่ mailing-list ของ erlang ก็ควรจะคึกครื้นเป็นธรรมดา
แล้ว่ทำไม่ mailing-list ของ ruby ถึงคึกไปด้วย
เหตุผลก็น่าจะประมาณนี้
1. Pragmatic Bookshelf หนังสือส่วนใหญ่ focus พวก ruby, rails, agile
มีกลุ่มลูกค้าชัดเจน มี brand royalty ค่อนข้างสูง
แฟนๆ ruby ก็เลยชอบไปหมด ไม่ว่าจะออกหนังสืออะไรมา
2. คนชอบ Ruby เป็นพวก "hyper-enthusiasts"
มีอะไรใหม่ๆ มันส์ๆ ก็ชอบ
สังเกตจากใน mailing list ก็มีหลายคนเขียนตอบไปว่า
มี erlang แล้ว อย่าลืมออกหนังสือ haskell ด้วยหล่ะ
ซึ่ง Pragmatic ก็ตอบไปว่า "อยากออก แต่ขั้นแรก ต้องหาคนเขียนที่เหมาะสมให้ได้ก่อน"

Related link from Roti

Tuesday, March 06, 2007

module_function [Ruby]

ziddik ถามเรื่องพฤติกรรมแปลกๆของ module_function
สำหรับคนที่ไม่รู้จัก module_function ลองมาดูว่ามันคืออะไรก่อน

สมมติเรา define Library math ไว้แบบนี้
module Math
def sin(x)
...
end
end


ปัญหาก็คือ คนใช้ต้อง include Math ก่อน
จึงจะใช้ได้
include Math
puts sin(10)

ซึ่งดูไม่สะดวกนัก
เช่นอาจจะเกิด name conflict ได้ง่าย

module function เข้ามาช่วยในจุดนี้โดย
module Math
def sin(x)
...
end

module_function :sin
end

จะทำให้เราสามารถเรียกใช้แบบนี้ได้
Math.sin(10)
หรือ
include Math
sin(10)


ขั้นตอนภายในจะแปลกๆหน่อยตรง
1. มันจะ copy function sin เป็น instance method อันใหม่
มี code แยกออกไปจากกัน (ไม่ share กัน)
2. มันจะ mark method ให้กลายเป็น private

ส่วนคำถามว่าทำไม มันถึง mark ให้เป็น private
มีคนพยายามเดาใจ Matz เหมือนกัน
แต่ผมอ่านแล้วก็ยังไม่เข้าใจ
Re: module_functions are private?

ส่วนตัวอย่างในคำถามของ ziddik
ถ้าอยากให้ใช้ได้ ต้องเขียนเป็นแบบนี้
module HelloModule
Version = "1.0"

def hello(name)
puts("Hello, #{name}. ")
end

module_function :hello
end


class Hello
include HelloModule
def myHello(name)
hello(name)
end
end

หรือจะ ใส่ public :hello ใน class Hello ก็ได้
แต่ตามที่ใน mailing list บอก
การทำเช่นนี้อาจจะเปิดโอกาสให้
shooting yourself in the foot.

Note: ผม search code ใน rails ดู
พบว่ามีการใช้ module_function อยู่นิดหน่อย
ใน package actionMailer กับ actionWebService
(ใช้อยู่นิดเดียว)

Related link from Roti

Ruby 's Require ภาค 2

ปกติถ้าเราจะใช้ class อะไร
เราก็ต้อง load class นั้นขึ้นมาใน context ของเราก่อน
เช่นเรามี file p.rb
class Pok
def hi
"hello"
end
end

ลอง สั่ง irb
$ irb
>> Pok.new
NameError: uninitialized constant Pok
from (irb):1
>> require 'pok'
=> true
>> Pok.new
=> #<Pok:0x11158a8>

นี่คือกลไกปกติใน ruby
แต่ถ้าใครเคยใช้ rails แล้ว จะเห็นว่า
มันไม่ได้เป็นไปตาม pattern ข้างบนนี้
สมมติเรามี file $RAILS_ROOT/app/model/activity.rb
class Activity < ActiveRecord::Base
end

ลองสั่ง script/console ดู

$ script/console
Loading development environment.
>> Activity.new
=> #<Activity:0x245efcc @new_record=true, @attributes={"name"=>nil}>

คำถามก็คือ rails ไป load activity.rb มา โดยเราไม่ต้องสั่ง require สักแอะได้อย่างไร

คำตอบอยู่ใน file ที่ชื่อ
/..verylong../active_support/dependencies.rb
ภายในมี method อยู่ตัวหนึ่งที่ชื่อ const_missing

สำหรับคนที่ไม่คุ้นกับ method นี้
method นี้เป็น method ใน class Module
หน้าที่ method นี้ก็คือ ในกรณีที่มีการเรียกใช้ Constant ที่ไม่มีอยู่จริง
>> class Module
>> def const_missing(name)
>> puts 'hi'
>> end
>> end
=> nil
>> Bunn
hi
=> nil
>> P
hi
=> nil
>>


ใน dependency.rb ก็มีการ override method นี้ดังนี้
class Module #:nodoc:
# Rename the original handler so we can chain it to the new one
alias :rails_original_const_missing :const_missing

# Use const_missing to autoload associations so we don't have to
# require_association when using single-table inheritance.
def const_missing(class_id)
file_name = class_id.to_s.demodulize.underscore
file_path = as_load_path.empty? ? file_name : "#{as_load_path}/#{file_name}"
begin
require_dependency(file_path)
...
long long code for error case

Magic ก็คือ require_dependency ที่ rails เรียกใช้
,require_dependency คือ function ที่ rails สร้างขึ้นมา
เพื่อใช้แทน require โดยมีข้อแตกต่างคือ
ในกรณีของ development environment
module, class ที่ load โดย require_dependency จะถูก
flush ออกไปหลังจากที่จบ request แล้ว
ทำให้ developer สามารถแก้ไข source code ได้อย่างสบายใจ
ไม่ต้อง restart web server ใหม่ทุกครั้งหลังจากแก้ไข code

ส่วนประโยค file_name = class_id.to_s.demodulize.underscore
ก็คือการ solve หาชื่อ file ที่จะ require
โดยมีหลักประมาณว่า
"Activity" -> 'activity'
"OrderItem" -> 'order_item'

Related link from Roti