Нам нужны твои мозги

Хотите расти как разработчик и найти крутую работу? Не протирайте штаны — займитесь Open Source проектами. Так легче всего попасть в лучшие команды разработчиков и положить себе в резюме настоящий проект, вместо нелепых «примеров кода». Но найти подходящий проект для участия сложно. Начинаются лень и отговорки, а за ними — отсутствие профессионального роста, критики по-настоящему крутых программистов, уныние и застой.

На Cult of Martians мы собираем интересные задачи для современных веб-программистов. Можно выбрать подходящую по сложности, продолжительности и специализации. Задачи не выдуманы «из воздуха» — каждая решает насущную проблему, и решить ее можно через создание нового Open Source проекта или улучшение существующего. Решайте задачи, прокачивайтесь, присылайте решение на оценку. Лучших могут пригласить к себе на работу компании, программистам которых понравится ваше решение.

Помощь запрашивает Вова Дем Вова ДемВова Дем

Бэк: Утилита для запуска WebSocket-клиентов с поддержкой сценариев

Для новичков, задача на неделю

Необходимо разработать Ruby-инструмент (CLI) для выполнения сценариев взаимодействия WebSocket-клиентов с сервером.

Утилита может быть использована как для blackbox-тестирования (в том числе и нагрузочного) WebSocket-приложений, так и просто в качестве удобного консольного клиента при разработке.

Польза: возможность применить на практике методы многопоточного программирования, попрактиковаться в написании CLI, поближе познакомиться с технологией WebSocket.

Как это должно работать?

Рассмотрим два примера. Предположим, что наш сервер — это Action Cable.

Пример №1

Предположим, что мы хотим проверить следующий сценарий (echo):

  • клиент отправляет сообщение серверу;

  • сервер в ответ отправляет такое же сообщение клиенту.

Запуск сценария будет производиться так:

$ wsdirector echo.yml ws://localhost:3444/cable

Здесь ws://localhost:3444/echo – путь к нашему WS-приложению, а echo.yml – файл сценария, который может выглядеть так:

# приветственное сообщение от Action Cable
- receive:
    # если поле data – объект, то
    # предполагаем, что это json
    data:
      type: "welcome"
# подписка на канал и ожидание подтверждения
- send:
    data:
      command: "subscribe"
      identifier: "{\"channel\":\"TestChannel\"}"
- receive:
    data:
      type: "subscription_confirmation"
      identifier: "{\"channel\":\"TestChannel\"}"
- send:
    data:
      command: "message"
      identifier: "{\"channel\":\"TestChannel\"}"
      data: "{\"text\": \"echo\",\"action\":\"echo\"}"
- receive:
    data:
      identifier: "{\"channel\":\"TestChannel\"}"
      message: "{\"text\": \"echo\"}"

В случае возникновения ошибки на каком-либо шаге, скрипт должен завершаться с ненулевым статусом (exit code) и выводить в STDERR информацию о том, на каком этапе сценария произошел сбой.

Пример №2

Давайте рассмотрим более интересный случай с несколькими клиентами с отличающимся поведением.

  • все клиенты подключаются к серверу и подписываются на канал;

  • клиенты X отправляют сообщение;

  • все клиенты должны получить такое число сообщений, сколько у нас клиентов X.

Кроме того, нам бы хотелось, чтобы мы могли легко конфигурировать число клиентов:

Наша команда будет выглядеть так:

$ wsdirector -s 10 broadcast.yml ws://localhost:3444/cable

А файл сценария broadcast.yml:

# сценарий публикующего клиента
- client:
    # :scale – это параметр переданный в команде через -s
    # multiplier говорит, сколько нам нужно таких клиентов 
    # (коэффициент "размножения")
    multiplier: ":scale"
    actions:
      # приветственное сообщение от Action Cable
      - receive:
          data:
            type: "welcome"
      # подписка на канал и ожидание подтверждения
      - send:
          data:
            command: "subscribe"
            identifier: "{\"channel\":\"TestChannel\"}"
      - receive:
          data:
            type: "subscription_confirmation"
            identifier: "{\"channel\":\"TestChannel\"}"
      # важный момент – ждём, пока все клиенты достигнут этой точки в сценарии
      - wait_all
      # отправляем сообщение в канал
      - send:
          data:
            command: "message"
            identifier: "{\"channel\":\"TestChannel\"}"
            data: "{\"text\": \"hello\", \"action\":\"broadcast\"}"

# сценарий обычного клиента
- client:
    # слушателей мы хотим в 10 раз больше
    multiplier: ":scale * 10"
    actions:
      # аналогичные действия для подписки на канал пропустим (кстати, как бы нам их переиспользовать?)
      ...
      - wait_all
      - receive:
          # каждый клиент должен получить сообщение от каждого публикующего клиента
          multiplier: ":scale"
          data:
            identifier: "{\"channel\":\"TestChannel\"}"
            message: "{\"text\": \"hello\"}"

Советы по реализации

Язык реализации: Ruby.

Код Action Cable каналов из примеров – gist.

Для реализации веб-сокет клиента можно использовать gem websocket-client-simple, а также посмотреть примеры его использования в Rails или Lite Cable.

Инструкции по выполнению

  1. Форкнуть проект wsdirector на GitHub.
  2. Реализовать необходимый функционал.
  3. Сделать Pull Request.

— Можно мне взять эту задачу?

Все задачи на этой странице еще не решены. Если задача вам интересна — берите и смело делайте. Спрашивать разрешения и становиться в очередь не нужно. Даже если кто-то сделает задачу быстрее вас, не останавливайтесь — ваше решение может быть лучше. Это Open Source!

Помощь запрашивает Вова Дем Вова ДемВова Дем

Бэк: Инструмент для создания частичного, анонимизированного дампа базы данных

Для продвинутых, задача на неделю

Необходимо разработать инструмент (gem) для создания частичного, анонимизированного дампа базы данных Rails (ActiveRecord) приложения.

Это позволит нам использовать на тестовых серверах данные, максимально приближенные к боевым, но при этом не делать полный дамп (который может быть очень большим). Дополнительно в целях безопасности мы также хотим анонимизировать часть данных (персональные данные и т.п.).

Польза: возможность познакомится с внутренностями ActiveRecord, попрактиковаться в написании Ruby gems и написать инструмент, которым будет активно пользоваться сообщество.

Почему ActiveRecord?

Решение подобной задачи в общем случае (на уровне самой БД) потребует от нас ручного описания связей между данными и постоянной актуализации этих связей.

Наличие же ORM решает эту проблему практически полностью (если все необходимые данные смоделированы с помощью этой ORM).

Как это должно работать?

В первую очередь нам необходимо описать, какие именно данные мы хотим выгрузить и какие изменить. Выглядеть это может так (для структуры приложения, описанной здесь):

EvilSeed.configure do |config|
  # Указываем корневые/начальные объекты – те, которые мы хотим выгрузить, со всеми
  # связанными данными.
  #
  # Первый аргумент – название модели, второй – условия для выборки
  config.root("Forum", id: 42) do |r|
    # мы хотим исключить некоторые ассоциации
    # для этого можем использовать регулярные выражения,
    # по которым будет матчится путь к ассоциации.
    #
    # путь к ассоциации – это строка, составленная из названий ассоциаций, по которым мы пришли,
    # разделенных точкой, где первая часть – это название модели.
    #
    # пример: "forum.users.questions"
    r.exclude(
      [
        # мы не хотим дампить настройки трекинг-пикселей
        /\btracking_pixels\b/
      ]
    )

    # кроме того, мы можем ограничить количество выгружаемых объектов ассоциации;
    # например, не более 100 элементов для всех
    r.limit_associations_size(100)

    # для конкретной ассоциации по пути
    r.limit_associations_size(10, "forum.questions")

    # данный подход удобен, если структура связей ближе к дереву, тогда
    # мы можем отсекать ветки
    # если же в структуре много циклов, то мы должны будем выйти за пределы лимита,
    # чтобы сохранить согласованность
  end

  # системные модели, например, роли
  config.root("Role") do |r|
    # исключаем все ассоциации
    r.exclude(/.*/)
  end

  # Преобразование атрибутов – это глобальная настройка
  config.customize("User") do |u|
    # ставим всем пользователям одинаковый пароль
    u["encrypted_password"] = encrypt("qwerty")
  end

  # Анонимизация – это синтаксический сахар для преобразования,
  # который может использовать, например, Faker для генерации данных
  config.anonymize("User")
    name { Faker::Name.name }
    email { Faker::Internet.email }
  end
end

Запускать будем следующим образом:

EvilSeed.dump("path/to/file")

Советы по реализации

Язык реализации: Ruby.

Инструкции по выполнению

  1. Форкнуть проект evil-seed на GitHub.
  2. Реализовать необходимый функционал.
  3. Сделать Pull Request.

Помощь запрашивает Андрей Ситник Андрей СитникАндрей Ситник

Фронт: Перевести Logux-сервер на µWS

Для новичков, задача на пару дней

Logux-сервер сейчас использует пакет ws для работы с веб-сокетами. Но пакет uWebSockets гораздо быстрее.

Польза: принять участие в разработке Logux; получить больше опыта в работе с веб-сокетами.

  1. Форкнуть logux-server.
  2. Заменить в проекте ws на uws.
  3. Проверить все тесты и починить проблемы.

Помощь запрашивает Эмиль Кашкевич Эмиль КашкевичЭмиль Кашкевич

Бэк: Поддержка Yarn для npmdc

Для новичков, задача на пару дней

Сейчас gem npmdc проверяет наличие установленных npm-пакетов и соответствие их версий на основе файла package.json.

Нужно добавить поддержку Yarn.

Польза: возможность попрактиковаться в программировании на чистом Ruby и написании gem’ов, помочь полезному проекту, который нужен в каждом современном большом Ruby веб-приложении.

Как это работает сейчас:

  1. Разбираем файл package.json и строим по нему дерево зависимостей с указанием версии.
  2. Разбираем содержимое папки node_modules.
  3. Для каждого пакета проверяем его наличие в папке node_modules и соответствие версии, указанной в package.json.
  4. Генерируем отчет и показываем его по завершению операции и/или в процессе выполнения (в зависимости от выбранного formatter’а).

Что нужно сделать:

  1. Добавить в конфиг опцию для выбора пакетного менеджера (NPM/Yarn).
  2. Если выбран Yarn, использовать yarn check:
    • следует предусмотреть отсутствие установленного Yarn;
    • следует предусмотреть путь к исполнительному файлу Yarn на случай, если пользователь планирует использовать глобально установленную версию;
    • вывод отчета проверки yarn check следует привести в соответствие с текущим форматом.
  3. Отобразить используемый пакетный менеджер в сообщениях об ошибках и подсказках.
  4. Добавить тесты.
  5. Добавить информацию о новом функционале в документацию.

Помощь запрашивает Андрей Ситник Андрей СитникАндрей Ситник

Фронт: Событийная архитектура PostCSS

Для продвинутых, задача на месяц

Нужно придумать новый API для плагинов PostCSS — чтобы они все работали вместе, в одном цикле прохода по AST-дереву.

Задание сложное. Оно меньше про код и больше про переговоры, анализ и архитектуру. Но зато дает максимальное количество опыта.

Польза: стать одним из ведущих коммитеров PostCSS, добавить в резюме строку о разработке грамотной архитектуры в мировом проекте.

Сейчас каждый плагин PostCSS проходит по AST-дереву CSS каждый раз заново. В итоге нельзя делать вложенные функции, и при большом количестве плагинов сильно проседает производительность.

У Babel есть более правильный visitor API: когда плагины подписываются на какие-то типы узлов. А внутри PostCSS запускается один проход по дереву, который в нужных местах дергает подписанные плагины.

Первый этап:

  1. Придумать такой API.
  2. Сделать пару примеров псево-кода: как API будет использоваться в простейших плагинах?
  3. Отправить эти примеры Андрею (Gist или проект на GitHub).

Помощь запрашивает Сергей Долганов Сергей ДолгановСергей Долганов

Бэк: Добавить новые источники данных в Ossert: Reddit

Для уверенных в себе, задача на несколько дней

Нужно добавить в Ossert новые источники данных. Отличным кандидадатом с достаточно богатым API является Reddit.

Это позволит охватить еще один важный аспект развития Open-Source библиотек — доступность и объем поддержки, а также заинтересованность в них.

Польза: возможность научиться работать с API Reddit и узнать больше о метриках и поведении разработчиков свободного ПО.

Как это должно работать?

На данный момент сбор информации уже работает для Rubygems, Github, Bestgems и StackOverflow. Необходимо совместимым образом организовать сбор с новых источников.

Советы по реализации

За основу предлагается взять любой из существующих классов для сбора в Ossert. Например, Ossert::Fetch::Rubygems, и реализовать класс, осуществляющий сбор интересной информации:

module Ossert
  module Fetch
    class Reddit
      def initialize(project)
        ...
      end

      def process
        # Метод сбора основных метрик по проекту
      end
    end
  end
end

Для начала, вам потребуется вникнуть в проблему, решаемую проектом — оценку зрелости свободного ПО.

Для этого нужно проанализировать возможность API вашего источника данных и выбрать:

  1. параметры для сбора;
  2. полезные метрики, которые можно измерить на их основании.

Например:

  • «сколько было комментариев к постам о проекте с момента последнего релиза»;
  • «сколько уникальных пользователей комментировали посты о проекте».

А затем, конечно, организовать сбор этих метрик и параметров.

Инструкции по выполнению

  1. Форкнуть проект Ossert на Github.
  2. Реализовать необходимый функционал.
  3. Сделать Pull Request.

Помощь запрашивает Вова Дем Вова ДемВова Дем

Бэк: Инструмент для тестирования совместимости для AnyCable

Для новичков, задача на неделю

Нужно написать скрипт (и упаковать его в гем), который позволит проводить аттестационное тестирование (от англ. “conformance testing”) приложений-серверов для AnyCable.

Это повысит скорость и удобство разработки новых реализаций серверов, а также поможет поддерживать актуальность текущих реализаций.

Польза: возможность подробно изучить ActionCable и AnyCable, попрактиковаться в написании гемов и написать инструмент, которым будет активно пользоваться сообщество.

Как это должно работать?

Предполагается простой и удобный интерфейс:

$ anycablebility http://localhost:3244/cable

После выполнения каждого сценария в стандартный вывод должна выводится информация вида: название сценария, результат (плюс или минус).

Советы по реализации

Язык реализации: Ruby.

В качестве начальных сценариев для тестирования можно взять приемочные тесты для демо-приложения.

В качестве DSL для написания сценариев и их запуска рекомендуется использовать RSpec.

Для непосредественного взаимодействия с сервером через веб-сокеты можно воспользоваться гемом action_cable_client.

Инструкции по выполнению

  1. Форкнуть проект anycablebility на Гитхабе.
  2. Реализовать необходимый функционал.
  3. Сделать Pull Request.

Помощь запрашивает Сергей Долганов Сергей ДолгановСергей Долганов

Бэк: API Endpoint Sampler

Для уверенных в себе, задача на неделю

Пусть существует Rails приложение с некоторым HTTP API. Будем каждый URL этого API считать эндпоинтом, например:

GET /api/v1/projects/:project_id/notes
GET /api/v1/projects/:project_id/notes/:note_id
POST /api/v2/projects
PUT /api/v3/projects/:project_id

Что хочется получить:

Gem для сбора, просмотра и тэгирования проб (пар запрос/ответ) определенных эндпоинтов HTTP API произвольного Rails-приложения. Тэгом считать произвольную строку.

Что нужно реализовать:

Gem, умеющий следующее:

  • Собирать пробы (пары запрос/ответ) с заданных эндпоинтов, сохранять их в соответствии с правилами:
    • Пробой считать кортеж из:
      • даты-времени;
      • URL запроса;
      • HTTP метода;
      • параметров запроса;
      • тела запроса;
      • тела ответа;
      • набора тэгов.
    • Лимитировать сбор проб: не более Y проб за время X;
    • Автоматически очищать устаревшие пробы по мере получения свежих;
    • Оказывать минимальное влияние на скорость выполнения самого запроса Rails приложением;
    • Хранение организовать на PostgreSQL.
  • Задавать базовую конфигурацию:
    • Ограничение на список эндпоинтов в целом (например, через regexp);
    • Черный список эндпоинтов;
    • Сколько запросов хранить по каждому эндпоинту;
    • Как долго хранить пробу;
    • Сохранять за время X не более Y проб.
  • Конфигурировать обработчики для назначения тегов в момент сбора (например, через lambda функции):

    Sampler.configure do |config|
      config.tag_with "slow", ->(request) { request.time > 200 }
    end
    
  • Просматривать сохраненные пробы, с возможностями:
    • Получения списка доступных эндпоинтов приложения (ограниченных в соответствии с конфигурацией) с количеством имеющихся по ним проб;
    • Получения сводной таблицы проб по эндпоинту с фильтрацией по тэгам (одному или нескольким) (id, timestamp, params, тэги, ссылки на просмотр тела запроса и ответа);
    • Удаления одной или нескольких проб;
    • Просмотра тел запроса и ответа пробы;
  • Устанавливаться в произвольное Rails приложение. Сделать можно, например, так:

    rails generate api:sampler:install