4

การจัดการข้อความและข้อมูลที่มีโครงสร้าง

โค้ด Ruby ใช้ร่วมจากโฟลเดอร์ en/ ของต้นฉบับ เพื่อให้สองภาษาผูกกับชุดทดสอบเดียวกัน

th/topic_04_strings_symbols_ranges_regex

ภาพรวม

ทำไมหัวข้อนี้จึงสำคัญ

งานซอฟต์แวร์จริงจำนวนมากคือการทำงานกับข้อความ เช่น ตรวจข้อมูลนำเข้า ปรับรูปแบบ string เปลี่ยน label ให้เป็น URL และจัดการ key ที่มีโครงสร้าง Ruby เด่นในงานแบบนี้ เพราะ strings และ regular expressions เป็นเครื่องมือที่ทั้งใช้งานจริงและเข้าถึงได้

สิ่งที่ผมอยากให้คุณทำได้เมื่อจบหัวข้อนี้

เมื่อจบหัวข้อนี้ คุณควรจะ:

  • อธิบายบทบาทของ symbols ในฐานะตัวระบุที่เบาและชัดได้
  • ใช้ ranges เพื่อแทนลำดับที่รวมปลายได้อย่างชัดเจน
  • ใช้ regular expressions กับงานตรวจสอบที่ตรงไปตรงมาได้
  • ปรับข้อความให้อยู่ในรูปที่ปลอดภัยสำหรับ URL แบบง่าย ๆ ได้
  • อธิบายเส้นแบ่งระหว่าง regex ที่มีประโยชน์กับ regex ที่ซับซ้อนเกินไปได้

จุดที่ผมใช้ดูความเข้าใจ

ผมอยากให้คุณแก้ปัญหาเรื่องข้อความทั่วไปได้อย่างชัดเจน โดยไม่ทำให้โค้ดกลายเป็นเวทมนตร์ ของ pattern ที่อ่านไม่ออก

โน้ตสั้น

Ruby มักให้ความรู้สึกดีเวลาโค้ดต้องจัดรูป ปรับแต่ง และตรวจสอบ strings แต่นั่นไม่ได้ หมายความว่าปัญหาทุกอย่างเกี่ยวกับข้อความควรถูกทำให้ฉลาดเกินจำเป็น เป้าหมายของหัวข้อ นี้คือการจัดการ string แบบใช้งานได้จริง และยังบอกเจตนาได้ชัด

สิ่งที่ผมอยากให้คุณสังเกต:

  • ranges สื่อช่วงที่รวมปลายได้เป็นธรรมชาติ
  • regex ช่วยตรวจสอบรูปแบบที่พบได้บ่อยอย่างกระชับ
  • การ normalize ข้อความเพียงเล็กน้อยก็ช่วยงานสายเว็บได้มาก
  • symbols มักทำให้ hashes ดูสะอาดขึ้น เมื่อ key เป็นตัวระบุที่คงที่

จุดที่ Ruby ทำได้ดีในหัวข้อนี้:

  • การจัดการข้อความทีละขั้นแบบง่าย ๆ มักอ่านได้เป็นธรรมชาติ
  • Ruby มี regex อยู่ในตัวภาษาอยู่แล้ว
  • transformation สั้น ๆ ก็ยังสื่อจุดประสงค์ทางธุรกิจได้ชัด

จุดที่ต้องระวังในหัวข้อนี้:

  • regex จะกลายเป็นโค้ดที่เขียนเสร็จแล้วอ่านไม่ออก ถ้าใช้เกินพอดี
  • กฎสำหรับจัดระเบียบข้อความต้องมีขอบเขตชัด ไม่อย่างนั้นจะเริ่มไม่สม่ำเสมอ

คำถามชวนคิด:

  • เมื่อไร regex เป็นคำตอบที่ชัดเจน และเมื่อไรกลายเป็นภาระในการดูแลรักษา

ตัวอย่างแบบลงมือดู

Example 1: ตรวจข้อมูลนำเข้าที่พบได้บ่อย

การตรวจรูปแบบอีเมลเป็นตัวอย่างสอนที่ดี เพราะทีมแทบทุกทีมต้องเจองานแบบนี้อยู่แล้ว แต่ ในแบบฝึกหัดของคอร์ส เราจะตั้งใจกำหนดกฎให้พอดี ไม่พยายามไล่ความสมบูรณ์ทุกกรณี

บทเรียนของตัวอย่างนี้ไม่ใช่ "สร้างระบบตรวจอีเมลที่สมบูรณ์แบบ" แต่คือ "ใช้ regex กับ กฎเฉพาะจุดที่สมเหตุสมผล และรู้ขอบเขตของมัน"

Example 2: สร้าง URL slug

การทำ slug เป็นตัวอย่างที่พบได้จริงใน blogs, CMS systems และ admin tools

slugifier.slugify("Ruby for Java Developers")
# => "ruby-for-java-developers"
worked_examples.md
ruby

เหตุผลที่ตัวอย่างนี้มีประโยชน์:

  • คุณมองเห็นการแปลงข้อมูลได้ทีละขั้น
  • ผลลัพธ์มีความหมายจริงใน web applications
  • โค้ดนี้แสดงจุดแข็งของ Ruby ในการจัดการข้อความแบบต่อเป็นขั้นสั้น ๆ

โพยสั้น

Range

(1..5).to_a
cheatsheet.md
ruby

regex สำหรับรูปแบบอีเมล

EMAIL_REGEX = /\A[^\s@]+@[^\s@]+\.[^\s@]+\z/
cheatsheet.md
ruby

ลำดับแปลงข้อความสำหรับทำ slug

text.downcase.strip
    .gsub(/\s+/, "-")
    .gsub(/[^a-z0-9-]/, "")
cheatsheet.md
ruby

จุดที่ควรจำ

  • \A และ \z ใช้ยึดทั้ง string
  • .. คือ range แบบรวมปลาย
  • ถ้าเป็น validation ทั่วไป ให้เลือก regex ที่อ่านได้ก่อน

คู่มือการเรียน

จุดประสงค์ของหัวข้อนี้

หัวข้อนี้อยากให้คุณเห็นว่า Ruby จัดการงานที่เกี่ยวกับข้อความได้สะอาดและใช้งานจริงได้ โดยไม่ต้องโชว์ความซับซ้อนของ regex เกินจำเป็น

ลำดับที่ผมแนะนำ

  1. อ่าน overview.md
  2. อ่าน shortnote.md แล้วสังเกตว่าตรงไหน regex ควรใช้แค่พอดี
  3. อ่าน worked_examples.md
  4. เปิด cheatsheet.md ไว้ตอนอ่าน example.rb
  5. ทำแบบฝึกหัดเรื่อง range
  6. ทำแบบฝึกหัดเรื่อง slug แล้วอธิบายแต่ละขั้นของการแปลงข้อมูล

สิ่งที่ผมอยากให้คุณสังเกต

  • การเขียน range กระชับแต่ยังอ่านได้
  • regex เหมาะกับกฎตรวจสอบสั้น ๆ ที่อยู่เฉพาะจุด
  • การแปลงข้อมูลทีละขั้นที่ดีควรอธิบายได้ทีละบรรทัด

คำถามที่อยากให้คุณพกไว้

  • ทำไมตัวอย่าง slug ถึงสอนได้ดีกว่าแบบฝึกหัด string ที่สมมติขึ้นลอย ๆ
  • อะไรจะทำให้วิธีใช้ regex นี้เริ่มเปราะเกินไปสำหรับงาน production
  • เมื่อไร key แบบ symbol เหมาะกว่าการใช้ string อิสระ

Source Files and Tests

โค้ด Ruby ใช้ร่วมจากโฟลเดอร์ en/ ของต้นฉบับ เพื่อให้สองภาษาผูกกับชุดทดสอบเดียวกัน

# EXAMPLE CODE
# Topic: topic_04_strings_symbols_ranges_regex
#
# Purpose:
# - This file demonstrates reference implementation for the concept.
# - It should pass tests from the beginning.
# - Read and understand it before solving exercises.

class Validator
  EMAIL_REGEX = /\A[^\s@]+@[^\s@]+\.[^\s@]+\z/

  def valid_email?(value)
    !!(value =~ EMAIL_REGEX)
  end
end
en/topic_04_strings_symbols_ranges_regex/example.rb
Ruby course source
# STUDENT TASK (BASIC)
# Topic: topic_04_strings_symbols_ranges_regex
#
# What to do:
# - Implement or improve the class/methods in this file.
# - Read tests in tests/topic_04_strings_symbols_ranges_regex_spec.rb under the "basic exercise" examples.
# - Make tests pass without breaking the example/advanced sections.
#
# Expected outcome:
# - You can run this topic tests and see all examples green after implementation.

class RangeBuilder
  def inclusive(a, b)
    (a..b).to_a
  end
end
en/topic_04_strings_symbols_ranges_regex/basic_exercise.rb
Ruby course source
# STUDENT TASK (ADVANCED)
# Topic: topic_04_strings_symbols_ranges_regex
#
# Academic purpose:
# - Practice a realistic text-normalization task rather than isolated string syntax.
# - Learn how Ruby can express a multi-step text transformation compactly and readably.
#
# Real-world use case:
# - Slugs appear in blog platforms, CMS tools, documentation systems, and admin panels.
# - Teams use them to convert human-readable titles into URL-friendly identifiers.
# - This is exactly the sort of small but common feature where Ruby feels elegant.
#
# Why Ruby is beautiful here:
# - String methods chain naturally.
# - Each transformation step reflects a concrete formatting decision.
# - The code is short enough to scan but still close to the product need.
#
# What to do:
# - Complete the challenge behavior requested by the guide.
# - Be able to explain what each `gsub` removes or reshapes.
# - Use tests in tests/topic_04_strings_symbols_ranges_regex_spec.rb under the "advanced exercise" examples.
#
# Expected outcome:
# - Advanced tests pass and you can connect the implementation to a web-facing use case.

class Slugifier
  def slugify(text)
    text.downcase.strip.gsub(/\s+/, "-").gsub(/[^a-z0-9-]/, "")
  end
end
en/topic_04_strings_symbols_ranges_regex/adv_exercise.rb
Ruby course source
# ANSWER KEY (BASIC)
# Topic: topic_04_strings_symbols_ranges_regex
#
# Solution idea:
# - Ruby ranges are inclusive with `..`.
# - Convert the range to an array because the tests want the concrete sequence.

class RangeBuilder
  def inclusive(a, b)
    (a..b).to_a
  end
end
en/topic_04_strings_symbols_ranges_regex/answer_basic_exercise.rb
Ruby course source
# ANSWER KEY (ADVANCED)
# Topic: topic_04_strings_symbols_ranges_regex
#
# Solution idea:
# - Normalize case first.
# - Remove surrounding whitespace.
# - Collapse internal whitespace into hyphens.
# - Strip any remaining characters that are not slug-safe for this exercise.

class Slugifier
  def slugify(text)
    text.downcase.strip.gsub(/\s+/, "-").gsub(/[^a-z0-9-]/, "")
  end
end
en/topic_04_strings_symbols_ranges_regex/answer_adv_exercise.rb
Ruby course source
# This spec is your learning companion for topic_04_strings_symbols_ranges_regex.
#
# How to use this file:
# 1) Run tests and observe failures/successes.
# 2) Keep EXAMPLE specs green from the beginning.
# 3) Implement BASIC exercise until BASIC specs pass.
# 4) Implement ADVANCED exercise and pass edge cases.
#
# Expected final result:
# - All examples in this file pass.
# - You understand both the concept and the implementation tradeoffs.

require_relative "../example"
require_relative "../basic_exercise"
require_relative "../adv_exercise"

RSpec.describe "topic_04_strings_symbols_ranges_regex" do
  describe "EXAMPLE purpose: understand the reference implementation" do
      it "validates email formats" do
        v = Validator.new
        expect(v.valid_email?("a@b.com")).to eq(true)
        expect(v.valid_email?("x y@z.com")).to eq(false)
      end
  end

  describe "BASIC EXERCISE purpose: implement the comparable task" do
      it "creates inclusive ranges" do
        expect(RangeBuilder.new.inclusive(1, 3)).to eq([1, 2, 3])
      end
  end

  describe "ADVANCED EXERCISE purpose: solve challenge and edge cases" do
      it "slugifies text" do
        expect(Slugifier.new.slugify(" Hello Ruby 3.4! ")).to eq("hello-ruby-34")
      end
  end
end
en/topic_04_strings_symbols_ranges_regex/tests/topic_04_strings_symbols_ranges_regex_spec.rb
Ruby course source
#!/usr/bin/env bash
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"

exec "${ROOT_DIR}/run_tests.sh" "$(basename "${SCRIPT_DIR}")"
en/topic_04_strings_symbols_ranges_regex/run_topic_tests.sh
Ruby course source

Study Prompts

  1. อ่าน test ก่อน แล้วบอกให้ได้ว่าพฤติกรรมใดเป็น example, basic exercise และ advanced exercise

  2. ลองทำแบบฝึกหัดก่อนเปิด answer files แล้วจดว่าคำตอบต่างจากวิธีคิดแรกของคุณตรงไหน