Project

dorm

0.0
No release in over 3 years
Dorm (Data ORM) is a lightweight, functional ORM built on Ruby's Data class. Features immutable records, monadic error handling inspired by dry-monads, and a functional programming approach to database operations.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
 Dependencies

Development

~> 2.0
~> 1.0
~> 13.0
~> 3.0
~> 1.0
~> 1.4
~> 0.9
 Project Readme

Dorm

Data ORM - A lightweight, functional ORM for Ruby built on the Data class introduced in Ruby 3.2. Features immutable records, monadic error handling, and a functional programming approach to database operations.

Features

  • 🔧 Immutable Records: Built on Ruby's Data class for immutable, value-based objects
  • 🚂 Railway-Oriented Programming: Monadic error handling inspired by dry-monads
  • 🎯 Functional Approach: Pure functions in modules instead of stateful classes
  • 🔍 Automatic CRUD: Metaprogramming generates standard operations from Data class introspection
  • Built-in Validations: Declarative validation rules with clear error messages
  • 🔄 Safe Updates: Use .with() for immutable updates that return new objects
  • 🗄️ Database Agnostic: Support for PostgreSQL and SQLite3

Installation

Add this line to your application's Gemfile:

gem 'dorm'

And then execute:

$ bundle install

Or install it yourself as:

$ gem install dorm

Quick Start

1. Configure Database Connection

require 'dorm'

Dorm.configure(
  adapter: :postgresql,
  host: 'localhost',
  dbname: 'myapp_development',
  user: 'postgres'
)

2. Define Your Data Structures

User = Data.define(:id, :name, :email, :created_at, :updated_at)
Post = Data.define(:id, :title, :body, :user_id, :created_at, :updated_at)

3. Create Repositories

Users = Dorm.repository_for(User,
  validations: {
    name: { required: true, length: 1..100 },
    email: { required: true, format: /@/ }
  }
)

Posts = Dorm.repository_for(Post,
  validations: {
    title: { required: true, length: 1..200 },
    body: { required: true },
    user_id: { required: true }
  }
)

4. Use With Monadic Error Handling

# Chain operations - if any fail, the rest are skipped
result = Users.create(name: "Alice", email: "alice@example.com")
  .bind { |user| Posts.create(title: "Hello", body: "World", user_id: user.id) }
  .map { |post| "Created post: #{post.title}" }

if result.success?
  puts result.value  # "Created post: Hello"
else
  puts "Error: #{result.error}"
end

# Safe value extraction with defaults
user = Users.find(1).value_or(nil)

# Check success/failure
user_result = Users.find(999)
if user_result.success?
  puts "Found: #{user_result.value.name}"
else
  puts "Not found: #{user_result.error}"
end

5. Immutable Updates

user = Users.find(1).value
updated_user = Users.save(user.with(name: "Alice Smith"))

if updated_user.success?
  puts "Updated: #{updated_user.value.name}"
end

Available Repository Methods

Every repository automatically gets these methods:

CRUD Operations

  • find(id) - Find record by ID
  • find_all - Get all records
  • create(attrs) - Create new record
  • update(record) - Update existing record
  • save(record) - Create or update (based on presence of ID)
  • delete(record) - Delete record

Query Methods

  • where(predicate) - Filter with a lambda/proc
  • find_by(**attrs) - Find first record matching attributes
  • find_all_by(**attrs) - Find all records matching attributes
  • count - Count total records

Examples

# Find operations
user = Users.find(1)
all_users = Users.find_all

# Query operations
active_users = Users.where(->(u) { u.active })
user_by_email = Users.find_by(email: "alice@example.com")
posts_by_user = Posts.find_all_by(user_id: user.value.id)

# CRUD with validation
new_user = Users.create(name: "Bob", email: "bob@example.com")
updated = Users.save(new_user.value.with(name: "Robert"))
deleted = Users.delete(updated.value)

Functional Composition

Use the included functional helpers for more complex operations:

include Dorm::FunctionalHelpers

# Pipeline processing
result = pipe(
  Users.find_all.value,
  partial(method(:filter), ->(u) { u.name.length > 3 }),
  partial(method(:map_over), ->(u) { u.email })
)

Validation Rules

Support for common validation patterns:

Users = Dorm.repository_for(User,
  validations: {
    name: {
      required: true,
      length: 1..100
    },
    email: {
      required: true,
      format: /@/
    },
    age: {
      range: 0..150
    }
  }
)

Database Support

PostgreSQL

Dorm.configure(
  adapter: :postgresql,
  host: 'localhost',
  dbname: 'myapp',
  user: 'postgres',
  password: 'secret'
)

SQLite3

Dorm.configure(
  adapter: :sqlite3,
  database: 'myapp.db'
)

Philosophy

This ORM embraces functional programming principles:

  • Immutability: Records are immutable Data objects
  • Pure Functions: Repository methods are pure functions in modules
  • Error Handling: Railway-oriented programming with Result monads
  • Composability: Operations can be chained and composed
  • Explicitness: No hidden state or magic behavior

Requirements

  • Ruby >= 3.2.0 (for Data class support)
  • Database adapter gem (pg for PostgreSQL, sqlite3 for SQLite)

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/yourusername/dorm.

License

The gem is available as open source under the terms of the MIT License.