หน้าเว็บ

วันอังคารที่ 28 ตุลาคม พ.ศ. 2557

โชว์ข้อมูลดีบักสำหรับ Rails

เราสามารถเรียกดูข้อมูลในตัวแปรต่างๆ ที่ใช้อยู่ใน Rails ได้โดยใช้เมธอดพิเศษของ Rails ซึ่งมีอยู่ 3 เมธอด ได้แก่


1. เมธอด debug
2. เมธอด to_yaml
3. เมธอด inspect

ทั้งสามแบบใช้งานคล้ายๆ กัน โดยเราสามารถเรียกใช้เมธอดนี้แล้วใส่ตัวแปรที่เราอยากตรวจสอบลงไปเป็น argument โดย debug และ to_yaml จะให้ผลลัพธ์คล้ายๆ กันจะต่างกันตรงที่ข้อมูลของตัวแปรที่เราตรวจสอบด้วย to_yaml จะอยู่ใน format ของ yaml เลย ในขณะที่ debug จะเป็นข้อความที่จัดเรียงให้อ่านง่ายกว่า ส่วน inspect นั้น ให้ผลลัพธ์เหมือนกับที่เราใช้ inspect ใน Ruby เลย

วิธีการนำไปใช้ก็ง่ายมาก คือเราสามารถเรียกเมธอดทั้ง 3 ตัวนี้ได้จาก view (ไฟล์ .html.erb) แล้วให้มันแสดงผลออกมาทางหน้าเพจเลย

<!DOCTYPE html>
<html>
<head>
  <title>Depot</title>
  <%= stylesheet_link_tag    "application", media: "all", "data-turbolinks-track" => true %>
  <%= javascript_include_tag "application", "data-turbolinks-track" => true %>
  <%= csrf_meta_tags %>
</head>
<body>

<%= yield %>
<%= debug(params) %>
</body>
</html>



จากโค้ดตัวอย่าง เราเรียกเมธอด debug จากหน้า layout (ไฟล์ app/views/application.html.erb) โดยตัวแปรที่เราต้องการตรวจสอบในที่นี้คือ param
ผลลัพธ์ที่ได้จะเป็นข้อความดีบัก ซึ่งมีหน้าตาประมาณนี้



ลองใช้ดูละกันครับ

วันพฤหัสบดีที่ 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 ให้เราโดยอัตโนมัติ


วันศุกร์ที่ 1 สิงหาคม พ.ศ. 2557

Mock ด้วย MiniTest

จุดประสงค์ของการ mock คือ ต้องการรู้ว่าเมธอดของ mock อ็อบเจกต์ถูกเรียกใช้อย่างถูกต้องตามที่คาดหวัง (expect) ไว้หรือไม่

สรุปก็คือการ mock เป็นการทดสอบให้แน่ใจว่าว่าเมธอดที่เราดูอยู่จะต้องถูกเรียกออกมาใช้จากอ็อบเจกต์ที่เราได้ mock เอาไว้ และค่าที่คืนออกมาจากเมธอดนั้นต้องเป็นค่าที่ถูกต้องด้วย
ลองดูตัวอย่างของการใช้ mock ดังนี้

require 'minitest/autorun'

class Label
  def initialize(note)
    @note = note
  end

  def emphasize
    @note.highlight
  end
end

describe Label do
  it "has all charecter upcase when emphasize" do
    special_note = "*** HAPPY DAY ***"

    note = MiniTest::Mock.new
    note.expect(:highlight, special_note)
    
    label = Label.new(note)
    label.emphasize.must_match(/\*\*\*/)
    note.verify
  end
end

ตัวอย่างข้างต้นอ็อบเจกต์ note เป็นอ็อบเจกต์ที่เราสมมติขึ้นมาหรือเรียกว่า mock มันเอาไว้ ทีนี้ เมธอดที่ต้อวการจะตรวจสอบว่าจะต้องถูกเรียกด้วยอ็อบเจกต์ note ก็คือ เมธอด highlight ดั้งนั้นจึงต้องใส่โค้ดที่ระบุความคาดหวังจากมธอด highlight ซึ่งคือโค้ด note.expect(:highlight, special_note)
จากนั้นจึงเป็นการทดสอบโดยกำหนดให้ label เรียกเมธอด emphasize แล้วจะต้องได้ค่าที่สอดคล้อง (must_match) กับ *** แล้วจึงสั่งให้ note.verify

ถ้าดูจาก spec อย่างเดียว ความหมายก็คือ ข้างในเมธอด emphasize จะทำงานอย่างไรไม่รู้ รู้แต่ว่า spec ข้อนี้จะผ่านได้ก็ต่อเมื่อ เมธอด highlight จะต้องถูกเรียกใช้จาก mock อ็อบเจกต์ (ในที่นี้คือ note) ตามที่ระบุไว้ใน expect ดังนั้น จึงเป็นการบอกโดยนัย หรือโดยปริยายว่า ภายใน emphasize จะต้องมีการเรียก note.highlight แน่นอน และค่าที่คืนออกมานั้นก็จะต้องสอดคล้องกับสตริง *** ด้วย

ถ้าเอาโค้ดข้างต้นไปรันด้วย minitest ผลของการทดสอบก็จะผ่านไปได้

C:\CodeProject\Ruby\TDD\minitest\example1>ruby spec/book_spec.rb
Run options: --seed 8903

# Running:

.

Finished in 0.005000s, 200.0000 runs/s, 400.0000 assertions/s.

1 runs, 2 assertions, 0 failures, 0 errors, 0 skips


ลองดูอีกสักตัวอย่างกับการ mock
spec มีหน้าตาแบบนี้...

describe Book do
  it "uses authors name in description" do
    author = MiniTest::Mock.new
    author.expect(:name, "Worrawut")

    book = Book.new("Royal Assassin", :author => author)
    book.description.must_match(/Written by Worrawut/)
    author.verify
  end
end

จากโค้ดจะเห็นว่าเรา mock เพื่อต้องการย้ำว่าเมธอด name ของอ็อบเจกต์ note จะต้องถูกเรียกใช้พร้อมกับให้ค่าที่ถูกต้อง เมื่อเมธอด description ของ book ถูกเรียกขึ้นมาใช้งาน
ดังนั้นโค้ดที่จะทำให้ spec นี้รันผ่านไปได้จึงมีหน้าตาดังนี้

class Book
  def initialize(title, h)
    @title = title
    @author = h[:author]
  end
  def description
    "#{@title} is Written by #{@author.name}"
  end
end

วันจันทร์ที่ 21 กรกฎาคม พ.ศ. 2557

ตัวอย่างการใช้ subject และ should ใน RSpec


นการ Test อ็อบเจกต์หรือคลาสใดๆ ด้วย Rspec รูปแบบหรือฟอร์มที่เป็นพื้นฐานที่สุดสำหรับทดสอบ incomming message (หรือ instance เมธอด) ก็คือการใช้ expect(object) to respond_to(something) รูปแบบนี้มีความหมายชัดเจนในตัวอยู่แล้ว แต่ถ้าหากเราจะต้อง test เมธอดหลายๆ เมธอด การใช้ฟอร์มแบบนี้อาจก่อให้เกิดโค้ดที่ซ้ำกันโดยไม่จำเป็นและที่สำคัญมันอาจทำให้เราตาลายได้

วิธีแก้ ง่ายมาก ให้กำหนดอ็อบเจกต์เบื้องต้น (default object) ให้กับ rspec เพื่อทำการทดสอบในขอบเขตนั้นๆ โดยใช้ subject และใช้ should เพื่ออ้างถึง default object ดังกล่าว

สมมติว่า test โค้ดของเรามักจะมีหน้าตาประมาณนี้

describe MyClass do
    before { @my_object = MyClass.new(color: "Red", weight: 60) }

    it "should respond to 'color'" do
      expect(@my_object).to respond_to(:color)
    end

    it "should respond to 'weight'" do
      expect(@my_object).to respond_to(:weight)
    end
end 

โค้ดข้างต้นสามารถลดรูปให้สั้นลงและอ่านง่ายขึ้นได้ โดยใช้ subject และ should เข้ามาช่วย ดังนี้

describe MyClass do
    before { @my_object = MyClass.new(color: "Red", weight: 60) }
    subject { @my_onject  }

    it { should respond_to(:color)  }
    it { should respond_to(:weight)  }
end

เมธอด subject {@my_object} เป็นตัวหนดให้ @my_object กลายเป็น default object ที่จะถูกนำมารัน test ด้วยเมธอดต่างๆ ของ rspec ส่วนเมธอด should จะทำหน้าที่ตรวจสอบเงื่อนไขว่าเป็นจริงหรือไม่
จากตัวอย่างนี้ respond_to(:color) จะมีความหมายเดียวกับการใช้เมธอด respond_to? (จากคลาส Object ใน Ruby) ซึ่งจะคืนค่า true ออกมา ทำให้ผ่านเงื่อนไขของการ test ในที่สุด

ลองดูผลลัพธ์ที่เกิดจากเมธอด respond_to? กัน

>> class MyClass
>> def color
>> end
>> end
=> nil
>> obj = MyClass
=> MyClass
>> obj.respond_to?("color")
=> true
>> obj.respond_to?("other")
=> false

วันศุกร์ที่ 18 กรกฎาคม พ.ศ. 2557

เริ่มใช้ Rails

มี project ส่วนตัวที่อยากทำออกมาให้เป็น web app
ตั้งใจว่าจะใช้ sinatra เหมือนที่เคยทำกับ project ก่อน แต่ก็ต้องเปลี่ยนใจกลับมาลองใช้ Rails อีกครั้ง

จริงๆ แล้วเคยลองใช้มาก่อนช่วง Rails 2.0 ออกใหม่ (ตอนนี้ 4.1 แล้ว) แล้วไม่ค่อยประทับใจ
ในตอนนั้น รู้สึกว่าต้องเสียเวลาทำความเข้าใจกับการ config ที่เยอะมาก MVC ก็ยังงงใช้ผิดๆ ถูกๆ และเหมือนถูกบังคับให้ต้อง migrate database อยู่บ่อย ๆ ซึ่งในตอนนั้นยังมองไม่เห็นภาพ ไม่รู้ว่าทำเพื่ออะไร
ซึ่งเมื่อเปรียบเทียบกับ sinatra ผมรู้สึกว่าชีวิตมันง่ายกว่า เข้าใจถึงคำว่าเบากว่า (lighweight) ขึ้นมาเลย

อย่างไรก็ตาม หลังจากที่ลองศึกษาข้อมูลเพิ่มขึ้นเรื่อยๆ ทำให้เกิดอาการอยากลองของดูอีกสักครั้ง
ตอนนี้กำลังไล่อ่าน tutorial ของ Rails อยู่จากหลายๆ แหล่ง หลักๆ เลยตามอยู่ที่ Ruby On Rails Tutorial (อ่าน online ฟรี :) )

ยังคงเกิดความรู้สึกเดิมคือ ทำไมมันเยอะ จะเยอะจะเทอะไปไหน
คือจะ build web app นี่ต้อง dependency เยอะอย่างงี้เลย ? (gem มากมาย รายการใน Gemfile ยาวขึ้นทุกที)
แต่ เอาน่ะ ไหนๆ จะลองแล้วคราวนี้อยากเอาให้สุดซะที