Статьи

Додзё отладки и рассечения — 5 видов оружия / техника выбора

Когда я начал работать с 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

Так что вы можете представить, как будет выглядеть что-то большее. Но, конечно, вы бы написали более избирательную функцию, например, фильтр по типу события или имени класса.

Надеюсь, вам понравилась моя первая статья, и некоторые из этого оружия дадут вам преимущество в следующем бою. В конце концов, помните, что скай-маршал Теат Меру сказал в «Звездном десантнике»: «Чтобы бороться с ошибкой, мы должны ее понять. Мы можем позволить себе еще один Клендатху.

Не стесняйтесь предлагать любые интересные методы, которые у вас могут быть. Просто оставьте комментарий и поделитесь им с нами!