Чтение онлайн

на главную - закладки

Жанры

Программирование на языке Ruby
Шрифт:

Если написанная вами реализация

method_missing
не хочет обрабатывать конкретный вызов, она должна вызвать
super
, а не возбуждать исключение. Тогда методы
method_missing
в суперклассах получат возможность разобраться с ситуацией. В конечном счете будет вызван
method_missing
, определенный в классе
Object
, который и возбудит исключение.

11.3.13. Отслеживание изменений в определении класса или объекта

А зачем, собственно? Кому интересны изменения, которым

подвергался класс?

Одна возможная причина — желание следить за состоянием выполняемой программы на Ruby. Быть может, мы реализуем графический отладчик, который должен обновлять список методов, добавляемых «на лету».

Другая причина: мы хотим вносить соответствующие изменения в другие классы. Например, мы разрабатываем модуль, который можно включить в определение любого класса. С момента включения будут трассироваться любые обращения к методам этого класса. Что-то в этом роде:

class MyClass

 include Tracing

 def one

 end

 def two(x, y)

 end

end

m = MyClass.new

m.one # Вызван метод one. Параметры =

m.two(1, 'cat') # Вызван метод two. Параметры = 1, cat

Он должен работать также для всех подклассов трассируемого класса:

class Fred < MyClass

 def meth(*a)

 end

end

Fred.new.meth{2,3,4,5) # вызван метод meth. Параметры =2, 3, 4, 5

Возможная реализация такого модуля показана в листинге 11.18.

Листинг 11.18. Трассирующий модуль

module Tracing

 def Tracing.included(into)

into.instance_methods(false).each { |m|

Tracing.hook_method(into, m) }

def into.method_added(meth)

unless @adding

@adding = true

Tracing.hook_method(self, meth)

@adding = false

end

end

 end

 def Tracing.hook_method(klass, meth)

klass.class_eval do

alias_method "old_#{meth}", "#{meth}"

define_method(meth) do |*args|

puts "Вызван метод #{meth}. Параметры = #{args.join(', ')}"

self.send("old_#{meth}",*args)

end

end

 end

end

class MyClass

 include Tracing

 def first_meth

 end

 def second_meth(x, y)

 end

end

m = MyClass.new

m.first_meth #
Вызван метод first_meth. Параметры =

m.second_meth(1, 'cat') # Вызван метод second_meth. Параметры = 1, cat

В этом коде два основных метода. Первый,

included
, вызывается при каждой вставке модуля в класс. Наша версия делает две вещи: вызывает метод
hook_method
каждого метода, уже определенного в целевом классе, и вставляет определение метода
method_added
в этот класс. В результате любой добавленный позже метод тоже будет обнаружен и для него вызван
hook_method
. Сам метод
hook_method
работает прямолинейно. При добавлении метода ему назначается синоним
old_name
. Исходный метод заменяется кодом трассировки, который выводит имя и параметры метода, а затем вызывает метод, к которому было обращение.

Обратите внимание на использование конструкции

alias_method
. Работает она почти так же, как
alias
, но только для методов (да и сама является методом, а не ключевым словом). Можно было бы записать эту строку иначе:

# Еще два способа записать эту строку...

# Символы с интерполяцией:

alias_method :"old_#{meth}", :"#{meth}"

# Преобразование строк с помощью to_sym:

alias_method "old_#{meth}".to_sym, meth.to_sym

Чтобы обнаружить добавление нового метода класса в класс или модуль, можно определить метод класса

singleton_method_added
внутри данного класса. (Напомним, что синглетный метод в этом смысле — то, что мы обычно называем методом класса, поскольку Class — это объект.) Этот метод определен в модуле
Kernel
и по умолчанию ничего не делает, но мы можем переопределить его, как сочтем нужным.

class MyClass

 def MyClass.singleton_method_added(sym)

puts "Добавлен метод #{sym.to_s} в класс MyClass."

 end

 def MyClass.meth1 puts "Я meth1."

 end

end

def MyClass.meth2

 puts "А я meth2."

end

В результате выводится следующая информация:

Добавлен метод singleton_method_added в класс MyClass.

Добавлен метод meth1 в класс MyClass.

Поделиться:
Популярные книги

Изгой Проклятого Клана. Том 5

Пламенев Владимир
5. Изгой
Фантастика:
аниме
фэнтези
попаданцы
5.00
рейтинг книги
Изгой Проклятого Клана. Том 5

Как я строил магическую империю 11

Зубов Константин
11. Как я строил магическую империю
Фантастика:
аниме
фэнтези
попаданцы
5.00
рейтинг книги
Как я строил магическую империю 11

Неудержимый. Книга XXX

Боярский Андрей
30. Неудержимый
Фантастика:
аниме
фэнтези
фантастика: прочее
попаданцы
5.00
рейтинг книги
Неудержимый. Книга XXX

Дворянин

Злотников Роман Валерьевич
2. Император и трубочист
Фантастика:
боевая фантастика
альтернативная история
5.00
рейтинг книги
Дворянин

#Бояръ-Аниме. Газлайтер. Том 37

Володин Григорий Григорьевич
37. История Телепата
Фантастика:
фэнтези
аниме
боевая фантастика
5.00
рейтинг книги
#Бояръ-Аниме. Газлайтер. Том 37

Древесный маг Орловского княжества

Павлов Игорь Васильевич
1. Орловское княжество
Фантастика:
аниме
фэнтези
фантастика: прочее
попаданцы
5.00
рейтинг книги
Древесный маг Орловского княжества

Двойник короля 17

Скабер Артемий
17. Двойник Короля
Фантастика:
аниме
фэнтези
попаданцы
5.00
рейтинг книги
Двойник короля 17

Последний Паладин. Том 6

Саваровский Роман
6. Путь Паладина
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Последний Паладин. Том 6

Вперед в прошлое 11

Ратманов Денис
11. Вперед в прошлое
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Вперед в прошлое 11

Черный Маг Императора 17

Герда Александр
17. Черный маг императора
Фантастика:
юмористическое фэнтези
попаданцы
аниме
фэнтези
фантастика: прочее
5.00
рейтинг книги
Черный Маг Императора 17

Законы Рода. Том 3

Мельник Андрей
3. Граф Берестьев
Фантастика:
фэнтези
аниме
5.00
рейтинг книги
Законы Рода. Том 3

Адвокат

Константинов Андрей Дмитриевич
1. Бандитский Петербург
Детективы:
боевики
8.00
рейтинг книги
Адвокат

Эпоха Опустошителя. Том I

Павлов Вел
1. Вечное Ристалище
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Эпоха Опустошителя. Том I

Имперец. Том 5

Романов Михаил Яковлевич
4. Имперец
Фантастика:
попаданцы
альтернативная история
аниме
6.00
рейтинг книги
Имперец. Том 5