/eno

Eno's Not an ORM

Primary LanguageRubyMIT LicenseMIT

Eno - the SQLite Toolkit for Ruby

Eno's Not an ORM

INSTALL | TUTORIAL | EXAMPLES

What is Eno?

Eno is a Ruby gem for working with SQLite databases in Ruby. Eno provides a set of tools for implementing various persistence and data access patterns using the SQLite embedded database engine.

Features:

  • Out of the box support for concurrent database access.
  • Automatic connection pooling, with support for thread- and fiber-based concurrency.
  • Data stores with specialized APIs for different data access patterns.
  • DSL for expressing SQL queries with support for CTEs and window functions.
  • Based on [Extralite].

Synopsis

Eno.connect '/path/to/my.db'

# define a query
q = Q {
  select :*
  from foo
}
# or alternatively
q = Eno.q { select_all; from foo }

# query mutation
Q.from(:foo).select_all

# get all rows
q.to_a

# iterate through records
q.each { |r| ... }

# mutate query
q.where { bar == 42 }.to_a

# count records
q.where { bar == 42 }.count
# which is short for:
q.where { bar == 42 }.select { count(:*) }.single_value

# parametrized queries
q = Q {
  select_all
  from foo
  where bar == _(bar)
}

q.to_a(bar: 42)

class Post < Eno::ModelStore
  schema(:post) do
    column id:      integer, :primary_ke
    column user_id: integer
    column title:   text
    column body:    text

    foreign_key user_id: [:users, :id]
  end
end

class User < Eno::ModelStore
  schema(:user) do
    column id:    integer, :primary_key
    column name:  text
  end

  one_to_many :posts do
    left join post
    on post.user_id == user.id
    order_by post.id
  end

  def posts
    this = self
    Post.where { user_id.in this.select { id } }
  end
end

h = User.insert(name: 'foo')
h.class #=> Hash
h[:id] #=> 1
h[:name] #=> 'foo'

u = User[1]
u #=> User <where id = 1>
name = u.values[:name]

u.update(name: 'bar')

posts = u.posts
posts.class #=> Post

posts.each do |r|
  puts r.title
  puts r.body
end

# checkout a connection explicitly and wrap in a transaction:
Eno.transaction do
  Posts.where { user_id == 3 }.update(user_id: nil)
  User[3].delete
end

# protection against updating/deleting without a where clause
User.delete
#=> Eno::Error 'To delete all records use the #all modifier, e.g. foo.all.delete'
User.all.delete #=> 1

# custom modifiers
class Post < Eno::ModelStore
  ...

  def by_category(category)
    where(category:)
  end

  def published
    where(published: true)
  end
end

foo_posts = Post.published.by_category('foo')

foo_posts.where_clause
#=> "where (published is true) and (category = 'foo')"

# Key-value store
cache = Eno::KVStore.new('cache')
cache.set('foo', 'bar')

# set TTL of 30 seconds
cache.setex('foo', 30, 'baz')

# periodic cache eviction
bouncer = Thread.new do
  loop do
    sleep 10
    cache.evict_expired_keys
  end
end