Направете gem "свързвщ" Ruby обекти със записи в база от данни, по подобие на ActiveRecord или пета задача.
Целта е да получим абстракция на SQL, като го заменим с по-красив и лесен за употреба Ruby код.
- Искаме да можем да свържем произволен написан от нас клас с таблица в база от данни, съдържаща колони за атрибутите на класа. Всеки такъв клас се нарича модел.
- Да се поддържа работа с поне 2 релационни бази от данни по ваш избор - например SQLite и PostgreSQL. Можете да използвате gem-ове като sqlite3 за комуникация със съответната база от данни. Нямате право да използвате наготово ORM библиотеки.
- Моделите трябва да поддържат следните заявки:
- Създаване, четене, изтриване и ъпдейт (CRUD)
- Сортиране по произволни атрибути и в произволна посока (ascending/descending)
- Филтриране по стойностите на един или повече атрибути (включително с неравенства)
- Лимитиране на брой върнати резултати при заявка
- Пропускане на определен брой записи от заявка
- Горните могат да се комбиринат и chain-ват
(например,
User.where(first_name: 'a').where(last_name: 'b').order(first_name: :desc)
)
- Да се поддържат прости агрегации като
count
иavg
- В доста от случаите моделите са свързани по някакъв начин. Например, един потребител може да има много коментари.
Имплементирайте възможност за задаване поне на 1:1 и 1:N асоциации между моделите.
Това означава че очакваме да можем да "свържем" две инстанции на модели в Ruby кода
(например,
user.comments = [Comment.new(...)]
) и това да се отрази в базата от данни след като запишем инстанцията. - Възможност за логване на направените към базата от данни заявки. Този лог трябва да е конфигурируем по следния начин:
- Дали да логва на стандартния изход или във файл.
- Лог записи на различни нива -
DEBUG
,INFO
,ERROR
. - Минимално ниво на лог записите, които да се виждат в лога.
- Очакваме модулите за комуникация с базата от данни да си общува с нея на нейния език. Не е смислено всяка операция вурху модела да предизвиква четене на всички записи от базата. Добър пример за това е филтрирането - вместо да заредим всички записи в паметта и да ги филтрираме с Ruby, трябва да инструктираме базата чрез подходящ филтър да ни върне само необходимите записи.
- Използвайте prepared statements и екранирайте всичко необходимо, така че да няма възможност за SQL инжекции.
- Интерфейсите за комуникация с различните бази от данни следва да са взаимно заменяеми и независими един от друг, както и да не зависят от имплементацията на Ruby моделите.