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

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

Жанры

Программирование на языке 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.

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

Мастер 8

Чащин Валерий
8. Мастер
Фантастика:
попаданцы
аниме
фэнтези
5.00
рейтинг книги
Мастер 8

Наследник

Назимов Константин Геннадьевич
3. Травник
Фантастика:
фэнтези
6.80
рейтинг книги
Наследник

Потомок бога

Решетов Евгений Валерьевич
1. Локки
Фантастика:
попаданцы
альтернативная история
аниме
сказочная фантастика
5.00
рейтинг книги
Потомок бога

Мрак

Мартовский Кот
Фантастика:
постапокалипсис
5.00
рейтинг книги
Мрак

Сильнейший Столп Империи. Книга 5

Ермоленков Алексей
5. Сильнейший Столп Империи
Фантастика:
аниме
фэнтези
фантастика: прочее
попаданцы
5.00
рейтинг книги
Сильнейший Столп Империи. Книга 5

Я — Легион

Злобин Михаил
3. О чем молчат могилы
Фантастика:
боевая фантастика
7.88
рейтинг книги
Я — Легион

Андер Арес

Грехов Тимофей
1. Андер Арес
Фантастика:
рпг
аниме
фэнтези
фантастика: прочее
5.00
рейтинг книги
Андер Арес

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

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

Душелов

Faded Emory
1. Внутренние демоны
Фантастика:
боевая фантастика
аниме
5.00
рейтинг книги
Душелов

Гранит науки. Том 1

Зот Бакалавр
1. Героями не становятся, ими умирают
Фантастика:
фэнтези
боевая фантастика
5.25
рейтинг книги
Гранит науки. Том 1

Черный дембель. Часть 2

Федин Андрей Анатольевич
2. Черный дембель
Фантастика:
попаданцы
альтернативная история
4.25
рейтинг книги
Черный дембель. Часть 2

Шайтан Иван 5

Тен Эдуард
5. Шайтан Иван
Фантастика:
попаданцы
альтернативная история
историческое фэнтези
5.00
рейтинг книги
Шайтан Иван 5

Тринадцатый VII

NikL
7. Видящий смерть
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Тринадцатый VII

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

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