Хотите расти как разработчик и найти крутую работу? Не протирайте штаны — займитесь Open Source проектами. Так легче всего попасть в лучшие команды разработчиков и положить себе в резюме настоящий проект, вместо нелепых «примеров кода». Но найти подходящий проект для участия сложно. Начинаются лень и отговорки, а за ними — отсутствие профессионального роста, критики по-настоящему крутых программистов, уныние и застой.
На Cult of Martians мы собираем интересные задачи для современных веб-программистов. Можно выбрать подходящую по сложности, продолжительности и специализации. Задачи не выдуманы «из воздуха» — каждая решает насущную проблему, и решить ее можно через создание нового Open Source проекта или улучшение существующего. Решайте задачи, прокачивайтесь, присылайте решение на оценку. Лучших могут пригласить к себе на работу компании, программистам которых понравится ваше решение.
Для новичков, задача на неделю
Gitter Sidecar — виджет чата для open source-проектов. Его использует PostCSS и Logux.
В виджете оказалось много лишнего кода. 17 KB кода, скорее всего, можно ужать до 5-10 KB.
Польза: опыт улучшения производительности, строка в резюме с PR в известный проект.
object-assign
.Array.from()
.process
из Node.js.Для уверенных в себе, задача на несколько дней
StoreModel — Ruby gem, позволяющий работать с JSON-колонками как с объектами, очень похожими на обычные ActiveRecord
модели.
Ранее в StoreModel был добавлен метод #unknown_attributes
, возвращающий Hash
с атрибутами, присутствующими в колонке БД, но не объявленными в модели (подробнее — в в документации). Однако, он не работает с array-типами:
class Config
include StoreModel::Model
attribute :model, :string
end
data = { name: "Apple iPhone" }
config = Config.to_type.cast_value(data)
config.unknown_attributes
# => { name: "Apple iPhone" }
Config.to_array_type.cast_value([data])
# => ActiveModel::UnknownAttributeError
Необходимо дополнить реализацию модуля StoreModel::Types::ArrayType
таким образом, чтобы он собирал все неизвестные атрибуты в #unknown_attributes
для каждого из объектов списка:
data = { name: "Apple iPhone" }
configs = Config.to_array_type.cast_value([data])
configs.first.unknown_attributes
# => { name: "Apple iPhone" }
Более подробно проблема описана в задаче.
Польза: прокачаться в Ruby, принести пользу сообществу.
Реализацию можно посмотреть в модуле StoreModel::Types::JsonType
(а PR целиком — тут). Хорошим решением будет сразу же избежать дублирования повторяющейся логики.
Инструкции по выполнению
Для уверенных в себе, задача на несколько дней
StoreModel — Ruby gem, позволяющий работать с JSON-колонками как с объектами, очень похожими на обычные ActiveRecord
модели.
Необходимо добавить в StoreModel возможность установить связь между объектом-оберткой и его родителем — объектом класса ActiveRecord
. В результате, у классов, включающих в себя StoreModel::Model
, должен появиться метод #parent
.
Польза: познакомиться с работой атрибутов в Rails, прокачаться в Ruby, принести пользу сообществу.
Необходимо реализовать метод #parent
, возвращающий объект-родитель в модуле StoreModel::Model
(или nil
в случаях, когда он еще не определен). Подробнее — в задаче.
class Config
include StoreModel::Model
attribute :model, :string
end
class Product < ApplicationRecord
attribute :config, Config.to_type
end
product = Product.create(params)
product.config.parent # => ссылка на product
В текущей реализации библиотеки нет возможности определить «родителя» объекта в момент его инициализации, поэтому придется сделать это иначе. Стоит разобраться с тем, как в Rails происходит инициализация атрибутов — рекомендую начать здесь. Стоит попробовать расширить метод инициализации атрибута, не забывая о том, что менять поведение нужно только для объектов, относящихся к библиотеке.
Инструкции по выполнению
Для новичков, задача на неделю
Estimo запускает JS-файл в Puppeteer (Chrome без UI) и сообщает, сколько потребовалось времени на компиляцию и запуск файла. Size Limit использует его для оценки времени выполнения JS-бандла.
Puppeteer очень долго скачивает сборку Chrome. В 1.0 Estimo перешел на Puppeteer Core и самм начал искать и скачивать Chrome. В итоге, если у пользователя уже установлен браузер Chrome, Estimo может использовать его.
Однако, код поиска и скачивания Chrome работает плохо, особенно на Travis CI. Нужно переписать код и исправить ошибки.
Польза: получить глубокий опыт в работе Puppeteer.
findChrome.js
.findChrome.js
поддержку переменных окружения PUPPETEER_
.--version
. Она должна быть больше 75. Если меньше, мы не используем встроенный Chrome.findChrome.js
в Travis CI при addons: chrome
в .travis.yml
и без.Для уверенных в себе, задача на выходные
Gem store_attribute добавляет возможность приведения типов для атрибутов Active Record моделей, созданных с помощью store_accessor
.
Данный gem предоставляет интерфейс (и использует «под капотом») Attributes API, за исключением возможности указания значений по умолчанию.
Необходимо добавить данный функционал в gem.
Польза: узнать, как устроен Attributes API в Active Record, принести пользу Rails сообществу.
Инструкции по выполнению
Для новичков, задача на неделю
Size Limit собирает JavaScript-библиотеку с помощью webpack, чтобы потом проверить результирующий размер.
Иногда непонятно, почему получился именно такой размер — хотелось бы посмотреть содержимое сборки.
Для этого можно добавить аргумент --save-bundle ./dir
в CLI.
Польза: получить опыт в open source проекте и новых синтаксисах JavaScript.
runner.js
новую опцию.getSize
.getSize
, чтобы результат сохранялся в нужную папку, а не во временную.test/index.test.js
и test/cli.test.js
.Для новичков, задача на неделю
В CLI Size Limit нужно добавить опцию size-limit --json
, которая будет
менять формат вывода на JSON:
[
{
name: "index.js",
passed: true,
size: 1024,
loading: 0.05,
running: 0.1
}
]
Польза: получить опыт в open source проекте и новых синтаксисах JavaScript.
runner.js
новую опцию.Для новичков, задача на неделю
Documentation.js — прекрасная замена мертвому JSDoc. Но все темы для него довольно плохие.
Самое грустное, что для проекта сделали отличную тему documentation-theme-light, но она не закончена.
Нужно добавить JS-скрипты генерации HTML и, опционально, чуть отполировать тему.
Польза: задача идеальна для CSS-разработчиков, которые хотят прокачаться в JavaScript.
documentation-theme-light
. Создать package.json
и LICENSE
по примеру Nano ID.git commit -m "Copy from unreleased documentation-theme-light" --author="Maya Oppa <mayagao@users.noreply.github.com>"
. Это сохранит авторство.woff2
.npx documentation *.js -f html -t ../documentation-theme-light -o docs
).README.md
, где указать оригинального автора и как использовать тему.andrey@sitnik.ru
или @sitnik
.Для новичков, задача на неделю
PostCSS парсит значения CSS Custom Properties как обычные значения в CSS-свойствах. Оказалось, что по спецификации там могут быть {
и другие символы.
/* Валидный CSS */
html {
--bracket-block: {1, 2, 3};
--JSON: [1, "2", {"three": 3}, [4]];
--javascript: function(rule) { console.log(rule) };
}
Нужно изменить парсер, чтобы правильно обрабатывать CSS Custom Properties.
Польза: получить опыт JavaScript в известном проекте.
postcss/
и postcss-parser-tests/
лежат в одной папке.postcss-parser-tests
новые примеры.lib/parser.js
.npx gulp build
.postcss-parser-tests/update
, обновить JSON в тестах.Для новичков, задача на неделю
В Logux Server надо провести рефакторинг, переведя код на новый синтаксис JavaScript. Поскольку мы теперь требуем Node.js >= 10
, Promise
можно заменить на async
/await
.
Польза: получить опыт в open source проекте и новом синтаксисе JavaScript.
Promise
, .then()
и .catch()
на async
, await
, catch
.yarn test
). Если все хорошо, взять следующий файл и повторить шаг 2.Для новичков, задача на неделю
В Autoprefixer нужно добавить поддержку нового свойства — mask-composite.
Польза: получить опыт JavaScript в известном проекте.
data/prefixes.js
, использовав данные caniuse-lite/data/features/css-masks
.test/cases/mask-composite.css
и test/cases/mask-composite.out.css
. Использовать эти CSS по примеру остальных хаков.lib/hacks/mask-composite.js
по примеру других хаков из lib/hacks/
.Для новичков, задача на несколько дней
Clowne — это Ruby gem для гибкого клонирования моделей, поддерживающий несколько ORM адаптеров.
На данный момент ActiveRecord
адаптер реализует клонирование следующих ассоциаций: has_one
, has_many
, has_and_belongs_to_many
. В первой версии это было сделано специально, чтобы избежать возможных избыточных клонирований и неверного результата в конечном итоге.
Но бывают ситуации, когда к клонируемой модели добавляют связь belongs_to
, и для ее поддержки необходимо переделывать весь стек клонера, что требует много изменений. Проще добавить клонирование лишь этой одной новой связи.
Для покрытия подобного рода кейсов нужно добавить в gem поддерку belongs_to
связей (только для ActiveRecord
адаптера).
Польза: прокачаться в Ruby, получить опыт работы со сложными Ruby библиотеками.
Советы по реализации
Ключевой точкой изменений будет класс поддерживаемых ассоциаций ActiveRecord. Сама же реализация будет очень похожа на класс HasOne
.
Подробную информацию о том, как работает Clowne, смотрите в документации.
Важно
belongs_to
.belongs_to
приводит к избыточному клонированию (подсказка: обратить вниание на модели Topic
, Post
, Image
).belongs_to
. Можно со ссылкой на интеграционный тест.Инструкции по выполнению
Для новичков, задача на несколько дней
Clowne — это Ruby gem для гибкого клонирования моделей.
Начиная с версии 1.0, Clowne поддерживает два коллбека для обработки клонируемой записи:
finalize
вызывается до сохранения записи в процессе формирования результата клонирования.after_persist
вызывается после сохранения записи для уже сформированной записи.Однако, существуют промежуточные ситуации, когда необходимо обработать собранную, но еще не сохраненную запись.
К примеру, чтобы избежать использования after_persist
и дополнительных операций c базой:
class UserCloner < Clowne::Cloner
# клонируем пользователя и его посты, которые являются черновиками
include_association :posts, scope: :draft
after_clone do |_origin, clone, mapper:, **|
# актуализируем значение атрибута пользователя
clone.draft_count = clone.posts.count
end
end
Альтернативным примером использования может послужить проверка собранной записи по некоторой бизнес-логике:
class UserCloner < Clowne::Cloner
include_association :posts, scope: :draft
after_clone do |origin, clone, errors:, **|
errors.unshift("user clone of #{origin.cache_key} is invalid") if clone.posts.empty?
end
end
errors = []
operation = UserCloner.call(user, errors: errors)
raise errors.join('; ') if errors.any?
Для покрытия подобного рода кейсов мы можно реализовать after_clone
коллбек.
Польза: прокачаться в Ruby, получить опыт работы со сложными Ruby библиотеками.
Советы по реализации
Имплементация after_clone
будет очень похожа на after_persist
, за исключением одного нюанса — вызов after_clone
должен происходить всегда (обратите внимание на Clowne::Utils::Operation#to_record
) и до сохранения записи.
Подробную информацию о том, как работает Clowne, смотрите в документации.
Инструкции по выполнению
Для уверенных в себе, задача на несколько дней
AnyCable позволяет использовать сторонние WebSocket-сервера, более производительные и конкурентно-способные, вместе с Action Cable.
Однако это накладывает определенные ограничения — в том числе, необходимость запускать несколько процессов вместо одного.
И хотя AnyCable предполагает использование стандартного Action Cable сервера при разработке и тестировании, многие разработчики (см. например, статью From Action to Any) предпочитают запускать полноценную конфигурацию локально.
Чтобы упростить локальную разработку, мы предлагаем написать AnyCable-совместимый вебсокет-сервер на Ruby с Rack интерфейсом.
Польза: познакомиться с AnyCable, получить опыт написания конкуретных приложений на Ruby.
Советы по реализации и предполагаемый API
Предполагается, что сервер будет монтироваться в приложение как Rack приложение. Например:
# config/routes.rb
Rails.application.routes.draw do
mount AnyCable::Rack => "/cable"
end
Мы не предполагаем использование сервера с большим количеством одновременных подключений, поэтому реализация работы с сокетами должна быть максимально простая и требовать минимум зависимостей.
Важно: сервер должен полностью реализовывать gRPC коммуникацию, без моков и т.п.; для этого потребуется запускать AnyCable gRPC сервер в фоне при монтировании сервера.
Подробную информацию о том, как работает AnyCable, смотрите в документации.
Инструкции по выполнению
Для новичков, задача на два-три дня
Фреймворк yabeda позволяет легко объявлять и собирать метрики о работе Ruby-приложения — и с помощью адаптеров экспортировать их в системы мониторинга. Сейчас есть адаптеры для Prometheus (yabeda-prometheus) и NewRelic (yabeda-newrelic), но это далеко не полный список систем, которых мы хотели бы поддерживать.
Необходимо написать адаптер (Ruby gem) для yabeda, который позволит отправлять кастомные метрики в DataDog.
Польза: познакомиться с работой систем мониторинга изнутри, получить опыт в Ruby open source, принести пользу сообществу.
Решение: победителем признана реализация Дмитрия Швецова на основе Dogstatsd сделанная в соавторстве с Николаем Малининым. Николай начинал решать задачу отдельно, но решил объединить усилия с Дмитрием.
Это простое задание в плане кодирования — необходимо унаследоваться от базового адаптера и «всего лишь» реализовать шесть хуков: три на регистрацию разных типов метрик и три на непосредственное измерение их значений.
Трудность состоит в том, чтобы понять, как именно их реализовать: придется изучить, как работают Ruby-клиенты к DataDog, выбрать, какой использовать, разобраться, как устроена yabeda.
Если вы никогда не работали с системами мониторинга, то вам поможет немного освоиться наша статья о Yabeda, слайды и видео с выступления автора на Ruby Russia 2018.
Вопросы, на которые нужно будет найти ответы:
collect
-блоков, которые не привязаны к каким-либо событиям.Релевантные материалы:
Для справки можно использовать два уже написанных адаптера — yabeda-prometheus и yabeda-newrelic. Они разные, так как Prometheus работает по pull-модели, а NewRelic — по push-модели (а по какой работает DataDog?).
Инструкции по выполнению
yabeda-datadog
на GitHub — gem, который будет адаптером для yabeda.Для уверенных в себе, задача на несколько дней
Gem graphql-ruby предоставляет объектно-ориентированный интерфейс для построения GraphQL API на Ruby.
Одной из ключевых особенностей GraphQL является возможность запросить клиентом только те данные, которые ему нужны (а не все те, которые предлагает API).
Однако, запросы к БД, которые выполняются при работе, например, с Active Record, не являются оптимальными:
используется "SELECT * ..."
вместо выборки лишь тех полей, которые необходимы для формирования ответа клиенту.
При большом количестве колонок в таблице или при хранении значений большого размера выгрузка всех полей и инициализация Active Record могут привести к разбуханию памяти или отрицательно повлиять на скорость выполнения запроса.
Необходимо написать плагин (gem) для graphql-ruby, который позволит «помочь» делать более эффективные запросы.
Польза: познакомиться с GraphQL в Ruby, получить опыт в Ruby open source, принести пользу сообществу.
Пример работы и возможный API
Рассмотрим следующий запрос:
query {
posts {
id
title
}
}
На стороне Ruby мы опишем наш интерфейс следующим образом:
module GraphqlAPI
module Types
class Query < GraphQL::Schema::Object
field :posts, Types::Post, null: false
def posts
Post.all
end
end
end
end
При выполнении GraphQL-запроса будет выполнен следующий запрос в БД: SELECT * FROM posts
, хотя нам было бы достаточно запросить всего два поля: id
и title
.
Учитывая, что GraphQL-запрос содержит информацию о запрашиваемых полях, мы можем использовать ее для построения более эффективного запроса в БД, например, используя следующий API:
module GraphqlAPI
module Types
class Query < GraphQL::Schema::Object
# добавить наш плагин в базовый класс для полей (см. ниже)
field_class.prepend(GraphQL::SmartSelect)
# добавим опцию для активации плагина
field :posts, Types::Post, null: false, smart_select: true
# в значении мы можем, например, указать, какие поля включать всегда
# (первичный ключ имеет смысл включать во все запросы)
field :posts, Types::Post, null: false, smart_select: [:id]
def posts
Post.all
end
end
class Post < GraphQL::Schema::Object
field_class.prepend(GraphQL::SmartSelect)
field :id, ID
field :title, String
# мы можем подсказать нашему плагину, какие поля из БД
# нужны для формирования данного поля API
field :contents, db_columns: [:raw_content]
# для AR ассоциаций мы должны автоматически выводить
# соответствующую колонку (внешний ключ), например, в
# данном примере мы добавим "user_id"
field :user, Types::User
end
end
end
Примечание: пример расширения API для полей смотрите в этом gist.
Инструкции по выполнению
Для уверенных в себе, задача на несколько дней
Gem graphql-ruby предоставляет объектно-ориентированный интерфейс для построения GraphQL API на Ruby.
Одним из преимуществ GraphQL часто называют возможность агрегировать данные из разных источников в одном запросе и прозрачно для клиента.
Отдельно рассматривают случай, когда один провайдер GraphQL API агрегирует данные из других GraphQL API. Этот сценарий носит название schema stitching.
К сожалению, на сегодняшний день поддержка объединения (или сшивания) схем отсутвует в реализации на Ruby.
Необходимо написать плагин (gem) для graphql-ruby, который позволит создавать «внешние» поля в локальную схему.
Польза: познакомиться с GraphQL в Ruby, получить open source опыт Ruby, принести пользу сообществу.
Пример работы и возможный API
На стороне Ruby мы опишем наш интерфейс следующим образом:
module GraphqlAPI
module Types
module Types
class BaseType < GraphQL::Schema::Object
# подключаем плагин
include GraphQL::RemoteFields
# resolver - это любой объект, который реализует метод #resolve_remote_field
remote_resolver MyResolver.new(url: "http://remote/api/graphql")
end
class Query < BaseType
field :posts,
[Types::Post],
null: false
field :payments,
[Types::Payment],
null: false,
# помечаем поле как `remote`
# – значение будет получаться через
# зарегистрированный resolver
remote: true
field :billing_info,
Types::BillingInfo,
null: true,
remote: true,
# можем явно указать другой resolver
remote_resolver: another_resolver,
# по умолчанию на "внешний" сервер отправляется неизмененный
# запрос, но можно определить правило (блок), чтобы изменить запрос
# (вместо "...fields" будут запрашиваемые поля
remote_query: ->(obj, ctx) { "query { billing { ...fields } }" }
end
end
end
end
class MyResolver
# resolver получает на вход запрос и текущий контекст
# (который будет использоваться, например, для аутентификации)
def resolve_remote_field(query, context)
end
end
Пример исходного запроса:
query {
posts {
id
title
}
payments {
id
}
billingInfo {
cardType
last4
}
}
Часть запроса будет выполнена локально:
query {
posts {
id
title
}
}
Другая часть будет проксирована на resolver по умолчанию:
query {
payments {
id
}
}
И, наконец, третья часть запроса будет выполнена с помощью another_resolver
:
query {
billing {
cardType
last4
}
}
Примечание: пример расширения API для полей смотрите в этом gist.
Инструкции по выполнению
Для уверенных в себе, задача на несколько дней
Gem test-prof включает в себя инструмент для запуска случайного набора тестов (документация).
Текущая реализация имеет ряд ограничений:
Необходимо усовершенстовать поддержку сэмплирования в TestProf.
Польза: узнать, как работают изнутри популярные библиотеки для тестирования в Ruby (RSpec и Minitest), принести пользу сообществу.
Инструкции по выполнению
Для новичков, задача на неделю
Gem anyway_config предоставляет единый API для конфигурирования Ruby приложения из разных источников данных (YAML файлы, переменные окружения), позволяя без лишних усилий следовать принципам Twelve-Factor App методологии.
Необходимо добавить возможность использовать аргументы коммандной строки в качестве еще одного источника данных для конфигурации.
Для этого предполагется добавить интеграцию стандартной Ruby библиотеки OptionParse
в anyway_config
.
Польза: познакомится с OptionParse в Ruby, принести пользу сообществу.
Инструкции по выполнению
Для новичков, задача на день
Nano ID — это очень маленькая библиотека, где идет борьба за каждый байт. Во многих файлах Nano ID есть строка алфавита. Одна из техник оптимизации — менять порядок символов в алфавите так, чтобы он повторял слова из JS-кода. Тогда gzip работает более эффективно.
Нужно написать скрипт, который бы перебирал все варианты порядка символов, чтобы найти оптимальный порядок.
Польза: получить опыт в JavaScript и оптимизации.
test/bruteforce
. Дать ему права на исполнение. Написать в начале #!/usr/bin/env node
../test/bruteforce index.js
.npx size-limit FILE
; парсить полученный размер в ответе Size Limit; запоминать, какой порядок символов дал минимальный размер.Для уверенных в себе, задача на несколько дней
Альтернативное решение от Леонида Батижевского.
Gem jwt_sessions
предоставляет базовый инструментарий для использования JWT для аутентификации пользователей.
Для хранения токенов сейчас используется Redis, и нет простой (то есть, без monkeypatching) возможности использовать другие хранилища — такие, например, как база данных, или память приложения (что может быть полезно в тестовом окружении).
Необходимо в рамках gem’а реализовать интерфейс для написания сторонних адаптеров, реализовать через него существующий Redis адаптер, а также добавить из коробки in-memory адаптер.
Польза: познакомиться поближе c JWT, применить навыки рефакторинга в Ruby, принести пользу сообществу.
Задачу можно разбить на несколько этапов.
Во-первых, нужно вынести общую логику из текущей реализации (RedisTokenStore
) в абстрактный или базовый адаптер (например, AbstractTokenStore
).
Во-вторых, переписать существующий RedisTokenStore
с использованием абстрактного адаптера.
И, наконец, добавить новый MemoryTokenStore
для хранения токенов в памяти приложения.
Также необходимо добавить параметр конфигурации, отвечающий за выбор адаптера:
JWTSessions.store_adapter = JWTSessions::RedisTokenStore.new(options)
# можно также использовать символы
JWTSessions.store_adapter = :redis, options
Для новичков, задача на день
Autoprefixer старается конвертировать Grid Layout для Internet Explorer. Эту функцию надо явно активировать, передавая опцию grid: true
.
Не у всех пользователей есть возможность выставить опции для Autoprefixer. Надо сделать включение функции с помощью комментария /* autoprefixer grid: on */
.
Польза: получить опыт JavaScript в известном проекте.
disabled()
в Processor
— он обеспечивает работу комментария /* autoprefixer: on */
./* autoprefixer: on */
сделать поддержку /* autoprefixer grid: on */
везде, где в Processor
используется options.grid
.Для новичков, задача на день
Autoprefixer старается конвертировать Grid Layout для Internet Explorer. Некоторые пользователи делают ошибку и пишут grid-area
и grid-row
и/или grid-column
в одном и том же правиле (это бессмысленно).
Надо показывать предупреждение, если в одном правиле есть grid-area
и grid-row
или grid-column
.
Польза: получить опыт JavaScript в известном проекте.
Processor
.Для новичков, задача на неделю
postcss-conic-gradient
— полифил для конических градиентов в CSS.
Он генерирует SVG-картинку для градиента и вставляет эту каринку в CSS как data:uri
. Для генерации изображения он сейчас использует Cairo — бинарную зависимость, которую надо отдельно ставить в систему.
Нужно заменить Cairo на какую-то другую библиотеку на чистом JavaScript или вручную генерировать SVG.
Польза: получить опыт работы с графикой в JavaScript, улучшить очень полезный полифил.
Для новичков, задача на день
Autoprefixer старается конвертировать Grid Layout для Internet Explorer. Однако свойства align-content
, align-items
, justify-content
и justify-items
нельзя сконвертировать.
Надо показывать предупреждение, если в одном правиле есть display: grid
или display: inline-grid
и свойства из списка выше.
Польза: получить опыт JavaScript в известном проекте.
Processor
.Для новичков, задача на несколько дней
Logux Rails — gem, позволяющий связать Logux-сервер и приложение на Ruby on Rails. В каталоге app/logux/
разработчик описывает права доступа и контроллеры для разных типов событий от Redux и подписок.
Если разработчик не создал обработчик для какого-то события или подписки, gem выбросит ошибки NoPolicyError
и NoActionError
. Вместо этого нужно в ответ выдавать ['unknownAction', meta.id]
или ['unknownChannel', meta.id]
.
Польза: получить open source портфолио в Ruby, научиться работать с Logux.
NoActionError
. Иногда имеет смысл не иметь код в app/logux/actions
для события.NoPolicyError
писать ['unknownAction', meta.id]
.NoPolicyError
писать ['unknownChannel', meta.id]
.Для новичков, задача на несколько дней
Logux Rails — gem, позволяющий связать Logux-сервер и приложение на Ruby on Rails. В каталоге app/logux/
разработчик описывает права доступа и контроллеры для разных типов событий от Redux и подписок.
Logux умеет откатить любое событие, которое было создано клиентом или отправлено с сервера. Для этого нужно сгенерировать особое событие. Нужно перенести эту логику в Rails в метод Logux.undo()
.
Польза: получить open source портфолио в Ruby, научиться работать с Logux.
this.log.add()
заменить на Logux.add()
.Для новичков, задача на несколько дней
Logux Rails — gem, позволяющий связать Logux-сервер и приложение на Ruby on Rails. В каталоге app/logux/
разработчик описывает права доступа и контроллеры для разных типов событий от Redux и подписок.
Нужно добавить две rake-задачи:
rake logux:actions
— чтобы вывести action.type
, которые ожидают все обработчики событий из app/logux/actions
.rake logux:channels
— чтобы вывести все подписки, которые ожидают обработчики подписок из app/logux/channels
.Польза: получить open source портфолио в Ruby, разобраться с Rake, научиться работать с Logux.
app/logux/actions
. Сгенерировать, какой action.type
они ждут.app/logux/channels
. Сгенерировать, какой action.channel
они ждут.lib/rake/logux_tasks.rb
.logux:actions
и logux:channels
.Для новичков, задача на несколько дней
Logux Rails — gem, позволяющий связать Logux-сервер и приложение на Ruby on Rails. В каталоге app/logux/
разработчик описывает права доступа и контроллеры для разных типов событий от Redux и подписок.
Нужно, чтобы в Logux::Actions
работала та же фильтрация входящих параметров, что и в контроллерах Rails.
Польза: получить open source портфолио в Ruby, научиться работать с Logux.
Logux::Actions
.Для новичков, задача на несколько дней
Logux Rails — gem, позволяющий связать Logux-сервер и приложение на Ruby on Rails. В каталоге app/logux/
разработчик описывает права доступа и контроллеры для разных типов событий от Redux и подписок.
Нужно улучшить вывод gem’а в консоль согласно списку в issue.
Польза: получить open source портфолио в Ruby, опыт работы с логером в Rails, научиться работать с Logux.
Для новичков, задача на несколько дней
Logux Rails — gem, позволяющий связать Logux-сервер и приложение на Ruby on Rails. В каталоге app/logux/
разработчик описывает права доступа и контроллеры для разных типов событий от Redux и подписок.
Каждое событие в Logux имеет уникальный ID в meta.id
. Но пока Logux Rails не умеет генерировать этот ID.
Польза: получить open source портфолио в Ruby, научиться работать с Logux.
node_id
вида server:#{ nanoid(8) }
.Logux.add()
ставить ему meta.id
.Для новичков, задача на несколько дней
Logux Rails — gem, позволяющий связать Logux-сервер и приложение на Ruby on Rails. В каталоге app/logux/
разработчик описывает права доступа и контроллеры для разных типов событий от Redux и подписок.
Сейчас при ошибке gem всегда выдает ['error']
в ответ. Нужно возвращать ['error', meta.id, error_stack]
или ['error', auth_id, error_stack]
.
Польза: получить open source портфолио в Ruby, научиться работать с Logux.
command_params
вызвала ошибку.action
нужно писать в ответ ['error', meta.id, error_stack]
.auth
нужно писать в ответ ['error', chunk.auth_id, e.backtrace.join("\n")]
.Для новичков, задача на несколько недель
Easings.net — каталог easing-функций. Easing-функции — очень важный элемент анимаций, поэтому многие разработчики посещают этот сайт, чтобы правильно подобрать анимацию на веб-странице.
Польза: получить опыт JavaScript-разработки; модернизировать популярную шпаргалку для разработчиков.
Для уверенных в себе, задача на несколько дней
В ActiveRecord предусмотрено несколько методов для предварительной загрузки ассоциированных записей (такие как includes
, eager_load
и другие). Это позволяет выгружать данные, избегая так называемой проблемы N+1 запросов.
В некоторых случаях мы не знаем заранее, какие ассоциации нам понадобятся при выполнении запроса (простой пример — работа с GraphQL).
Приходится либо предзагружать лишние данные (например, указывая все ассоциации в includes
), либо использовать альтернативные подходы, такие как batch loading.
Необходимо написать ленивую реализацию метода preload
, которая предзагружает ассоциации только если они используются.
Польза: детально узнать, как работает Rails и ActiveRecord изнутри, принести пользу Rails-сообществу.
Пример работы
class User < ApplicationRecord
has_many :posts
end
class Post < ApplicationRecord
has_many :comments
belongs_to :user
end
class Comment < ApplicationRecord
belongs_to :user
belongs_to :post
end
# Инициируем выборку: выгружаем пользователей с публикациями,
# комментариями и авторами комментариев
users = User.lazy_preload(posts: { comments: :user } ).limit(10)
# Будет выполнен один запрос
#=> SELECT * FROM users LIMIT 10
users.each do |user|
# Запрос на загрузку постов будет выполнен при первом
# обращении к ассоциации на любом пользователе,
# но записи будут загружены сразу для всех пользователей
#
# SELECT * FROM posts WHERE user_id in (...)
user.posts.each do |post|
p post.title
end
end
# Комментарии не будет предзагружены, так как мы к ним не обращались.
Для новичков, задача на день
Nano ID генерирует случайные ID. Так как идентификаторы полностью случайные, частью буквенно-цифрового идентификатора может стать обсценное слово.
Нужно создать nanoid-good
, который будет проверять ID на плохие слова и, если надо, генерировать ID еще раз.
Польза: получить больше опыта в JavaScript и пополнить open-source портфолио.
nanoid-good
. Не публиковать его, пока Ситник не проверит.nanoid
в зависимостях.index.js
, где будет функция, принимающая те же аргументы, что nanoid
, но возвращающая ID без обсценных слов.non-secure.js
, где будет такая же логика, как и в index.js
, но будет использоваться nanoid/non-secure
.Для новичков, задача на день
Сейчас yaspeller
берет свои настройки только из файла .yaspellerrc
. Хотелось бы, чтобы он поддерживал поле "yaspeller"
в package.json
, как многие остальные инструменты из npm.
Чтобы не писать много кода поиска настроек, можно использовать библиотеку cosmiconfig
.
Польза: получить больше опыта в JavaScript.
cosmiconfig
. На этом этапе искать только файл .yaspellerrc
."yaspeller"
в package.json
и добавить поиск его в cosmiconfig
.Для продвинутых, задача на неделю
Контракт — это набор валидаций (тестов), которые запускаются во время 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 должен в плане простоты быть его антиподом.
Для новичков, задача на неделю
shortid
использует опасные методы генерации ID. К сожалению, его использует уже слишком много проектов. Поэтому надо заменить в нем метод генерации ID, сохранив прежний API.
Польза: получить больше опыта в JS.
nanoid
.nanoid/generate
, вместо собственного алгоритма перестановки алфавита.Для продвинутых, задача на неделю
В современном JavaScript даже у маленького проекта есть куча инструментов разработки — хотя бы линтер и тесты. Для каждого инструмента надо иметь настройки. Иногда настроек получается больше, чем кода самой библиотеки.
Можно держать все эти настройки в отдельных файлах — тогда в папке проекта будет много файлов. Например, из-за длинного списка файлов README на GitHub будет далеко после большого списка файлов.
Можно перенести настройки в package.json
, но тогда они окажутся в npm-пакете, что увеличит его размер.
Можно решить проблему, если создать инструмент clean-publish
. Он будет копировать файлы проекта во временную папку, убирать лишнее из package.json
и вызывать npm publish
на временной папке.
Польза: получить больше опыта в JavaScript.
clean-publish
.clean-publish.js
и прописать его в поле bin
в package.json
.package.json
. Например, eslintConfig
и jest
. Также можно убирать devDependencies
и все scripts
кроме нужных для npm (например, postinstall
).npm publish
его цветной вывод идет в вывод clean-publish
.Для новичков, задача на день
В некоторых случаях Autoprefixer генерирует вложенные медиа-выражения. В этих случаях надо объединять медиа-выражения в одно.
@media (min-width: 30em) {
.wrapper {
display: grid;
grid-template-areas: "a b";
}
}
@media (min-width: 60em) {
.wrapper {
grid-template-areas: "a b";
}
}
@media (min-width: 30em) {
.a {
grid-area: a;
}
}
@media (min-width: 30em) {
.b {
grid-area: b;
}
}
Польза: указать в резюме коммит в известный проект.
Для новичков, задача на день
Autoprefixer умеет генерировать префиксы для Grid Layout только если имена областей уникальные. Например, в этом случае сгенерировать префиксы нельзя:
.parent-alpha {
grid-template-areas: "delta echo";
}
.parent-beta {
grid-template-areas: "echo delta";
}
.grid-cell {
-ms-grid-column: ???; /* не понятно, от какого родителя брать местоположение */
arid-area: echo;
}
В этих случаях надо выводить предупреждение.
Польза: указать в резюме коммит в известный проект.
Для уверенных в себе, задача на неделю
В хороших парсерах разбор разбит на два шага: строка → токены и токены → дерево объектов. Первый шаг называется токенайзер.
Сейчас токенайзер возвращает [token_name, content, start_line, start_column, end_line, end_column]
. Есть мнение, что можно сильно ускорить токенайзер, если:
Uint32Array(token_code, offset_start, offset_end, start_line, start_column, end_line, end_column)
. То есть, вместо строки с именем токена использовать цифровой код. А content
парсер может взять сам из строки входящего CSS по offset_start
и offset_end
.Uint32Array
. В итоге не будет постоянно создаваться больше объектов и вызываться сборщик мусора.Польза: указать в резюме коммит в известный проект.
Для новичков, задача на день
FactoryDefault – это один из инструментов, входящих в состав TestProf.
Он позволяет переиспользовать уже созданные фабрикой объекты для вложенных ассоциаций, решая таким образом проблему каскадов фабрик.
Необходимо сделать так, чтобы FactoryDefault учитывал используемые при создании ассоциаций трейты.
Польза: узнать, как работает FactoryGirl изнутри, принести пользу сообществу.
Инструкции по выполнению
Для новичков, задача на неделю
Autoprefixer умеет вставлять префиксы Grid Layout для Internet Explorer. Поддерживается даже grid-template-areas
.
Но, в некоторых случаях, префиксы для grid-template-areas
в медиа-выражении могут быть вставлены не в том месте. Нужно изменить логику.
Польза: указать в резюме коммит в известный проект.
Сейчас для кода
.grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-template-areas: "a b"
"c d";
}
@media (min-width: 600px) {
.grid {
grid-template-areas: "a b c d";
}
}
.grid__cell--a {
grid-area: a;
}
Autoprefixer сгенерирует:
.grid {
display: -ms-grid;
display: grid;
-ms-grid-columns: (1fr)[2];
grid-template-columns: repeat(2, 1fr);
grid-template-areas: "a b"
"c d";
}
@media (min-width: 600px) {
.grid {
grid-template-areas: "a b c d";
}
.grid__cell--a {
-ms-grid-row: 1;
-ms-grid-column: 1;
}
}
.grid__cell--a {
-ms-grid-row: 1;
-ms-grid-column: 1;
grid-area: a;
}
В итоге, код будет работать неправильно, так как второй .grid__cell--a
(вне @media
) перезапишет значение первого. Правильный вариант:
.grid {
display: -ms-grid;
display: grid;
-ms-grid-columns: (1fr)[2];
grid-template-columns: repeat(2, 1fr);
grid-template-areas: "a b"
"c d";
}
@media (min-width: 600px) {
.grid {
grid-template-areas: "a b c d";
}
}
.grid__cell--a {
-ms-grid-row: 1;
-ms-grid-column: 1;
grid-area: a;
}
@media (min-width: 600px) {
.grid__cell--a {
-ms-grid-row: 1;
-ms-grid-column: 1;
}
}
test/cases/grid-template-*
.insertAreas
.Для новичков, задача на неделю
Многие пользователи Autoprefixer и Babel забывают обновлять базу браузеров caniuse-lite
. В этой базе есть информация о времени релиза каждой версии браузера. Взяв дату самой свежей версии браузера, можно примерно узнать время релиза caniuse-lite
. Если база не обновлялась уже полгода, надо показать предупреждение пользователю.
Польза: указать в резюме коммит в известный проект.
node.js
найти самый свежий браузер из caniuse-lite
. Сравнить время релиза с текущим временем. Вывести предупреждение, если разница больше полугода.caniuse-lite
(и для npm
, и для yarn
— в зависимости от наличия yarn.lock
рядом с package.json
).Для новичков, задача на неделю
Некоторым пользователям не нравится, когда на странице много анимаций. В Safari есть специальное медиа-выражение @media (prefers-reduced-motion) { }
. Если пользователь не хочет анимаций, Safari будет его применять.
Но большинство фронтенд-разработчиков про него забывают. Эту проблему может решить новое правило для Stylelint.
Польза: указать в резюме коммит в известный проект.
transition
или animation
и проверяет, что такой же селектор есть внутри @media (prefers-reduced-motion) { }
.Для новичков, задача на неделю
Многие пользователи пользуются сайтами с клавиатуры; у некоторых даже нет другой возможности. Но большинство фронтенд-разработчиков забывают указать :focus
стили.
Эту проблему можно уменьшить, если добавить в Stylelint правило, которое будет напоминать разработчику писать :focus
стиль.
Чтобы понять, каким элементам нужен :focus
, можно использовать стили :hover
. Можно считать, что всем стилям с :hover
, нужен какой-то парный стиль с :focus
.
Польза: указать в резюме коммит в известный проект.
.some:hover { } .some:focus { }
, и .some:hover, .some:focus { }
.Для новичков, задача на неделю
Скоро postcss-preset-env заменит cssnext и станет одним из «главных» плагинов PostCSS.
У плагина есть сайт, но его главная страница совсем не информативная.
Задача подойдет, если вы больше фокусируетесь на дизайне и CSS.
Польза: указать в резюме коммит в известный проект.
jonathantneal
.cssnext
, чем он может быть полезен разработчикам. Отдельно можно сравнить его с некоторыми функциями Sass и провести ассоциацию с CoffeeScript и Babel.Для новичков, задача на неделю
Скоро postcss-preset-env заменит cssnext и станет одним из «главных» плагинов PostCSS. Но на сайте PostCSS мы все еще рекомендуем cssnext.
Задача подойдет, если вы больше фокусируетесь на дизайне и CSS, но хотите получить больше опыта с React.
Польза: указать в резюме коммит в известный проект.
color-mod()
и Custom Media.Для новичков, задача на неделю
cssnano сжимает CSS и используется по умолчанию в webpack. Эта задача поможет с выпуском cssnano 4.
Сейчас cssnano, встретив padding: 10px; padding: var(--name)
, удалит первое свойство. Надо исправить это поведение.
Польза: указать в резюме коммит в известный проект.
Для новичков, задача на неделю
cssnano сжимает CSS и используется по умолчанию в webpack. Эта задача поможет с выпуском cssnano 4.
Сейчас cssnano, встретив :-ms-input-placeholder {} ::-ms-input-placeholder { }
, объединит правила в :-ms-input-placeholder, ::-ms-input-placeholder { }
. К сожалению, это ломает поддержку Internet Explorer.
Польза: указать в резюме коммит в известный проект.
Для уверенных в себе, задача на неделю
cssnano сжимает CSS и используется по умолчанию в webpack. Эта задача поможет с выпуском cssnano 4.
Правило mergeLonghand
иногда работает неправильно. Надо изучить его и починить.
Польза: указать в резюме коммит в известный проект.
Для новичков, задача на неделю
cssnano сжимает CSS и используется по умолчанию в webpack. Эта задача поможет с выпуском cssnano 4.
Сейчас cssnano, встретив:
.a { all: initial; color: red }
.b { all: initial; color: blue }
может превратить свойства в:
.a { color: red }
.a, .b { all: initial; color: red }
.b { color: blue }
В итоге правило будет работать неправильно.
Польза: указать в резюме коммит в известный проект.
Для новичков, задача на неделю
cssnano сжимает CSS и используется по умолчанию в webpack. Эта задача поможет с выпуском cssnano 4.
Нужно разобраться с ошибкой шрифтов.
Польза: указать в резюме коммит в известный проект.
Для новичков, задача на неделю
cssnano сжимает CSS и используется по умолчанию в webpack. Эта задача поможет с выпуском cssnano 4.
Надо обновить зависимости и API.
Польза: указать в резюме коммит в известный проект.
yarn upgrade-interactive --latest
в проекте.yarn upgrade
.Для новичков, задача на неделю
Сейчас Autoprefixer понимает особые комментарии /* autoprefixer: off */
и /* autoprefixer: on */
, которые выключают или включают его на весь блок стилей.
Нужно добавить поддержку комментария /* autoprefixer: ignore next */
,
чтобы Autoprefixer игнорировал только следующее свойство или правило.
Польза: указать в резюме коммит в известный проект.
Control Comments
.Для новичков, задача на неделю
Browserslist — общий конфиг списка браузеров для Autoprefixer, Babel и других front-end инструментов. В конфиге разработчики пишут запросы вида last 2 versions
, Browserslist переводит это в список конкретных версий браузеров.
Но для Babel и ESLint нужно указывать не только браузеры, но и версии Node.js.
Польза: указать в резюме коммит в проект с 20 млн. загрузок в месяц.
browserslist('node 9.11.1') => ['node 9.11.0']
и тесты для него.node 9.11
, загружающий node 9.11.1
.node 9
, загружающий node 9.11.1
.maintained node versions
, возращающий все поддерживаемые в данный момент версии.Для новичков, задача на неделю
CSS-свойство color-adjust
из черновика CSSWD работает в webkit-браузерах через свойство -webkit-print-color-adjust
.
Нужно добавить поддержку этого свойства в Автопрефиксер.
Польза: написать в резюме, что сделали минорный релиз Автопрефиксера.
-webkit-print-color-adjust
и color-adjust
. Узнать, кто их поддерживает и, примерно, с какой версии. Начальные ссылки есть в issue Автопрефиксера./cc @ai
.data/prefixes.js
.lib/hacks/
. Сделать в нем замену имени свойства по примерам других хаков.lib/prefixes.js
.test/cases/
.test/autoprefixer.test.js
.Для уверенных в себе, задача на несколько дней
Нужно добавить в Ossert новые источники данных. Отличным кандидадатом с достаточно богатым API является Twitter.
Это позволит измерить еще один важный аспект развития Open-Source библиотек — хайп, реакцию на посты про проект, а также количество заинтересованных проектом.
Польза: возможность научиться работать с API Twitter, написать надежный API клиент и узнать больше о метриках и поведении разработчиков свободного ПО.
Как это должно работать?
Сейчас сбор информации уже работает для RubyGems, GitHub, Bestgems и StackOverflow. Нужно совместимым образом организовать сбор с новых источников.
Кейс с Twitter представляет особую сложность, так как поиск по истории твитов доступен только на 7 дней назад. Таким образом, это будет первый источник данных с инкрементальным сбором. То есть, нужно побороть возможность утраты уже накопленных данных.
Также, стоит отметить, что у Twitter API нет схемы, так что придется продумать валидации исходящих и распознавание получаемых данных.
Советы по реализации
module Ossert
module Fetch
class Twitter
def initialize(project)
...
end
def process
# Метод сбора основных метрик по проекту
end
end
end
end
Для этого нужно проанализировать возможность API вашего источника данных и выбрать:
Рекомендации:
"<author>/<project name>"
Охват (количество пользователей, которые увидели информацию) = количество твитов о геме * подписчиков авторов твитов + количество ретвитов оригинальных твитов * подписчиков ретвитеров
У нас нет возможности собрать полную историю по запросам API, поэтому предлагается для всех метрик Twitter использовать Moving Average за семь дней. То есть, мы каждый день собираем данные за последние 7 дней и считаем среднее значение (с пересечением усредняемых данных).
В Ossert данные выводятся по кварталам, так что для Twitter потребуется как минимум отдельный способ хранения.
Также неизвестными являются ограничения на количество запросов, хотя надо продумать как сделать возможной синхронизацию более чем 100K гемов.
Бонус
Если получится организовать надежный сбор, то метрики Twitter будут удостоенны отдельного раздела на ossert.evilmartians.io из-за повышенной сложности сбора и отличного фрейминга по времени.
Инструкции по выполнению
Для новичков, задача на неделю
LiteCable – это альтернативная реализация фреймворка ActionCable из Rails, совместимая с ActionCable-клиентами, но при этом имеющая минимум зависимостей.
Необходимо добавить в LiteCable интеграцию с Plezi (Iodine) “из коробки” (по аналогии с тем, как это сделано для AnyCable).
Польза: познакомиться и научиться работать с новым веб-сервером для Ruby приложений (Iodine); разобраться во внутреннем устройстве real-time фреймворков.
Iodine – это новый веб-сервер для Ruby приложений (альтернатива Unicorn/Puma/etc), спроектированный специально для работы с большим количеством одновременных подключений (например, веб-сокетов).
LiteCable реализует бизнес-логику работы с сокетами (“каналы”) и предоставляет API для интеграции с непосредственно сервером (на данный момент “из коробки” доступна интеграция с AnyCable.
Задача состоит в том, чтобы сделать использование Iodine в качестве сервера для LiteCable возможным и максимально простым.
Вместо “голого” Iodine можно сделать интеграцию с Plezi – микро-фреймворком для работы с веб-сокетами.
Информацию о внутреннем устройстве подписок можно посмотреть тут.
Инструкции по выполнению
Для новичков, задача на неделю
NanoID — JavaScript-библиотека для генерации случайных ID. Как и у UUID, есть определенная вероятность того, что будет сгенерированы два одинаковых ID.
Вероятность очень мала; в то же время, у многих пользователей генерируется небольшое количество идентификаторов, так что они могут уменьшить длину желаемого ID без особых рисков. Но чтобы понять, насколько можно уменьшить такую длину, нужно сделать калькулятор.
Польза: получить опыт JS-разработки; получить публичный сайт в свое резюме.
require('nanoid')
, для нестандартного — require('nanoid/generate')
.Для продвинутых, задача на неделю
Необходимо написать инструмент для Ruby-стека, который будет отслеживать выполнение потенциально опасных действий внутри транзакции БД (например, HTTP-запросов или отправки фоновых задач в очередь).
Польза: разобраться в том, как работают популярные ORM и другие библиотеки для работы с «внешним миром»; написать инструмент, которым будет активно пользоваться Ruby-сообщество.
Основная задача — подсказать разработчику, что внутри транзакции выполняется какая-то небезопасная операция.
Например:
# вызов HTTP
User.transaction do
user = User.new(user_params)
user.save!
# происходит вызов внешнего API
PaymentsService.charge!(user)
end
# создание фоновой задачи
User.transaction do
user.update!(confirmed_at: Time.now)
UserMailer.successful_confirmation(user).deliver_later
end
В случае, если в итоге наша транзакция не будет зафиксирована («закоммичена»), может нарушится согласованность данных в нашей бизнес-логике — и другие ошибки (так как эти операции не откатятся).
Isolator должен уметь уведомлять об ошибках разными способами:
Внимание: при использовании в тестовом окружении необходимо учесть, что тесты могут выполнятся внутри транзакции (use_transaction_tests
в Rails). В этом случае мы должны игнорировать «внешнюю», тестовую транзакцию.
require "isolator"
(или добавить в Gemfile
).Инструкции по выполнению
Для продвинутых, задача на пару дней
postgresql_lwrp — самый удобный и гибкий кукбук для системы управления конфигурациями Chef, обеспечивающий установку и настройку БД PostgreSQL.
Нужно обеспечить его работу в Chef Client версии 13.
Польза: возможность попрактиковаться в написании кукбуков Chef, разобраться в особенностях Chef 13.
Этот кукбук создавался и активно развивался в период, когда самой свежей версией Chef client была 12-я. Релиз 13-й версии принес много нововведений, в том числе ломающих совместимость. Необходимо обновить код кукбука с учетом этих нововведений.
В процессе работы потребуется разобраться со списком сторонних кукбуков, от которых зависит postgresql_lwrp. Возможно, потребуется их замена или полный рефакторинг соответствующих мест, так как они тоже могут быть несовместимы с Chef Client 13.
Может быть удобнее выполнять задание, если сразу сделать отдельные Test Kitchen suites, использующие свежий Chef Client 13. Test Kitchen suites для Chef Сlient 12 стоит сохранить, чтобы проверять обратную совместимость: 12-я версия все еще активно используется в старых инсталляциях.
Что нужно сделать:
Для новичков, задача на пару дней
Sniffer – это гем для логирования и анализа HTTP-траффика, который поддерживает большинство ruby-библиотек для работы с HTTP-запросами.
Hеобходимо добавить поддержку Excon.
Польза: получить опыт разработки gem’ов, разобраться в принципах работы с HTTP-запросами в Ruby, помочь полезному проекту
Что нужно сделать:
Для новичков, задача на неделю
Прямо сейчас Size Limit читает свои настройки из package.json
. Но было бы хорошо добавить поддержку отдельного файла настроек, например, .size-limit
с JSON внутри.
Польза: получить больше опыта работы с Node.js.
./cli.js
для использования с cosmiconfig
(или написать код поиска файла настроек самому, если cosmiconfig
не подходит).Для новичков, задача на неделю
Browserslist — инструмент для Autoprefixer и Babel для выбора браузеров, которые нужно поддерживать в проекте.
Есть хорошая идея добавить запрос вида since 2013
для выбора версий
браузеров, вышедших с 2003 года.
Польза: принять участие в проекте с 11 млн. загрузок.
src/packer/agents.js
копирование release_date
из caniuse-db/fulldata-json/data-2.0.json
.src/agents.test.js
.@ai @ben-eb
.QUERIES
новый запрос.Для новичков, задача на неделю
Autoprefixer умеет вставлять -ms-
префиксы для CSS Grid Layout, заменяя синтаксис, но эта поддержка очень ограничена. Есть мнение, что ее можно улучшить.
Польза: разобраться в Grid Layout, принять участие в большом open-source проекте.
data/prefixes.js
, если их там еще нет.lib/hacks
создать/добавить логику изменения синтаксиса.test/cases/grid.*
.Для новичков, задача на неделю
Необходимо разработать OmniAuth-стратегию для входа в приложения на Ruby с учетной записью eBay в виде отдельного гема.
Сейчас есть гемы, которые реализуют вход через метод Auth’n’auth (например, подзаброшенный гем omniauth-ebay), а через метод OAuth — нет. (Технически оба метода работают по протоколу OAuth, что вносит путаницу).
Для работы с новыми REST API eBay пользователь должен входить именно через новый метод входа.
Польза: возможность разобраться, как же работает вход через соцсети протокол OAuth и библиотека OmniAuth.
Решение: итоговая реализация основана на решении Игната Закревского, но и Андрей Падерин и Илья Долгирев прислали рабочие решения, за что им выражается благодарность.
Что нужно реализовать:
Ruby gem с OmniAuth-стратегией и тестами на взаимодействие с eBay.
Советы по реализации
Язык реализации: Ruby.
Документация начинается здесь и продолжается здесь.
Примечание: После успешной аутентификации пользователя на стороне eBay, у нас нет никакой информации, кто же к нам пришел. Для этих целей провайдеры обычно предоставляют так называемый UserInfo endpoint, но проблема в том, что в новых eBay REST API такого нет. Поэтому для получения информации необходимо делать запрос к методу getUser
старого Trading API с указанием нового токена заголовке X-EBAY-API-IAF-TOKEN
(мы пишем это здесь, потому что в документации этого не найти).
Инструкции по выполнению
Для продвинутых, задача на неделю
Необходимо написать инструмент для клонирования моделей (например, Active Record) с гибкими настройками. Существующие гемы (deep_clonable и amoeba), к сожалению, неудобны при работе со сложной бизнес-логикой и завязаны на ActiveRecord.
Польза: попрактиковаться в написании расширяемого DSL на Ruby и написать инструмент, которым будет активно пользоваться сообщество.
class PostCloner < Clowne::Cloner
# клонировать все ассоциированные объекты
include_all
# клонировать указанную ассоциацию
include_association :comments
# указать scope для ассоциации
include_association :comments, -> { where('created_at > ?', 1.year.ago) }
# или именнованный scope
include_association :comments, :this_year
# исключить ассоциацию (если она была добавлена через include_all или в родительском конфиге)
exclude_association :likes
# обнулить поля
nullify :external_id, :modified_at, :modified_by
# пользовательский блок, вызываемый в конце клонирования
finalize do |source, record|
# ...
end
end
Для клонирования объекта вызываем:
cloned_post = PostCloner.call(post)
# Дополнительно указываем контекст и произвольные параметры
cloned_post = PostCloner.call(post, for: :another_user, user_id: 123)
class PostCloner < Clowne::Cloner
...
# в конфигурации описываем именнованный контекст
context :another_user do
nullify :user_id
# в модификаторах ассоциаций имеем доступ к доп. параметрам
include_association :likes, -> (params) { where.not(user_id: params[:user_id]) }
# и в finalize
finalize do |source, record, params|
record.user_id = params[:user_id]
end
end
end
# Только произвольные параметры
cloned_post = PostCloner.call(post, user_id: 123)
# По умолчанию для модели Comment
class CommentCloner < Clowne::Cloner
...
end
class SimpleCommentCloner < Clowne::Cloner
...
end
class PostCloner < Clowne::Cloner
include_associations :comments, clone_with: SimpleCommentCloner
end
Если для модели не существует конфига, то клонируем только атрибуты, без ассоциаций
Механизм клонирования должен подразумевать интеграцию с разными фреймворками (не только Active Record), то есть должен быть внутренний API для написания адаптеров для произвольных фреймворков
По умолчанию должна быть только поддержка ActiveRecord:
class Post < ActiveRecord::Base
# Явно указать класс клонера
clone_with ArticleCloner
end
post.clone(params) == ArticleCloner.call(post, params)
Инструкции по выполнению
Для продвинутых, задача на неделю
Необходимо реализовать возможность хранить данные для ассоциаций (внешние ключи) в поле колонки типа JSONB (PostgreSQL), сохранив при этом большую часть функционала ассоциаций Active Record.
Польза: возможность научиться работать с внутренностями ActiveRecord и JSONB.
Рассмотрим простой пример:
class Profile < ActiveRecord::Base
# передаём дополнительную опцию `store`,
# которая указывает, что мы хотим хранить ключ
# не в отдельной колонке ("user_id"),
# а в jsonb-колонке "extra".
belongs_to :user, store: :extra
end
class SocialProfile < ActiveRecord::Base
belongs_to :user, store: :extra
end
class User < ActiveRecord::Base
# в обратной связи указываем, что связь через store.
# Вопрос: можно ли опустить явное указание того,
# что используется store, и "узанавать" его из связяннoй модели?
has_one :profile, foreign_store: :extra
has_many :social_profiles, foreign_store: :extra
end
Этот функционал позволяет хранить информацию о разных ассоциациях в одной JSONB колонке.
foreign_key
, inverse_of
и т.д.)preload
/ includes
:User.all.includes(:profile)
#=> SELECT * FROM users
#=> SELECT * FROM profiles where extra->>'user_id'::int IN (...)
eager_load
/ joins
.add_reference :profiles, :users, store: :extra, index: true
Используя JSONB, мы можем реализовать связь многие-ко-многим без промежуточной таблицы (если, например, количество связей небольшое):
class Label < ActiveRecord::Base
# в поле extra['user_ids'] мы будем хранить массив id пользователей,
has_and_belongs_to_many :users, store: :extra
end
class User < ActiveRecord::Base
# здесь список лейблов в extra['label_ids']
has_and_belongs_to_many :labels, store: :extra
end
С точки зрения API функционал должен повторять имеющийся для HABTM.
Также было бы неплохо ответить на вопрос – в каких случаях выгоднее использовать такой подход вместо классического?
Лучшим ответом на этот вопрос будет предъявление бенчмарков и их результатов.
Для новичков, задача на неделю
TestProf – это набор инструментов для профилирования тестов, который включает в себя FactoryProf, анализатор использования фабрик в тестах.
Необходимо добавить поддержку Fabrication (альтернатива FactoryGirl) в FactoryProf: сбор общей статистики по использованию фабрик, построение factory flame графиков.
Польза: узнать, как работают профайлеры кода изнутри, познакомиться с гемом Fabrication.
Инструкции по выполнению
Для новичков, задача на неделю
EventProf – один из инструментов, входящих в состав TestProf. На данный момент работает “из коробки” только с фреймворком RSpec.
Необходимо реализовать интеграцию Minitest и EventProf.
Польза: узнать, как работают профайлеры кода изнутри, научиться писать расширения для Minitest.
Инструкции по выполнению
Для новичков, задача на неделю
EmojiMart — один из лучших контролов для выбора эмодзи. Сейчас у него в зависимостях есть огромный core-js
. Эта зависимость нужна для поддержки ES2016+ в IE. Но, судя по всему, они загружают такую большую зависимость ради пары строк кода.
Нужно убрать core-js
и починить работу в IE более простыми способами.
Польза: получить опыт оптимизации размера проекта.
yarn install
.build:example
и открыть example/index.html
в IE. Убедиться, что всё сейчас работает.core-js
из проекта.build:example
и открыть example/index.html
в IE. Найти ошибки.for (item of array)
на for (var i = 0; i < array.length; i++)
.core-js
.core-js
оказался не нужен, с помощью yarn
удалить core-js
из зависимостей.npx size-limit
и обновить лимит размера в package.json
.Для новичков, задача на неделю
EmojiMart — один из лучших контролов для выбора эмодзи. Но сейчас они собирают всё с помощью webpack. В итоге, если пользователь подключает emoji-mart
и core-js
, у него в сборке оказывается два core-js
— один пользовательский и второй от EmojiMart.
Нужно переделать сборку с webpack на просто Babel, сохраняя древовидную структуру. После чего добавить плагин для вырезания propTypes
.
Польза: получить опыт оптимизации размера проекта.
yarn
удалить webpack
и webpack-bundle-analyzer
из зависимостей.build:dist
и watch
, чтобы они использовали Babel вместо webpack. Можно сразу вызывать babel-cli
из package.json
или изменить скрипты в scripts/
.main
и size-limit
в package.json
на использование dist/index.js
вместо dist/emoji-mart.js
.babel-plugin-transform-react-remove-prop-types
.Для новичков, задача на неделю
Logux Status показывает состояние синхронизации. Функция status()
является фундаментом, чтобы построить свой UI состояния синхронизации.
Одни из шагов синхронизации — connecting
и connectingAfterWait
. Они говорят о том, что Logux пытается подключится к серверу.
Когда нет Wi-Fi, состояния connecting
и connectingAfterWait
отображаются слишком быстро — всего на 1—10 мс. В итоге интерфейс неприятно мигает.
Польза: принять участие в разработке Logux.
status.js
и как он используется в badge.js
.sync.on('state', …)
, чтобы connecting
передавался в callback
не сразу, а только если он не изменилася за 100 мс.Для новичков, задача на неделю
Logux, при разрыве связи, раз в несколько секунд пытается подключиться снова.
Браузеры посылают событие online
при появлении связи. Мы можем запускать попытку связи чуть раньше.
Польза: принять участие в разработке Logux; получить опыт работы с редкими API браузера.
reconnect.js
код, который при событии online
пробует подключиться снова.Для новичков, задача на неделю
Logux хранит события в логе. Чтобы чистить лог, он использует понятие «смысла жизни». У каждого события есть «смысл жизни» — массив строк. В любой момент разработчик может убрать смысл жизни у события. Как только у события не остается смыслов жизни, оно удаляется.
Однако, на практике, часто нужно одно и то же действие — хранить в логе только одно событие какого-то типа. Для этого приходится часто повторять один и тот же код:
log.add(
{ type: 'app/A' },
{ reasons: ['app/lastA'] }
).then(meta => {
log.removeReason('app/lastA', { maxAdded: meta.added - 1 })
})
Этот код добавляет в лог событие со смыслом жизни app/lastA
и удаляет этот смысл у всех предыдущих событий.
Код приходится писать слишком часто. Стоит добавить опцию keepLast
, которая бы делала то же самое.
log.add(
{ type: 'app/A' },
{ keepLast: 'app/lastA' }
)
Польза: принять участие в разработке Logux.
keepLast
в Log#add()
.Для новичков, задача на неделю
Yaspeller — JS-утилита, которая проверяет орфографию, посылая запрос на сервера Яндекса. Интернет не всегда стабилен — иногда запрос заканчивается ошибкой связи.
Нужно исправить Yaspeller, чтобы он, в случае ошибки, делал еще 2 запроса и только потом возвращал ошибку.
Польза: получить опыт разработки под Node.js.
lib/post.js
.ETIMEDOUT
делать еще 2 попытки.Для новичков, задача на неделю
Logux может хранить лог в разных хранилищах — в памяти или IndexedDB. На подходе SQL-хранилище.
Сейчас у каждого хранилища свои тесты — много тестов дублируются. Нужно создать общие тесты для всех хранилищ.
Польза: принять участие в разработке Logux; получить опыт тестирования и Promise.
yarn run docs
.Store
из docs/Store.html
.test/memory-store.test.js
.test/indexed-store.test.js
.Store
внутри функции eachTest
в index.js
по примеру теста is a object
.is a object
.logux-store-tests
в форк logux-core
— в package.json
можно указывать версию из GitHub.MemoryStore
проходит новые тесты. Почистить старые тесты от уже ненужных тестов API.logux-store-tests
в форк logux-client
.IndexedStore
проходит новые тесты. Почистить старые тесты от уже ненужных тестов API.Для новичков, задача на неделю
Сейчас у Logux Server два типа логов — Human и Bunyan. Но можно сильно упростить код, если генерировать формат Human из Bunyan.
Польза: принять участие в разработке Logux; получить опыт разработки на Node.js и Bunyan.
./test/__snapshots__/show.js
и посмотреть все варианты вывода сервера.reporters/human
.reporters/human
, чтобы он использовал новый модуль. Проверить, что старые тесты проходят.Для новичков, задача на день
Необходимо доработать сообщение об ошибке RSpec-матчера exceed_query_limit
из гема rspec-sqlimit таким образом, чтобы показывать выполненные SQL запросы вместе с их переменными.
Польза: возможность сделать первый вклад в Open Source проект (особенно если вы не знаете, с чего начать). Попутно можно познакомиться с механизмами ActiveSupport Notifications и Instrumentation, разобраться в устройстве матчеров RSpec.
RSpec матчер rspec-sqlimit публикует сообщение об ошибке (превышение лимита SQL запросов) в следующем виде:
require "rspec-sqlimit"
RSpec.describe "N+1 safety" do
it "doesn't send unnecessary requests to db" do
expect { User.create(name: "Joe") }.not_to exceed_query_limit(1)
end
end
Failure/Error: expect { User.create }.not_to exceed_query_limit(0).with(/INSERT/)
Expected to run maximum 0 queries that match (?-mix:INSERT)
The following 1 queries were invoked among others (see mark ->):
1) begin transaction (0.072 ms)
-> 2) INSERT INTO "users" ("name") VALUES (?) (0.368 ms)
3) commit transaction (147.559 ms)
В текстах запросов выше знаком (?)
отмечены непривязанные параметры.
Эти параметры содержатся под ключом :binds
(отдельно от текста запроса) в данных, передаваемом хуком sql.active_record.
Необходимо извлечь параметры запроса из данных и добавить их к сообщению об ошибке, чтобы оно обрело следующий вид:
Failure/Error: expect { User.create }.not_to exceed_query_limit(0).with(/INSERT/)
Expected to run maximum 0 queries that match (?-mix:INSERT)
The following 1 queries were invoked among others (see mark ->):
1) begin transaction (0.072 ms)
-> 2) INSERT INTO "users" ("name") VALUES ("Joe") (0.368 ms)
3) commit transaction (147.559 ms)
Это позволит точнее анализировать, что “пошло не так” при прогоне теста, быстрее находить ошибки, вызывающие лишние запросы к базе данных.
Инструкции по выполнению
Для новичков, задача на пару дней
У Stylelint есть сайт со списком правил. Они перечисленны списком, но их сложно читать последовательно — нет ссылок на следующее и предыдущее правило.
Польза: получить опыт в JS.
Для новичков, задача на неделю
Необходимо разработать Ruby-инструмент (CLI) для выполнения сценариев взаимодействия WebSocket-клиентов с сервером.
Утилита может быть использована как для blackbox-тестирования (в том числе и нагрузочного) WebSocket-приложений, так и просто в качестве удобного консольного клиента при разработке.
Польза: возможность применить на практике методы многопоточного программирования, попрактиковаться в написании CLI, поближе познакомиться с технологией WebSocket.
Решение: итоговая реализация основана на решениях Кирилла Архипова и Сергея Елушева, за что им выражается особая благодарность.
Как это должно работать?
Рассмотрим два примера. Предположим, что наш сервер — это Action Cable.
Предположим, что мы хотим проверить следующий сценарий (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
информацию о том, на каком этапе сценария произошел сбой.
Давайте рассмотрим более интересный случай с несколькими клиентами с отличающимся поведением.
все клиенты подключаются к серверу и подписываются на канал;
клиенты 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.
Инструкции по выполнению
Для продвинутых, задача на неделю
Необходимо разработать инструмент (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.
Инструкции по выполнению
Для новичков, задача на один-два дня
В GitHub-репозитории tram-policy опубликована документация к gem, реализующему базовый класс для контексто-специфичных валидаторов в Ruby. В описании приводится мотивация, примеры интерфейса и поддерживаемых фич; дан список классов и объектов, составляющих публичный интерфейс. Сейчас репозиторий содержит только документацию без какого-либо кода (помимо тривиальных объявлений).
Задача состоит в том, чтобы полностью разработать gem в соответствии с описанием.
Польза: возможность попрактиковаться в написании gem’ов, реализовать один из базовых паттернов разработки средствами чистого Ruby (попутно сняв «ауру магизма» с валидаторов, как мы привыкли к ним в Rails), познакомиться с классической библиотекой I18n, создать простыми средствами один из базовых компонентов для сложных Rails/PORO приложений.
Решение: Второе и третье места с благодарностью за участие в разработке достаются Анастасии Власовой и Сергею Чечаеву, также предложившим свои решения.
Задача должна быть по силам новичку с базовыми знаниями Ruby и фреймворка RSpec.
Обратите внимание на то, что все необходимые зависимости уже объявлены в спецификации (файл .tram-policy.gemspec
) и Gemfile
gem’а. Не добавляйте новых зависимостей сверх указанных. Поскольку речь идет о публичной библиотеке, код должен быть хорошо покрыт тестами.
Для новичков, задача на неделю
Нужно сверстать виджет статуса синхронизации по макетам Антона Ловчикова.
Польза: принять участие в разработке Logux; получить больше опыта в webpack и создании виджетов.
Badges.png
и Badges usage.svg
.badge.js
с функцией badge
. Она должна принимать
Client
и объект настроек.synchronized
виджет не виден.disconnected
, нужно отобразить
«Нет интернета» в виджете.wait
— добавить «Ваши данные не сохранены».connecting
и sending
нужно показывать
«Сохраняю ваши данные», но только если предыдущее состояние было wait
.synchronized
,
нужно на пару секунд показать «Ваши данные сохранены».wrong-protocol
или wrong-subprotocol
нужно
показывать «Обновите страницу».badge/ru.js
и badge/en.js
нужно указать текст по умолчанию.badge/default.js
. SVG-иконки подключать
через require('./icon.svg')
. Учесть, что стили на странице могут быть
любыми, так что лучше сбросить наследуемые свойства.test/demo
. Результат можно посмотреть, запустив
yarn run demo
.README.md
.Для новичков, задача на пару дней
У CSSComb есть шикарный инструмент для генерации настроек. Он содержит неколько шагов, и на каждом шаге показывает несколько примеров кода. Пользователь выбирает, какой ему больше нравится — и после всех шагов получает готовые настройки для CSSComb.
Надо создать такой же для Stylelint. Только показывать настройки прямо в процессе.
Польза: получить реальный сайт в резюме, получить опыт в JS.
Для новичков, задача на пару дней
Bunyan становится форматом логов де-факто в Node.js. Для удобной интеграции с инфраструктурой в Logux Server нужно добавить поддержку этого формата.
Польза: принять участие в разработке Logux; получить больше опыта в JS и построении систем логирования.
reporters/human
в reporters/bunyan
— заменить код, сохранив
API. Общий код можно вынести в reporters/common.js
.server.js
загружать разные файлы reporters
в зависимости от опции.Для новичков, задача на пару дней
Browserslist используют Автопрефиксер, ESLint и Babel. У него есть консольный интерфейс. Но он не покрыт тестами — регулярно в нем находят проблемы.
Польза: получить больше опыта в тестировании.
./cli.js
. Например, можно его вызывать
с помощью spawn()
.Для новичков, задача на пару дней
Для UI-функций недостаточно модульного тестирования. В Logux Status нужна страница, где можно было бы в реальности посмотреть на все его возможности.
Польза: принять участие в разработке Logux; получить больше опыта в JS и webpack.
ServerSync
через LocalPair
.reasons: ['test']
в метадату.Для новичков, задача на пару дней
В Logux Status есть log()
, который выводит в консоль информацию о том,
что происходит с Logux. Вывод идет обычным текстом,
хотя Chrome уже поддерживает цветной вывод.
Польза: принять участие в разработке Logux; получить больше опыта в JS
и console
.
log.js
префикс Logux:
.Logux:
фирменным желтым фоном с логотипа Logux.Для новичков, задача на пару дней
Logux-сервер работает с клиентом по веб-сокету. При ошибке сервера, клиент не отобразит страницу с ошибкой, как было бы с PHP или Ruby on Rails.
Поэтому надо добавить способ отображения серверной ошибки к консоли браузера.
Польза: внести свой вклад в протокол Logux; получить больше опыта в JS.
["debug", string type, any data]
.BaseSync
метод sendDebug()
для отправки команды
и событие debug
при получении команды.BaseServer
метод debugError(error)
, который отправит
всем клиентам команду debug
с типом error
и stacktrace.Server
автоматический вызов debugError
при ошибке,
если this.env
равен development
.debug
с ошибкой, выводить ее в консоль,
помечая, что это ошибка сервера. Желательно, чтобы ошибка показывалась как
обычная JS-ошибка со стандартным UI для stacktrace.Для новичков, задача на пару дней
Logux написан на ES5, чтобы работать в браузере без компиляции. Но Logux Server не будет работать в браузере. Минимальной версией node.js будет 4, так что его можно переписать на ES6.
Польза: принять участие в разработке Logux; получить больше опыта в работе с ESLint и ES6.
node4.js
, где подключить node/no-deprecated-api
и node/no-unsupported-features
(для node.js 4)
из eslint-plugin-node.node4.js
правила, которые обяжут использовать ES6-функции,
достпуные в node.js 4.index.js
и для node4.js
.Для новичков, задача на пару дней
Logux Status — это подборка методов отображения статуса синхронизации
в браузере. Нужно добавить вывод процесса синхронизации через console.log
.
Польза: принять участие в разработке Logux; получить больше опыта в JS.
log.js
с функцией log
. Она должна принимать
Sync
.Sync
, а объект со свойством sync
(например, Logux Client),
то брать Sync
оттуда.connect
, state
, error
и clientError
.
По этим событиям выводить в консоль браузера, что происходит с Logux.add
у sync.log
. Выводить в консоль
информацию о новом событии в логе.index.js
и добавить тесты на то, что она там есть.README.md
.Для новичков, задача на пару дней
Logux написан на ES5, чтобы работать в браузере без компиляции. Но Logux Server не будет работать в браузере. Минимальной версией node.js будет 4, так что его можно переписать на ES6.
Польза: принять участие в разработке Logux; получить больше опыта в работе с ESLint и ES6.
eslint-config-logux/node4
.Для новичков, задача на пару дней
Logux Status — это подборка методов отображения статуса синхронизации
в браузере. Нужно добавить смену <title>
при ошибке, чтобы подсветить
вкладку в браузере.
Польза: принять участие в разработке Logux; получить больше опыта в JS.
attention.js
с функцией attention
. Она должна принимать
Sync
.<title>
перестанет работать.Sync
, а объект со свойством sync
(например, Logux Client),
то брать Sync
отткуда.error
и при любом событии,
кроме timeout
, добавлять к <title>
*
и убирать ее при открытии вкладки
(через Page Visibility API).index.js
и добавить тесты на то, что она там есть.README.md
.Для новичков, задача на пару дней
Logux Status — это подборка методов отображения статуса синхронизации в браузере. Нужно добавить смену favicon при онлайне, офлайне и ошибке.
Польза: принять участие в разработке Logux; получить больше опыта в JS.
favicon.js
с функцией favicon
. Она должна принимать
Sync
и набор ссылок на favicon для обычного состояния, офлайна и ошибки.Sync
, а объект со свойством sync
(например, Logux Client),
то брать Sync
отткуда.state
и смену обычный/офлайн favicon.error
и при любом событии,
кроме timeout
, сменять иконку на иконку ошибки.index.js
и добавить тесты на то, что она там есть.README.md
.Для новичков, задача на пару дней
Logux Status — это подборка методов отображения статуса синхронизации в браузере. Нужно добавить подтверждение закрытия вкладки при неотправленных событиях в логе.
Польза: принять участие в разработке Logux; получить больше опыта в JS.
confirm.js
с функцией confirm
. Она должна принимать
Sync
и текст предупреждения (с текстом по умолчанию).Sync
, а объект со свойством sync
(например, Logux Client),
нужно брать Sync
отткуда.state
, чтобы при наличии неотправленных
событий в onbeforeunload
предупреждать пользователя, что не все данные
успели синхронизироваться.index.js
и добавить тесты на то, что она там есть.README.md
.Для новичков, задача на пару дней
Logux-сервер сейчас использует пакет ws
для работы с веб-сокетами.
Но пакет uWebSockets
гораздо быстрее.
Польза: принять участие в разработке Logux; получить больше опыта в работе с веб-сокетами.
ws
на uws
.Для уверенных в себе, задача на неделю
Библиотека Logidze позволяет работать с версиями объектов ActiveRecord моделей и историей их изменений.
Необходимо расширить эту интеграцию, добавив поддержку ассоциаций.
Польза: возможность познакомится с внутренностями ActiveRecord и научиться с ними работать.
Примеры работы, или что делать?
Предположим, что у нас есть две модели:
class Post < ActiveRecord::Base
has_many :comments
has_logidze
end
class Comment < ActiveRecord::Base
belongs_to :post
has_logidze
end
Мы хотим, чтобы при работы с версиями одной модели, соответствующие ассоциации (те, для которых включено версионирование) также подгружались для соответствующей отметки во времени. Пример:
# 2017-01-19
post = Post.create!(post_params)
# 2017-01-22
comment = post.comments.create(body: 'My comment')
# 2017-01-24
comment.update!(body: 'New text')
# смотрим пост до обновления комментария (первый случай)
old_post = post.at('2017-01-23')
# сам пост в этот момент не менялся, но у нас есть ассоциация, которая поменялась
old_post.comments.first #=> 'My comment'
# для более старой версии комментариев не было (второй случай)
very_old_post = post.at('2017-01-20')
very_old_post.comments.size #=> 0
Подводные камни и вопросы для обсуждения
1) В первом случае объект old_post
– это тот же самый объект post
, так как Logidze возвращает сам объект, если не было изменений с указанного времени. Но для корректной загрузки ассоциаций нам необходимо знать о времени.
Можно изменить это поведение и возвращать объект, который знает о запрашиваемом времени; либо оставить это на откуп разработчикам и советовать им использовать touch: true
, чтобы в родительской модели всегда были изменения.
2) Сохранение прошлой версии должно сохранять и соответствующие ассоциации, но только при явном указании:
# не сохраняет старые версии ассоциаций
old_post.save!
post.undo!
# сохраняет предыдущие версии ассоциаций
old_post.save!(associations_versions: true)
post.undo!(old_post.save!(associations_versions: true))
Для обновления ассоциаций мы можем использовать встроенный механизм – ActiveRecord::AutosaveAssociation.
Нужно только научиться включать и выключать его (в случае, если была использована опция autosave: true
) самостоятельно.
Во втором случае (very_old_post
) нужно также учесть и удаление записей.
Идеи реализации
Нам необходимо уметь вклиниваться в процесс загрузки ассоциаций для объекта, добавляя вызов метода at(time)
на загруженных объектах (см. ActiveRecord#associtaion
и ActiveRecord::Associations::Association#load_target
).
При этом нужно учитывать, какие ассоциации имеют версионирование (т.е. has_logidze
).
Инструкции по выполнению
Для новичков, задача на пару дней
Logux-сервер сейчас использует свою систему снимков для тестирования. Но у Jest уже есть хорошая система снимков. Нужно перенести тесты на них.
Польза: принять участие в разработке Logux; получить больше опыта в тестировании.
test/server.test.js
и test/reporter.test.js
на снимки
Jest. Файлы test/snapshots/reports.js
и test/servers/servers.js
можно перенести в тесты — они больше не нужны отдельно.yarn run snapshots
, которая вызовет jest -u
,
а потом выведет снимки в консоль — с ее помощью будет удобно смотреть,
что сервер будет выводить в консоль во время работы.Для новичков, задача на пару дней
Нужно вывести подробное описание по самым распространенным ошибкам запуска Logux-сервера и объяснить что делать, чтобы их исправить.
Польза: принять участие в разработке Logux.
server.js
перехват ошибки при вызове метода listen()
.error-helper.js
, который будет форматировать и выводить
подобные ошибки при запуске сервера. Пример можно посмотреть в reporter.js
.Для новичков, задача на пару дней
Сейчас порт, хост и пути к TLS-ключам можно задать только в коде сервера. Нужно добавить в Logux-сервер чтение CLI-аргументов и переменных среды.
Польза: принять участие в разработке Logux; разобраться в Logux-сервере.
options = BaseServer#loadOptions(process, defaults)
.
В нем брать настройки из аргументов --port/-p
, --host/-h
,
--key/-k
, --cert/-c
и переменных среды LOGUX_PORT
, LOGUX_HOST
,
LOGUX_KEY
и LOGUX_CERT
.--help
выводить справку по аргументам, переменным среды
и завершать работу.test/servers/
.app.listen()
в примере кода сервера
в README.md
.README.md
.Для новичков, задача на пару дней
Нужно добавить в Logux Sync код, который будет проверять формат и типы принятых сообщений.
Польза: принять участие в разработке Logux; разобраться с его протоколом.
Общее правило в разработке серверов — данные клиента не должны вызвать
ошибку и падение сервера. Но сейчас Logux Sync не проверяет входящие сообщения.
Например, что в сообщении ["ping"]
отсутствует число.
В итоге, ошибка в реализации клиента или действия злоумышленика способны привести к падению сервера. Проблема отягощается отсутствием проверки типов в JS.
Нужно добавить проверку типов в Logux Sync. Желательно не использовать большие библиотеки, так как Logux Sync используется и на клиенте.
wrong-format
, как это делается
в BaseSync#onMessage
.Для новичков, задача на пару дней
PostCSS использует чат Gitter для помощи разработчикам. У сервиса появился скрипт для добавления чата на любой сайт. Надо добавить его на postcss.org.
Польза: получить опыт с React и красивую строку в резюме.
Сайт postcss.org написан на Phenomic, специальном React-фреймворке для статичных сайтов.
Но скрипт Gitter — не компонент React, а обычный скрипт. Главный вопрос — где их совместить.
Для уверенных в себе, задача на месяц
PostCSS имеет сменные синтаксисы. Это особо полезно для Stylelint, чтобы проверять исходники. Сейчас можно работать с Less и SCSS.
Нужно взять dart-sass и сделать на его основе парсер синтаксиса Sass, который использует отступы.
Польза: познакомиться с командой Sass; многие крупные проекты смогут использовать ваш проект для Stylelint.
dart-sass — это официальный парсер Sass, написанный на Dart. Его можно скопилировать в JS, поэтому он уже есть на npm.
Самый надежный способ — взять этот парсер, получить Sass AST и сконвертировать его в PostCSS AST.
@ai
. Если стесняетесь языка — напишите Ситнику,
он поможет.postcss-sass
и использовать этот API.
Пример конвертирования AST можно посмотреть
в postcss-csso.Для новичков, задача на пару дней
Нужно добавить на клиент предупреждение о нешифрованом веб-сокете в продакшене. Убрать эти ограничения на сервере.
Польза: принять участие в разработке Logux и разобраться с его внутренним устройством.
Веб-сокеты практически бессмысленно использовать без шифрования
— старые прокси будут резать непонятный им трафик.
Использование WebSockets поверх TLS (wss://
) решает проблемы
со старыми прокси.
Чтобы пользователи потом не жаловались, мы принуждаем их сразу использовать
wss://
в продакшене. Но текущая проверка протокола на стороне сервера не очень
оправдана — TLS часто реализуется на уровне nginx.
ws://
вместо wss://
а домен не равен 127.0.0.1
, localhost
, ip6-localhost
,
::1
и не размещен в зоне .dev
.console.warn
.console.warn
.SSL is required in production mode
.Для новичков, задача на пару дней
Нужно написать eslint-plugin-es5
с правилами для ESLint для проверки того,
что в коде не используется ES2016.
Польза: получить опыт расширения ESLint, поучаствовать в проекте Logux.
Logux не использует Babel. Поскольку часть кода будет работать в старых браузерах, при разработке Logux мы используем ES5.
Есть опасность, что по привычке в код попадет какая-то функция из ES2016 — тесты на Node.js пройдут, а после релиза в Сафари все сломается.
eslint-plugin-es5
. Например, скопировать структуру
eslint-plugin-babel.const
и let
(за основу можно взять правило
no-var).Для новичков, задача на пару дней
Сейчас gem npmdc проверяет наличие установленных npm-пакетов и соответствие их версий на основе файла package.json
.
Нужно добавить поддержку Yarn.
Польза: возможность попрактиковаться в программировании на чистом Ruby и написании gem’ов, помочь полезному проекту, который нужен в каждом современном большом Ruby веб-приложении.
Как это работает сейчас:
package.json
и строим по нему дерево зависимостей с указанием версии.node_modules
.node_modules
и соответствие версии, указанной в package.json
.Что нужно сделать:
yarn check
:
yarn check
следует привести в соответствие с текущим форматом.Для новичков, задача на пару дней
Автопрефиксер, Stylelint, babel-preset-env и cssnext используют Browserslist, чтобы пользователь мог разом указывать список браузеров, которые нужно поддерживать в этом проекте.
Browserslist поддерживает указание браузеров по проценту рыночной доли. Можно указывать реальную статистику пользователей сайта, чтобы точнее считать долю.
Нужно добавить поддержку файла browserslist-stats.json
для указания
статистики сайта.
Польза: сделать функцию, которой будут пользоваться куча веб-разработчиков.
browserslist-stats.json
.
Учтите, что он может быть выше или ниже, чем обычный файл browserslist
.stats
.stats
или process.env.BROWSERSLIST_STATS
,
то искать browserslist-stats.json
не нужно.Для новичков, задача на пару дней
Автопрефиксер, Stylelint, babel-preset-env и cssnext используют Browserslist, чтобы пользователь мог разом указывать список браузеров, которые нужно поддерживать в этом проекте.
Нужно доработать Browserslist, чтобы он смотрел не только в browserslist
,
но и в ключ browserslist
в package.json
.
Польза: сделать функцию, которой будут пользоваться многие веб-разработчики.
Эта функция будет использоваться в create-react-app
Дэна Абрамова.
Формат package.json
ожидается примерно такой:
{
"name": "my-app",
"private": true,
"dependencies": { },
"browserslist": [
"last 1 version",
"ie 10"
]
}
package.json
. Этот файл должен также
рекурсивно искаться во всех родительских папках.package.json
с нужным ключем и
browserslist
— выкидывать ошибку, так как ситуация потенциально опасная.test/fixtures
.Для новичков, задача на пару дней
Автопрефиксер, Stylelint, babel-preset-env и cssnext используют Browserslist, чтобы пользователь мог разом указывать список браузеров, которые нужно поддерживать в этом проекте.
Нужно доработать Browserslist, чтобы в browserslist
можно было указывать
отдельный список для production
и development
среды.
Польза: сделать функцию, которой будут пользоваться многие веб-разработчики.
Эта функция будет использоваться в create-react-app
Дэна Абрамова.
Формат browserslist
станет примерно такой:
[development]
last 1 version
[production]
last 2 versions
> 1 %
env
в API Browserslist.env
в значение process.env.BROWSERSLIST_ENV
,
process.env.NODE_ENV
или "development"
.[name]
."defaults"
.env
.
Если главы, указанной в env
, нет, то брать "defaults"
.Для новичков, задача на неделю
В библиотеку Logidze нужно добавить функционал фильтрации полей, учитываемых при версионировании.
Это позволит уменьшить размер лога, а значит расход ресурсов и время на обработку.
Польза: возможность поработать с базой данных на более низком уровне, попрактиковаться в написании процедур и разобраться в том, как работают триггеры.
Как это должно работать?
Есть два противоположных подхода к фильтрации данных: белый список и черный список.
В первом случае мы явно указываем, какие поля хотим добавить в лог:
$ rails generate logidze:model Post --only="title,user_id,tags"
Во втором — поля, которые мы хотим исключить из версионирования:
$ rails generate logidze:model Post --except="created_at,updated_at"
Инструкции по выполнению
Для новичков, задача на пару дней
Нужно добавить поддержку синтаксиса SugarSS в плагин PostCSS для Sublime Text.
Польза: очень простая задача, чтобы разобраться в опенсорсе; позволит понять, как устроен Sublime Text внутри; попасть в анонсы PostCSS.
Для продвинутых, задача на месяц
Нужно придумать новый API для плагинов PostCSS — чтобы они все работали вместе, в одном цикле прохода по AST-дереву.
Задание сложное. Оно меньше про код и больше про переговоры, анализ и архитектуру. Но зато дает максимальное количество опыта.
Польза: стать одним из ведущих коммитеров PostCSS, добавить в резюме строку о разработке грамотной архитектуры в мировом проекте.
Сейчас каждый плагин PostCSS проходит по AST-дереву CSS каждый раз заново. В итоге нельзя делать вложенные функции, и при большом количестве плагинов сильно проседает производительность.
У Babel есть более правильный visitor API: когда плагины подписываются на какие-то типы узлов. А внутри PostCSS запускается один проход по дереву, который в нужных местах дергает подписанные плагины.
Первый этап:
Для уверенных в себе, задача на несколько дней
Нужно добавить в Ossert новые источники данных. Отличным кандидадатом с достаточно богатым API является StackOverflow.
Это позволит охватить еще один важный аспект развития Open-Source библиотек — доступность и объем поддержки, а также заинтересованность в них.
Польза: возможность научиться работать с API StackOverflow и узнать больше о метриках и способах измерения зрелости свободного ПО.
Как это должно работать?
На данный момент сбор информации уже работает для Rubygems, Github и Bestgems. Необходимо совместимым образом организовать сбор с новых источников.
Советы по реализации
За основу предлагается взять любой из существующих классов для сбора в Ossert. Например, Ossert::Fetch::Rubygems
, и реализовать класс, осуществляющий сбор интересной информации:
module Ossert
module Fetch
class StackOverflow
def initialize(project)
...
end
def process
# Метод сбора основных метрик по проекту
end
end
end
end
Для начала, вам потребуется вникнуть в проблему, решаемую проектом — оценку зрелости свободного ПО.
Для этого нужно проанализировать возможность API вашего источника данных и выбрать:
Например:
А затем, конечно, организовать сбор этих метрик и параметров.
Инструкции по выполнению
Для уверенных в себе, задача на несколько дней
Нужно добавить в 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 вашего источника данных и выбрать:
Например:
А затем, конечно, организовать сбор этих метрик и параметров.
Инструкции по выполнению
Для новичков, задача на неделю
Нужно написать скрипт (и упаковать его в гем), который позволит проводить аттестационное тестирование (от англ. “conformance testing”) приложений-серверов для AnyCable.
Это повысит скорость и удобство разработки новых реализаций серверов, а также поможет поддерживать актуальность текущих реализаций.
Польза: возможность подробно изучить ActionCable и AnyCable, попрактиковаться в написании гемов и написать инструмент, которым будет активно пользоваться сообщество.
Как это должно работать?
Предполагается простой и удобный интерфейс:
$ anycablebility http://localhost:3244/cable
После выполнения каждого сценария в стандартный вывод должна выводится информация вида: название сценария, результат (плюс или минус).
Советы по реализации
Язык реализации: Ruby.
В качестве начальных сценариев для тестирования можно взять приемочные тесты для демо-приложения.
В качестве DSL для написания сценариев и их запуска рекомендуется использовать RSpec.
Для непосредественного взаимодействия с сервером через веб-сокеты можно воспользоваться гемом action_cable_client.
Инструкции по выполнению
Для уверенных в себе, задача на неделю
Пусть существует 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, умеющий следующее:
Конфигурировать обработчики для назначения тегов в момент сбора (например, через lambda функции):
Sampler.configure do |config|
config.tag_with "slow", ->(request) { request.time > 200 }
end
Устанавливаться в произвольное Rails приложение. Сделать можно, например, так:
rails generate api:sampler:install
Для новичков, задача на неделю
Нужно сделать скрипт, который будет брать пару популярных сайтов (типа GitHub) и на их верстке тестировать все минификаторы с помощью gemini или подобного тестирования по скриншотам. Результатом работы будет HTML страница с результатами каждого минификатора.
Польза: разобраться в тестировании по снимкам; познакомиться со всеми разработчиками CSS-минификаторов; строчка в резюме о том, что на качество всех CSS-минификаторов повлияли лично вы.
В качестве примера можно посмотреть на css-minification-benchmark
.
css-minification-benchmark
).