I'm a Ruby on Rails / jQuery web developer. Follow me at @sikachu

Operator Precedence ใน Ruby

December 19th, 2009 Posted in Programming, Ruby | Comments

Operator Precedence หรือลำดับสำคัญของตัวดำเนินการนั้น ถือเป็นหลักสำคัญของโปรแกรมเมอร์ ที่จะทำให้สามารถทำการเขียนอัลกอริธึมเพื่อแก้ไขปัญหาต่างๆ ได้ ในทางคณิตศาสตร์นั้น มักมีคนสรุปมันออกมาง่ายๆ ว่า “วงเล็บมาก่อน คูณหารก่อนบวกลบ”

ถ้าใครเคยเห็น code ภาษา Ruby นั้น คงจะเคยเห็นว่า Ruby นั้นมีทั้ง operator && และ || เหมือนกับในภาษา C แล้วยังมี and กับ or ที่เพิ่มเข้ามาใน Ruby ซึ่งเป็นสิ่งที่ทำให้โค้ดนั้นดูสวยงามขึ้น อ่านง่ายขึ้นตามหลักของ Ruby

ลองดูโค้ดด้านล่างนี้เป็นตัวอย่างครับ

irb(main):001:0> dessert = "Cheesecake"
=> "Cheesecake"
irb(main):002:0> drink = "Coffee"
=> "Coffee"
irb(main):003:0> dessert == "Cheesecake" && drink == "Tea"
=> false
irb(main):004:0> dessert == "Cheesecake" and drink == "Tea"
=> false

จะเห็นได้ว่า && และ and นั้นให้ผลลัพธ์ที่เหมือนกัน ทำให้บางครั้งเราสามารถใช้ and มาแทนที่ && และ or มาแทนที่ || ได้

แต่ entry นี้มันชื่อ Operator Precedence … เพราะฉะนั้นเรื่องราวมันไม่ได้จบแค่นี้ครับ คำถามต่อไปคือ คุณคิดว่าค่าของตัวแปร first_order และ second_order จะเป็นอย่างไร ในคำสั่งต่อไปนี้:

first_order = dessert == "Cheesecake" && drink == "Tea"
second_order = dessert == "Cheesecake" and drink == "Tea"

คำตอบลองดูในนี้เลยครับ:

irb(main):005:0> first_order = dessert == "Cheesecake" && drink == "Tea"
=> false
irb(main):006:0> second_order = dessert == "Cheesecake" and drink == "Tea"
=> false
irb(main):007:0> first_order
=> false
irb(main):008:0> second_order
=> true

อะไรนี่ 0_o!

จะเห็นได้จากบรรทัดที่ 005 และ 006 ว่า ทั้งคู่ก็คืนค่าออกมาเป็น false ทั้งนั้น แล้วทำไมค่าที่เก็บใน first_order กับ second_order มันกลับไม่เหมือนกัน

คำตอบคือ เป็นเพราะ Operator Precedence ของ Ruby ครับ

เหตุผลที่ second_order นั้นมีค่าเป็น true นั้น เพราะว่าใน Ruby นั้น เครื่องหมาย = จะมีลำดับขั้นที่สูงกว่า and และ or ครับ

กล่าวคือ ในการเขียน statement ว่า

>> second_order = dessert == "Cheesecake" and drink == "Tea"

เจ้าตัว Ruby Interpreter นั้น จะทำการ evaluate ออกมาเป็นอย่างนี้

001> second_order = dessert == "Cheesecake" and drink == "Tea"
002> (second_order = dessert == "Cheesecake") and drink == "Tea"
003> true and drink == "Tea"
004> false

เพราะฉะนั้น คำสั่งข้างต้นที่ใช้ and นั้น ถ้าอยากให้มีผลเช่นเดียวกับ && จำเป็นที่จะต้องใส่วงเล็บครอบให้เป็นอย่างนี้ครับ

>> second_order = (dessert == "Cheesecake" and drink == "Tea")

มีคนชอบบอกว่า Ruby เป็นภาษาที่แปลก … ผมเชื่อแล้วแหละว่าแปลกจริงๆ ;)

เปลี่ยน Package มือถือ

October 1st, 2009 Posted in Apple, Gadgets, My Idea | Comments

หลังจากที่ทำการเปลี่ยนไปใช้ iPhone แล้วพบว่าตัวเองต้องเปิดๆ ปิดๆ Edge อยู่ตลอดเวลา ทำให้รู้สึกว่า “ทำไมมันยุ่งยากอย่างนี้ฟระ !!”

สุดท้ายแล้วก็เลยตัดสินใจ เปลี่ยน Package EDGE/GPRS เป็น Unlimited 999 บาทซะ (ของ DTAC ครับ) เพราะว่า DTAC มันไม่มีแบบที่เหมา Kb/Mb เยอะๆ แบบ AIS

เนื่องจาก cost ของ EDGE/GPRS จะพุ่ง ก็เลยต้องหา Package ที่ลดค่าโทรมา เลยทำการ Analyze การใช้โทรศัพท์ตัวเอง ปรากฎว่า ..

Screen shot 2552-09-25 at 21.50.07.png

(ค่าโทรข้างล่าง ต้องบวกไปอีก 299 นะครับ เป็นค่า Package)

อุแม่เจ้า เพิ่งเห็นว่าเราใช้โทรศัพท์แค่เดือนนึงสูงสุด 124 นาทีเอง … นี่ถือว่าน้อยมากนะเนี่ย (เพราะส่วนใหญ่ชอบส่ง SMS มากกว่า -*-)

สุดท้ายแล้ว ก็เลยทำการเปลี่ยน Package เป็นดังนี้

- โทรมันจิงดิ 200 นาที ในเครือข่าย/200 นาทีนอกเครือข่าย 250 บาท (เล็กที่สุด + คุ้มที่สุดแล้ว)
- SMS 110 ข้อความ 79 บาท (คุ้มๆ)
- EDGE/GPRS Unlimited Monthly 999 บาท

ตกๆ แล้ว เดือนนึงจะจ่ายประมาณ 1500 บาท … ไม่รู้แม่จะว่ามั้ย หุหุ (แม่ยังจ่ายค่ามือถือให้อยู่ครับ)

หวังว่าสิ้นเดือนหน้า DTAC จะมาพร้อมกับ Package GPRS ราคาประหยัดลงหน่อยนะครับ สัก 599 กำลังดี … เข้าใจว่าระบบเร็ว แรง แต่ขอราคาถูกๆ ด้วยได้ไหมครับ T_T

(ถึงไม่เอา iPhone เข้ามาขาย แต่เห็นใจแล้วลดราคาให้คนใช้ iPhone หน่อยเต๊อะ …)

Tweetie Update

October 1st, 2009 Posted in Apple, Garbage | Comments

วันนี้ตอนเปิดเครื่องมาตอนเช้า Tweetie มันขึ้นให้ Update เป็น version 1.2.4 ครับ .. แต่แล้วพอ Sparkle มันเปิดหน้าจอมา อดยิ้มไม่ได้เลย

จะฮาไปไหน สำหรับ Change Log อันนี้

Screen shot 2552-10-01 at 11.39.21.png

อารมณ์แบบ .. ก็มันพังนี่ฝร่า แต่แก้แล้วนะ! 555+

Snow Leopard และ Automatic Printer Driver Download

October 1st, 2009 Posted in Apple, My Life | Comments

ไม่คิดว่าการลง Driver จะง่ายขนาดนี้

มันเป็น feature ที่ควรจะมีตั้งนานแล้ว และควรจะมีทุก OS อยู่แล้ว สำหรับการที่ตัว OS วิ่งไปหา Driver สำหรับ Device ผ่านทางอินเตอร์เน็ต

ผมเห็นว่า Windows XP มีสักพักแล้ว … แต่รู้สึกว่าไม่เคยหา Driver อะไรเจอเลย จนกระทั่งมาเจอ Snow Leopard มันขึ้นอย่างนี้ !!

Screen shot 2552-09-18 at 22.30.17.png

อุ ..​แล้วจะช้าอยู่ใย? Install โลด !!

Screen shot 2552-09-18 at 22.30.21.png

มีขึ้น License Agreement ให้อ่าน … ยอมทำตามก็ได้ฟระ -*- Accept ไป

Screen shot 2552-09-18 at 22.30.26.png

อ่าวห้ย สรุปว่าหาเจอมั้ยเนี่ย ….

Screen shot 2552-09-18 at 22.31.04.png

และแล้วก็เรียบร้อย ~

สรุปว่าใครก็ได้ confirm ผมทีว่า Windows 7 ก็มีอะไรอย่างนี้ด้วย ถูกต้องช่ายมั้ยครับ? :D

ADSL2+ กับ Negative Upstream

October 1st, 2009 Posted in My Life, My Project | Comments

เมื่อนานมาแล้ว Internet ของที่บริษัท Infogination นั้น สมัคร Package ไว้ที่ความเร็ว 2048/512 เท่านั้น เพราะว่าคนน้อย และมีคนใช้อินเตอร์เน็ตน้อย ไม่ต้องการความเร็วมาก

แต่ปรากฎว่าหลังจากที่ @hunt และ @sikachu ย้ายเข้ามา … ความต้องการใช้อินเตอร์เน็ตมากขึ้น และตอนนั้น True เพิ่งออก Package ใหม่คือ 8192/512 … ก็เลยตัดสินใจเปลี่ยนมันซะ !!

คราวนี้ หลังจากเปลี่ยนเรียบร้อย ก็ปรากฎว่า อินเตอร์เน็ตติดๆ ดับๆ -*-

ด้วยความที่ hardware ตัวเก่า (modem ของ Billion) มันมีอายุนานมากแล้ว ก็เลยคิดว่ามันเป็นต้นเหตุของอินเตอร์เน็ตหลุด เลยจัดการซื้อ modem ใหม่มาเปลี่ยน มันก็ไม่หาย … เลยลองตรวจสอบสาเหตุของมัน ปรากฎว่า …

SNR Margin ของ Upstream ติดลบครับ !!

Screen shot 2552-09-04 at 11.33.43.png

ลองสอบถามช่างทรู เรียกมาตรวจสอบถึงที่ออฟฟิตแล้ว ก็ไม่ได้คำตอบเป็นที่พอใจ … เพราะว่าพนักงานไม่เข้าใจว่า Margin ติดลบมันหมายความว่ายังไง

มันหมายความว่า Noise มากกว่า Signal ไงครับ! เข้าใจมั้ยยยยยยยย ~!!

สุดท้าย ต้องยอมเปลี่ยนเป็น G.dmt เพื่อให้เน็ตเสถียรขึ้น (เพิ่งรู้ว่า G.dmt connect ที่ 12M ได้)

Screen shot 2552-09-04 at 11.27.44.png

แล้วก็ต้องใช้มันต่อไป … คาดว่าถ้าไม่ไหวอีกรอบ ก็คงโทรไปโวยอีก เห้อออ -*-

Optimize database query ด้วย :include

August 7th, 2009 Posted in My Project, Programming, Ruby, Ruby on Rails | Comments

ในการเขียนโปรแกรมบน Ruby on Rails นั้น เรามักที่จะใช้ ActiveRecord ในการทำหน้าที่เป็น ORM ระหว่างตัว Application กับ database ซื่งทำให้การเรียก Record นั้น สามารถทำได้อย่างง่ายดาย เช่น ถ้าผมจะเรียกดู post ทั้งหมดที่มีอยู่ในระบบ ผมแค่สั่ง

Post.find(:all)    # หรือว่า Post.all ก็ได้ ใน Rails 2.x

ซึ่งตรงนี้ ถ้าเราไปดูใน Log file จะพบว่า ActiveRecord นั้น จะใช้คำสั่งค้นหาข้อมูลประมาณนี้ครับ

  Post Load (0.1ms)   SELECT * FROM "posts"

(ผมใช่ sqlite3 เพราะฉะนั้น table name/field name จะถูกใส่ไว้ใน quote ครับ)

ถ้าสมมุติในโปรแกรมนั้น เราได้ทำ Association ระหว่าง Post และ Comment (Post has many comments) และระหว่าง Comment กับ User (comment belongs to user)

class Post < ActiveRecord::Base
  has_many :comments
 
  # ...
 
end
 
class Comment < ActiveRecord::Base
  belongs_to :post
  belongs_to :user
 
  # ...
 
end

ถ้าเราต้องการจะแสดงผล comment แต่ละอันด้วย เราก็สามารถทำได้โดยเรียกเมธอด #comments ที่ถูกสร้างขึ้นมาอัตโนมัติโดยการทำ association และเช่นเดียวกัน ถ้าเราต้องการแสดงด้วยว่า comment นั้นถูกเขียนโดยใคร เราก็สามารถเรียกเมธอด #user บน comment เช่นกัน

<% Post.find(:all).each do |post| %>
  <!-- display post -->
  <% post.comments.each do |comment| %>
    By: <%= comment.user.username %>
    <!-- display comments -->
  <% end %>
<% end %>

คราวนี้ สมมุติว่าบล็อกเรามีทั้งหมด 10 Post แล้วแต่ละอันมี 5 comment … SQL ที่ออกมานั้น จะเป็นประมาณนี้ครับ

  Post Load (0.1ms)   SELECT * FROM "posts"
  Comment Load (0.5ms)   SELECT * FROM "comments" WHERE ("comments".
"post_id" = '1')
  User Load (0.2ms)   SELECT * FROM "users" WHERE ("user"."id" = '5')
  User Load (0.2ms)   SELECT * FROM "users" WHERE ("user"."id" = '24')
  User Load (0.2ms)   SELECT * FROM "users" WHERE ("user"."id" = '30')
  User Load (0.2ms)   SELECT * FROM "users" WHERE ("user"."id" = '4')
  CACHE (0.0ms)   SELECT * FROM "users" WHERE ("user"."id" = '5')
  Comment Load (0.3ms)   SELECT * FROM "comments" WHERE ("comments".
"post_id" = '2')
  User Load (0.2ms)   SELECT * FROM "users" WHERE ("user"."id" = '38')
  User Load (0.2ms)   SELECT * FROM "users" WHERE ("user"."id" = '14')
  User Load (0.2ms)   SELECT * FROM "users" WHERE ("user"."id" = '40')
  User Load (0.2ms)   SELECT * FROM "users" WHERE ("user"."id" = '2')
  User Load (0.2ms)   SELECT * FROM "users" WHERE ("user"."id" = '9')
  ...

จะเห็นได้ว่า ในสถานการณ์ที่แย่ที่สุดนั้น (user ที่มา comment แต่ละ post นั้น ไม่ตรงกันเลย เป็นต้น) ActiveRecord จำเป็นต้องทำการ Query ทั้งหมด 1 + 10 + (10 * 5) = 61 ครั้ง ซึ่งไม่มีประสิทธิภาพเลยครับ เพราะเปลือง Query มากมาย

ดังนั้น เพื่อให้ Query ทั้งหมดนี่มีประสิทธิภาพมากขึ้น ActiveRecord จึงมี key หนึ่งชื่อว่า :include เอาไว้สำหรับสั่งว่าให้ ActiveRecord นั้นทำการโหลด Model ที่ associates กับ object นี้ขึ้นมาด้วยพร้อมๆ กันเลย เพื่อประหยัด Query ครับ เพราะฉะนั้นโค้ดในการค้นหาของเราจะเปลี่ยนเป็น

Post.find(:all, :include => {:comments => :user}).each do |post|
  # ... display post
  post.comments.each do |comment|
    By: <%= comment.user.username %>
    # ... display comments
  end
end

แล้วผลของมันน่ะหรอครับ? 61 query -> 3 queries ครับ!

  Post Load (0.1ms)   SELECT * FROM "posts"
  Comment Load (0.3ms)   SELECT * FROM "comments" WHERE ("comments".
"post_id" IN (1, 2, 3, 4, 5))
  User Load (1.3ms)   SELECT * FROM "users" WHERE ("user"."id" IN (5, 24,
30, 4, 2, 38, 14, 40, 2, 9, 23, 41, 48, 50, 32, 10, 48)

เพราะฉะนั้นการใช้ :include นั้น เป็นการ optimize query อย่างได้ผลทีเดียวละครับ โดยจะเห็นได้ว่าเรายังสามารถโหลด model แบบ nested ได้โดยการใช้ Hash และโหลดโมเดลหลายๆ อันพร้อมกันโดยใช้ Array ครับ อย่างเช่นถ้าเราต้องการโหลด Attachments จาก Comment และโหลด Tags จาก Post ด้วย เราก็สามารถใช้คำสั่งอย่างนี้ได้ครับ

Post.find(:all, :include => [{:comments => [:user, :attachments]}, 
:tags])

คำเตือน: Use it, but don’t abuse it!

ในบางครั้ง การใช้ :include นั้น อาจจะทำให้เวลาในการ query นั้นลดลงได้ถ้าเทียบกับการ query object เล็กๆ หลายๆ ครั้งแทน เพราะฉะนั้นมันไม่ใช่สิ่งที่เวิร์คที่สุดครับ ต้องปรับใช้ให้เข้ากับงานซะมากกว่า โดยที่ผมแนะนำให้ใช้ #find method ตามปกติก่อน แล้วจึงค่อยเพิ่ม :include เข้าไปถ้าเราเห็นว่ามีการ query record จำนวนมากๆครับ … ถือซะว่าการใช้ :include นั้นเป็นการ refactor code ครับ และไม่ใช่สิ่งที่ต้องมาคิดตั้งแต่แรกว่าตรงนี้ต้องใช้มันหรือไม่ ;)

truemove เปิดตัว iPhone 3GS แล้ว

August 4th, 2009 Posted in Garbage | Comments

หลังจากที่เมื่อวันก่อน ผมได้โพสไปเรื่องราคาของ iPhone 3GS ที่ว่าคุณปพนธ์ได้กล่าวเอาไว้ว่าราคาของ iPhone ใหม่นั้น ต้องแพงกว่าเครื่องเก่าแน่นอน

ปรากฎว่าหลังจากการเปิดตัวนั้น มันไม่จริงตามที่พูดครับ .. โดยราคาตอนนี้ดูได้ตามนี้เลย:

table.jpg

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

แต่ไปๆ มาๆ ไปเจอคนวิเคราะห์มาว่า การบอกว่า “เครื่องใหม่จะแพงกว่าเครื่องเก่า” ของคุณปพนธ์นั้น กลับอาจจะทำให้ทรูนั้นยังสามารถขายเครื่อง iPhone 3G ได้เรื่อยๆ เพราะคนคิดว่าเครื่องใหม่ยังไงก็ต้องมาในราคาสูงกว่าแน่นอน

ถือว่าเป็นกลยุทธทางการตลาด ที่ใช้ได้เลยนะครับเนี่ย … แต่อย่าทำบ่อยนะครับ เดี๋ยวคนจะไม่เชื่อสิ่งที่คุณพูดเอา …

เพราะฉะนั้น ถ้าตามราคานี้ … ผมว่าผมสนับสนุนให้คนซื้อเครื่องจาก truemove ครับ … แต่ไปใช้ค่ายอื่นนะครับ เพราะว่า edge ของ truemove นี่ ไม่ไหวจริงๆ …

true iPhone 3GS และราคาที่ต่างไป

August 2nd, 2009 Posted in Apple, Garbage | Comments

หลังจากที่พยายามติดตามข่าวสารเกี่ยวกับการนำเข้า true iPhone 3GS … ตอนนี้ก็เห็นแล้วครับว่าเว็บไซต์ของ truemove นั้นได้ทำการเปลี่ยนเป็นรูปของ iPhone 3GS เรียบร้อยแล้ว

Picture 5.png

ซึ่งตรงนี้ผมว่าเป็นข่าวดี (มั้ง?) กับการที่ true จะนำเครื่องศูนย์เข้ามาขายสักที และทำให้ผู้ที่อยากจะใช้ไม่จำเป็นต้องไปซื้อมาจากต่างประเทศ …

… จนกระทั่งผมเห็นข้อความในกระทู้นี้

[คุณ] ปพนธ์ [ผู้บริหารของ true] เคยให้สัมภาษณ์ว่า ทรูเริ่มเตรียมการเรื่องการจัดการสต็อกสินค้าเพื่อนำ iPhone รุ่นใหม่เข้าเมืองไทยแล้วตั้งแต่เดือนมิถุนายน โดยขณะที่ให้สัมภาษณ์ยังไม่ได้ข้อสรุปราคา iPhone รุ่นใหม่

“iPhone รุ่นใหม่ก็ต้องแพงกว่า (รุ่นเดิมที่ขายตอนนี้) แน่นอน” ปพนธ์กล่าว

ผมยังไม่เคยเห็นเลยครับว่า Apple นั้นมี trend ของการเพิ่มราคาสินค้าที่จะเข้ามาแทนรุ่นเก่า โดยที่ส่วนใหญ่เราจะเห็นกันๆ นั้นก็คือการที่ราคาของเครื่อง Macintosh ที่ราคาแทบจะไม่ลดไม่เพิ่มเลย แม้มีเครื่องรุ่นใหม่เข้ามาก็จะเข้า “มาแทน” เท่านั้น

ซึ่งตรงนี้ ก็เป็นสิ่งที่ Apple กำหนดไว้ตลอด

คราวนี้ ลองมามองทางด้านราคาของ iPhone ที่ทรูกล่าวอ้างว่าราคานั้นต้องขึ้นเพราะเป็นรุ่นใหม่บ้าง ปรากฎว่า .. มันไม่จริงเลยครับว่าเป็นรุ่นใหม่แล้วต้องขึ้นราคา ผมเลยขอยกตัวอย่างจาก Apple Store HK ครับ

เริ่มจากราคาของ iPhone 3G

iphone3g.jpg

ต่อด้วยราคาของ iPhone 3GS

iphone3gs.jpg

จะเห็นได้ว่า ราคานั้นแทบไม่ต่างกันเลย อยู่ในช่วงราคาเดียวกันด้วยซ้ำครับ (ซึ่ง 16GB ที่มาแทน 8GB นี่ จะเห็นได้ว่าราคานั้นถูกลงกว่าเดิมอีก) ซึ่งนั่นก็เป็นมาตรฐานราคาของ Apple ครับ ถือว่าราคาไม่เคลื่อนสักเท่าไร

ดังนั้นตามที่มีข่าวลือว่าราคา 29,000 – 34,000 นั้น … ลองคิดดูดีๆ ครับว่าถ้าทรูออกราคานั้นมาจริง ผมว่าคงจะสู้ไม่ได้กับราคาตู้ตาม MBK และราคาที่ HK แน่นอน … และแน่นอนว่าทรูต้องแย่แน่ๆ กับการทำยอดให้ถึงเป้า ซึ่งผมก็เห็นด้วยตามความเห็นของคุณ NBC ครับ

สัญญา ที่ ทรู ทำไว้กับ แอปเปิ้ล ตกลงว่าจะตั้งราคา เอากำไรจากราคาทุน
ได้ไม่เกิน 5 % ถ้า ทรู ตั้งราคาเวอร์ ก็จะผิดสัญญา และ สังคมก็จะลงโทษด้วยการซื้อเครื่องหิ้ว ซึ่ง คนไทย ก็ไป สิงคโปร์ และ ฮ่องกง กันโครม ๆ อยู่แล้ว พนักงาน ทรูก็จะนั่งตบยุงไปตามระเบียบ กรรมตามทันแน่นอน เร็วเสียด้วย

เหลือเวลาอีกไม่ถึง 24 ชม. หวังว่าทางทรูคงจะคิดได้นะครับว่าควรจะทำอย่างไรกับการตั้งราคา … ตั้งราคาไม่สูงมาก เพื่อที่เชียร์ให้คนซื้อเครื่องในประเทศ กับการตั้งราคาพรีเมียมเอากำไรเยอะๆ แต่ต้องเสียส่วนแบ่งไปให้กับประเทศอื่นๆ ที่ขาย iPhone แบบไม่ unlock … ถึงแม้จะมี truewifi แต่อย่างนี้ผมว่าคงช่วยไม่ได้จริงๆ ครับ

ปล. ผมเป็นคนหนึ่งที่เชียร์คนไทยให้ซื้อเครื่องจากทรูครับ เพื่อให้ประเทศของเราเป็นอีกหนึ่งเป้าหมายของ Apple ในการพัฒนาสิ่งต่างๆ ให้ตามทันประเทศอื่นๆ เสียที แต่ถ้าทรูออกราคามาเหมือนกับ “ขโมย” กัน …​ผมก็คงเชียร์ให้ไปซื้อที่อื่นกันครับ

Thin Rolling Restart Patch

August 2nd, 2009 Posted in My Project, Programming, Ruby, Ruby on Rails | Comments

I wrote a patch for Thin web server to do a rolling restart (i.e. restart the server one at a time) when I was working for my final thesis, Localmapia. I put my source on Github, and already inform the developer of Thin. However, seems like he forgot about it.

Anyway, I just updated my code to reflect the edge version of Thin. You can checkout my code from:

http://github.com/sikachu/thin

Also, if you want to install my version of Thin, just do this command to install from source:

git clone git://github.com/sikachu/thin.git
cd thin
rake install

So, to use this feature, you just add onebyone: true into your thin cluster file, or use the flag -O or --onebyone when you’re issue restart command.

I still wishing this would be merged into the main branch. :)

(If you don’t know about Thin, it’s a web server purely written in Ruby. It uses very small memory, very lightweight, yet very powerful. Usually people will use it with Nginx acting as reverse proxy. I wrote this patch because during normal restart Nginx would display 501 gateway error, which this patch solves the problem by having at least 1 old-version running at the time of restart.)

Object#try ใน Rails 2.3

August 2nd, 2009 Posted in My Project, Programming, Ruby, Ruby on Rails | Comments

เคยเจอปัญหาบ้างไหมครับ กับการที่บางครั้ง object ที่เราเรียก method ไปเนี่ย มันกลายเป็น nil ขึ้นมา ทำให้เกิด exception ขึ้นมา

>> @user.username
NoMethodError: You have a nil object when you didn't expect it!
The error occurred while evaluating nil.username
	from (irb):1

ซึ่งตรงนี้ เพื่อที่จะหลบ exception ในบางครั้งทำให้ Developer ต้องทำการเช็คก่อนว่า object นั้นเป็น nil หรือไม่ เช่น

>> (@user ? @user.usename : "Guest")
=> "Guest"

ซึ่งตรงนี้ทำให้โค้ดนั้นดูุวุ่นวายมากขึ้น และทำให้โค้ดนั้นดูไม่ค่อยเหมือน Ruby สักเท่าไร (ซึ่งผมก็เห็นด้วยว่า <cond> ? <if-true> : <if-false> นั้น มันดูแปลกๆ) จึงทำให้มีคนคิด Object#try ออกมา แล้วทางทีมผู้พัฒนา Ruby on Rails ถึงเอาเข้าไปเพิ่มใน Rails 2.3 ระหว่างที่ Ruby กำลังรอเพิ่ม method นี้เข้าไปอยู่

เพราะฉะนั้นหลังจากการเพิ่ม method นี้ ทำให้เราสามารถที่จะทำอย่างนี้ได้

>> @user.try(:username)
=> nil
>> @user.try(:username) || "Guest"
=> "Guest"

แล้วยังทำให้ เราสามารถทำ method chaining ได้ด้วย (เพราะทุกอย่างมี #try)

>> @user.try(:username).try(:capitalize)
=> nil
>> # Fetch @user from record
?> @user = User.first
=> #<User id: 1, email: "test@test.com", usename: "test", persistence_token: "95908c3801d55ce389af90f0909192cbda4e37c632afd6b50c2...", created_at: "2009-06-30 05:44:43", updated_at: "2009-07-16 08:56:46", crypted_password: "f37c87cb2d731c0c0710ae9c2b9721d352f6336e38e988bb907...", password_salt: "UIENYMKzDjJYgy2E89Qn", status: nil>
>> @user.try(:username).try(:capitalize)
=> "Test"

ลองปรับไปใช้กับ Application กันนะครับ คิดว่าส่วนนี้น่าจะช่วยให้ debug กันง่ายขึ้นเยอะเลย ;)