GreenBean/README.md
2025-02-17 21:46:57 +03:00

556 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# GreenBean
GreenBean - это легковесная JavaScript библиотека для работы с данными, вдохновленная RedBeanPHP. Она предоставляет простой и гибкий способ хранения данных в JSON-формате с поддержкой связей между объектами.
## 📖 Содержание
- [Введение](#введение)
- [Установка](#установка)
- [Концепции](#концепции)
- [Быстрый старт](#быстрый-старт)
- [Основные операции](#основные-операции)
- [Работа со связями](#работа-со-связями)
- [Продвинутые возможности](#продвинутые-возможности)
- [Лучшие практики](#лучшие-практики)
- [API Reference](#api-reference)
- [Лицензия](#лицензия)
## Введение
### Что такое GreenBean?
GreenBean - это JavaScript библиотека, которая позволяет работать с данными в простом и гибком стиле. Вместо того чтобы заранее определять структуру данных (схему), GreenBean позволяет создавать и изменять объекты "на лету".
### Для кого это?
- 👨‍💻 Разработчикам, которые хотят быстро прототипировать приложения
- 🎓 Начинающим, которым нужен простой способ работы с данными
- 🚀 Проектам, где структура данных часто меняется
- 🛠️ Небольшим и средним приложениям
### Почему GreenBean?
- **Простота**: Нет необходимости писать схемы и миграции
- **Гибкость**: Структура данных может меняться в процессе разработки
- **Удобство**: Автоматическое создание связей между объектами
- **Надежность**: Поддержка транзакций и защита от одновременных изменений
- **Производительность**: Данные хранятся в JSON-файле, что идеально для небольших проектов
## Установка
### Прямое подключение
1. Скопируйте файл `greenbean.js` в ваш проект
2. Установите зависимость uuid:
```bash
npm install uuid
```
3. Импортируйте GreenBean в ваш проект:
```javascript
import GreenBean from './greenbean.js';
```
Убедитесь, что в вашем `package.json` установлен тип модуля:
```json
{
"type": "module"
}
```
## Концепции
### Бины (Beans)
В GreenBean все объекты называются "бинами". Бин - это простой JavaScript объект, который автоматически получает:
- Уникальный идентификатор (`id`)
- Тип (`__type`)
- Метаданные (`__meta`)
```javascript
// Создание бина
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` для комментариев
```javascript
// Создание бинов разных типов
const user = await gb.dispense('user');
const post = await gb.dispense('post');
const comment = await gb.dispense('comment');
```
### Динамические свойства
Вы можете добавлять любые свойства к бину в любое время:
```javascript
const user = await gb.dispense('user');
// Добавляем свойства
user.name = 'Иван';
user.email = 'ivan@example.com';
// Позже можем добавить новые свойства
user.age = 25;
user.city = 'Москва';
await gb.store(user);
```
## Быстрый старт
### Создание базы данных
```javascript
import GreenBean from 'greenbean';
// Создаем экземпляр GreenBean
const gb = new GreenBean('data.json');
// База данных создастся автоматически
```
### Пример простого блога
```javascript
// Создаем автора
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}`);
});
```
## Основные операции
### Создание объектов
```javascript
// Простое создание
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);
```
### Поиск объектов
```javascript
// Поиск всех пользователей
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');
```
### Обновление объектов
```javascript
// Загружаем объект
const user = await gb.load('user', userId);
// Обновляем свойства
user.name = 'Новое имя';
user.age = 26;
// Сохраняем изменения
await gb.store(user);
```
### Удаление объектов
```javascript
// Удаление одного объекта
await gb.trash(user);
// Удаление нескольких объектов
const inactiveUsers = await gb.find('user', { status: 'inactive' });
for (const user of inactiveUsers) {
await gb.trash(user);
}
```
## Работа со связями
### Типы связей
GreenBean поддерживает три типа связей:
1. **Один-к-одному** (one-to-one)
- Один объект связан с другим объектом
- Пример: пользователь и профиль
2. **Один-ко-многим** (one-to-many)
- Один объект связан с несколькими объектами
- Пример: пост и комментарии
3. **Многие-ко-многим** (many-to-many)
- Объекты могут быть связаны с множеством других объектов
- Пример: пользователи и группы
### Связь один-к-одному
```javascript
// Пример: пользователь и профиль
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);
```
### Связь один-ко-многим
```javascript
// Пример: категория и товары
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);
```
### Связь многие-ко-многим
```javascript
// Пример: студенты и курсы
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);
```
## Продвинутые возможности
### Транзакции
Транзакции позволяют выполнять несколько операций как единое целое:
```javascript
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);
}
```
### Заморозка данных
Заморозка предотвращает случайные изменения данных:
```javascript
// Замораживаем базу данных
gb.freeze();
try {
const user = await gb.dispense('user');
user.name = 'Иван';
await gb.store(user); // Выбросит ошибку
} catch (error) {
console.error('Нельзя изменять замороженные данные');
}
// Размораживаем для внесения изменений
gb.freeze(false);
```
### События
GreenBean позволяет отслеживать различные события:
```javascript
// Подписываемся на событие сохранения
gb.on('save', () => {
console.log('База данных сохранена');
});
// Можно использовать для логирования
gb.on('save', () => {
const now = new Date().toISOString();
console.log(`[${now}] Данные сохранены`);
});
```
## Лучшие практики
### Организация кода
```javascript
// Создайте класс для работы с определенным типом объектов
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'
});
```
### Обработка ошибок
```javascript
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);
});
```
### Валидация данных
```javascript
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