Fixing 403 From TRUE

Posted by PunNeng, Sun Nov 11 16:04:00 UTC 2007

คุณ sirn หาสาเหตุของ HTTP Response 403 ตอนที่สั่ง gem install ว่าเป็นเพราะ TRUE(ห่วย)
ผมก็เลยไล่ code ของ rubygems(0.9.4) ดู จนไปเจอ remote_fetcher.rb ที่ทำหน้าที่ติดต่อกับ http://gems.rubyforge.org/yaml

code ในส่วนที่ request มีหน้าตาแบบนี้(บรรทัดที่ 72)

resp = http.head(u.request_uri)

ถูกต้องตามที่คุณ sirn บอกไว้ทุกประการ ผมก็เลยเพิ่ม {'User-Agent' => "Ruby"}

resp = http.head(u.request_uri, {'User-Agent' => "Ruby"})

ก็ทำงานได้ปกติดี ไม่มีปัญหา

ปล. จะเปลี่ยน User-Agent เป็น fxxking TURE ก็ทำงานได้นะครับ

3 comments | Filed Under: Ruby | Tags: errors ruby rubygems true

AOP :: Asoby

Posted by PunNeng, Sat Jun 02 01:15:00 UTC 2007

มาลอง tool ที่ชื่อว่า Asoby หรือ Aspect-Oriented Programming in Ruby กันบ้าง

ผมว่า code มันดูสวยดีนะ การทำ pointcut ก็มีเงื่อนไขเพิ่มขึ้น เพิ่มความสามารถเพิ่มขึ้นจาก AspectR อยู่เยอะเลย
วิธีเล่น(บน Ubuntu) นะครับ

$ wget ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.5.tar.gz
$ tar zxvf ruby-1.8.5.tar.gz
$ wget http://asoby.redirectme.net/asoby/patch/asoby-0.01-2007.01.23.patch
$ wget http://asoby.redirectme.net/asoby/patch/asoby-0.01-2007.01.23.patch.md5sum
$ md5sum -c asoby-0.01-2007.01.23.patch.md5sum
$ patch -p0 < asoby-0.01-2007.01.23.patch
$ cd ruby-1.8.5
$ ./configure && make
$ gedit logging_test.rb

เอา code มาแปะหน่อย

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
require "aspect"

class Logging < Aspect
  def self.enable!
    super
    @logger = Logger.new
  end

  def self.tick; "#{Time.now.strftime('%Y-%m-%d %X')}"; end   

  pointcut(:log) { call(Product.setPrice($p)) }
  before(:log) { @logger.writeLog("#{context.self_obj.price} is changed to #{$p}") }
  after(:log) { @logger.writeLog("#{tick} #{context.class_name}##{context.method_name}: returned #{context.retval} and exited") }
end

class Product
  attr_reader :price
  def initialize
    @price = 0.0
    # logger = Logger.new
  end

  def setPrice(p)
    # @logger.writeLog("#{price} is changed to #{p}")
    @price = p
  end
end

class Logger  
  def initialize  
    # open log file  
  end  

  def writeLog(msg)  
    # write msg to log file  
    puts msg  
  end  
end  


Logging.enable!
product = Product.new
product.setPrice(13)  
product.setPrice(12)

สั่ง save แล้วรัน

$ ./ruby --weave logging_test.rb

มาดูผล

ข้อมูลจาก link ข้างบนนั่นแหละ

0 comments | Filed Under: Ruby | Tags: aop asoby howto ruby

Ruby :: Converting between Strings and Symbols

Posted by PunNeng, Tue May 22 03:10:00 UTC 2007

เล่นกับ symbol หน่อย อยากจะใช้ symbol เป็นตัวระบุอะไรบางอย่าง ใน ruby จะทำไง มาเล่นกันหน่อย

ทำได้ไม่ยาก เพียงแต่ใช้ Symbol#to_s หรือ Symbol#id2name ก็จะแปลง symbol ไปเป็น String หรือแปลงกลับ โดยการใช้ String#to_sym หรือ String.intern ตัวอย่าง

  1
  2
  3
  4
:symbol.to_s             # => "symbol"
:symbol.id2name          # => "symbol"
"symbol".to_sym          # => :symbol
"symbol".intern          # => :symbol

ข้อดีของ symbol อยู่ตรงที่มันไม่เปลืองหน่วยความจำและประหยัดเวลา ลองนี่ดู

  1
  2
  3
  4
"symbol".object_id       # => 23211108
"symbol".object_id       # => 23207142
:symbol.object_id        # => 3041550
:symbol.object_id        # => 3041550

ใช้ object_id ในการเรียกค่าอ้างอิง เราจะได้ค่าอ้างอิงจาก string สองค่า ที่ไม่ซ้ำกัน นั่นหมายความว่า มันย้ายหน่วยความจำไปอีกที่นึง และเราจะได้ค่าอ้างอิงจาก symbol เพียงค่าเดียว ประหยัดทั้งหน่วยความจำ และเวลา

แล้วเราจะใช้ string ทำอะไรล่ะ ==' เมื่อมี symbol แล้ว แน่นอน ว่า string ทำอะไรได้เยอะกว่า symbol แน่นอน แล้วจะดูยังไงว่าเมื่อไหร่ จะใช้ symbol หรือ string ?? ง่ายๆ เลย

  • ถ้าข้อมูลสำคัญ ใช้ string
  • ถ้าต้องการระบุอะไรบางอย่าง ก็ใช้ symbol

ทีนี้ flag ต่างๆ นานา ที่เราแค่ทำบันทึกค่าไว้ อาจจะเอามาแค่เปรียบเทียบกัน ก็ใช้ symbol เอาดีกว่า

  1
  2
  3
flag1 = :complete
flag2 = :complete
puts "done" if flag1 == flag2      # => done

เย่ๆ

ปล. ชาว Flash เตรียมตัวเมามันกับ ActionScript 3.0 ได้แล้ว :)

0 comments | Filed Under: Ruby | Tags: howto ruby string

AOP :: Ruby on AspectR

Posted by PunNeng, Sun Apr 15 20:16:00 UTC 2007

มาลอง implement ต่อจากคราวที่แล้วกัน โดยผมทดลองบน Ubuntu

เริ่มต้นด้วย download AspectR มาติดตั้งกันก่อน

wget http://rubyforge.org/frs/download.php/18623/aspectr-0-3-7.tar.gz
tar xvf aspectr-0-3-7.tar.gz
cd aspectr-0-3-7
sudo ruby install.rb

จากนั้นสร้าง file มาตัวนึงก่อน ผมจะตั้งชื่อว่า logging_test.rb แล้วก็เพิ่ม code ดังนี้

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
require 'aspectr' 
include AspectR 

class Product
  attr_reader :price
  def initialize
    @price = 0.0
    # logger = Logger.new
  end

  def setPrice(p)
    # @logger.writeLog("#{price} is changed to #{p}")
    @price = p
  end
end

class Logger
  def initialize
    # open log file
  end

  def writeLog(msg)
    # write msg to log file
    puts msg
  end
end

class Logging < Aspect
  def initialize
    @logger = Logger.new
  end
#  def tick; "#{Time.now.strftime('%Y-%m-%d %X')}"; end 

  def log_enter(method, object, exitstatus, *args) 
    @logger.writeLog("#{object.price} is changed to #{args.first}")
  end

#  def log_exit(method, object, exitstatus, *args)
#    @logger.writeLog("#{tick} #{self.class}##{method}: returned #{exitstatus} and exited") 
#  end

end

logging = Logging.new
logging.wrap(Product, :log_enter, nil, /set/)
# logging.wrap(Product, nil,:log_exit, /set/)
# logging.wrap(Product, :log_enter, :log_exit, /set/)  # advice = around
product = Product.new.setPrice(13)

หลังจาก require และ include มาแล้ว ก็ประกาศเหมือนเดิม เพียงแต่เอาส่วนของ logger ออก แล้วเพิ่มส่วนของ aspect(Advice) เข้าไป

  1
  2
  3
  4
  5
  6
  7
  8
  9
class Logging < Aspect
  def initialize
    @logger = Logger.new
  end

  def log_enter(method, object, exitstatus, *args) 
    @logger.writeLog("#{object.price} is changed to #{args.first}")
  end
end

parameters ที่บังคับมีอยู่สี่ตัว คือ

  • method ที่ถูกระบุเป็น join point
  • object ที่เรียกใช้ method ที่ถูกระบุเป็น join point
  • exitstatus เป็นค่าที่ method ที่ถูกระบุเป็น join point ทำการ return ออกมา
  • *args เป็น arguments ที่ส่งเข้ามาใน method ที่ถูกระบุเป็น join point

มาดูตอนเรียกใช้บ้าง(Weave)

  1
  2
logging = Logging.new
logging.wrap(Product, :log_enter, nil, /set/)

ประกาศ object ที่เป็น aspect ก่อน แล้วทำการสร้าง pointcut ด้วยการห่อ(wrap) join point, advice เข้าไป ต้องใส่ Class , before, after, regular expression ตามลำดับ โดยวิธีการเลือก join point มันจะทำการเลือกโดยใช้ regular expression เช่นในกรณีนี้ จะเรียกใช้ log_enter ก็ต่อเมื่อ join point เข้าเงื่อนไขของ reg exp ที่ใส่ลงไป ในี้ที่จะเป็น method ทุกตัว ที่มี 'set' อยู่ในชื่อของ method ต่างจาก AspectJ เพราะมันจะใช้อีกรูปแบบนึง เช่น

pointcut set() : execution(* set*(..) ) && this(Point);

ต้องเล่ารูปแบบของ AspectJ ก่อน บนนี้ มันจะแยก code ไว้สองส่วน คือ class ปกติ กับ aspect แต่ตอน compile มันจะใช้การผสาน(Weave) รวมก้อน object และ และก้อน aspect เข้าด้วยกัน เวลาเรียกใช้งาน ก็เรียกใช้งานตามปกติ ไม่ต้องมาทำการประกาศแบบ AspectR เพราะมันถูกประกาศไปแล้วในก้อน aspect

มาดูผลลัพธ์

ในส่วนของ advice ที่เป็น after อาจจะทำการเพิ่ม(ผม comment ไว้)ได้ดังนี้

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
class Logging < Aspect
  def initialize
    @logger = Logger.new
  end
  def tick; "#{Time.now.strftime('%Y-%m-%d %X')}"; end 

  def log_enter(method, object, exitstatus, *args) 
    @logger.writeLog("#{object.price} is changed to #{args.first}")
  end

  def log_exit(method, object, exitstatus, *args)
    @logger.writeLog("#{tick} #{self.class}##{method}: returned #{exitstatus} and exited") 
  end

end

โดยในนี้จะมี Inner-Type Declaration อยู่ด้วย คือ tick method ตอน wrap เราอาจจะใส่ดังนี้

  1
  2
  3
# logging.wrap(Product, nil,:log_exit, /set/)
# or
logging.wrap(Product, :log_enter, :log_exit, /set/)  # advice = around

มาดูผลลัพธ์

ปล. เห็นโครงของ advice บน Ruby on Rails คร่าวๆ แฮะ คือ before_filter, after_filter และ around_filter

0 comments | Filed Under: Ruby | Tags: aop aspect ruby

AOP :: Aspect-Oriented Programming

Posted by PunNeng, Thu Apr 12 03:30:00 UTC 2007

Aspect-Oriented Programming หรือ การโปรแกรมเชิงลักษณะ(พี่ใหม่เรียกว่างั้น) อธิบายยากจริงวุ้ย เอากรณีตัวอย่างเลยละกัน สุดฮิตเรื่อง logging

เริ่มต้นจาก อยากจะจัดการกับ product ของตัวเอง โดยมีความต้องการให้มันทำหน้าที่(concern) ดังนี้

  1. เก็บราคาขายของ product ต่างๆ
  2. เมื่อมีการเปลี่ยนแปลงราคา จะต้องถูกบันทึกข้อมูล

ก็สร้าง product กันก่อน

  1
  2
  3
  4
  5
  6
  7
  8
  9
class Product
  attr_reader :price  
  def initialize
    price = 0.0
  end
  def setPrice(p)
    price = p
  end
end

concern ข้อที่หนึ่ง ก็จัดการไปได้ละ (ในความเป็นจริงจะมี method ที่ีจำเป็นมากกว่านี้) มาดูข้อสองบ้าง จะต้องบันทึกข้อมูล เอาเป็นเขียนใส่ log file ละกัน(สุดฮิต) เวลามันเปลี่ยนแปลง ก็ให้มันบันทึกทีนึง

  1
  2
  3
  4
  5
  6
  7
  8
class Logger
  def initialize
    # open log file
  end
  def writeLog(msg)
    # write msg to log file
  end
end

ทีนี้ก็เรียก writeLog ใน Product class

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
class Product
  attr_reader :price
  def initialize
    @price = 0.0  
    @logger = Logger.new
  end
  def setPrice(p)
    @logger.writeLog("#{price} is changed to #{p}")
    @price = p
  end
end

มาถึงตรงนี้ มันก็ยังไม่มีปัญหาหรอกครับ แต่มันดูผิดแนวคิดจาก Encapsulation ไปหน่อย จาก Managing Software Defects in an Object-Oriented Environment ตรงส่วนของ encapsulation เขาบอกประมาณว่า

ใน encapsulation นั้น ก้อน object จะถูกประกาศเป็นชุดหรือกลุ่มของ concern ที่ีมีความสัมพันธ์กัน

หรืออีกนัยหนึ่งคือ Object น้ันๆ ก็ควรจะเกี่ยวกับ Object น้ันๆ(เป็น modular) อย่าเอาอย่างอื่นมาเกี่ยว(เป็นไปได้ยาก) แต่ดู concern ตัวที่สองแล้ว มันไม่ค่อยจะสัมพันธ์กันเท่าไหร่ class นี้ จึงเกิดการ crosscut ขึ้น ซึ่งเกิดจากไอ้ concern ตัวที่สองนี่แหละ ไอ้นี่แหละ เรียกว่า crosscutting concern

crosscutting concern หมายความว่า ลักษณะหนึ่งๆ ที่ไปขวาง ไปกระทบ ไปกวนประสาท concern หลัก ซึ่งในที่นี้ ก็น่าจะเกี่ยวกับ product เท่านั้น

เมื่อมี concern เพิ่มมาเรื่อยๆ ทั้งที่สัมพันธ์และไม่สัมพันธ์กับ concern หลัก จะทำให้ code เรามันกระจัดกระจาย(Code Scattering) และยุ่งเหยิง(Tangled code) เพิ่มนั่น ปะนี่ เอาไอ้นั่นเข้า เอาไอ้นี่ออก เพื่อทำให้ concern มันสมบูรณ์ คนออกแบบกับโปรแกรมเมอร์มันก็ตายสิครับ =='

โอ้ววว กิจการขายดิบขายดี จนต้องขายของอย่างอื่นอีก ก็ต้องขอขอบคุณ OO ละครับ ที่มันกลไก Inheritance ไว้ให้ใช้ ก็ทำการ extend Product class ไปเลย
ผมเล็งเห็นว่าการบันทึกข้อมูลต่างๆ มันสำคัญ เพราะฉะนั้น ใน product ชิ้นใหม่ ผมจะเพิ่ม concern ไปอีก คือ จะเพิ่มการบันทึกเพิ่มมากขึ้นในส่วนที่จำเป็น และจะมี method อย่างอื่นเสริมเข้ามาด่้วย แต่ไม่เกี่ยวกับ Product class ตัวแรกนะ
ปัญหามาแล้ว มองกันไกลๆ code มันจะไม่น้อยอย่างนี้ มันจะอิรุงตุงนัง ยุ่งเหยิงไปหมด เพียงแค่เติมเต็มเจ้า concern ที่เพิ่มเติมเข้ามาเท่าน้ัน ตัว AOP นี้แล จะมาช่วยลดความยุ่งเหยิงพวกนี้ออกไป

จัดการมันใหม่(refactor) ดีไหม ??

มาดูว่า AOP จะช่วยแก้ไขปัญหานี้ได้ยังไง

ถ้าใช้ AOP แล้ว เราจะต้องแยกไอ้ concern ต่างๆ นานา ออกมาจัดการได้ ตามความเหมาะสมก่อน แล้วค่อยเตรียมกลไกการจัดการ concern ที่มันเกิดการ crosscut

AOP ต้องใช้ tools ช่วย โดยมีสองส่วนหลักๆ คือ

  • ภาษาที่ใช้เขียน code ปกติ เรียกว่า component language
  • ภาษาที่ใช้เขียน code ฝั่ง concern เพื่อเอาไปต่อกับ component language จะเรียกว่า aspect language
ซึ่งผมเลือก Ruby และ AspectR เป็นตัวช่วย

หลังจากได้ tools มาแล้ว จะทำอะไรกับมันได้บ้าง1 เราต้องมี

  • Join point คือ จุดเชื่อม เป็นจุดที่เราจะระบุว่าจะให้ aspect ของเราทำงานที่ไหนบน source code ซึ่งเราจะระบุได้ ณ จุดที่เห็นกันจะๆ เช่น class, method หรือ exception เป็นต้น
  • Pointcut คือ จุดตัด เป็นที่ที่เราจะระบุ join point ว่าจะให้ join point อันไหน ทำงานอะไร(จัดการกับ concern อะไร) ก็ตรงนี้แล
  • Advice คือ การลำดับเหตุการณ์ เป็นตัวบอกว่าจะให้ทำก่อน(before) หรือหลัง(after)การเรียก pointcut ซึ่งใน AspectJ มีหลายรูปแบบ เช่น before, after หรือ around เป็นต้น
  • Inner-Type Declaration คือ การประกาศในก้อน aspect ของเรา อาจจะเป็น method หรือตัวแปร ที่ไว้ใช้ในการทำงานที่เราระบุไว้กับ pointcut

คราวหน้าผมจะมา implement ให้ดู

สรุปเล็กน้อย
AOP เป็น concept ของการเขียนโปรแกรมอีกอย่างหนึ่ง ถูกคิดค้นโดย Gregor Kiczales และทีมของเขาที่ Xerox PARC มีจุดประสงค์เพื่อแยก concern ออกมา โดยเฉพาะสิ่งที่เรียกว่า crosscutting concern ให้ตัว class ที่เราใช้งานมันมีลักษณะเป็น module มากขึ้น

AOP จะไม่มาแทนที่ OOP แต่มันเป็นส่วนเสริมที่ทำให้ OOP มีประสิทธิภาพมากยิ่งขึ้น

1ข้ออ้างอิงมาจาก AspectJ ก่อนละกัน เพราะที่ผมใช้ จะใช้ AspectR มันค่อนข้างจะเหมือนกัน

ข้อมูลจาก
http://en.wikipedia.org/wiki/Cross-cutting_concern http://en.wikipedia.org/wiki/Aspect-oriented_programming http://en.wikipedia.org/wiki/Cross-cutting_concern http://en.wikipedia.org/wiki/Aspectj ฯลฯ มันเยอะครับ 4 เว็บหลัก แล้วตาม link ใน page ไปเรื่อยๆ ละกันนะครับ

1 comment | Filed Under: Ruby | Tags: aop aspect ruby

codegent: we're hiring