Хэл Фултон - Программирование на языке Ruby
class MyClass
attr_accessor :alpha, :beta
def initialize(a, b)
@alpha, @beta = a, b
end
end
obj1 = MyClass.new(23,34)
obj2 = MyClass.new("abc","xyz")
Тогда можно сделать следующее:
$ irb -rfoo
irb(main):001:0> obj = MyClass.new(88,99)
=> #
Отметим, что хотя обращаться к сущностям, определенным в файле (например, к константе MyClass) мы можем, это не относится к локальным переменным. Локальные переменные доступны только в самом файле, require (выполненное хоть внутри, хоть вне irb) доступа к ним не дает.
Новичков часто смущает информация, выводимая irb:
$ irb -rfoo
irb(main):001:0> puts "hello"
hello
=> nil
Позвольте, что тут делает nil? Разумеется, это значение, возвращенное методом puts.
Еще один источник недоразумений — метод eval. Взгляните на следующий сеанс:
$ irb
irb (main) : 001:0> eval("var = 567")
=> 567
irb(main) :002:0> eval("var")
=> 567
irb(main):003:0> var
=> 567
Вас ничего не удивляет? Но давайте запустим следующий сценарий и посмотрим, что произойдет:
р eval("var = 567")
р eval("var")
р var
# Results:
# 567
# 567
# temp.rb:3: undefined local variable or method 'var' for main:Object
# (NameError)
У Ruby есть такая особенность: когда вы выполняете eval, а вслед за ним еще один, они в некотором смысле разделяют «общую область видимости». Поэтому к переменной, определенной в первой строке, можно обратиться во второй (вне или внутри irb). Но различие проявляется, когда мы попытаемся обратиться к той же переменной без использования eval. В irb это работает, а в сценарии мы получаем ошибку. Что происходит?
Поведение сценария следует считать более правильным. Не забывайте, что сама программа irb написана на Ruby; здравый смысл подсказывает, что она, скорее всего, внутри вызывает eval. Но мы только что убедились, что результат применения eval может отличаться от того, что мы видим на верхнем уровне, поэтому исполнение кода внутри irb не всегда идентично исполнению такого же кода в сценарии. Об этом не следует забывать, особенно если вы ставите какие-нибудь экзотические эксперименты.
Имейте в виду, что irb настраивается в широких пределах. При запуске она читает все инициализационные файлы, которые может найти, в следующем порядке:
• файл ~/.irbrc;
• файл .irbrc;
• файл irb.rс;
• файл _irbrc;
• путь, указанный в переменной окружения $irbrc.
Инициализационные файлы пишутся на Ruby. В них можно настраивать текст приглашения и многое другое. Подробно эти файлы обсуждаются в книге «Programming Ruby». Ниже мы сделаем лишь несколько замечаний.
Если ваша версия Ruby собрана с поддержкой библиотеки GNU readline (обычно так и есть), то вы можете перемещаться по истории команд с помощью клавиш со стрелками вверх и вниз. Еще важнее, что работает механизм завершения по клавише Tab: когда вы набираете начало идентификатора, а затем нажимаете Tab, irb пытается дописать имя идентификатора за вас.
Чтобы включить механизм завершения, добавьте в файл .irbrc следующий фрагмент:
IRB.conf[:AUTO_INDENT] = true
IRB.сonf[:USE_READLINE] = true
IRB.conf[:LOAD_MODULES] ||= []
IRB.conf[:LOAD_MODULES] |= ['irb/completion']
В файле .irbrc может содержаться произвольный код. Например, я часто пользуюсь описанным ниже методом. Для краткости он назван sm (сокращение от «show methods»), а цель — вывести (в алфавитном порядке) список всех методов, которые можно вызывать для данного объекта, за исключением тех, которые он унаследовал от своих предков:
def sm(obj)
list = obj.methods
anc = obj.class.ancestors — [obj.class]
anc.each {|a| list -= a.instance_methods }
list.sort
end
Вот пример его использования:
irb(main):001:0> str = "hello"
=> "hello"
irb(main):002:0> sm str
=> ["%", "*", "+", "<<", "<=>", "[]", "[]=", "capitalize",
"capitalize!", "casecmp", "center", "chomp", "chomp!", "chop", "chop!",
"concat", "count", "crypt", "delete", "delete!", "downcase", "downcase!",
"dump", "each", "each_byte", "each_line", "empty?", "gsub", "gsub!", "hex",
"index", "insert", "intern", "length", "ljust", "lstrip", "lstrip!", "match",
"next", "next!", "oct", "replace", "reverse", "reverse!", "rindex", "rjust",
"rstrip", "rstrip!", "scan", "size", "slice", "slice!", "split", "squeeze",
"squeeze!", "strip", "strip!", "sub", "sub!", "succ", "succ!", "sum",
"swapcase", "swapcase!", "to_f", "to_i", "to_str", "to_sym", "tr", "tr!",
"tr_s", "tr_s!", "unpack", "upcase", "upcase!", "upto"]
irb(main):003:0> sm String
=> ["allocate", "new", "superclass"]
irb(main):004:0> sm 123 => ["%", "*", "**", "+", "-", "/", "<<", ">>", "[]", "^",
"id2name", "power!", "rdiv", "rpower", "size", "to_f", "to_sym, "|", "-"]
Программа irb позволяет запускать подсеансы внутри сеанса, хотя это используется и нечасто. Можно запустить несколько сеансов и переключаться между ними, у каждого сеанса собственный набор привязок.
Может быть, вам это и не кажется полезным, но можно, например, задать объект в сочетании с подкомандой irb. Тогда контекстом подсеанса станет этот объект, псевдопеременная self будет ссылаться на него, он же станет областью видимости и т.д.:
$ irb
irb(main):001:0> t0 = Time.now
=> Mon Jul 31 04:51:50 CDT 2006
irb(main):002:0> irb t0
irb#1(Mon Jul 31 04:51:50 CDT 2006):001:0> strftime("%a %b %c")
=> "Mon Jul Mon Jul 31 04:51:50 2006"
irb#1(Mon Jul 31 04:51:50 CDT 2006):002:0> to_i
=> 1154339510
irb#1(Mon Jul 31 04:51:50 CDT 2006):003:0> self + 1000
=> Mon Jul 31 05:08:30 CDT 2006
irb#1(Mon Jul 31 04:51:50 CDT 2006):004:0> wday
=> 1
irb#1(Mon Jul 31 04:51:50 CDT 2006):005:0> class
SyntaxError: compile error
(irb#1):5: syntax error, unexpected $end
from (irb#1):5
irb#1(Mon Jul 31 04:51:50 CDT 2006):006:0> self.class
=> Time
irb#1(Mon Jul 31 04:51:50 CDT 2006):007:0> quit
=> #<IRB::Irb: @scanner=#<RubyLex:0xb7ee8394>,
@signal_status=:IN_EVAL, @context=#<IRB::Context:0xb7ee86f0>>
irb(main):003:0> quit
$
Мы уже убедились в полезности библиотеки ruby-breakpoint (см. главу 16). В сочетании с ней irb становится мощным средством отладки, поскольку вы можете установить точку прерывания и «оказаться» в сеансе irb. Конечно, это не настоящий отладчик, потому что не позволяет исполнять код в пошаговом режиме.
Иногда бывает полезна библиотека xmp. Она принимает предложения на Ruby, исполняет их и помещает возвращаемое значение в комментарий. В книге «Programming Ruby» рассматривается xmp, а также библиотека rtags (которая генерирует файл TAGS для редакторов emacs или vi).
У irb есть еще одна приятная особенность, о которой стоит знать. Понятно, что irb умеет анализировать написанный на Ruby код, но лексический анализатор несложно использовать и в других приложениях. Вот простой пример программы, которая открывает саму себя и анализирует собственный код, выводя отсортированный список всех идентификаторов и констант:
require 'irb/ruby-lex'
file = File.new(__FILE__)
parse = RubyLex.new # (file)
parse.set_input(file)
idents = []
loop do
token = parse.token
break if token.nil?
if token.is_a? RubyToken::TkIDENTIFIER or
token.is_a? RubyToken::TkCONSTANT
idents << token.name
end
end
p idents.uniq.sort
# Выводится:
# ["File", "RubyLex", "RubyToken", "TkCONSTANT", "TkIDENTIFIER", "file",
# "idents", "loop", "name", "new", "p", "parse", "require", "set_input",
# "sort", "token", "uniq"]
Насколько мне известно, этот механизм не документирован на английском языке. Но если вам необходим лексический анализатор Ruby, то можете изучить исходный текст и адаптировать его под свои нужды.
21.4. Утилита ri
Сокращение ri, вероятно, означает «Ruby index» или нечто подобное. Это командная утилита для чтения документации, предоставляющая информацию о классах, методах, модулях и т.д. Пример:
$ ri each_with_index
------------------------------------------------------------
enumObj.each_with_index {|obj, i| block } -> nil
------------------------------------------------------------
Calls block with two arguments, the item and its index,
for each item in enumObj.
hash = Hash.new
%w(cat dog wombat).each_with_index {|item, index|
hash[item] = index
}
hash #=> {"dog"=>1, "wombat" =>2, "cat"=>0}
Отметим, что в ней есть ошибки и странности. Можете сообщать о них (а равно об опечатках и других неточностях), если найдете человека, который сознается в авторстве.
Мартин Анкерл (Martin Ankerl) написал графическую утилиту fxri, которая работает устойчиво. Она получает данные от RDoc, как и ri. Кроме того, в ней есть панель, где запущена irb.