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

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

Жанры

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

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

Люди и нелюди

Бубела Олег Николаевич
2. Везунчик
Фантастика:
фэнтези
попаданцы
9.18
рейтинг книги
Люди и нелюди

Студент из прошлого тысячелетия

Еслер Андрей
2. Соприкосновение миров
Фантастика:
героическая фантастика
попаданцы
аниме
5.00
рейтинг книги
Студент из прошлого тысячелетия

Петля, Кадетский корпус. Книга восьмая

Алексеев Евгений Артемович
8. Петля
Фантастика:
боевая фантастика
попаданцы
аниме
5.00
рейтинг книги
Петля, Кадетский корпус. Книга восьмая

Курсант поневоле

Шелег Дмитрий Витальевич
1. Кровь и лёд
Фантастика:
попаданцы
аниме
5.00
рейтинг книги
Курсант поневоле

Чужак из ниоткуда 3

Евтушенко Алексей Анатольевич
3. Чужак из ниоткуда
Фантастика:
космическая фантастика
альтернативная история
5.00
рейтинг книги
Чужак из ниоткуда 3

Хозяин Теней 2

Петров Максим Николаевич
2. Безбожник
Фантастика:
попаданцы
аниме
фэнтези
5.00
рейтинг книги
Хозяин Теней 2

Мы – Гордые часть 8

Машуков Тимур
8. Стальные яйца
Фантастика:
попаданцы
аниме
фэнтези
5.00
рейтинг книги
Мы – Гордые часть 8

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

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

Князь Андер Арес 2

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

Рубежник

Билик Дмитрий Александрович
1. Бедовый
Фантастика:
юмористическая фантастика
городское фэнтези
мистика
5.00
рейтинг книги
Рубежник

Бестужев. Служба Государевой Безопасности. Книга третья

Измайлов Сергей
3. Граф Бестужев
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Бестужев. Служба Государевой Безопасности. Книга третья

Газлайтер. Том 10

Володин Григорий
10. История Телепата
Фантастика:
боевая фантастика
5.00
рейтинг книги
Газлайтер. Том 10

Личный аптекарь императора. Том 6

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

Мрак

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