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

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

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

Бэк: Active Record JSONB ассоциации

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

Необходимо реализовать возможность хранить данные для ассоциаций (внешние ключи) в поле колонки типа JSONB (PostgreSQL), сохранив при этом большую часть функционала ассоциаций Active Record.

Польза: возможность научиться работать с внутренностями ActiveRecord и JSONB.

Часть 1. Все яйца в одну корзину.

Рассмотрим простой пример:

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

Часть 2. Много-Много.

Используя 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.

Также было бы неплохо ответить на вопрос – в каких случаях выгоднее использовать такой подход вместо классического?

Лучшим ответом на этот вопрос будет предъявление бенчмарков и их результатов.