Ruby / Basics /

Constants

Константа - это тип переменной, которая всегда начинается с заглавной буквы. Они могут быть определены только вне методов, если только не используется метапрограммирование. Константы используются для значений, которые не должны меняться, но Ruby не запрещает их изменять.


Для объявления константы не требуется никаких специальных символов или синтаксиса. Нужно лишь, чтобы первая буква была заглавной.

ABC = 1
Goo = 2
Foo = 3

Константу можно определить только внутри класса или модуля(обычно в верхней части) -

class Blog
URL = "apecherin.dev"
AUTHOR = "Artem Pecherin"
end

module ExternalService
API_URL = "api.com"
end

Нельзя определять константу внутри метода -

def the_method
ABC = 1
end

# syntax error found (SyntaxError)
# "dynamic constant assignment"

Доступ к значению константы -

p Blog::AUTHOR

# "Artem Pecherin"

Можно переопределить значение константы. В результате будет предупреждающее сообщение, программа будет работать, но лучше избегать этого.

ABC = 1
ABC = 2

# warning: already initialized constant ABC

Поэтому любую константу лучше делать неизменяемой при помощи метода #freeze

AUTHOR = "Artem Pecherin".freeze
AUTHOR << "o"

# RuntimeError: can't modify frozen String


Области видимости константы

Если вы константа определяется вне какого-либо класса, на верхнем уровне кода, эта константа будет доступна везде. Константы также доступны в дочерних классах -

class A
FOO = 1
end

class B < A
def foo
puts FOO
end
end

B.constants

# [:FOO]

Константы, определенные вне вложенного модуля или класса, также доступны внутри вложенных классов -

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 Mixin
A = 123
end

class Product
include Mixin

puts A
end

# 123

Но константа недоступна, когда происходит расширение класса -

class Product
extend Mixin

puts A
end

# uninitialized constant Product::A

Так же, когда используется метод, включенный в модуль, Ruby будет искать константы, начиная с того места, где определен этот метод -

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

Так же, есть особенность в работе констант при вложении классов(это же касается и модулей). Необходимо избегать сокращения при определении класса -

class A
FOO = 1
end

class A::B
class C
puts FOO
end
end

# uninitialized constant A::B::C::FOO (NameError)

class A
FOO = 1
end

class A
class B
class C
puts FOO
end
end
end

# 1