Хотите расти как разработчик и найти крутую работу? Не протирайте штаны — займитесь Open Source проектами. Так легче всего попасть в лучшие команды разработчиков и положить себе в резюме настоящий проект, вместо нелепых «примеров кода». Но найти подходящий проект для участия сложно. Начинаются лень и отговорки, а за ними — отсутствие профессионального роста, критики по-настоящему крутых программистов, уныние и застой.
На Cult of Martians мы собираем интересные задачи для современных веб-программистов. Можно выбрать подходящую по сложности, продолжительности и специализации. Задачи не выдуманы «из воздуха» — каждая решает насущную проблему, и решить ее можно через создание нового Open Source проекта или улучшение существующего. Решайте задачи, прокачивайтесь, присылайте решение на оценку. Лучших могут пригласить к себе на работу компании, программистам которых понравится ваше решен ие.
Для продвинутых, задача на неделю
Контракт — это набор валидаций (тестов), которые запускаются во время production сессии взаимодействия с внешней системой по API (например, CRM системой или социальными сетями).
Такие валидации решают проблему упрощения отладки и распознавания отклонений в поведении внешних зависимостей. Пример реализации — poro_contract.
Нужно создать новый gem (назовем его simple_contracts
), который:
Польза: получить отличный опыт с многопоточным программированием в Ruby; освоить контрактный подход к тестированию API.
Базовый контракт реализован в poro_contract. К сожалению, это не gem и там не хватает одного важного куска.
На практике мы столкнулись со следующей проблемой. Пусть в приложении реализован клиент к разным социальным сетям (Twitter, Facebook, VK) и публикация идет от имени официальной страницы или группы. Нужно обезопасить себя от ошибочных публикаций, поэтому в приложении добавлена возможность удалить пост из всех социальных сетей. И только через месяц после очередного такого удаления разработчик может узнать, что социальная сеть иногда не удаляет пост на самом деле, хотя вызов API и ответ от API был корректным.
Для того, чтобы собрать отладочную информацию по таким кейсам, хочется добавить в контракт правило, которое отрабатывает после запроса на удаление поста — приложение еще раз проверяет все социальные сети, чтобы убедиться, что пост на самом деле удален. Если пост не удалился, нужно сохранить подробности сессии удаления, поднять исключение или отправить нотификацию. Поэтому, именно в этот момент возникают дополнительные IO операции, которые не хочется делать в основном потоке.
# SimpleContract - should not support #match_async!
# SimpleContract[:parallel] - marks that inherited Contract, should be thread-safe and could run in parallel with #match_async!
class TwitterContract < SimpleContract[:parallel]
# ... other rules
def guarantee_verified_delete
return true if @request.path !~ %r{statuses/destroy}
return true if Twitter::REST::Client.new(@credentials).statuses(@post.tweet_id).empty?
false
end
end
# Use synchronously, (raises exception, "Fails Fast"™):
@post = Post.find(params[:post_id])
TwitterContract.new.match! { response = TwitterAPI.destroy(@post) }
# Use asynchronously, (does not affect TwitterAPI.destroy, but tracks any problems with TwitterContract validation):
TwitterContract.match_async! { response = TwitterAPI.destroy(@post) }
Архитектурных решений видится несколько:
В решении хочется увидеть анализ за и против каждого архитектурного подхода. Также хочется увидеть обоснование выбранного инструмента для параллельного выполнения контракта.
На что обратить внимание:
Инструкции по выполнению
simple_contracts
) и покрыть тестами.P.S.: Есть еще gem blood_contracts
— в нем параллельное выполнение работает ненадежно, поэтому ориентироваться на него не стоит. BloodContracts позиционируется как gem более сложно устроенный, a la “Rails for Contracts”. Поэтому simple_contracts должен в плане простоты быть его антиподом.