Несколько паттернов для написания надёжного ruby кода
В этом посте я решил собрать некоторые паттерны и сниппеты из книги Confident Ruby, которые мне понравились и пригодились. В книге их около 30, я ограничусь всего четырьмя, которыми вы можете воспользоваться уже сегодня.
Array()
Array() превращает любой аргумент в массив. Отличается от to_a
тем, что действительно превращает в массив что угодно.
Например:
'firefly'.to_a #=> NoMethodError: undefined method `to_a' for "firefly":String
Array('firefly') #=> ['firefly']
Hash#fetch для проверки наличия ключей
Лучший способ проверить существует ли ключ в хэше и получить этот ключ — это вызвать метод fetch. Если ключа в хэше нет, то fetch
вызовет исключение KeyError: key not found
.
hash = {marko: 'polo'}
hash.fetch(:marko) #=> 'polo'
hash.fetch(:john) #=> KeyError: key not found: :john
Альтернативный синтаксис обработки исключений
Что бы не засорять код метода begin/rescue/end блоками ruby позволяет перенести rescue
в конец метода, тем самым отделив рабочую часть кода от обработки исключений.
Сравните:
def kill_mockingbird
begin
# выполняем работу
rescue
# обрабатываем исключения
end
end
def kill_mockingbird
# выполняем работу
rescue
# обрабатываем исключения
end
На две строчки короче и метод разбит на логические составляющие.
Использование собственных методов для преобразования данных
Допустим, у нас есть библиотека для некоего AuthorAPI. Допустим, API ожидает что поля name и email присутствуют обязательно.
def post_author(author)
api.send {
name: author.name
email: author.email
}
end
Сразу проблема: у author должны быть определены методы name
и email
. Мы можем ограничить доступные аргументы и позволить передавать только хэш с атрибутами. А можем сделать использование нашей библиотеки более удобным и позволить помимо хэша использовать свои классы, при условии что для них определён метод to_author
. Рефакторим:
def post_author(properties_or_obj = {})
# Теперь пользователь может передать свой объект или просто хэш с параметрами
author = properties_or_obj.is_a?(Hash) ? properties_or_obj : properties_or_obj.to_author
# fetch позволяет передать блок, который вызывается если ключ не найден
name = author.fetch(:name) { raise KeyError, "Name must be supplied for Author API requests" }
email = author.fetch(:email) { raise KeyError, "Email must be supplied for Author API requests" }
api.send {
name: name,
email: email
}
end
class User
def to_author
{
name: [first_name, last_name].join(","),
email: email
}
end
end
post_author(current_user) #=> Yay!
Что думаете об этих паттернах? Как сами боретесь с duck typing и conditional hell?