Constant ใน Ruby คืออะไร?
Constant เป็นประเภทของตัวแปรที่ขึ้นต้นด้วยตัวพิมพ์ใหญ่เสมอ สามารถกำหนดได้เฉพาะ นอกเหนือจาก เมธอด เว้นแต่ใช้ metaprogramming
Constants ใช้สำหรับค่าที่ไม่ควรเปลี่ยนแปลง แต Ruby ไม่บังคับให้เปลี่ยนไม่ได้
ลักษณะแบบนี้:
FRUIT = "orange"
ตอนนี้:
เพราะ constants เป็นหัวข้อสำคัญ เราจะสำรวจรายละเอียดในบทความนี้!
วิธีการกำหนด Constants
Constant ไม่ต้องการสัญลักษณ์หรือ syntax พิเศษในการประกาศ แค่ทำให้ตัวอักษรแรกเป็นตัวพิมพ์ใหญ่
ต่อไปนี้เป็น constants ที่ถูกต้อง:
ABC = 1 Goo = 2 Foo = 3
สังเกตว่าไม่สามารถกำหนด constants ภายในเมธอดได้
จะได้ error message นี้:
def the_method ABC = 1 end # "dynamic constant assignment"
จึงควรกำหนด constants นอกเมธอด ปกติจะวางไว้บนสุดของ class เพื่อมองเห็นชัด
class RubyBlog URL = "rubyguides.com" AUTHOR = "Jesus Castello" end
เข้าถึง constants เหล่านี้ได้จากในเมธอดของ class หรือนอก class ด้วย syntax:
p RubyBlog::AUTHOR # "Jesus Castello"
เราจะพูดถึง scope ของ constants ต่อไป ค่อยติดตาม!
Uninitialized Constant Error
Error พบบ่อยคือ:
puts Foo # "uninitialized constant Foo (NameError)"
แปลในใจว่า "constant not found"
จุดสำคัญ: Ruby classes เป็น constants
ตัวอย่าง:
Array String Hash
เป็น constants เพราะตัวอักษรแรกพิมพ์ใหญ่
ตอน define class คุณสร้าง object Class มอบหมายให้ constant ชื่อ class นั้น
ทำไมสำคัญ?
เพราะสาเหตุส่วนใหญ่ของ "uninitialized constant" error คือลืม require ไฟล์หรือ gem ที่กำหนด constant นั้น หรือสะกดชื่อผิด
จงระวัง!
Constants เปลี่ยนแปลงได้
เหมือนกล่าวไว้ Ruby constants เปลี่ยนได้
ตัวอย่าง:
ABC = 1 ABC = 2
แต่จะได้ warning:
2: warning: already initialized constant ABC
โปรแกรมยังทำงานได้ แต่อย่าทำบ่อย
ไม่มีวิธีป้องกันไม่ให้ constant เปลี่ยน เพราะตัวแปรใน Ruby ไม่ใช่ container แค่ pointer ไปยัง object หรือ label ก็ได้
ทำได้ดีสุดคือใช้ immutable objects
ตัวอย่าง:
AUTHOR = "Jesus Castello".freeze AUTHOR << "o" # RuntimeError: can't modify frozen String
บทความเกี่ยวข้อง: Ruby mutability และ freeze method
ในตัวอย่างนี้ ยังเปลี่ยนได้ว่า AUTHOR ชี้ไปไหน freeze ป้องกันแค่การเปลี่ยน object เอง
Constant Methods
มีเมธอดเฉพาะสำหรับจัดการ constants:
| Method | คำอธิบาย |
|---|---|
| constants | คืนค่า array ของ symbols แทน constants ที่กำหนดใน class |
| const_get | คืนค่าของ constant รับ parameter เป็น symbol หรือ string |
| const_set | ตั้งค่า constant รับ 2 parameters: ชื่อ constant เป็น symbol และค่า constant |
| const_missing | เหมือน method_missing แต่สำหรับ constants |
| const_defined? | คืน true ถ้า constant ที่กำหนด (เป็น symbol) ถูก define แล้ว |
| remove_const | ลบ constant |
| private_constant | ทำให้ constant เป็น private เข้าถึงนอก class ด้วย Class::ABC ไม่ได้ |
มี metaprogramming tricks ใช้เมธอดเหล่านี้ได้
ตัวอย่าง:
module Food
class Bacon; end
class Chocolate; end
end
puts "Classes defined inside #{Food}:"
puts Food.constants
ยังใช้ string เช่น "Array" ได้ class จริง:
array_class = Object.const_get("Array")
แต่ระวัง เพราะผู้ใช้อาจ inject code ได้ถ้า string มาจาก params หรือ user input
ใน Rails มี constantize method ทำ const_get ให้ แต่ไม่ทำ security checks
CheatSheet

Ruby Constant Scope
สร้าง constant นอก class ทุกอย่าง ที่ top-level จะเข้าถึงได้ทุกที่
Constants ยังเข้าถึงได้ใน child classes
class A
FOO = 1
end
class B < A
def foo
puts FOO
end
end
B.constants # [:FOO]
Constants ที่กำหนดนอก nested module/class เข้าถึงได้ใน nested classes
module Food
STORE_ADDRESS = "The moon"
class Bacon
def foo; puts STORE_ADDRESS; end
end
end
fb = Food::Bacon.new
fb.foo # "The moon"
Module Mixing
Constants จาก mixed-in modules เข้าถึงได้:
module Mixin A = 123 end class Product include Mixin puts A end # 123
สังเกต: ทำงานตอน include module ไม่ทำงานถ้า extend
ตัวอย่าง:
class Product extend Mixin puts A end # uninitialized constant Product::A
อีกอย่าง:
ใช้เมธอดที่ include จาก module Ruby จะหา constants เริ่มจากที่เมธอดถูก define
ตัวอย่าง:
module Parent
def print_value
VALUE
end
end
class Child
include Parent
VALUE = 1
end
# Works
p Child::VALUE
# uninitialized constant Parent::VALUE
p Child.new.print_value
ควรจำไว้!
Module Nesting
อีกตัวอย่าง nested classes (modules เหมือนกัน)
class A
FOO = 1
end
class A::B
class C
puts FOO
end
end
สังเกต notation A::B ที่ลองใช้เป็น shortcut แต่ class C จะเข้าถึง FOO โดยตรงไม่ได้
จึงควรใช้ nesting แบบนี้:
class A
FOO = 1
end
class A
class B
class C
puts FOO
end
end
end
ตัวอย่างแรก ยังทำ ::A::FOO ได้ แต่ถ้าชื่อ class เปลี่ยนจะ error
Syntax ::A::FOO ทำงานเพราะบอก Ruby ให้หาใน top-level scope ที่ constants เช่น Array และ String อยู่
Video
สรุป
คุณเรียนรู้ Ruby constants ตัวแปรประเภทหนึ่งที่มีพฤติกรรมน่าสนใจ สามารถเปลี่ยนค่าได้แต่จะมี warning
ยังเรียนรู้ว่า class names เป็น constants และควรหลีกเลี่ยง const_get กับ user input
ถ้าชอบโพสต์นี้ อย่าลืมแชร์ ให้คนอื่นเข้าใจ constants ได้