Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Программирование на языке Ruby.docx
Скачиваний:
18
Добавлен:
06.09.2019
Размер:
1.74 Mб
Скачать

Листинг 11.17. Отражение и переменные экземпляра

class SomeClass

 def initialize

  @a = 1

  @b = 2

 end

 def mymeth

  # ...

 end

 protected :mymeth

end

x = SomeClass.new

def

 x.newmeth

 # ...

end

iv = x.instance_variables       # ["@b", "@a"]

p x.methods.size                # 42

p x.public_methods.size         # 41

p x.public_methods(false).size  # 1

p x.private_methods.size        # 71

p x.private_methods(false).size # 1

p x.protected_methods.size      # 1

p x.singleton_methods.size      # 1

Если вы работаете с Ruby уже несколько лет, то заметите, что эти методы немного изменились. Теперь параметры по умолчанию равны true, а не false.

11.3.9. Просмотр стека вызовов

And you may ask yourself:

Well, how did I get here?[13]

Talking Heads, «Once in a Lifetime»

Иногда необходимо знать, кто вызвал метод. Эта информация полезна, если, например, произошло неисправимое исключение. Метод caller, определенный в модуле Kernel, дает ответ на этот вопрос. Он возвращает массив строк, в котором первый элемент соответствует вызвавшему методу, следующий — методу, вызвавшему этот метод, и т.д.

def func1

 puts caller[0]

end

def func2

 func1

end

func2 # Печатается: somefile.rb:6:in 'func2'

Строка имеет формат «файл;строка» или «файл;строка в методе».

11.3.10. Мониторинг выполнения программы

Программа на Ruby может следить за собственным выполнением. У этой возможности есть много применений; интересующийся читатель может заглянуть в исходные тексты программ debug.rb,profile.rb и tracer.rb. С ее помощью можно даже создать библиотеку для «проектирования по контракту» (design-by-contract, DBC), хотя наиболее популярная в данный момент библиотека такого рода этим средством не пользуется.

Интересно, что этот фокус реализован целиком на Ruby. Мы пользуемся методом set_trace_func, который позволяет вызывать указанный блок при возникновении значимых событий в ходе исполнения программы. В справочном руководстве описывается последовательность вызова set_trace_func, поэтому здесь мы ограничимся простым примером:

def meth(n)

 sum = 0

 for i in 1..n

  sum += i

 end

 sum

end

set_trace_func(proc do |event, file, line,

 id, binding, klass, *rest|

 printf "%8s %s:%d %s/%s\n", event, file, line,

  klass, id

 end)

meth(2)

Отметим, что здесь соблюдается стандартное соглашение о заключении многострочного блока в операторные скобки do-end. Круглые скобки обязательны из-за особенностей синтаксического анализатора Ruby. Можно было бы, конечно, вместо этого поставить фигурные скобки.

Вот что будет напечатано в результате выполнения этого кода:

line prog.rb:13 false/

    call prog.rb:1 Object/meth

    line prog.rb:2 Object/meth

    line prog.rb:3 Object/meth

  c-call prog.rb:3 Range/each

    line prog.rb:4 Object/meth

  c-call prog.rb:4 Fixnum/+

c-return prog.rb:4 Fixnum/+

    line prog.rb:4 Object/meth

  c-call prog.rb:4 Fixnum/+

c-return prog.rb:4 Fixnum/+

c-return prog.rb:4 Range/each

    line prog.rb:6 Object/meth

  return prog.rb:6 Object/meth

С этим методом тесно связан метод Kernel#trace_var, который вызывает указанный блок при каждом присваивании значения глобальной переменной.

Предположим, что вам нужно извне протрассировать выполнение программы в целях отладки. Проще всего воспользоваться для этого библиотекой tracer. Пусть имеется следующая программаprog.rb:

def meth(n)

 (1..n).each {|i| puts i}

end

meth(3)

Можно запустить tracer из командной строки:

% ruby -r tracer prog.rb

#0:prog.rb:1::-:       def meth(n)

#0:prog.rb:1:Module:>: def meth(n)

#0:prog.rb:1:Module:<: def meth(n)

#0:prog.rb:8::-:       meth(2)

#0:prog.rb:1:Object:>: def meth(n)

#0:prog.rb:2:Object:-: sum = 0

#0:prog.rb:3:Object:-: for i in 1..n

#0:prog.rb:3:Range:>:  for i in 1..n

#0:prog.rb:4:Object:-: sum += i

#0:prog.rb:4:Fixnum:>: sum += i

#0:prog.rb:4:Fixnum:<: sum += i

#0:prog.rb:4:Object:-: sum += i

#0:prog.rb:4:Fixnum:>: sum += i

#0:prog.rb:4:Fixnum:<: sum += i

#0:prog.rb:4:Range:<:  sum += i

#0:prog.rb:6:Object:-: sum

#0:prog.rb:6:Object:<: sum

Программа tracer выводит номер потока, имя файла и номер строки, имя класса, тип события и исполняемую строку исходного текста трассируемой программы. Бывают следующие типы событий: '-' — исполняется строка исходного текста, '>' — вызов, '<' — возврат, 'С' — класс, 'Е' — конец. (Если вы автоматически включите эту библиотеку с помощью переменной окруженияRUBYOPT или каким-то иным способом, то может быть напечатано много тысяч строк.)