Разные мелочи

July 2, 2012

3 недели в блог не писал и уже тяжело снова начинать. Так однажды я 2 года спортом не позанимался и тоже лень было возвращаться. Есть правда хорошое решение - нужно себя заставлять. И вот сегодня я хочу рассказать про разные мелочи, с которыми столкнулся в свое время.

Kernel.caller

Вы будете смеятся, но раньше, чтобы вывести стек вызова методов приложения я использовал конструкцию:

def some
  begin
    raise 
  rescue => e
    p e.backtrace # => ["demo.rb:3:in `some'", "demo.rb:9:in `<main>'"]
  end
end

some

После C++ мне казалось, что это вполне нормально (в C++ попробуйте вывести стек приложения в лог, там не побалуешь). Но это же руби, здесь можно все, причем очень просто! Случайно в интернете обратил внимание на системный вызов Kernel.caller. Напечатать цепочку вызовов можно так:

def some
  p caller # => ["demo.rb:5:in `<main>'"]
end

some

Добавление ошибки валидации на :base

Отображая ошибки валидации с помощью стандартного object.errors.full_messages, мы иногда получаем такие сообщения: "Password Укажите пожалуйста пароль". Чтобы быстро избавиться от обязательно названия атрибута в начале сообщения, можно добавлять ошибки валидации следующим образом: object.errros.add(:base, 'Укажите пожалуйста пароль'). full_message не будет добавлять название атрибута в этом случае - activemodel/lib/active_model/errors.rb#L287.

Rescue в одну строку

Этим шаблоном я еще не воспользовался, но он интригует. Все мы используем конструкции для установки значений по умолчанию:

a = some || "default"
b = other.presence || "start" # Rails only, обрабатывает nil? и blank?

Оказывается можно так же легко установить дефалтовое значение, если метод вызывает исключение:

a = another rescue "after exception"

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

IndexBy

Чего только не придумают руби-программисты, чтобы писать меньше кода. Недавно наткнулся на удивительный метод index_by в рейлс:

# Выдержка из документации:

# Convert an enumerable to a hash:
people.index_by(&:login)
  # => { "nextangle" => <Person ...>, "chade-" => <Person ...>, ...}

Ассерты

В С++ очень активно используются ассерты для проверки входных параметров, при выполнении каких-то подозрительных действий, для проверки возвращаемых значений и т.д. Это является частным случаем программирования по контракту. В рейлс, для собственного спокойствия, я тоже часто вставляю такого рода проверки. Приведу несколько типовых случаев:

# 1.
# не обращайте внимание на цепочку if-elseif, не могу себя
# отучить в пользу case.
if mode == :some
  # ...
elsif mode == :another
  # ...
else
  raise "unreacheable: #{mode}"
end

# 2.
options.assert_valid_keys(:key_a, :key_b)

# 3.
raise "wrong #{state}" unless %w[start finish].include?(state)

Я обычно не заморачиваюсь внятными сообщения в эксепшенах, так как если ошибка и случится, то по трейсу всегда легко отследить проблему. Данные конструкции призваны подсказывать программисту, что в коде возник какой-то случай, о котором раньше не позаботились. Также эти конструкции легко расширяются, когда появляются новые режимы, ключи или состояния.

Для первого раза думаю хватит. Рад возвращению в онлайн. Раньше мы работали над большим количеством маленьких проектов и я прежде всего оптимизировал скорость и стандартность разработки. Сейчас у нас есть парочку средних проектов, на несколько человеко-месяцев (мифических :-) каждый. При увеличении кодовой базы возникают проблемы другого сорта, с которыми я буду сталкиваться, осмыслять и рассказывать вам. До новых встреч!

comments powered by Disqus