| .idea | ||
| greenbean.js | ||
| package.json | ||
| README.md | ||
GreenBean
GreenBean - это легковесная JavaScript библиотека для работы с данными, вдохновленная RedBeanPHP. Она предоставляет простой и гибкий способ хранения данных в JSON-формате с поддержкой связей между объектами.
📖 Содержание
- Введение
- Установка
- Концепции
- Быстрый старт
- Основные операции
- Работа со связями
- Продвинутые возможности
- Лучшие практики
- API Reference
- Лицензия
Введение
Что такое GreenBean?
GreenBean - это JavaScript библиотека, которая позволяет работать с данными в простом и гибком стиле. Вместо того чтобы заранее определять структуру данных (схему), GreenBean позволяет создавать и изменять объекты "на лету".
Для кого это?
- 👨💻 Разработчикам, которые хотят быстро прототипировать приложения
- 🎓 Начинающим, которым нужен простой способ работы с данными
- 🚀 Проектам, где структура данных часто меняется
- 🛠️ Небольшим и средним приложениям
Почему GreenBean?
- Простота: Нет необходимости писать схемы и миграции
- Гибкость: Структура данных может меняться в процессе разработки
- Удобство: Автоматическое создание связей между объектами
- Надежность: Поддержка транзакций и защита от одновременных изменений
- Производительность: Данные хранятся в JSON-файле, что идеально для небольших проектов
Установка
Прямое подключение
- Скопируйте файл
greenbean.jsв ваш проект - Установите зависимость uuid:
npm install uuid
- Импортируйте GreenBean в ваш проект:
import GreenBean from './greenbean.js';
Убедитесь, что в вашем package.json установлен тип модуля:
{
"type": "module"
}
Концепции
Бины (Beans)
В GreenBean все объекты называются "бинами". Бин - это простой JavaScript объект, который автоматически получает:
- Уникальный идентификатор (
id) - Тип (
__type) - Метаданные (
__meta)
// Создание бина
const user = await gb.dispense('user');
// Бин автоматически получает следующую структуру:
{
id: "550e8400-e29b-41d4-a716-446655440000",
__type: "user",
__meta: {
created: "2025-02-17T18:44:08.000Z",
modified: "2025-02-17T18:44:08.000Z"
}
}
Типы бинов
Тип бина - это просто строка, которая определяет "категорию" объекта. Например:
userдля пользователейpostдля постовcommentдля комментариев
// Создание бинов разных типов
const user = await gb.dispense('user');
const post = await gb.dispense('post');
const comment = await gb.dispense('comment');
Динамические свойства
Вы можете добавлять любые свойства к бину в любое время:
const user = await gb.dispense('user');
// Добавляем свойства
user.name = 'Иван';
user.email = 'ivan@example.com';
// Позже можем добавить новые свойства
user.age = 25;
user.city = 'Москва';
await gb.store(user);
Быстрый старт
Создание базы данных
import GreenBean from 'greenbean';
// Создаем экземпляр GreenBean
const gb = new GreenBean('data.json');
// База данных создастся автоматически
Пример простого блога
// Создаем автора
const author = await gb.dispense('author');
author.name = 'Иван Петров';
author.email = 'ivan@example.com';
await gb.store(author);
// Создаем пост
const post = await gb.dispense('post');
post.title = 'Мой первый пост';
post.content = 'Привет, мир!';
post.created = new Date().toISOString();
await gb.store(post);
// Связываем автора и пост
await gb.xown(post, 'author', author);
// Создаем комментарии
const comment1 = await gb.dispense('comment');
comment1.text = 'Отличный пост!';
comment1.author = 'Мария';
await gb.store(comment1);
const comment2 = await gb.dispense('comment');
comment2.text = 'Спасибо за статью';
comment2.author = 'Петр';
await gb.store(comment2);
// Связываем пост и комментарии
await gb.xown(post, 'comments', [comment1, comment2]);
// Получаем пост с автором и комментариями
const loadedPost = await gb.load('post', post.id);
const postAuthor = await gb.getXown(loadedPost, 'author');
const comments = await gb.getXown(loadedPost, 'comments');
console.log(`Пост: ${loadedPost.title}`);
console.log(`Автор: ${postAuthor.name}`);
console.log('Комментарии:');
comments.forEach(comment => {
console.log(`- ${comment.author}: ${comment.text}`);
});
Основные операции
Создание объектов
// Простое создание
const user = await gb.dispense('user');
user.name = 'Иван';
await gb.store(user);
// Создание с начальными данными
const post = await gb.dispense('post');
Object.assign(post, {
title: 'Заголовок',
content: 'Содержание',
tags: ['javascript', 'tutorial']
});
await gb.store(post);
Поиск объектов
// Поиск всех пользователей
const allUsers = await gb.find('user');
// Поиск по критерию
const activeUsers = await gb.find('user', { status: 'active' });
// Поиск одного объекта
const ivan = await gb.findOne('user', { name: 'Иван' });
// Загрузка по ID
const user = await gb.load('user', 'some-uuid');
Обновление объектов
// Загружаем объект
const user = await gb.load('user', userId);
// Обновляем свойства
user.name = 'Новое имя';
user.age = 26;
// Сохраняем изменения
await gb.store(user);
Удаление объектов
// Удаление одного объекта
await gb.trash(user);
// Удаление нескольких объектов
const inactiveUsers = await gb.find('user', { status: 'inactive' });
for (const user of inactiveUsers) {
await gb.trash(user);
}
Работа со связями
Типы связей
GreenBean поддерживает три типа связей:
-
Один-к-одному (one-to-one)
- Один объект связан с другим объектом
- Пример: пользователь и профиль
-
Один-ко-многим (one-to-many)
- Один объект связан с несколькими объектами
- Пример: пост и комментарии
-
Многие-ко-многим (many-to-many)
- Объекты могут быть связаны с множеством других объектов
- Пример: пользователи и группы
Связь один-к-одному
// Пример: пользователь и профиль
const user = await gb.dispense('user');
user.name = 'Иван';
await gb.store(user);
const profile = await gb.dispense('profile');
profile.bio = 'Программист JavaScript';
profile.avatar = 'avatar.jpg';
await gb.store(profile);
// Связываем пользователя и профиль
await gb.xown(user, 'profile', profile);
// Получаем профиль пользователя
const userProfile = await gb.getXown(user, 'profile');
console.log(userProfile.bio); // "Программист JavaScript"
// Удаляем связь
await gb.xown(user, 'profile', null);
Связь один-ко-многим
// Пример: категория и товары
const category = await gb.dispense('category');
category.name = 'Электроника';
await gb.store(category);
// Создаем несколько товаров
const products = [];
for (const name of ['Телефон', 'Ноутбук', 'Планшет']) {
const product = await gb.dispense('product');
product.name = name;
await gb.store(product);
products.push(product);
}
// Связываем категорию и товары
await gb.xown(category, 'products', products);
// Получаем все товары категории
const categoryProducts = await gb.getXown(category, 'products');
categoryProducts.forEach(product => {
console.log(`- ${product.name}`);
});
// Добавляем новый товар к существующим
const newProduct = await gb.dispense('product');
newProduct.name = 'Смарт-часы';
await gb.store(newProduct);
const updatedProducts = [...products, newProduct];
await gb.xown(category, 'products', updatedProducts);
Связь многие-ко-многим
// Пример: студенты и курсы
const student = await gb.dispense('student');
student.name = 'Мария';
await gb.store(student);
// Создаем несколько курсов
const courses = [];
for (const name of ['JavaScript', 'Python', 'Web Design']) {
const course = await gb.dispense('course');
course.name = name;
await gb.store(course);
courses.push(course);
}
// Записываем студента на курсы
await gb.shared(student, 'course', courses);
// Получаем все курсы студента
const studentCourses = await gb.getShared(student, 'course');
console.log('Курсы студента:');
studentCourses.forEach(course => {
console.log(`- ${course.name}`);
});
// Отписываем от одного курса
const remainingCourses = courses.slice(1);
await gb.shared(student, 'course', remainingCourses);
Продвинутые возможности
Транзакции
Транзакции позволяют выполнять несколько операций как единое целое:
try {
await gb.transaction(async () => {
// Создаем заказ
const order = await gb.dispense('order');
order.total = 1000;
await gb.store(order);
// Создаем элементы заказа
const item1 = await gb.dispense('orderItem');
item1.name = 'Товар 1';
item1.price = 600;
await gb.store(item1);
const item2 = await gb.dispense('orderItem');
item2.name = 'Товар 2';
item2.price = 400;
await gb.store(item2);
// Связываем заказ и элементы
await gb.xown(order, 'items', [item1, item2]);
// Если произойдет ошибка, все изменения будут отменены
throw new Error('Что-то пошло не так');
});
} catch (error) {
console.error('Транзакция отменена:', error.message);
}
Заморозка данных
Заморозка предотвращает случайные изменения данных:
// Замораживаем базу данных
gb.freeze();
try {
const user = await gb.dispense('user');
user.name = 'Иван';
await gb.store(user); // Выбросит ошибку
} catch (error) {
console.error('Нельзя изменять замороженные данные');
}
// Размораживаем для внесения изменений
gb.freeze(false);
События
GreenBean позволяет отслеживать различные события:
// Подписываемся на событие сохранения
gb.on('save', () => {
console.log('База данных сохранена');
});
// Можно использовать для логирования
gb.on('save', () => {
const now = new Date().toISOString();
console.log(`[${now}] Данные сохранены`);
});
Лучшие практики
Организация кода
// Создайте класс для работы с определенным типом объектов
class UserService {
constructor(gb) {
this.gb = gb;
}
async create(userData) {
const user = await this.gb.dispense('user');
Object.assign(user, userData);
await this.gb.store(user);
return user;
}
async findByEmail(email) {
return await this.gb.findOne('user', { email });
}
async addToGroup(user, group) {
await this.gb.shared(user, 'group', group);
}
}
// Использование
const userService = new UserService(gb);
const user = await userService.create({
name: 'Иван',
email: 'ivan@example.com'
});
Обработка ошибок
async function safeOperation(callback) {
try {
return await callback();
} catch (error) {
console.error('Ошибка:', error.message);
// Можно добавить логирование или обработку ошибок
throw error;
}
}
// Использование
await safeOperation(async () => {
const user = await gb.dispense('user');
user.name = 'Иван';
await gb.store(user);
});
Валидация данных
function validateUser(user) {
const errors = [];
if (!user.name) {
errors.push('Имя обязательно');
}
if (!user.email?.includes('@')) {
errors.push('Некорректный email');
}
return errors;
}
// Использование
const user = await gb.dispense('user');
user.name = 'Иван';
user.email = 'invalid-email';
const errors = validateUser(user);
if (errors.length > 0) {
console.error('Ошибки валидации:', errors);
} else {
await gb.store(user);
}
API Reference
Основные методы
dispense(type)
Создает новый объект указанного типа.
store(bean)
Сохраняет объект в базе данных.
load(type, id)
Загружает объект по типу и ID.
find(type, criteria)
Ищет объекты по критериям.
findOne(type, criteria)
Ищет один объект по критериям.
trash(bean)
Удаляет объект из базы данных.
Методы для работы со связями
xown(bean, property, related)
Устанавливает связь один-к-одному или один-ко-многим.
getXown(bean, property)
Получает связанные объекты.
shared(bean, property, related)
Устанавливает связь многие-ко-многим.
getShared(bean, property)
Получает объекты из связи многие-ко-многим.
Служебные методы
freeze(state = true)
Замораживает или размораживает базу данных.
transaction(callback)
Выполняет операции в транзакции.
nuke()
Очищает всю базу данных.
Лицензия
MIT