Ruby предлагает мощные инструменты метапрограммирования, такие как define_method и define_singleton_method, которые позволяют динамически создавать методы во время выполнения. В этой статье разберём ключевые различия между ними, типичные ошибки и практические примеры использования в Rails-приложениях. Вы узнаете, как правильно применять эти методы для DRY-кода и гибкой настройки объектов.
Метапрограммирование — это когда ты создаёшь методы, пока никто не видит. Ruby позволяет делать это двумя способами: define_method и define_singleton_method. На собеседованиях любят спрашивать: “А в чём разница?” — и смотрят, как ты вспоминаешь статью вроде этой.
🏷 define_method
Добавляет инстанс-метод в класс или модуль.
class Robot
define_method(:greet) do |name|
"Hello, #{name}!"
end
end
puts Robot.new.greet("Ruby") # => Hello, Ruby!
💡 Важно: метод определяется на уровне класса, но работает на экземплярах.
🧍 define_singleton_method
Добавляет метод только одному конкретному объекту — его singleton-методу.
robot = Object.new
robot.define_singleton_method(:greet) do
"I'm unique!"
end
puts robot.greet # => I'm unique!
💡 Этот метод нельзя вызвать на уровне class, только на объекте.
🧠 Ключевые отличия
| define_method | define_singleton_method | |
|---|---|---|
| Где определяет метод | В классе/модуле | В singleton-классе объекта |
| Работает на… | Экземплярах класса | Только на данном объекте |
| Может ли быть у класса | Да (class << self) |
Да (если вызвать на объекте) |
Контекст self |
Экземпляр при вызове | Тот самый объект |
🧨 Подводные камни
define_methodне принимаетreturnв блоке — будетLocalJumpError.- Нельзя использовать обычные
defвнутриdefine_method— только блок. - При попытке вызвать
define_singleton_methodв теле класса: 💥NoMethodError.
🎓 Примеры использования
define_method — генерация DRY-методов
[:title, :author, :year].each do |field|
define_method("get_#{field}") do
instance_variable_get("@#{field}")
end
end
define_singleton_method — конфигурация на лету
config = {}
config.define_singleton_method(:set) do |key, value|
self[key] = value
end
config.set(:theme, :dark)
🔧 Для классов тоже можно
class << MyClass
define_method(:greet) { "hi" }
end
Это добавит метод класса, потому что class << self открывает singleton class.
🔚 Вывод:
define_method — это про шаблоны и автоматизацию. define_singleton_method — про кастомную магию. Оба мощные. Оба опасны в руках у разработчика без кофеина.
Следующая тема: переменные класса, экземпляра и глобальные — да начнётся хаос с @, @@ и $.