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

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

Жанры

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

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

Наследие Маозари 3

Панежин Евгений
3. Наследие Маозари
Фантастика:
рпг
аниме
5.00
рейтинг книги
Наследие Маозари 3

На границе империй. Том 10. Часть 9

INDIGO
Вселенная EVE Online
Фантастика:
боевая фантастика
космическая фантастика
попаданцы
5.00
рейтинг книги
На границе империй. Том 10. Часть 9

Воронцов. Перезагрузка. Книга 3

Тарасов Ник
3. Воронцов. Перезагрузка
Фантастика:
попаданцы
альтернативная история
фэнтези
фантастика: прочее
6.00
рейтинг книги
Воронцов. Перезагрузка. Книга 3

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

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

Гнездо Седого Ворона

Свержин Владимир Игоревич
2. Трактир "Разбитые надежды"
Фантастика:
боевая фантастика
7.50
рейтинг книги
Гнездо Седого Ворона

Леди Малиновой пустоши

Шах Ольга
Любовные романы:
любовно-фантастические романы
6.20
рейтинг книги
Леди Малиновой пустоши

Я царь. Книга XXVIII

Дрейк Сириус
28. Дорогой барон!
Фантастика:
боевая фантастика
аниме
попаданцы
5.00
рейтинг книги
Я царь. Книга XXVIII

Ренегат космического флота

Борчанинов Геннадий
4. Звезды на погонах
Фантастика:
боевая фантастика
космическая фантастика
космоопера
5.00
рейтинг книги
Ренегат космического флота

Светлая тьма. Советник

Шмаков Алексей Семенович
6. Светлая Тьма
Фантастика:
юмористическое фэнтези
городское фэнтези
аниме
сказочная фантастика
фэнтези
5.00
рейтинг книги
Светлая тьма. Советник

Кай из рода красных драконов 2

Бэд Кристиан
2. Красная кость
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Кай из рода красных драконов 2

Назад в СССР 5

Дамиров Рафаэль
5. Курсант
Фантастика:
попаданцы
альтернативная история
6.64
рейтинг книги
Назад в СССР 5

Звездная Кровь. Изгой V

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

Ратник

Ланцов Михаил Алексеевич
3. Помещик
Фантастика:
альтернативная история
7.11
рейтинг книги
Ратник

Отмороженный 5.0

Гарцевич Евгений Александрович
5. Отмороженный
Фантастика:
боевая фантастика
рпг
5.00
рейтинг книги
Отмороженный 5.0