# 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