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

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

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

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

Бэк: Добавить поддержку полиморфных типов в Ruby gem StoreModel

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

StoreModel — Ruby gem, позволяющий работать с JSON-колонками как с объектами, очень похожими на обычные ActiveRecord модели.

Необходимо добавить в StoreModel полиморфные типы, которые будут оборачивать значение из JSON в нужный класс–обертку согласно условиям.

Польза: познакомиться с работой атрибутов в Rails, прокачаться в Ruby, принести пользу сообществу.

Постановка задачи

Пусть в модели Product есть атрибут config, у которого есть две версии с разным набором атрибутов. В первой версии были атрибуты model и color, во второй — model_name (вместо model), color и version:

// v1
{
  "model": "iPhone X",
  "color": "red"
}

// v2
{
  "model_name": "iPhone X",
  "color": "red",
  "version": "2"
}

Необходимо реализовать метод #one_of в модуле StoreModel, который будет принимать на вход блок. В качестве аргумента блока будет передаваться JSON из БД, а вернуть необходимо соответствующий класс:

class ConfigV1
  include StoreModel::Model

  attribute :model, :string
  attribute :color, :string
end

class ConfigV2
  include StoreModel::Model

  attribute :model_name, :string
  attribute :color, :string
  attribute :version, :integer
end

Config = StoreModel.one_of { |json| json[:version] == 2 ? ConfigV2 : ConfigV1 }

class Product < ApplicationRecord
  attribute :config, Config.to_type
end

product = Product.create(config: { model: "iPhone X", color: "red" })
product.config.class # => ConfigV1

product = Product.create(config: { model_name: "iPhone X", color: "red", version: 2 })
product.config.class # => ConfigV2

Бонус: можно предусмотреть сокращенный вариант для случаев, когда в JSON хранится название нужного класса (допустим, в поле type):

class Product < ApplicationRecord
  attribute :config, StoreModel.one_of(class_field: :type).to_type
end

product = Product.create(config: { model: "iPhone X", color: "red", class_field: "ConfigV1" })
product.config.class # => ConfigV1

product = Product.create(config: { model_name: "iPhone X", color: "red", class_field: "ConfigV2" })
product.config.class # => ConfigV2

Обсуждение можно найти в задаче.

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

  1. Метод #one_of должен возвращать класс, который реализует методы #to_type и #to_array_type (см. модуль TypeBuilders).
  2. В момент инициализации класса–обертки нужно обработать полиморфные типы — вызвать блок и инициализировать нужный класс, передав туда JSON.
  3. Можно предложить свой вариант DSL.

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

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

Присылайте свои решения: