หน้าเว็บ

วันพฤหัสบดีที่ 9 ตุลาคม พ.ศ. 2557

Method lookup และ super

ทุ
กครั้งที่ message ถูกส่งไปยังผู้รับ (receiver) Ruby จะทำการตรวจสอบทันทีว่าภายในคลาสต้นสังกัดของอ็อบเจกต์ที่เป็นผู้รับ message นั้นมีเมธอดที่มีชื่อเดียวกันกับชื่อของ message ที่ถูกส่งเข้ามาหรือไม่
กระบวนการค้นหาเมธอดที่มีชื่อเดียวกับ message นั้น หากค้นหากันภายในคลาสของผู้รับแล้วไม่เจอเมธอดที่มีชื่อเดียวกับ message Ruby จะทำการค้นหาต่อไปยัง โมดูลที่คลาสนั้นผนวก (include) เข้ามา รวมถึงคลาสแม่ทุกๆ คลาสที่สืบทอดต่อกันมา เรื่อยไปขึ้นไปจนถึงคลาส BasicObject ซึ่งเป็นคลาสแม่ลำดับบนสุดของทุกๆ คลาส กันเลยทีเดียว 
เรื่องของลำดับขั้นในการค้นหาเมธอดของอ็อบเจกต์เมื่อตัวมันได้รับ message นั้นถือว่ามีความสำคัญไม่น้อย เพราะนอกจากจะช่วยให้เราแยกแยะการทำงานของอ็อบเจกต์และเมธอดได้แล้ว ยังช่วยให้เราเข้าใจถึงการ override ของ Ruby อีกด้วย

สมมติว่าเรากำหนดให้คลาส A, คลาส B และ โมดูล M มีความสัมพันธ์กันตามโค้ดต่อไปนี้

module M
  def print
    puts “this is print method”
  end
end

class A
  include M

วันศุกร์ที่ 12 กันยายน พ.ศ. 2557

เทคนิคการแปลงข้อมูลโดยใช้ Type Conversion

R
uby มีความสามารถที่เรียกว่า built-in conversion ที่ช่วยให้เมธอดสามารถ เปลี่ยน (convert) อ็อบเจกต์ที่ป้อนเข้ามา ให้เป็นค่าของข้อมูลพื้นฐานอย่างเช่น ตัวเลข สตริงหรืออาร์เรย์ ตามที่เมธอดนั้นคาดหวังได้<br /> คำอธิบายข้างต้นอาจทำให้ไม่เห็นภาพ ลองดูตัวอย่างตามนี้ ครับ<br /> สมมติว่าเรามี อาร์เรย์ grade ซึ่งประกอบด้วยชื่อระดับราคาและความหรูหราของโรงแรม ตั้งแต่ โมเต็ล จนถึง โรงแรมระดับลักเซอร์รี่ เราต้องการแสดงข้อความที่ระบุชื่อของโรงแรมจากอ็อบเจกต์ hotel และพร้อมบอกระดับชั้น ตามจำนวนของดาว ซึ่งแทนด้วย rating ที่เป็น attribute ของ hotel โดย rating จะเป็นตัวเลข มีค่าตั้งแต่ 0 ถึง 5 เราสามารถสร้างอ็อบเจกต์ hotel ง่ายๆ ได้โดยใช้ Struct ดังนี้

Hotel = Struct.new(:name, :rating, :address)
h1 = Hotel.new("Sweet Inn", 1, "Bangkok")
h2 = Hotel.new("Centrara Grand", 5, "Phuket")

กำหนดให้ระดับมาตรฐานและบริการ (ความไฮโซ) ของ hotel อยู่ในอาร์เรย์ที่ชื่อ grade

grade = ["Motel", "Bed and breakfasts", "Contel", "Boutique", "Resort", "Luxury"]

เราสามารถแสดงข้อความที่ต้องการได้ดังนี้

วันพฤหัสบดีที่ 21 สิงหาคม พ.ศ. 2557

เก็บตกเทคนิคการใช้ irb

ก็บตกเทคนิคการใช้ irb ที่น่าจะเป็นประโยชน์ครับ

เปลี่ยน irb prompt ให้สั้นลง
irb prompt แบบเดิมหน้าตาแบบนี้
C:\>irb
irb(main):001:0>

ถ้าต้องการย่อ prompt ให้สั้นลง อ่านสบายขึ้น ให้ใส่อ็อบชั่น --simple-prompt เวลาเรียก irb ดังนี้
C:\>irb --simple-prompt
>>

อ้างอิงค่า return จากบรรทัดก่อนหน้า
เราสามารถอ้างอิงค่าสุดท้ายที่ return จากการรันโค้ดก่อนหน้าได้จาก "_" ดังตัวอย่างต่อไปนี้

>> "Manchester United F.C".gsub(/United/, "City")
=> "Manchester City F.C"
>> better_team = _
=> "Manchester City F.C"
>>

วันอังคารที่ 19 สิงหาคม พ.ศ. 2557

การใช้เมธอด collect

มธอด collect เป็นหนึ่งในเมธอดของโมดูล Enumerable ที่มิกซ์อิน (mix-in) อยู่ในคลาสพื้นฐานอย่าง คลาส Array, Hash หรือ Struct
ในเบื้องต้นเราสามารถเรียกใช้เมธอด collect จากอ็อบเจกต์ที่เป็นอินสแตนซ์ของคลาสที่ว่าข้างต้น

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

ประโยชน์ของ collect คือเราสามารถใช้มันเพื่อ "transform" ข้อมูลที่เราต้องการ
ลองดูตัวอย่างการใช้ เมธอด collect ดังต่อไปนี้

>> [1, 2, 3, 4, 5].collect { |x| x*5 }    => [5, 10, 15, 20, 25]
>> [65, 66, 67].collect { |x| x.chr }     => ["A", "B", "C"]

จะเห็นว่าเราสามารถใช้ collect เพื่อลองเปลี่ยนอาร์เรย์ของข้อมูลที่เป็นตัวเลขอย่าง [65, 66, 67] ให้กลายเป็นอาร์เรย์ของตัวอักษร ดังนี้ ซึ่งการแปลงร่างของเราอาศัยเมธอด chr ซึ่งจะมองค่าของตัวเลขที่เรียกใช้มันไปเป็นค่าของ ASCII code แล้วคืนค่าออกมาเป็นตัวอักษร
สังเกตุว่าในการวนรอบแต่ละรอบ ค่าของสมาชิกแต่ละตัวของอ็อบเจกต์ receiver (ในที่นี้คือ [65, 66, 67]) จะถูกโยนลงไปในบล็อก ซึ่งผลลัพธ์สุดท้ายที่ได้จากการรันบล็อกในแต่ละรอบจะถูกเก็บลงไปในอาร์เรย์ เมื่อวนรอบจนครบ
ค่าของอาร์เรย์ดังกล่าวก็จะถูกคืนออกมาเป็นค่า return ของเมธอด collect ซึ่งในที่นี้ก็คือ ["A", "B", "C"] นั่นเอง

collect จะคืนค่าออกมาเป็นอาร์เรย์ที่มีจำนวนสมาชิกเท่ากับจำนวนสมาชิกของอ็อบเจกต์ตั้งต้นเสมอ เช่น

[1, 2, 3, 4, 5].collect do |x|
  x*5 if x > 3
end

=> [nil, nil, nil, 20, 25]

คุณอาจจะไม่ค่อยคุ้น หรือยังนึกไม่ออกว่าจะใช่ collect ตอนไหน ถ้าคุณพบว่าคุณมักจะเขียนโค้ด ออกมาในรูปแบบนี้ ...

def my_transform(data)
  a = []
  data.each do |x|
    # do something that manipulate x
    a << "#{x}!!!"
  end
  return a
end

puts my_transform([1,2,3])

ซึ่งให้ผลลัพธ์ดังนี้
1!!!
2!!!
3!!!

แต่คุณสามารถใช้ collect เพื่อนำปรับปรุงโค้ดใหม่ โดยให้ผลลัพธ์เหมือนเดิมได้ดังนี้

def my_transform2(data)
  data.collect { |x| "#{x}!!!" }
end

puts my_transform2([1,2,3])

ในโค้ดใหม่ คุณจะเห็นว่าเราไม่จำเป็นต้อง setup ตัวแปรโลคอลอย่าง a ขึ้นมา เพราะว่าเมธอดค่าที่คืนออกมาจาก collect นั้นเป็นอาร์เรย์อยู่แล้ว

ลองนำไปใช้ดูนะครับ

วันพฤหัสบดีที่ 7 สิงหาคม พ.ศ. 2557

สรุปการใช้คำสั่ง rails generate

คำสั่ง rails generate เป็นหนึ่งในเครื่องมือที่ใช้ในการอำนวยความสะดวก และช่วยจัดระเยียบของไฟล์และโฟล์เดอร์เพื่อให้ตรงกับรูปแบบที่ rails กำหนดไว้เป็นค่า default

คำสั่งนี้จะทำการสร้างโค้ดหรือไฟล์ให้เราโดยอัตโนมัติในกรณีที่ ต้องการสร้างหรือเปลี่ยนแปลง model หรือ controller ของเรา หรือ

โดยทั่วไปนั้นการใช้งาน rails generate จะแบ่งออกเป็น 3 กรณีด้วยกัน

1. ใช้สร้าง controller
โดยปกติแล้วเมื่อทำงานภายใต้ Rails ขั้นตอนของการสร้างหน้า page ขึ้นมาหน้าหนึ่งนั้น จะต้องสร้างไฟล์ที่ทำหน้าที่แสดงผลผ่านทาง browser ซึ่งเป็นไฟลฺ์ .erb โดยไฟล์นี้จะอยู่ในโฟลเดอร์ของ view และอาจจะต้องสร้างไฟล์ในส่วนของ controller ขึ้นมาเพื่อทำหน้าที่กำหนด logic หรือ เงื่อนไข สำหรับการแสดงผลในหน้าของ view เช่นอาจจะต้องไปดึงข้อมูลจาก database แล้วเอามาแสดงผลซึ่งตรงนี้ก็จะรับผิดชอบโดย controller

เราสามารถลดขั้นตอนของการสร้างไฟล์และโค้ดในส่วนของ view และ controller ได้โดยการสั่งให้ Rails สร้างไฟล์ที่เป็น view และ controller ให้เราผ่านคำสั่งเพียงคำสั่งเดียว

สมมติว่าเราต้องการสร้างหน้า page เกี่ยวกับข้อมูลเบื้องต้นของบริษัท โดยกำหนดให้ชื่อของ section นี้ เป็น company และหน้าแรกของ section ให้ชื่อว่า about
เราจะใช้คำสั่งดังนี้
 
>rails generate controller welcome index

หลังจากรันคำสั่งแล้ว ไฟล์ต่อไปนี้จะถูกสร้างขึ้นโดยอัตโนมัติ

app/controllers/company_controller.rb
app/controllers/view/about.html.erb
app/helpers/company_helper.rb
...
...

เราสามารถไปใส่รายละเอียดสำหรับ logic ในไฟล์ company_controller.rb และกำหนดรูปร่างหน้าตาของ view ได้ในไฟล์ about.html.erb 

2. ใช้สร้าง model ใหม่
สำหรับ Rails นั้น การสร้าง model คือการสร้าง table ใน database เพื่อเก็บข้อมูลของสิ่งที่เราสนใจ (เช่น ลูกค้า, คำสั่งซื้อ) ซึ่งข้อมูลเหล่านั้นจะถูกมองเป็นอ็อบเจกต์ table ที่สร้างขึ้นก็จะสอดคล้องกับคลาสของอ็อบเจกต์ตัวนั้น คุณลักษณะต่างๆ อ็อบเจกต์ก็จะเปรียบเทียบได้กับ field หรือเป็น column ของ table (ซึ่งก็คือคลาส) โดยการจัดการกับข้อมูลในลักษณะของอ็อบเจกต์นี้จะเป็นไปตามหลักการ ORM (Object Relational Mapping) ซึ่งถูกใช้ใน database อย่าง ActiveRecord หรือ DataMapper

ขั้นตอนของการสร้าง model ค่อนข้างที่จะเป็น routine job นั้นคือ คุณต้องสร้างคลาสของข้อมูลที่ต้องการจะเก็บไว้ใน database ขึ้นมา โดยในคลาสนี้คุณจะต้องกำหนดคุณลัษณะของข้อมูลซึ่งจะถูกใช้เป็นชื่อของ column ใน table ที่จะถูกสร้างขึ้นในลำดับถัดไป จากนั้นจะต้องสร้างคลาสอีกคลาสหนึ่งเพื่อให้ Rails ใช้อ้างอิงในการ "migrate" ซึ่งก็คือการสร้าง table ใน database โดยจะเป็นขั้นตอนสุดท้ายที่จะถูกจัดการด้วย rake โดยใช้ rake:migrate

กระบวนการสร้าง model ที่ยุ่งยากข้างต้นสามารถทำให้สั้นลงได้โดยใช้คำสั่ง rails generat
โดยสมมติว่าเราต้องการสร้าง model ที่ชื่อว่า User โดยประกอบด้วย field ง่ายๆ 2 field ได้แก่ name และ string เราก็สามารถทำได้โดยใช้คำสั่งต่อไปนี้

> rails generate model User name:string email:string 

หลังจากใช้คำสั่งแล้ว Rails จะสร้างไฟล์ ขึ้นมา 2 ไฟล์ได้แก่

db/migrate/[time_stamp]_create_user.rb
app/models/user.rb

เมื่อลองตรวจสอบดูจะพบว่า
โค้ดในไฟล์ user.rb เป็นโค้ดที่เก็บคลาสที่ชื่อว่า User (ชื่อเดียวกับไฟล์และ table ที่จะถูกสร้างขึ้นใน database) ณ ตอนนี้เป็นคลาสเปล่าๆ ไม่มีโค้ดใดๆ

class User < ActiveRecord::Base
end

ส่วนโค้ดในไฟล์ [time_stamp]_cretae_user.rb จะเป็นโค้ดที่รับผิดชอบในการสร้าง table ที่ชื่อว่า User ลงใน database (ในที่นี้ก็คือ ActiveRecord)

class CreateUsers < ActiveRecord::Migration
  def change
    create_table :users do |t|
      t.string :name
      t.string :email

      t.timestamps
    end
  end
end

เมื่อได้ไฟล์ทั้ง 2 ไฟล์แล้วเราเพียงแค่พิมพ์คำสั่ง rake db:migrate ลงใน command prompt แล้ว Rails ก็จะทำการสร้าง table ที่ชื่อว่า User ที่ประกอบด้วย column name และ email ตามที่เรากำหนดไว้ให้โดยอัตโนมัติ

3. ใช้เพื่อเปลี่ยนแปลงแก้ไขโครงสร้างของ Table ใน database
ในกรณีที่เราต้องการแก้ไขโครงสร้างของ table ที่เราสร้างขึ้นก่อนหน้า เช่น ต้องการเพิ่ม field ของเบอร์โทรของผู้ใช้ (phone_number) ซึ่งเป็น field ใหม่เข้าไป เราจะไม่เข้าไปแก้ไข table ผ่านทาง database โดยตรงแต่จะใช้กระบวนการ migration ของ Rails แทน ทั้งนี้เพื่อให้การจัดการข้อมูลของ Rails ในลักษณะของอ็อบเจกต์โมเดลนั้นสอดคล้องกัน

แต่ปัญหาคือการ migration แต่ละครั้งมีขั้นตอนยุ่งยากพอควร
ขั้นตอนในการ migration แบบคร่าวๆ จะเริ่มตั้งแต่ คุณจะต้องสร้างคลาสที่เป็นตัวแทนของการ migration ในครั้งนั้นขึ้นมา โดยระบุการเปลี่ยนแปลงการต้องการเอาไว้ภายในคลาส เช่น เพิ่ม column เพิ่ม index ลบ column เป็นต้น (ทำผ่านทางเมธอดตามรูปแบบที่กำหนดของ Rails) โดยให้ระบุด้วยว่าเป็นคลาสที่สืบทอดมาจากคลาส ActiveRecord::Migration เสมอ
เมื่อสร้างคลาสเรียบร้อยแล้วก็ให้ทำการรันคำสั่ง rake db:migrate เพื่อสั่งให้ Rails ดำเนินการเปลี่ยนแปลงที่เราต้องการกับ database

เช่นเดียวกับ 2 ข้อที่ผ่านมา เราสามารถลดขั้นตอนที่ยุ่งยากโดยการใช้คำสั่ง rails generate เพื่อให้ Rails สร้างไฟล์และโค้ดที่เหมาะสมกับการเปลี่ยนแปลงให้โดยอัตโนมัติ

สมมติว่าเราต้องการเพิ่ม field ชื่อ phone_number เข้าไปในโมเดล User เราก็ทำได้โดยใช้คำสั่ง

> rails generate migration add_phone_number_to_users phone_number:string

เมื่อรันคำสั่งแล้ว เราก็จะได้ไฟล์
db/migrate/[time_stamp]_add_phone_number_to_users.rb

มีหน้าตาดังนี้

class AddPhoneNumberToUsers < ActiveRecord::Migration
  def change
    add_column :users, :phone_number, :string
  end
end

จากนั้นก็เพียงแค่ใช้คำสั่ง rake db:migrate เพื่อให้ Rails ดำเนินการ update database ให้เราโดยอัตโนมัติ