Когда я начал работать с Ruby около года назад, я сначала чувствовал себя немного потерянным. С сильным знанием Java я просто не привык к такому метапрограммированию, миксинам и динамической / утиной типизации. Многому научившись с помощью парного программирования, я также начал просто ковыряться с помощью консоли IRB и Rails. Конечно, такого рода вещи не являются естественными для программиста на Java, но когда вы попадаете на автобусе по городу с вашим нетбуком и без подключения к Интернету, IRB — один из самых увлекательных и вызывающе выглядящих способов бодрствовать
Но какой бы крутой ни была интерактивная оболочка Ruby, с ней нельзя справиться ни с одной ошибкой или более экзотической языковой конструкцией. В процессе изучения Ruby я выбрал свое оружие для битвы кода. Особенно пытаясь получить контроль над Rails с его тоннами метапрограммирования, вы можете использовать хороший арсенал, чтобы выиграть битву, поэтому я решил показать вам пять моих фаворитов.
1. Удар кулаком
Прежде всего, у каждого новичка в программировании есть фаворит на все времена: отладка классической печати.
obj = Dir.new('/usr/bin')
puts obj
=> <Dir:0x94a6470>
# which should be replaced with
puts obj.inspect
=> <Dir:/usr/bin>
# or even shorter
p obj
=> <Dir:/usr/bin>
Эта техника проста и прекрасно работает, если ваш противник находится в пределах досягаемости. С другой стороны, ваши руки начнут кровоточить после нескольких ударов, а точнее через несколько ударов позже, и поэтому вы должны научиться бороться с лучшим оружием в ближайшее время.
2. Убийственный взгляд
Ваши глаза становятся черными как смоль, пока вы продолжаете смотреть на своего врага, пока вдруг он не разорвется на части. Я думаю, мы все практиковали это, когда были маленькими … или я был просто странным ребенком. Однако аналогия здесь заключается в том, чтобы продолжать смотреть на вашу проблему со всех сторон и пытаться понять все ее аспекты. Итак, прежде всего мы должны узнать больше о нашем противнике.
n = 12 ** 34
n.class
=> Bignum
n.methods.sort
=> [:to_s, :coerce, :-@, :+, :-, :*, :/, :%, :div, :divmod, :modulo, ..., :class, :dup, ...]
Вау, это много методов! Но подождите: класс и: dup? Они выглядят знакомыми и, вероятно, унаследованы от самого класса Object. Может быть, нам стоит взглянуть на методы, которые предлагает Бигнум.
n.public_methods(false).sort
=> [:%, :&, :_, :_*, :+, :-, :-@, :/, :<, :<<, :<=, :<=>, :==, :===, :>, :>=, :>>, :[], :^, :abs, :coerce, :div, :divmod, :eql?, :even?, :fdiv, :hash, :magnitude, :modulo, :odd?, :remainder, :size, :to_f, :to_s, :|, :~]
Теперь это выглядит намного яснее. Но вот еще один совет. Допустим, вы просто интересуетесь всеми методами, начинающимися с «to_», вы можете просто сделать
n.methods.sort.grep /^to_/
=> [:to_c, :to_enum, :to_f, :to_i, :to_int, :to_r, :to_s]
3. Угрожая друзьям и семье
Теперь, когда мы знаем, кто наш враг, мы должны узнать больше о его друзьях и семье на случай, если нам понадобится угрожать ублюдку, как в старом добром мафиозном фильме.
n.class.superclass
=> Integer
Integer.superclass
=> Numeric
n.class.ancestors
=> [Bignum, Integer, Numeric, Comparable, Object, Kernel, BasicObject]
АГА! Таким образом, Bignum явно наследуется от Integer, а Integer от Numeric. Давайте проверим это очень быстро.
Integer > Bignum && Numeric > Integer
=> true
ОК, круто, это имеет смысл. Итак, мы только что узнали, кто является родителем и прародителем Бигнума, но как насчет его братьев, дядей или детей? Вы, вероятно, задаетесь вопросом, есть ли что-то похожее на метод Class.ancestors, но для обхода дерева наследования (или графика, если вы хотите быть педантичным) в противоположном направлении. Краткий ответ: Нет. Длинный ответ: Вы можете построить его самостоятельно.
class Class
def descendants
ObjectSpace.each_object(::Class).select { |c| c < self }
end
end
Этот маленький трюк может пригодиться, если вы ковыряетесь в странной модели данных какого-то более крупного проекта. Он просто смотрит на все классы в
все пространство объекта и выбирает те, которые наследуют от рассматриваемого класса. Я знаю, что это немного грубо, но это делает работу. Давайте попробуем это на нашем примере. Мы только что узнали, что Integer и Numeric являются родителями и прародителями Bignum. Это означает, что теперь мы можем расспросить их о братьях и дядюшках Бигнума, а также о его детях.
Integer.descendants
=> [Bignum, Fixnum]
# Bignum has one brother</h1>
Numeric.descendants
=> [Complex, Rational, Bignum, Float, Fixnum, Integer]
# And 3 uncles: Complex, Rational and Float
Bignum.descendants
=> []
# Unfortunately Bignum has no child
4. Самурайский меч
Будучи чрезвычайно элегантным и универсальным, самурайский меч или катана являются мощным оружием. Он острый, как бритва, и может быть раскрыт за доли секунды, чтобы создать смертельный порез. В нашем случае это будет отладчик, который удобно встроен в базовую систему Ruby. Вы можете просто запустить свой скрипт с библиотекой отладки
$ ruby -r debug [options] [programfile] [arguments]
или вы можете разместить вызов отладки прямо в вашем коде
# boring code above
require 'debug'
# interesting code below
Но на самом деле вы должны посмотреть Gem ‘ruby-debug’, который делает все, что делает встроенный отладчик, только быстрее и лучше. Вот отличная шпаргалка и даже целый эпизод RailsCasts об этом. Использование становится интуитивно понятным, когда вы овладеваете консольным отладчиком, и когда вы это делаете, вы, вероятно, больше не хотите использовать что-либо GUI’ish.
5. Боевой топор
Если ничего не помогает, и вы все еще не знаете, как победить своего противника, это может быть один из тех случаев, когда насилие является единственным решением. В этом случае мы берем боевой топор.
set_trace_func proc { |event, file, line, id, binding, classname|
printf "%8s %s:%-2d %10s %8sn", event, file, line, id, classname
}
Эта функция ядра является последним средством отладки, поскольку она может отслеживать каждое событие, которое происходит во время выполнения программ. С помощью этой вещи вы даже можете различить чистый вызов метода Ruby и собственный вызов метода C, так что будьте готовы к некоторому grep’n’read, потому что вывод этого маленького скрипта
set_trace_func proc { |event, file, line, id, binding, classname|
printf "%8s %s:%-2d %10s %8sn", event, file, line, id, classname
}
def foo(bar)
bar / Math::E
end
n = 12 ** 34
puts foo(n)
выглядит так
$ ruby test.rb
c-return test.rb:3 set_trace_func Kernel
line test.rb:4
c-call test.rb:4 method_added Module
c-return test.rb:4 method_added Module
line test.rb:5
c-call test.rb:5 ** Fixnum
c-return test.rb:5 ** Fixnum
line test.rb:6
call test.rb:4 foo Object
line test.rb:4 foo Object
c-call test.rb:4 / Bignum
c-return test.rb:4 / Bignum
return test.rb:4 foo Object
c-call test.rb:6 puts Kernel
c-call test.rb:6 puts IO
c-call test.rb:6 to_s Float
c-return test.rb:6 to_s Float
c-call test.rb:6 write IO
1.8107891504915702e+36
c-return test.rb:6 write IO
c-call test.rb:6 write IO
c-return test.rb:6 write IO
c-return test.rb:6 puts IO
c-return test.rb:6 puts Kernel
Так что вы можете представить, как будет выглядеть что-то большее. Но, конечно, вы бы написали более избирательную функцию, например, фильтр по типу события или имени класса.
Надеюсь, вам понравилась моя первая статья, и некоторые из этого оружия дадут вам преимущество в следующем бою. В конце концов, помните, что скай-маршал Теат Меру сказал в «Звездном десантнике»: «Чтобы бороться с ошибкой, мы должны ее понять. Мы можем позволить себе еще один Клендатху.
Не стесняйтесь предлагать любые интересные методы, которые у вас могут быть. Просто оставьте комментарий и поделитесь им с нами!