πŸ“¦ Π Π°Π±ΠΎΡ‚Π° с константами ΠΈ ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹ΠΌΠΈ Π² Ruby: ΠΌΠ΅Ρ‚Π°ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ Π±Π΅Π· Π±ΠΎΠ»ΠΈ

ΠšΠΎΠ½ΡΡ‚Π°Π½Ρ‚Ρ‹ ΠΈ ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹Π΅ Π² Ruby β€” это Π½Π΅ просто Ρ…Ρ€Π°Π½ΠΈΠ»ΠΈΡ‰Π° Π΄Π°Π½Π½Ρ‹Ρ…, Π° ΠΏΠΎΠ»Π½ΠΎΡ†Π΅Π½Π½Ρ‹Π΅ инструмСнты мСтапрограммирования. Π’ ΡΡ‚Π°Ρ‚ΡŒΠ΅ Ρ€Π°Π·Π±Π΅Ρ€Ρ‘ΠΌ ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹ для Ρ€Π°Π±ΠΎΡ‚Ρ‹ с Π½ΠΈΠΌΠΈ, ΠΈΡ… ΠΏΠΎΠ΄Π²ΠΎΠ΄Π½Ρ‹Π΅ ΠΊΠ°ΠΌΠ½ΠΈ ΠΈ Ρ€Π΅Π°Π»ΡŒΠ½Ρ‹Π΅ кСйсы примСнСния β€” ΠΎΡ‚ динамичСского создания классов Π΄ΠΎ Ρ…ΠΈΡ‚Ρ€Ρ‹Ρ… фиксов legacy-ΠΊΠΎΠ΄Π°.


πŸ” Π’Π²Π΅Π΄Π΅Π½ΠΈΠ΅: Π·Π°Ρ‡Π΅ΠΌ это Π½ΡƒΠΆΠ½ΠΎ?

ΠŸΡ€Π΅Π΄ΡΡ‚Π°Π²ΡŒΡ‚Π΅: Π²Ρ‹ ΠΎΡ‚ΠΊΡ€Ρ‹Π²Π°Π΅Ρ‚Π΅ старый ΠΏΡ€ΠΎΠ΅ΠΊΡ‚, Π° там…

class Magic
  KLASS = case ENV['MODE']
          when 'prod' then ProductionSpell
          when 'test' then TestSpell
          else DebugSpell
          end
end

И Π½ΡƒΠΆΠ½ΠΎ это ΠΊΠ°ΠΊ-Ρ‚ΠΎ Ρ‚Π΅ΡΡ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ, ΠΌΠ΅Π½ΡΡ‚ΡŒ ΠΈΠ»ΠΈ Π²ΠΎΠΎΠ±Ρ‰Π΅ ΡƒΠ΄Π°Π»ΡΡ‚ΡŒ. Π’ΠΎΡ‚ Ρ‚ΡƒΡ‚-Ρ‚ΠΎ ΠΈ пригодятся ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹ Ρ€Π°Π±ΠΎΡ‚Ρ‹ с константами!


πŸ“Œ ΠšΠΎΠ½ΡΡ‚Π°Π½Ρ‚Ρ‹: динамичСский доступ

const_get / const_set β€” Ρ‡ΠΈΡ‚Π°Π΅ΠΌ ΠΈ пишСм константы

ВСория:
Π­Ρ‚ΠΈ ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹ ΠΏΠΎΠ·Π²ΠΎΠ»ΡΡŽΡ‚ ΠΏΠΎΠ»ΡƒΡ‡Π°Ρ‚ΡŒ ΠΈ ΡƒΡΡ‚Π°Π½Π°Π²Π»ΠΈΠ²Π°Ρ‚ΡŒ константы ΠΏΠΎ строковому ΠΈΠΌΠ΅Π½ΠΈ.

ΠŸΡ€ΠΈΠΌΠ΅Ρ€: ДинамичСскоС созданиС классов для ΠΏΠ»Π°Π³ΠΈΠ½ΠΎΠ²:

# Π—Π°Π³Ρ€ΡƒΠΆΠ°Π΅ΠΌ всС ΠΏΠ»Π°Π³ΠΈΠ½Ρ‹ ΠΈΠ· ΠΏΠ°ΠΏΠΊΠΈ
Dir['plugins/*.rb'].each do |file|
  plugin_name = File.basename(file, '.rb').camelize
  klass = Object.const_set(plugin_name, Class.new(PluginBase))
  klass.load(file)
end

АнтипаттСрн:
Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ const_set для пСрСопрСдСлСния стандартных классов:

# ❌ ΠŸΠ»ΠΎΡ…ΠΎ: сломаСт вСсь String Π² систСмС
Object.const_set(:String, MyWeirdString)

const_defined? β€” ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° Π½Π° сущСствованиС

ВСория:
ΠŸΡ€ΠΎΠ²Π΅Ρ€ΡΠ΅Ρ‚, ΠΎΠΏΡ€Π΅Π΄Π΅Π»Π΅Π½Π° Π»ΠΈ константа Π² Ρ‚Π΅ΠΊΡƒΡ‰Π΅ΠΌ контСкстС.

ΠŸΡ€ΠΈΠΌΠ΅Ρ€: БСзопасная Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠ° ΠΌΠΎΠ΄ΡƒΠ»Π΅ΠΉ:

unless Object.const_defined?(:Redis)
  require 'redis'
  Object.const_set(:Redis, Redis)
end

Π›ΠΎΠ²ΡƒΡˆΠΊΠ°:
ΠœΠ΅Ρ‚ΠΎΠ΄ ΠΏΡ€ΠΈΠ½ΠΈΠΌΠ°Π΅Ρ‚ Π²Ρ‚ΠΎΡ€ΠΎΠΉ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ inherit (ΠΈΡΠΊΠ°Ρ‚ΡŒ Π² Ρ€ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΡΠΊΠΈΡ… классах):

module A; MY_CONST = 1; end
class B; include A; end

B.const_defined?(:MY_CONST) #=> false
B.const_defined?(:MY_CONST, true) #=> true

remove_const β€” ΡƒΠ΄Π°Π»Π΅Π½ΠΈΠ΅ констант

ВСория:
УдаляСт константу ΠΈΠ· Ρ‚Π΅ΠΊΡƒΡ‰Π΅Π³ΠΎ модуля/класса.

ΠŸΡ€ΠΈΠΌΠ΅Ρ€: ΠžΡ‡ΠΈΡΡ‚ΠΊΠ° Π² тСстах:

RSpec.describe MyGem do
  after do
    MyGem.send(:remove_const, :Config) if MyGem.const_defined?(:Config)
  end
end

Π’Π°ΠΆΠ½ΠΎ:
Π£Π΄Π°Π»Π΅Π½ΠΈΠ΅ констант β€” опасная опСрация! Π‘ΡƒΡ‰Π΅ΡΡ‚Π²ΡƒΡŽΡ‰ΠΈΠ΅ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Ρ‹ этого класса останутся Π² памяти.


πŸ—οΈ ΠŸΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹Π΅ экзСмпляра

instance_variable_get / instance_variable_set

ВСория:
Π§Ρ‚Π΅Π½ΠΈΠ΅ ΠΈ запись ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹Ρ… экзСмпляра ΠΏΠΎ ΠΈΠΌΠ΅Π½ΠΈ (Π²ΠΊΠ»ΡŽΡ‡Π°Ρ @).

ΠŸΡ€ΠΈΠΌΠ΅Ρ€: ДСсСриализация:

json = { "@name" => "John", "@age" => 30 }

user = User.new
json.each do |key, value|
  user.instance_variable_set(key, value)
end

АнтипаттСрн:
Π—Π»ΠΎΡƒΠΏΠΎΡ‚Ρ€Π΅Π±Π»Π΅Π½ΠΈΠ΅ Π² production-ΠΊΠΎΠ΄Π΅ вмСсто Π½ΠΎΡ€ΠΌΠ°Π»ΡŒΠ½Ρ‹Ρ… ΠΌΠ΅Ρ‚ΠΎΠ΄ΠΎΠ² доступа.


instance_variables β€” список ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹Ρ…

ВСория:
Π’ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅Ρ‚ массив ΠΈΠΌΠ΅Π½ ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹Ρ… экзСмпляра (с @).

ΠŸΡ€ΠΈΠΌΠ΅Ρ€: ΠžΡ‚Π»Π°Π΄ΠΊΠ° состояния ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π°:

def debug(obj)
  puts "Variables: #{obj.instance_variables.join(', ')}"
end

πŸ›οΈ ΠŸΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹Π΅ класса

class_variable_get / class_variable_set

ВСория:
Аналогично instance-ΠΌΠ΅Ρ‚ΠΎΠ΄Π°ΠΌ, Π½ΠΎ для ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹Ρ… класса (@@).

ΠŸΡ€ΠΈΠΌΠ΅Ρ€: НаслСдуСмый кСш:

class Base
  @@cache = {}

  def self.add_to_cache(key, value)
    class_variable_set(:@@cache, class_variable_get(:@@cache).merge(key => value))
  end
end

ΠžΡΡ‚ΠΎΡ€ΠΎΠΆΠ½ΠΎ:
ΠŸΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹Π΅ класса Π²ΠΈΠ΄Π½Ρ‹ Π²ΠΎ всСй ΠΈΠ΅Ρ€Π°Ρ€Ρ…ΠΈΠΈ наслСдования!


class_variables β€” список ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹Ρ… класса

ВСория:
Π’ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅Ρ‚ массив ΠΈΠΌΠ΅Π½ ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹Ρ… класса.

ΠŸΡ€ΠΈΠΌΠ΅Ρ€: ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° Π½Π° β€œΠ·Π°ΡΠΎΡ€Π΅Π½ΠΈΠ΅β€ класса:

if MyClass.class_variables.any?
  logger.warn "Class variables detected in #{MyClass}"
end

🎯 Π’Ρ‹Π²ΠΎΠ΄: ΠΊΠΎΠ³Π΄Π° это Ρ€Π΅Π°Π»ΡŒΠ½ΠΎ Π½ΡƒΠΆΠ½ΠΎ?

  1. ΠœΠ΅Ρ‚Π°ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅: ΠΏΠ»Π°Π³ΠΈΠ½Ρ‹, динамичСскиС классы
  2. ВСсты: ΠΌΠΎΠΊΠΈ, стабы, очистка состояния
  3. Π˜Π½ΡΡ‚Ρ€ΡƒΠΌΠ΅Π½Ρ‚Ρ‹: Π΄Π΅Π±Π°Π³Π³Π΅Ρ€Ρ‹, сСриализаторы
  4. Legacy-ΠΊΠΎΠ΄: ΠΊΠΎΠ³Π΄Π° Π΄Ρ€ΡƒΠ³ΠΎΠ³ΠΎ Π²Ρ‹Ρ…ΠΎΠ΄Π° Π½Π΅Ρ‚

Π—ΠΎΠ»ΠΎΡ‚ΠΎΠ΅ ΠΏΡ€Π°Π²ΠΈΠ»ΠΎ:
Если ΠΌΠΎΠΆΠ½ΠΎ ΠΎΠ±ΠΎΠΉΡ‚ΠΈΡΡŒ ΠΎΠ±Ρ‹Ρ‡Π½Ρ‹ΠΌΠΈ ΠΌΠ΅Ρ‚ΠΎΠ΄Π°ΠΌΠΈ β€” ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠΉΡ‚Π΅ ΠΈΡ…. Π­Ρ‚ΠΈ инструмСнты β€” ΠΊΠ°ΠΊ хирургичСскиС скальпСли: ΠΌΠΎΡ‰Π½Ρ‹Π΅, Π½ΠΎ опасныС.

# Иногда Π»ΡƒΡ‡ΡˆΠ΅ Ρ‚Π°ΠΊ:
MyClass.const_get(:Settings) #=> πŸš€

# Π§Π΅ΠΌ Ρ‚Π°ΠΊ:
eval "MyClass::Settings" #=> πŸ’₯

πŸ—“ Π”Π°Ρ‚Π° ΠΏΡƒΠ±Π»ΠΈΠΊΠ°Ρ†ΠΈΠΈ: 25.04.2025, Π½ΠΎ это Π½Π΅ Ρ‚ΠΎΡ‡Π½ΠΎ...

Ruby ΠΌΠ΅Ρ‚Π°ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ константы ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹Π΅ рСфлСксия