Persistant Sets of Structured Data
db-struct is a Ruby gem that provides class DBStruct, a class that
is similar to Ruby's built-in Struct class but stores its data in a
SQLite database. In addition, each subclass also provides access to
the database via an interface that closely mimics a Ruby Hash,
including support for enumeration.
It is currently at the "experimental toy" stage of development.
Installation
Simply install it via gem:
gem install --prerelease db-struct
Note that you will also need to install SQLite3 separately. On stock Ruby, that's:
gem install sqlite3
while with JRuby, it's:
gem install jdbc-sqlite3
It uses Sequel to do the heavy lifting. This is installed as a dependency, but you'll need to know how to open a database with it.
Overview
Let's start with a simple example, a table of books. The first thing we need to do is create a database connection:
DB = Sequel.sqlite("books.sqlite3")
We can then define the structure and the underlying table:
Book = DBStruct.with(DB, :books) do
field :title, String
field :author, String
field :date, Time
field :edition, Integer
end
Book is now a subclass of DBStruct; creating it will also create a
table named :books if it doesn't exist.
We can create an instance of Book like this:
b1 = Book.new(title: "Diseases of the Dragon",
author: "Lady Sybil Ramkin",
date: Time.new(1989, 11, 1),
edition: 1)
The contents are immediately written to the database, but this object
behaves more or less like a Ruby Struct:
puts b1.title
b1. = "Lady Sybil Ramkin-Vimes"
puts b1.
Note that these are not part of a transaction. If you need that (and
you probably will), you can the transaction method:
Book.transaction {
puts b1.title
b1. = "Lady Sybil Ramkin-Vimes"
puts b1.
}
This starts a transaction, evaluates the block and commits. If there
is an exception inside the block, the transaction is rolled back
instead. Transactions can be safely nested. (DBStruct#transaction
is simply a thin wrapper around Sequel::Database#transaction.)
The class method items returns a DBStruct::BogoHash, which behaves
like a Hash mapping numeric row IDs to corresponding Book objects:
puts Book.items[b1.rowid].title
The usual enumeration operations are also available:
Book.items.values.each{|book|
puts " #{book.title} by #{book.}"
}
first_editions = Book.items
.select{|id, book| book.edition == 1}
.map{|id, _| id}
You can also add a special field type called a group. This can be
used to subdivide the table:
Book = DBStruct.with(DB, :books) do
group :category, String
field :title, String
field :author, String
field :date, Time
field :edition, Integer
end
b1 = Book.new(category:"non-fiction",
title: "Diseases of the Dragon",
author: "Lady Sybil Ramkin",
date: Time.new(1989, 11, 1),
edition: 1)
A group is just an ordinary field except that Books.items will
filter by them:
non_fiction = Book.items("non-fiction")
Multiple groups are allowed and nil can be used as a wildcard when
selection them.
Alternately, you can filter using a Sequel where clause:
dragon_books = Book.where(Sequel.like(:title, "%Dragon%"))
but if you need that often, you may well be better off dealing with
Sequel directly.