Программирование на языке Ruby - Хэл Фултон
Шрифт:
Интервал:
Закладка:
В листинге 16.4 приведен код простой программы (даже слишком простой, чтобы ее отлаживать).
Листинг 16.4. Простая программа для демонстрации работы отладчикаSTDOUT.sync = true
def palindrome?(word)
word == word.reverse
end
def signature(w)
w.split("").sort.join
end
def anagrams?(w1,w2)
signature(w1) == signature(w2)
end
print "Give me a word: "
w1 = gets.chomp
print "Give me another word: "
w2 = gets.chomp
verb = palindrome?(w1) ? "is" : "is not"
puts "'#{w1}' #{verb} a palindrome."
verb = palindrome?(w2) ? "is" : "is not"
puts "'#{w2}' #{verb} a palindrome."
verb = anagrams?(w1,w2) ? "are" : "are not"
puts "'{w1}' and '#{w2}' #{verb} anagrams."
В листинге 16.5 показан полный сеанс отладки. Некоторые места не слишком понятны, потому что на консоль выводят данные и сама программа, и отладчик.
Листинг 16.5. Сеанс отладки простой программы$ ruby -rdebug db.rb
Debug.rb
Emacs support available.
db.rb:1:STDOUT.sync = true
(rdb:1) b palindrome?
Set breakpoint 1 at db.rb:palindrome?
(rdb:1) b anagrams?
Set breakpoint 2 at db.rb:anagrams?
(rdb:1) b
Breakpoints:
1 db.rb:palindrome?
2 db.rb:anagrams?
(rdb:1) n
db.rb:3:def palindrome?(word)
(rdb:1) n
db.rb:7:def signature(w)
(rdb:1) n
db.rb:11:def anagrams?(w1,w2)
(rdb:1) n
db.rb:15:print "Give me a word: "
(rdb:1) n
Give me a word: db.rb:16:w1 = gets.chomp
(rdb:1) live
db.rb:16:undefined local variable or method 'live' for main:Object
(rdb:1) n
live
db.rb:18:print "Give me another word: "
(rdb:1) n
Give me another word: db.rb:19:w2 = gets.chomp
(rdb:1) n
evil
db.rb:21:verb = palindrome?(w1) ? "is" : "is not"
(rdb:1) с
Breakpoint 1, palindrome? at db.rb:palindrome?
db.rb:3:def palindrome?(word)
(rdb:1) n
db.rb: 4: word == word.reverse
(rdb:1) word
"live"
(rdb:1) n
db.rb: 22: puts "'#{w1}' #{verb} a palindrome."
(rdb:1) verb "is not"
(rdb:1) n
'live' is not a palindrome.
db.rb:24:verb = palindrome?(w2) ? "is" : "is not"
(rdb:1) n
db.rb:24:verb = palindrome?(w2) ? "is" : "is not"
(rdb:1) n
Breakpoint 1, palindrome? at db.rb:palindrome?
db.rb:3:def palindrome?(word)
(rdb:1) n
db.rb:4: word == word.reverse
(rdb:1) c
'evil' is not a palindrome.
Breakpoint 2, anagrams? at db.rb:anagrams?
db.rb:11:def anagrams?(w1,w2)
(rdb:1) n
db.rb:12: signature(w1) == signature(w2)
(rdb:1) n
db.rb:28:puts "'#{w1}' and '#{w2}' #{verb} anagrams."
(rdb:1) verb
"are"
(rdb:1) c
'live' and 'evil' are anagrams.
Если вы затребуете другие библиотеки, то, возможно, придется в самом начале «перешагнуть» через несколько методов. Я рекомендую перед началом отладки установить где-нибудь в собственном коде точку прерывания, а потом выполнить команду continue для останова в этой точке.
Отладчик понимает и много других команд. Можно просматривать стек вызовов и перемещаться по нему. Можно «наблюдать» за выражениями и автоматически останавливать выполнение, как только выражение изменится. Можно добавлять выражения в «отображаемый список». Поддерживается работа с несколькими потоками и переключение между ними.
Вероятно, полной и качественной документации вы нигде не найдете. Если возникнет такая необходимость, рекомендуется обратиться к оперативной справке и действовать методом проб и ошибок.
Современные отладчики имеют графический интерфейс. Если вам необходим такой инструмент, загляните в главу 21, где обсуждаются интегрированные среды разработки для Ruby.
16.4. Использование irb в качестве отладчика
Библиотеку ruby-breakpoint написал Флориан Гросс (Florian Gross). Этот великолепный, несмотря на малый объем, инструмент позволяет расставлять в программе точки прерывания методом breakpoint. Когда в процессе исполнения встречается точка прерывания, запускается сеанс irb (программа интерактивной работы с Ruby irb подробно рассматривается в главе 21).
Эта библиотека не входит в стандартный дистрибутив. Установить ее можно, например, выполнив команду gem install ruby-breakpoint.
Внесем несколько изменений в программу из листинга 16.4. Поместим в начало директиву require 'breakpoint' и добавим вызов метода breakpoint после обоих обращений к gets:
require 'breakpoint'
# ...
w2 = gets.chomp
breakpoint
# ...
Теперь запустим ее. В следующем протоколе сеанса показано, как мы входим в irb, после чего можем делать все что угодно — в частности, вызывать ранее определенные методы и изменять значения переменных.
$ ruby myprog.rb
Give me a word: parental
Give me another word: prenatal
Executing break point at myprog.rb:23
irb(main):001:0> w1
=> "parental"
irb(main):002:0> w2
=> "prenatal"
irb(main):003:0> palindrome?(w1)
=> false
irb(main):004:0> palindrome?("detartrated")
=> true
irb(main):005:0> signature(w1)
=> "aaelnprt"
irb(main):006:0> quit
'parental' is not a palindrome.
'prenatal' is not a palindrome.
'parental' and 'prenatal' are anagrams.
Особенно подкупает, что отлаживаемая программа может быть не только командной или текстовой. Существует клиент drb (распределенный Ruby), который позволяет удаленно отлаживать программу Ruby, работающую в другом процессе.
Чтобы воспользоваться этой возможностью, нужно включить вызов следующего метода в отлаживаемую программу (естественно, до первого обращения к методу breakpoint):
Breakpoint.activate_drb("druby://127.0.0.1:2001", "localhost")
# Запустить сервер на порту 2001 машины localhost.
Запустите клиент командой breakpoint_client. Каждые три секунды он будет пытаться установить соединение с сервером, пока это не получится или вы не завершите его принудительно.
$ breakpoint_client druby://localhost:2001
No connection to breakpoint service at druby://localhost:2001 (DRb::DRbConnError)
Tries to connect will be made every 3 seconds...
После установления соединения вы можете и не получить приглашение irb. Программа будет выполняться до точки прерывания — вот тогда-то вы и увидите приглашение.
Дополнительную информацию об этой библиотеке поищите в документации, которая входит в комплект поставки.
16.5. Измерение покрытия кода
Очень полезно знать, какие части программы не были протестированы, а следовательно, нуждаются в автономных тестах. Иногда и сам инструмент для замера покрытия может обнаружить ошибки. Допустим, в программе есть предложение if, которое «должно» исполняться примерно в половине всех случаев. Если выясняется, что оно не исполняется никогда, значит, имеет место ошибка.
Командную утилиту rcov (и соответствующую библиотеку) написал Маурисио Фернандес (Mauricio Fernandez). Устанавливается она в виде gem-пакета.
В простейшем случае для ее запуска достаточно указать имя вашей программы в качестве параметра:
rcov myfile.rb
Одновременно с исполнением вашей программы rcov будет собирать статистику. По умолчанию она создает каталог coverage, в котором вы найдете HTML-файлы. В файле index.html представлены сводные результаты и ссылки на исходные тексты, где строки, которые хотя бы раз исполнялись, подсвечены.
Из-за цветового кодирования трудно привести черно-белый снимок с экрана. Но сам инструмент настолько прост, что, потратив пару минут, вы сможете увидеть все сами.
Хотя программа rcov полезна даже в стандартном режиме, она понимает порядка 30 различных параметров. Можно указать каталог для выходных файлов, образцы имен файлов, для которых собирать и не собирать статистику, задать режим сортировки по именам файлов и многое другое. Можно выводить результаты в текстовом виде и даже запросить цветную диаграмму покрытия. Рекомендую прочитать поставляемую документацию, запросить справку командой rcov -h и… получать удовольствие.
Можно использовать rcov и в качестве библиотеки для написания аналогичных инструментов анализа. Ее API состоит из трех основных классов:
• Rcov::FileStatistics позволяет отличить исполняемые предложения от комментариев (и тем самым уточнить статистику покрытия);
• Rcov::CodeCoverageAnalyzer применяется для трассировки выполнения, возвращает информацию о покрытии и счетчики выполненных предложений;
• Rcov::CallSiteAnalyzer нужен для того, чтобы понять, где определены методы и откуда они вызываются.
Обсуждение API далеко выходит за рамки этого раздела. Почитайте документацию и начинайте экспериментировать.
16.6. Измерение производительности