Книга: Программирование на языке Ruby
10.3. Библиотека KirbyBase
10.3. Библиотека KirbyBase
KirbyBase — небольшая библиотека, с которой должен освоиться каждый программист на Ruby. В настоящее время она не входит в стандартный дистрибутив, а если бы входила, то была бы еще полезнее.
KirbyBase — плод трудов Джейми Криббса (Jamey Cribbs), названный, к слову, в честь его собаки. Во многих отношениях это полноценная база данных, но есть причины, по которым мы рассматриваем ее здесь, а не вместе с MySQL и Oracle.
Во-первых, это не автономное приложение. Это библиотека для Ruby, и без Ruby ее использовать нельзя. Во-вторых, она вообще не знает, что такое язык SQL. Если вам без SQL не обойтись, то эта библиотека не для вас. В-третьих, если приложение достаточно сложное, то функциональных возможностей и быстродействия KirbyBase может не хватить.
Но несмотря на все это, есть немало причин любить KirbyBase. Это написанная целиком на Ruby библиотека, состоящая из единственного файла, которую не нужно ни устанавливать, ни конфигурировать. Она работает на всех платформах, и созданные с ее помощью файлы можно переносить с одной платформы на другую. Это «настоящая» база данных в том смысле, что данные не загружаются целиком в память.
Библиотекой легко пользоваться, а ее интерфейс выдержан в духе Ruby с легким налетом DBI. В общем, база данных соответствует каталогу, а каждая таблица — одному файлу. Формат данных в таблицах таков, что человек может их читать (и редактировать). Дополнительно таблицы можно зашифровать — но только для того, чтобы затруднить редактирование. База знает об объектах Ruby; допускается их хранение и извлечение без потери информации.
Наконец, благодаря интерфейсу dRuby библиотека может работать в распределенном режиме. К данным, хранящимся в KirbyBase, можно с одинаковым успехом обращаться как с локальной, так и с удаленной машины.
Чтобы открыть базу данных, нужно сначала указать, является ли она локальной. Следующие два параметра обычно равны nil
, а четвертый указывает каталог, в котором будут храниться файлы с данными (по умолчанию это текущий каталог).
Чтобы создать таблицу, вызывается метод create_table
объекта, представляющего базу данных; ему передается имя таблицы (объект Symbol
); имя файла на диске образуется из этого имени. Затем передается последовательность пар символов, описывающих имена и типы полей.
require 'kirbybase'
db = KirbyBase.new(:local, nil, nil, "mydata")
books = db.create_table(:books, # Имя таблицы.
:title, :String, # Поле, тип, ...
:author, :String)
В текущей версии KirbyBase распознает следующие типы полей: String
, Integer
, Float
, Boolean
, Time
, Date
, DateTime
, Memo
, Blob
и YAML
. К тому моменту, когда вы будете читать эту главу, возможно, появятся и новые типы.
Для вставки записи в таблицу применяется метод insert
. Ему можно передать список значений, хэш или любой объект, отвечающий на заданные имена полей.
books.insert("The Case for Mars","Robert Zubrin")
books.insert(:title => "Democracy in America",
:author => "Alexis de Tocqueville")
Book = Struct.new(:title, :author)
book = Book.new("The Ruby Way","Hal Fulton")
books.insert(book)
В любом случае метод insert
возвращает идентификатор строки, соответствующей новой записи (вы можете использовать его или игнорировать). Это «скрытое» автоинкрементное поле, присутствующее в каждой записи любой таблицы. Для выборки записей служит метод select
. Без параметров он выбирает все поля всех записей таблицы. Набор полей можно ограничить, передав в качестве параметров символы. Если задан блок, то он определяет, какие записи отбирать (примерно так же, как работает метод find_all
для массивов).
list1 = people.select # Все люди, все поля.
list2 = people.select(:name,:age) # Все люди, только имя и возраст.
list3 = people.select(:name) {|x| x.age >= 18 && x.age < 30 }
# Имена всех людей от 18 до 30 лет.
В блоке допустимы любые операции. Это означает, например, что можно формулировать запрос с помощью регулярных выражений (в отличие от типичной SQL-базы).
Результирующий набор, возвращаемый KirbyBase, можно сортировать по нескольким ключам в порядке возрастания или убывания. Для сортировки по убыванию перед именем ключа ставится минус. (Это работает, потому что в класс Symbol
добавлен метод, соответствующий унарному минусу.)
sorted = people.select.sort(:name,-:age)
# Отсортировать в порядке возрастания name и в порядке убывания age.
У результирующего набора есть одно интересное свойство: он может предоставлять массивы, «срезающие» результат. С первого раза это довольно трудно понять.
Предположим, что есть результирующий набор записей, представляющих людей, и в каждой записи хранятся имя, возраст, рост и вес. Понятно, что этот результирующий набор можно индексировать как массив, но одновременно он имеет методы, названные так же, как поля. Каждый такой метод возвращает массив значений только соответствующего ему поля. Например:
list = people.select(:name,:age,:heightweight)
p list[0] # Вся информация о человеке 0.
p list[1].age # Только возраст человека 1.
p list[2].height # Рост человека 2.
ages = list.age # Массив: возрасты всех людей.
names = list.name # Массив: имена всех людей.
В KirbyBase есть ограниченные средства печати отчетов; достаточно вызвать метод to_report
для любого результирующего набора. Пример:
rpt = books.select.sort(:title).to_report
puts rpt
# Выводится:
# recno | title | author
# -----------------------------------------------------------
# 2 | Democracy in America | Alexis de Tocqueville
# 1 | The Case for Mars | Robert Zubrin
# 3 | The Ruby Way | Hal Fulton
Атрибут таблицы encrypt
можно установить в true
— тогда данные нельзя будет читать и редактировать, как обычный текст. Но имейте в виду, что для этого применяется шифр Вигенера — не «игрушечный», но и не являющийся криптографически безопасным. Так что пользоваться шифрованием имеет смысл только для того, чтобы помешать редактированию, но никак не для сокрытия секретных данных. Обычно режим шифрования устанавливается в блоке при создании таблицы:
db.create_table(:mytable, f1, :String, f2, :Date) {|t| t.encrypt = true }
Поскольку удаленный доступ — интересное средство, уделим ему немного внимания. Вот пример сервера:
require 'kirbybase'
require 'drb'
host = 'localhost'
port = 44444
db = KirbyBase.new(:server) # Создать экземпляр базы данных.
DRb.start_service("druby://#{host} :#{port)", db)
DRb.thread.join
Это прямое применение интерфейса dRuby (см. главу 20). На стороне клиента следует при подключении к базе данных задать символ :client
вместо обычного :local
.
db = KirbyBase.new(:client,'localhost',44444)
# Весь остальной код не изменяется.
Можно также выполнять обычные операции: обновлять и удалять записи, удалять таблицы и т.д. Есть и более сложные механизмы, о которых я не буду рассказывать подробно: связи один-ко-многим, вычисляемые поля и нестандартные классы записей. Подробнее см. документацию по KirbyBase на сайте RubyForge.
- 17.2.1. Библиотека setup.rb
- 15.2.1. Стандартная библиотека rss
- Библиотека Ext Core
- А.2.4. Библиотека ccmalloc
- 13.2. Локали и библиотека С
- 15.5.1. Библиотека dbug — усовершенствованный printf()
- Клиентская библиотека Firebird
- Стандартная библиотека ввода
- API, POSIX и библиотека С
- Библиотека Oracle XDK
- Управление библиотеками общего доступа
- 15. Библиотека языка Си и файлы ввода-вывода