บทเรียน 91 · Rails · Day 1

ติดตั้ง Ruby แล้วลองพิมพ์บรรทัดแรก — ตัวแปร, object และ string

~20 นาที · ควรผ่าน บทเรียน 90 (Rails คืออะไร) มาก่อน

บทที่แล้วเราเห็นภาพรวมว่า Rails คือร้านแฟรนไชส์ที่ได้ครัวครบชุด แต่ก่อนจะเปิดร้านได้ ต้องหัดจับมีดกับเขียงก่อน — มีดกับเขียงของเราคือภาษา Ruby
อ่านจบแล้วคุณจะ: เช็คได้ว่าเครื่องมี Ruby รุ่นไหน · เปิด irb แล้วลองพิมพ์โค้ดทีละบรรทัดได้ · สร้างตัวแปรเก็บชื่อบทความ · เข้าใจว่าทำไม Ruby เขียน 42.even? ได้แต่ Go เขียนไม่ได้ · และแทรกค่าลงในข้อความได้โดยไม่ติดกับดัก

🔁 ทวนก่อนเริ่ม (จากบทเรียน 90)

🧰 เครื่องที่ใช้ตรวจ: Ruby 4.0.9 · Rails 8.1.4 · irb 1.18.0 · Go 1.26.5 · สไลด์ใช้ Ruby 4.0.6 · Rails 8.1.3 — เลขท้ายต่างกันเป็นแค่รุ่นแก้บั๊ก ไม่มีผลกับบทนี้ · ทุกผลรันในบทนี้ก็อปมาจากเครื่องจริง

1 · terminal กับ mise — เช็คว่ามี Ruby รุ่นไหน

terminal คือหน้าต่างที่พิมพ์คำสั่งเป็นตัวหนังสือ — ตัวเดียวกับที่คุณใช้พิมพ์ go run main.go ตอนเรียน Go บรรทัดที่ขึ้นต้นด้วย $ ในบทนี้คือคำสั่งที่พิมพ์ใน terminal (ไม่ต้องพิมพ์ตัว $)

ปัญหาแรกของ Ruby คือ เครื่องเดียวมักมีหลายรุ่น เช่น Mac แถม Ruby รุ่นเก่ามาก (2.6) ติดเครื่องมาให้แล้ว แต่คอร์สนี้ใช้รุ่น 4 ถ้าเรียกผิดตัว โค้ดของคอร์สจะพังแบบงง ๆ

mise (อ่านว่า "มีซ") คือโปรแกรมที่ เก็บโปรแกรมไว้หลายรุ่น แล้วเลือกให้ว่าตอนนี้จะใช้รุ่นไหน สไลด์หน้า 14 ใช้มันติดตั้ง Ruby รุ่น 4 แล้วค่อยติดตั้ง Rails ต่อ

🧰 เทียบให้เห็นภาพ — ไขควงด้ามเดียว หัวสลับได้

mise เหมือนไขควงด้ามเดียวที่มีหัวหลายขนาดในกล่อง · Ruby 2.6 กับ Ruby 4.0.9 คือหัวคนละขนาด · ไฟล์ mise.toml ที่สไลด์โชว์คือป้ายที่แปะไว้กับโปรเจกต์ว่า "งานนี้ใช้หัวเบอร์ 4.0.6 นะ" ใครในทีมหยิบโปรเจกต์ไปทำก็ได้หัวเบอร์เดียวกัน

ที่ไม่เหมือนกัน: ไขควงจริงคุณต้องเปลี่ยนหัวเองด้วยมือ · mise เปลี่ยนให้เองอัตโนมัติ — แต่ต้องตั้งค่าให้ terminal "ปลุก" mise ขึ้นมาก่อนหนึ่งครั้ง (คู่มือติดตั้งของ Rails ให้เพิ่มบรรทัด mise activate ลงไฟล์ ~/.zshrc — ไฟล์ตั้งค่าที่ terminal ของ Mac อ่านทุกครั้งที่เปิดหน้าต่างใหม่) ถ้าข้ามขั้นนี้ เครื่องจะหยิบ Ruby 2.6 ของ Mac มาใช้แทน

ใน Go คุณไม่ค่อยเจอเรื่องนี้ เพราะติดตั้ง Go ตัวเดียวแล้วใช้ยาวได้ · ใน Ruby การมีตัวเลือกรุ่นเป็นเรื่องปกติของทุกทีม

คำสั่งในสไลด์ (ไม่ต้องรันซ้ำถ้าเครื่องคุณติดตั้งไว้แล้ว):

$ mise use -g ruby@4      # ติดตั้ง Ruby รุ่น 4 ตัวล่าสุด แล้วตั้งให้ใช้เป็นค่าเริ่มต้นทั้งเครื่อง
$ gem install rails       # ติดตั้ง gem ชื่อ rails ลงใน Ruby ตัวนั้น

ที่ต้องรันเป็นคือคำสั่ง อ่านอย่างเดียว ที่แค่ถามว่ามีรุ่นไหน — ผลรันจริง:

$ ruby -v
ruby 4.0.9 (2026-10-23 revision 3e93b23afc) +PRISM [arm64-darwin25]
$ rails -v
Rails 8.1.4
$ mise --version
2026.8.1 macos-arm64 (2026-08-03)

-v ย่อมาจาก version · ขอแค่บรรทัดแรกขึ้นต้นว่า ruby 4. ก็พร้อมเรียนแล้ว · วันที่และรหัสในวงเล็บของเครื่องคุณจะต่างจากนี้ ไม่เป็นไร · mise --version อาจมีบรรทัด mise WARN บอกว่ามีรุ่นใหม่ต่อท้าย — ไม่ต้องสนใจ

⚠️ ถ้า ruby -v ขึ้นว่า ruby 2.6.10p210 (2022-04-12 revision 67958) [universal.arm64e-darwin26] (ผลจริงของ Ruby ที่ติดมากับ Mac) แปลว่า terminal ยังไม่ได้ปลุก mise — กลับไปทำขั้น mise activate ในคู่มือติดตั้ง แล้วปิด-เปิด terminal ใหม่

🗣 ศัพท์ที่เพิ่งเรียนไป
version manager
โปรแกรมเก็บภาษาหลายรุ่นแล้วสลับให้ (mise เป็นตัวหนึ่ง · ตัวอื่นที่อาจได้ยินคือ rbenv, nvm) · ถาม senior ได้ว่า "ทีมนี้ตรึงรุ่น Ruby ไว้ที่ไฟล์ไหน"
gem install
คำสั่งติดตั้ง gem หนึ่งตัวลงเครื่อง · ในโปรเจกต์ Rails จริงจะจดรายการ gem ไว้ในไฟล์ Gemfile แทน (บทเรียน 98) · พิมพ์บอก AI ได้ว่า "ruby -v shows 2.6 but I installed Ruby 4 with mise, what did I miss?"

2 · irb — กระดาษทดเลขของ Ruby

ตอนเรียน Go ถ้าอยากลองอะไรนิดเดียว ต้องสร้างไฟล์ มี package main มี func main() แล้วค่อย go run Ruby มีทางลัด: พิมพ์ irb ใน terminal แล้วพิมพ์โค้ดได้ทีละบรรทัด กด Enter เห็นผลทันที irb ติดมากับ Ruby ไม่ต้องติดตั้งเพิ่ม

🧰 เทียบให้เห็นภาพ — กระดาษทดเลข

เขียนไฟล์ .rb แล้วรัน = เขียนรายงานส่งครู ต้องเรียบร้อย มีหัวมีท้าย · irb = กระดาษทดเลข ขีดเขียนลองอะไรก็ได้ ผิดก็เขียนบรรทัดใหม่

ที่ไม่เหมือนกัน: กระดาษทดยังเก็บไว้ดูทีหลังได้ · พอพิมพ์ exit ออกจาก irb ทุกอย่างที่พิมพ์ไว้หายหมด — โค้ดที่อยากเก็บต้องก็อปไปใส่ไฟล์ .rb เอง

ลองเลย — นี่คือหน้าจอจริงหลังพิมพ์ irb (ตัดข้อความต้อนรับตอนเปิดออก):

irb(main):001> 1 + 2
=> 3
irb(main):002> title = "Hello Rails"
=> "Hello Rails"
irb(main):003> title
=> "Hello Rails"
irb(main):004> exit

ในบทเรียนและสไลด์ คุณจะเห็นคอมเมนต์ # => "HELLO RAILS" ต่อท้ายโค้ด — แปลว่า "บรรทัดนี้ได้ผลเป็น…" คือการก็อปผลจาก irb มาเขียนไว้นั่นเอง · (# ใน Ruby คือคอมเมนต์ เหมือน // ใน Go)

🗣 ศัพท์ที่เพิ่งเรียนไป
irb / REPL
irb ย่อจาก Interactive Ruby · คนทั่วไปเรียกเครื่องมือแบบนี้ว่า REPL (อ่าน → รัน → พิมพ์ผล → วนใหม่) · พิมพ์บอก AI ได้ว่า "give me a one-liner I can paste into irb to try this"
return value
ค่าที่บรรทัดหนึ่งได้ออกมา · ใน Ruby ทุกบรรทัดมีผลเสมอ irb เลยโชว์ => ได้ทุกบรรทัด · เจอ # => true ในเอกสาร อ่านว่า "บรรทัดนี้ได้ true"

3 · ตัวแปร — ตั้งชื่อแล้วใส่ค่า จบ

ตัวแปรคือชื่อที่เราตั้งให้ค่าหนึ่ง จะได้เรียกใช้ทีหลังได้ · ใน Ruby แค่เขียน ชื่อ = ค่า ก็สร้างตัวแปรเสร็จ ไม่มีคำว่า var ไม่มี := ไม่ต้องบอกชนิด

Go — ต้องประกาศ

var title string = "Hello Rails"
author := "Nan"   // := เดาชนิดให้
views := 42

Ruby — ให้ค่าครั้งแรก = สร้างเลย

title  = "Hello Rails"
author = "Nan"
views  = 42

(กล่องซ้ายสีแดงไม่ได้แปลว่า Go ผิด แค่ใช้แยกฝั่งให้ดูง่าย) · เว้นวรรคหน้า = ให้ตรงกันเป็นแค่ความสวย ไม่มีผลอะไร · นี่คือบทความจำลองของเรา: ชื่อเรื่อง คนเขียน และยอดอ่าน

🧰 เทียบให้เห็นภาพ — สติกเกอร์ชื่อบนกล่องข้าวในตู้เย็นออฟฟิศ

ตัวแปร Ruby เหมือนสติกเกอร์เขียนชื่อแปะบนกล่อง · title = "Hello Rails" คือ "แปะสติกเกอร์ title ไว้บนกล่องที่มีข้อความ Hello Rails" · ไม่ต้องไปขอจองสติกเกอร์ก่อน หยิบมาแปะได้เลย · ใน Go ต้องไปลงชื่อจองก่อนว่า "ช่องนี้ของฉัน ใส่ได้เฉพาะ string"

ที่ไม่เหมือนกัน: ในออฟฟิศถ้าใครเขียนชื่อผิด เพื่อนจะทักว่า "นี่ของใคร" · Ruby ไม่ทัก — เขียนชื่อสะกดผิดตอนให้ค่า มันคิดว่าคุณตั้งใจแปะสติกเกอร์ใบใหม่ (ดูข้างล่าง)

ราคาของความสบายนี้: ลองพิมพ์ชื่อผิดตอนให้ค่า (veiws แทน views) แล้วเทียบสองภาษา — ผลรันจริง:

Go — build ไม่ผ่าน

views := 1
veiws = views + 1
// ./main.go:7:2: undefined: veiws

(โชว์แค่ส่วนใน func main() · เลขบรรทัดนับจากไฟล์เต็ม)

Ruby — รันผ่าน ไม่บ่นสักคำ

views = 1
veiws = views + 1
puts views
# พิมพ์ 1 ออกมา · ไม่มี error

Go รู้ว่า = คือ "เปลี่ยนค่าตัวแปรที่มีอยู่แล้ว" จึงฟ้องว่าไม่มีตัวชื่อ veiws · Ruby ใช้ = ทั้งสร้างและเปลี่ยน เลยแยกไม่ออกว่าคุณพิมพ์ผิด · (puts = พิมพ์ค่าออกจอแล้วขึ้นบรรทัดใหม่ เหมือน fmt.Println)

ข่าวดีคือถ้าพิมพ์ผิดตอนอ่านค่า Ruby ฟ้องตอนรันและเดาให้ด้วย · ไฟล์นี้สร้าง title แล้วตั้งใจเรียกผิดเป็น titel:

# typo2.rb
title = "Hello Rails"
puts titel

ผลรันจริง (ตัดบรรทัดที่วาดลูกศรชี้ตำแหน่งออก):

typo2.rb:3:in '<main>': undefined local variable or method 'titel' for main (NameError)
Did you mean?  title

อ่านว่า "ไฟล์ typo2.rb บรรทัด 3 ไม่รู้จักชื่อ titel · หมายถึง title หรือเปล่า" · (บรรทัดแรกที่เป็นคอมเมนต์ชื่อไฟล์ก็นับเป็นบรรทัดด้วย) · '<main>' กับ for main แปลว่าโค้ดอยู่ชั้นนอกสุดของไฟล์ ไม่ต้องสนใจ · (คำว่า method ใน error คือ "คำสั่ง" — เจอในหัวข้อ 4)

🗣 ศัพท์ที่เพิ่งเรียนไป
local variable
ตัวแปรชื่อขึ้นต้นตัวเล็ก เกิดขึ้นตอนให้ค่าครั้งแรก · พิมพ์บอก AI ได้ว่า "check this Ruby file for misspelled variable names — Ruby won't error on assignment"
dynamic typing
ตัวแปรไม่ผูกกับชนิดข้อมูล วันนี้ใส่ตัวเลข พรุ่งนี้ใส่ข้อความก็ได้ · ตรงข้ามกับ Go · ถาม senior ได้ว่า "ทีมใช้อะไรช่วยจับ type ผิดใน Ruby ไหม"
NameError
error ตอนรันเมื่อเรียกชื่อที่ Ruby ไม่รู้จัก · ดูบรรทัด Did you mean? ก่อนเสมอ ส่วนใหญ่เป็นแค่สะกดผิด

ค่าคงที่ — ขึ้นต้นตัวใหญ่

ค่าคงที่คือค่าที่ตั้งครั้งเดียวแล้วไม่ควรเปลี่ยน เช่นชื่อเว็บ · Go ใช้คำว่า const นำหน้า · Ruby ไม่มีคำนี้ แต่ดูตัวอักษรตัวแรกของชื่อแทน: ขึ้นต้นตัวเล็ก = ตัวแปร · ขึ้นต้นตัวใหญ่ = ค่าคงที่ (ปกติเขียนตัวใหญ่ทั้งคำ)

Go — มีคำว่า const

const BlogName = "wongnok_blog"

Ruby — แค่ขึ้นต้นตัวใหญ่

BLOG_NAME = "wongnok_blog"

แล้วถ้าดื้อ เปลี่ยนค่าคงที่ทีหลังล่ะ? ลองเดาก่อนกดเฉลย:

# const.rb
BLOG_NAME = "wongnok_blog"
puts BLOG_NAME
BLOG_NAME = "my_blog"
puts BLOG_NAME

รัน ruby const.rb แล้วจะ (ก) หยุดด้วย error · (ข) เตือนแล้วพิมพ์ my_blog · หรือ (ค) เงียบแล้วพิมพ์ wongnok_blog สองครั้ง?

ได้ (ข) — ผลรันจริงใน terminal:

$ ruby const.rb
wongnok_blog
const.rb:4: warning: already initialized constant BLOG_NAME
const.rb:2: warning: previous definition of BLOG_NAME was here
my_blog

Ruby ยอมเปลี่ยนให้ แค่เตือนสองบรรทัด: "บรรทัด 4 ตั้งค่า BLOG_NAME ซ้ำ" และ "ตั้งครั้งแรกที่บรรทัด 2" (บรรทัด 1 คือคอมเมนต์ชื่อไฟล์) · ส่วน Go ถ้าเขียน BlogName = "my_blog" ต่อจาก const จะ build ไม่ผ่าน — ผลจริง: cannot assign to BlogName (neither addressable nor a map index expression) (แปลว่า "ค่านี้เปลี่ยนไม่ได้")

(ถ้าส่งผลไปเก็บในไฟล์แทนการดูบนจอ ลำดับบรรทัดเตือนอาจขึ้นก่อน เพราะคำเตือนกับผลปกติออกคนละช่อง — ไม่ต้องตกใจ)

🧰 เทียบให้เห็นภาพ — ป้ายชื่อร้านที่ติดหน้าร้าน

ตัวแปรคือสติกเกอร์ชื่อที่ลอกไปแปะกล่องใหม่ได้ทุกวัน (ภาพจากข้างบน) · ค่าคงที่คือป้ายชื่อร้านที่ติดหน้าร้าน ตั้งใจให้อยู่อย่างนั้นตลอด · ใน Go ป้ายชื่อร้านถูกเชื่อมติดผนัง ถอดไม่ได้เลย

ที่ไม่เหมือนกัน: ป้ายชื่อร้านจริงต้องจ้างช่างมาถอด · ใน Ruby ป้ายแค่แปะสก็อตเทป ใครจะเปลี่ยนก็ทำได้ทันที แค่ Ruby ตะโกนเตือนหนึ่งที (warning) แล้วยอมให้เปลี่ยน

กติกาตั้งชื่อ ที่สไลด์หน้า 15 ย้ำ: ตัวแปรใช้ snake_case (ตัวเล็กหมด คั่นคำด้วย _ เช่น post_title) · ชื่อ class ใช้ PascalCase (ตัวใหญ่ต้นทุกคำ เช่น BlogPost)

class คือแม่พิมพ์ของข้อมูล คล้าย struct ใน Go (เจอละเอียดในบทเรียน 95) · ชื่อ class ขึ้นต้นตัวใหญ่ เพราะใน Ruby มันก็นับเป็นค่าคงที่ตัวหนึ่ง · ระวังสับสนกับ Go: Go ใช้ตัวใหญ่-เล็กบอกว่าแพ็กเกจอื่นเรียกใช้ได้ไหม (export) ส่วน Ruby ใช้บอกว่าเป็นค่าคงที่หรือไม่

ใน Rails กติกานี้สำคัญมาก — เหมือนร้านแฟรนไชส์ที่ทุกสาขาวางของตำแหน่งเดียวกัน Rails เดาว่าของอยู่ไฟล์ไหนจากชื่อ ตั้งชื่อผิดแบบ = หาของไม่เจอ (ละเอียดในบทเรียน 99)

🗣 ศัพท์ที่เพิ่งเรียนไป
constant
ชื่อขึ้นต้นตัวใหญ่ · เปลี่ยนค่าได้แต่ได้ warning: already initialized constant · เห็นคำเตือนนี้ในงานจริง = มีคนตั้งค่าซ้ำโดยไม่ตั้งใจ ควรแก้ · พิมพ์บอก AI ได้ว่า "why does Ruby only warn when I reassign a constant?"
snake_case / PascalCase
post_title สำหรับตัวแปร · BlogPost สำหรับ class · ใน Rails ตั้งผิดแล้วหาไฟล์ไม่เจอ ไม่ใช่แค่ไม่สวย · ถาม senior ได้ว่า "ใน Rails ชื่อ class กับชื่อไฟล์ต้องตรงกันแบบไหน"

4 · ทุกอย่างเป็น object — ทุกค่ามีคำสั่งติดตัว

ใน Go ค่าอย่าง "Hello Rails" หรือ 42 เป็นค่าเปล่า ๆ ทำอะไรเองไม่ได้ · อยากรู้ความยาวต้องส่งเข้าฟังก์ชัน len(title) · อยากได้ตัวใหญ่ต้องไปยืม strings.ToUpper(title)

ใน Ruby ทุกค่าเป็น object แปลว่าทุกค่ามีคำสั่งของตัวเองติดมาด้วย — คำสั่งพวกนี้เรียกว่า method · เรียกใช้ด้วยการเขียนจุดต่อท้ายค่า แล้วตามด้วยชื่อ method

🧰 เทียบให้เห็นภาพ — ของในบ้านที่มีคู่มือติดตัวมา

ใน Ruby ทุกของในบ้านมาพร้อมคู่มือการใช้ติดตัว · หม้อหุงข้าวมีปุ่ม "หุง" · ทีวีมีปุ่ม "เปิด" · title.upcase คือ "หยิบ title ขึ้นมา แล้วกดปุ่ม upcase ของมัน" · ใน Go ของในบ้านเป็นของเปล่า อยากทำอะไรต้องไปหยิบเครื่องมือจากลิ้นชักมาทำกับมัน (strings.ToUpper(title))

ที่ไม่เหมือนกัน: ของจริงคุณมองเห็นปุ่มทั้งหมดก่อนกด · Ruby ปล่อยให้คุณเขียนกดปุ่มที่ไม่มีได้ แล้วค่อยพังตอนรันถึงบรรทัดนั้น · และของคนละชนิดมีปุ่มไม่เหมือนกัน — ตัวเลขไม่มีปุ่ม upcase

เทียบงานเดียวกันสองภาษา — ผลรันจริงทั้งคู่:

Go — ส่งค่าเข้าฟังก์ชัน

title := "Hello Rails"
fmt.Println(len(title))             // 11
fmt.Println(strings.ToUpper(title)) // HELLO RAILS
fmt.Println(42%2 == 0)           // true

Ruby (irb) — กดปุ่มบนตัวค่าเอง

irb(main):001> title = "Hello Rails"
=> "Hello Rails"
irb(main):002> title.length
=> 11
irb(main):003> title.upcase
=> "HELLO RAILS"
irb(main):004> 42.even?
=> true
irb(main):005> 7.even?
=> false

ใน Go ลองเขียนแบบเดียวกันกับตัวเลขดู: views := 42 แล้ว fmt.Println(views.Even()) — build ไม่ผ่าน (ผลจริง): views.Even undefined (type int has no field or method Even) แปลว่า "int ไม่มีคำสั่งชื่อ Even" · ชนิดพื้นฐานของ Go อย่าง int กับ string ไม่มี method ติดตัว

ขนาด nil (ค่าพิเศษที่แปลว่า "ไม่มีค่า" — เจอในหัวข้อ 5) กับ true ก็เป็น object · ถามได้ด้วย .class ว่า "เป็นของชนิดไหน" — ผลรันจริง:

irb(main):001> 42.class
=> Integer
irb(main):002> "hello".class
=> String
irb(main):003> nil.class
=> NilClass
irb(main):004> true.class
=> TrueClass

Integer = จำนวนเต็ม · String = ข้อความ · NilClass = ชนิดของ nil · TrueClass = ชนิดของ true · ทุกตัวตอบได้ เพราะทุกตัวเป็น object

ถ้าเรียก method ที่ของชนิดนั้นไม่มี จะพังตอนรัน — ผลรันจริง (ตัดบรรทัดชี้ตำแหน่งออก):

# nomethod.rb
views = 42
puts views.upcase

# nomethod.rb:3:in '<main>': undefined method 'upcase' for an instance of Integer (NoMethodError)

อ่านท่อน for an instance of Integer ("กับของชิ้นหนึ่งที่เป็นชนิด Integer") ก่อนเสมอ — มันบอกว่า "ของในมือคุณตอนนี้คือตัวเลข" ซึ่งไม่มีปุ่ม upcase

ต่อจุดเป็นทอด ๆ (chain)

ผลของ method ก็เป็น object อีกตัว จึงต่อจุดไปได้เรื่อย ๆ อ่านจากซ้ายไปขวา · ดูทีละขั้น — ผลรันจริง:

irb(main):001> "  hello rails  ".strip
=> "hello rails"
irb(main):002> "  hello rails  ".strip.upcase
=> "HELLO RAILS"
irb(main):003> "  hello rails  ".strip.upcase.length
=> 11
  1. .strip — ตัดช่องว่างหัวท้ายทิ้ง ได้ "hello rails"
  2. .upcase — เอาผลขั้น 1 มาทำตัวใหญ่ ได้ "HELLO RAILS"
  3. .length — เอาผลขั้น 2 มานับ ได้ 11

ใน Go งานเดียวกันต้องซ้อนฟังก์ชันแล้วอ่านจากในออกนอก: len(strings.ToUpper(strings.TrimSpace(s))) · ท่า chain นี้คุณจะเห็นใน Rails บ่อยมาก

ตัวอย่างจากสไลด์หน้า 16: Post.published.recent.limit(10) · วันนี้ไม่ต้องเข้าใจว่าข้างในทำงานยังไง แค่ให้เห็นว่าเป็นจุดต่อกันแบบเดียวกับข้างบน อ่านซ้ายไปขวาได้ว่า "บทความ → เฉพาะที่เผยแพร่แล้ว → ใหม่สุดก่อน → เอาแค่ 10 ชิ้น"

🗣 ศัพท์ที่เพิ่งเรียนไป
object / method
object = ค่าที่มีคำสั่งติดตัว · method = คำสั่งแต่ละตัวนั้น · "everything is an object" คือประโยคประจำตัวของ Ruby · ถาม AI ได้ว่า "what methods does a Ruby String have for changing letter case?"
method call (dot notation)
การเรียก method ด้วยจุด ค่า.method · ถ้าลงท้าย ? มักคืน true/false
method chaining
ต่อ .method หลายตัวติดกัน แต่ละตัวทำกับผลของตัวก่อนหน้า · พิมพ์บอก AI ได้ว่า "explain this Ruby method chain step by step"
NoMethodError
error ตอนรันเมื่อเรียก method ที่ของชนิดนั้นไม่มี · ดูท่อน for an instance of … ว่าของในมือเป็นชนิดอะไรจริง ๆ

5 · string — ข้อความ และการแทรกค่า

string คือข้อความ · เวลาทำเว็บเราต้องเอาค่าในตัวแปรไปใส่ในข้อความตลอด เช่น "บทความ: ข้าวมันไก่ร้านดัง" · ใน Go ใช้ fmt.Sprintf("บทความ: %s", title) · ใน Ruby เขียน #{ชื่อตัวแปร} ลงในข้อความตรง ๆ

🧰 เทียบให้เห็นภาพ — แบบฟอร์มที่มีช่องว่าง กับป้ายที่พิมพ์ตายตัว

เครื่องหมายคำพูดคู่ "…" = แบบฟอร์มที่มีช่องให้เติม · #{title} คือช่องว่างที่ Ruby เอาค่าของ title มากรอกให้ · เครื่องหมายคำพูดเดี่ยว '…' = ป้ายที่พิมพ์ตายตัว เขียนอะไรไว้ก็ได้แบบนั้นทุกตัวอักษร

ที่ไม่เหมือนกัน: แบบฟอร์มจริงมีเส้นช่องว่างให้เห็นชัด · ใน Ruby สองแบบนี้หน้าตาแทบเหมือนกัน ต่างกันแค่ขีดเดียวหรือสองขีด และถ้าใช้ผิดแบบ Ruby ไม่เตือนเลย

# quote.rb
title = "ข้าวมันไก่ร้านดัง"
puts "บทความ: #{title}"
puts 'บทความ: #{title}'

สองบรรทัดสุดท้ายต่างกันแค่เครื่องหมายคำพูด — พิมพ์อะไรออกมาบ้าง?

ผลรันจริง:

$ ruby quote.rb
บทความ: ข้าวมันไก่ร้านดัง
บทความ: #{title}

บรรทัดสองพิมพ์ #{title} ออกมาตรง ๆ ทั้งก้อน · ไม่มี error ไม่มีคำเตือน — สไลด์หน้า 17 เรียกว่าเป็นกับดักที่คนเพิ่งเริ่มเจอบ่อยที่สุดข้อหนึ่ง · ใน Rails ถ้าพลาดจุดนี้ ผู้ใช้จะเห็นตัวอักษร #{...} โผล่บนหน้าเว็บ · (# ที่อยู่ใน "#{}" ไม่ใช่คอมเมนต์ — มันเป็นส่วนของช่องเติมค่า)

ถ้าลอง single quote ใน irb จะเห็น \ โผล่มา (ผลจริง: 'Hi #{author}' → => "Hi \#{author}") · \ นั่นคือ irb บอกว่า "# ตรงนี้เป็นตัวอักษรธรรมดา" · ไม่ได้อยู่ในข้อความจริง ตอน puts จะไม่เห็น

"ข้อความ" + ตัวเลข = พัง

ต่อข้อความด้วย + ได้เหมือน Go ("a" + "b" ได้ "ab") · แต่ถ้าอีกฝั่งเป็นตัวเลข ทั้งสองภาษาไม่ยอม — ต่างกันแค่ตอนที่ไม่ยอม:

Go — ไม่ยอมตั้งแต่ build

views := 42
s := "อ่านแล้ว " + views
// ./main.go:7:7: invalid operation:
// "อ่านแล้ว " + views
// (mismatched types untyped string and int)

(โชว์แค่ส่วนใน func main())

Ruby — รันได้จนถึงบรรทัดนั้นแล้วพัง

views = 42
puts "อ่านแล้ว " + views
# plus.rb:2:in 'String#+': no implicit
# conversion of Integer into String (TypeError)

(ผมตัด error ยาวบรรทัดเดียวให้เป็นหลายบรรทัดเพื่อให้พอดีจอ ข้อความไม่ได้แก้ · ฝั่ง Ruby ของจริงมีบรรทัด from plus.rb:2:in '<main>' ต่อท้ายอีกบรรทัด ตัดออกให้สั้น) · ฝั่ง Go: untyped string แค่หมายถึงข้อความที่พิมพ์ตรง ๆ ในโค้ด · ใจความคือ "string กับ int คนละชนิด บวกกันไม่ได้"

ฝั่ง Ruby: 'String#+' อ่านว่า "method ชื่อ + ของ String" — ใช่ ใน Ruby แม้แต่ + ก็เป็นปุ่มที่ติดมากับข้อความ · ทั้งบรรทัดแปลว่า "ปุ่ม + ของข้อความไม่แปลงตัวเลขเป็นข้อความให้เอง (TypeError = ใช้ของผิดชนิด)" · แก้ได้สองท่า — ผลรันจริงได้เหมือนกัน:

views = 42
puts "อ่านแล้ว " + views.to_s + " ครั้ง"   # .to_s = แปลงเป็น string เอง
puts "อ่านแล้ว #{views} ครั้ง"             # #{} แปลงให้อัตโนมัติ

# อ่านแล้ว 42 ครั้ง
# อ่านแล้ว 42 ครั้ง

คน Ruby เลือกท่าที่สองแทบทุกครั้ง เพราะ #{} แปลงทุกอย่างเป็นข้อความให้ ไม่ต้องจำว่าค่าไหนเป็นชนิดอะไร

nil — "ไม่มีค่า"

บางทีบทความก็ยังไม่มีคำโปรย · Ruby ใช้ค่าพิเศษชื่อ nil แปลว่า "ไม่มีค่า" · ใน Go ตัวแปร string ที่ยังไม่ใส่ค่าจะเป็น "" (ข้อความว่าง) แต่ใน Ruby คุณจะเจอ nil บ่อยกว่า — ผลรันจริง:

irb(main):001> subtitle = nil
=> nil
irb(main):002> subtitle
=> nil

เอา nil ไปใส่ใน #{} จะได้ช่องว่างเปล่า ไม่พัง: puts "คำโปรย: [#{subtitle}]" พิมพ์ คำโปรย: [] (ผลรันจริง) · บทนี้ขอแค่จำว่า nil = ไม่มีค่า · เรื่องละเอียดและกับดักของมันอยู่ในบทเรียน 94

🗣 ศัพท์ที่เพิ่งเรียนไป
string interpolation
การแทรกค่าลงในข้อความด้วย #{…} · ทำงานในเครื่องหมายคำพูดคู่เท่านั้น · พิมพ์บอก AI ได้ว่า "convert these string + concatenations to Ruby interpolation"
TypeError
error ตอนรันที่บอกว่า "ของชนิดนี้ใช้ตรงนี้ไม่ได้" · มักมาคู่กับ no implicit conversion of X into Y = "ไม่แปลง X เป็น Y ให้เอง ต้องแปลงเอง"
to_s
method แปลงค่าเป็น string (s = string) · ตัวพี่น้องที่จะเจอคือ to_i (เป็นจำนวนเต็ม)
nil
ค่าที่แปลว่า "ไม่มีค่า" · เป็น object ชนิด NilClass · คล้าย nil ของ Go แต่ใน Ruby โผล่บ่อยกว่ามาก · ถาม senior ได้ว่า "ตรงนี้มีโอกาสเป็น nil ไหม" — คำถามที่ช่วยกัน bug ได้เยอะที่สุดใน Ruby

6 · ภารกิจ — ป้ายบทความหนึ่งบรรทัด

🎯 ภารกิจ (~10 นาที)

เราจะจำลองบทความหนึ่งชิ้นด้วยตัวแปรสามตัว แล้วพิมพ์ป้ายบทความออกจอ

  1. ใน terminal รัน ruby -v ให้เห็นว่าขึ้นต้นด้วย ruby 4.
  2. สร้างไฟล์ชื่อ card.rb ใส่ตัวแปร title = " hello rails " (มีช่องว่างหัวท้ายตั้งใจ) · author = "nan" · views = 42
  3. เขียน puts บรรทัดเดียว ให้ title ถูกตัดช่องว่างและเป็นตัวใหญ่ · author ขึ้นต้นตัวใหญ่ (ใช้ method capitalize) · และมียอดอ่าน — ห้ามใช้ +
  4. รัน ruby card.rb ควรเห็น:
    📝 HELLO RAILS โดย Nan · อ่านแล้ว 42 ครั้ง

ถ้าเจอ error นี้ แปลว่า…

# card.rb
title  = "  hello rails  "
author = "nan"
views  = 42

puts "📝 #{title.strip.upcase} โดย #{author.capitalize} · อ่านแล้ว #{views} ครั้ง"

ผลรันจริง:

$ ruby card.rb
📝 HELLO RAILS โดย Nan · อ่านแล้ว 42 ครั้ง
  • ใน #{} ใส่ chain ได้เลย — title.strip.upcase ทำงานเสร็จแล้วค่อยถูกเติมลงช่อง
  • author.capitalize = ทำตัวแรกเป็นตัวใหญ่ ที่เหลือตัวเล็ก
  • views เป็นตัวเลข แต่ #{} แปลงให้เอง ไม่ต้อง .to_s
  • ตัวแปร title ตัวเดิมยังมีช่องว่างอยู่ — method พวกนี้คืนค่าใหม่ ไม่ได้แก้ของเดิม

7 · ทดสอบความเข้าใจ

author = "Nan" แล้วรัน puts 'สวัสดี #{author}' จะเห็นอะไรบนจอ?

ตัวเลือกที่ 3 ถูก · เครื่องหมายคำพูดเดี่ยวคือ "ป้ายพิมพ์ตายตัว" ไม่มีช่องเติมค่า · ตัวเลือกที่ 1 จะถูกถ้าใช้ "…" · ตัวเลือกที่ 2 ผิดเพราะ Ruby ไม่ถือว่าผิด (คุณอาจตั้งใจพิมพ์แบบนั้นจริง) · ตัวเลือกที่ 4 คือสิ่งที่เกิดเมื่อแทรก nil ลงใน "…" ไม่ใช่กรณีนี้

จุดที่ต้องจำ: อยากแทรกค่า ต้องใช้ "…" คำพูดคู่เท่านั้น

เขียน BLOG_NAME = "a" แล้วถัดไปเขียน BLOG_NAME = "b" — Ruby ทำอะไร?

ตัวเลือกที่ 2 ถูก · ผลรันจริงได้ warning: already initialized constant แล้วค่าใหม่ถูกใช้ · ตัวเลือกที่ 1 คือพฤติกรรมของ Go ไม่ใช่ Ruby · ตัวเลือกที่ 3 ผิดเพราะค่าเปลี่ยนจริง · ตัวเลือกที่ 4 ผิดเพราะมีคำเตือนเสมอ — นั่นคือสิ่งเดียวที่ทำให้ "ค่าคงที่" ต่างจากตัวแปรธรรมดา

จุดที่ต้องจำ: ขึ้นต้นตัวใหญ่ = ค่าคงที่ · เปลี่ยนได้แต่โดนเตือน · เห็นคำเตือนนี้ = ควรแก้

ทำไม Ruby เขียน 42.even? ได้ แต่ Go เขียน views.Even() แล้ว build ไม่ผ่าน?

ตัวเลือกที่ 4 ถูก · ตัวเลข 42 ใน Ruby มีปุ่ม even? ติดมา ส่วน int ของ Go เป็นค่าเปล่าไม่มี method · ตัวเลือกที่ 1 ผิด — Go เช็คได้ด้วย 42%2 == 0 แค่ไม่ใช่ method · ตัวเลือกที่ 2 ผิด — 42.class ได้ Integer ไม่ได้แปลงอะไร · ตัวเลือกที่ 3 ผิด — ? เป็นแค่ตัวอักษรหนึ่งในชื่อ method

จุดที่ต้องจำ: Ruby: ค่า.method · Go: ฟังก์ชัน(ค่า)

views = 42 แล้ว puts "อ่านแล้ว " + views พังด้วย TypeError — แก้แบบที่คน Ruby นิยมที่สุดคือข้อไหน?

ตัวเลือกที่ 1 ถูก · #{} แปลงตัวเลขเป็นข้อความให้เอง · ตัวเลือกที่ 2 ยังพังเหมือนเดิม เพราะปัญหาอยู่ที่ + ไม่ใช่ชนิดของเครื่องหมายคำพูด · ตัวเลือกที่ 3 รันได้ แต่ต้องเปลี่ยนยอดอ่านเป็นข้อความ เอาไปบวกเลขต่อไม่ได้ · ตัวเลือกที่ 4 ยังพัง เพราะ views.class ได้ Integer ซึ่งไม่ใช่ string

จุดที่ต้องจำ: เอาค่าใส่ข้อความ ใช้ "…#{ค่า}…" ดีกว่า + เสมอ

💬 งงตรงไหน ถามกลับได้เลย — ก็อปบรรทัดที่งงไปถาม AI ว่า "อธิบายบรรทัด Ruby นี้ทีละส่วน เทียบกับ Go" หรือเปิด irb แล้วลองพิมพ์เอง เป็นวิธีที่เร็วที่สุด

ถ้าเปิดสไลด์ดูประกอบ

หน้าสไลด์เขียนของจริงต้องสนใจไหม
14 mise use -g ruby@4 แล้วได้ ruby 4.0.6 ruby@4 = รุ่น 4 ตัวล่าสุด ณ วันที่ติดตั้ง · เครื่องที่ตรวจได้ 4.0.9 ไม่ต้อง
14 ไม่ได้เขียนขั้น mise activate คู่มือติดตั้ง Rails ให้เพิ่มบรรทัดนี้ลง ~/.zshrc ก่อน ไม่งั้น Mac ใช้ Ruby 2.6 ต้อง ถ้าเพิ่งติดตั้ง
14 mise "โหลด .env ให้เอง" ต้องเขียน [env] _.file = ".env" ใน mise.toml เอง ไม่งั้นไม่โหลด ยังไม่ต้อง (ยังไม่ใช้ .env ใน Day 1)
14 โชว์ mise.toml คู่กับ mise use -g -g จดลงไฟล์ตั้งค่าทั้งเครื่อง (~/.config/mise/config.toml) ไม่ได้สร้าง mise.toml ไม่ต้อง
15 class BlogPost < ApplicationRecord ถูกต้อง แต่ยังไม่ต้องเข้าใจ · class กับ < อยู่บทเรียน 95 · ApplicationRecord อยู่ Day 2 ไม่ต้อง
14 rails -v ได้ Rails 8.1.3 เครื่องที่ตรวจได้ 8.1.4 (รุ่นแก้บั๊ก) ไม่ต้อง

อ้างอิง