# Отсортировать в порядке возрастания 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.
10.4. Подключение к внешним базам данных
Благодаря усилиям многих людей Ruby может взаимодействовать с разными базами данных, от монолитных систем типа Oracle до более скромного MySQL. Для полноты описания мы включили в него также текстовые файлы в формате CSV.
Уровень функциональности, реализованный в этих пакетах, постоянно изменяется. Обязательно познакомьтесь с последней версией документации в сети. Неплохой отправной точкой станет архив приложений Ruby.
10.4.1. Интерфейс с SQLite
SQLite — популярная база данных для тех, кто ценит программное обеспечение, которое не нужно конфигурировать. Это небольшая автономная исполняемая программа, написанная на языке С, которая хранит всю базу данных в одном файле. Хотя обычно она используется для небольших баз, но теоретически способна управиться с терабайтными объемами.
Привязка Ruby к SQLite довольно прямолинейна. API, написанный на С, обернут в класс SQLite::API
. Поскольку при этом методы отображаются один в один и интерфейс не назовешь образцом объектной ориентированности, пользоваться этим API стоит только в случае острой необходимости.
В большинстве ситуаций вам будет достаточно класса SQLite::Database
. Вот пример кода:
require 'sqlite'
db = SQLite::Database.new('library.db')
db.execute('select title,author from books') do |row|
p row
end
db.close
# Выводится:
# ['The Case for Mars', 'Robert Zubrin']
# ['Democracy in America', 'Alexis de Tocqueville']
# ...
Если блок не задан, то метод execute
возвращает объект ResultSet
(по сути, курсор, который можно перемещать по набору записей).
rs = db.execute('select title,author from books')
rs.each {|row| p row } # Тот же результат, что и выше.
rs.close
Если получен объект ResultSet
, то программа должна будет рано или поздно закрыть его (как показано в примере выше). Если нужно обойти список записей несколько раз, то с помощью метода reset
можно вернуться в начало. (Это экспериментальное средство, которое в будущем