Familia - 2.0
Organize and store Ruby objects in Valkey/Redis using native database types (an ORM of sorts).
Familia provides object-oriented access to Valkey/Redis using their database types. Unlike traditional ORMs that map objects to relational tables, Familia maps Ruby objects directly to Valkey's native data structures (strings, lists, sets, sorted sets, hashes) as instance variables.
Caution
Familia 2 is in pre-release and not ready for production use. (October 2025)
Traditional ORM vs Familia
Traditional ORMs convert your objects to SQL tables. A product with categories becomes two tables with a join table. Checking if a tag exists requires a query with joins.
Familia stores your objects using Valkey/Redis data structures directly. A product with categories uses an actual Valkey/Redis list. Checking if a tag exists is a native O(1) Valkey/Redis operation.
# Traditional ORM - everything becomes SQL tables
class Product < ActiveRecord::Base
  has_and_belongs_to_many :tags  # Creates products_tags junction table
end
product.tags.include?(tag)  # SELECT * FROM products_tags WHERE ...
# Familia - uses Valkey/Redis data types directly
class Product < Familia::Horreum
  set :tags                  # Actual Valkey/Redis set
end
product.tags.include?("electronics")  # Valkey/Redis SISMEMBER - O(1) operationWhat This Means in Practice
When you define a Familia model, each data type declaration creates the corresponding Valkey/Redis structure:
class Product < Familia::Horreum
  identifier_field :sku
  field :name, :price        # Stored in Valkey/Redis hash
  list :categories           # Actual Valkey/Redis list
  set :tags                  # Actual Valkey/Redis set
  zset :ratings              # Actual Valkey/Redis sorted set
  counter :views             # Valkey/Redis string with atomic increment
end
# These are Valkey/Redis native operations, not ORM abstractions
product.categories.push("electronics")   # LPUSH
product.tags.add("popular")              # SADD
product.ratings.add(4.5, "user123")      # ZADD with score
product.views.increment                  # INCR (atomic)The performance characteristics you rely on in Valkey/Redis remain unchanged. Set membership is O(1). Sorted sets maintain order automatically. Counters increment atomically without read-modify-write cycles.
Quick Start
1. Installation
# Add to Gemfile
gem 'familia', '>= 2.0'
# Or install directly
gem install familia2. Configure Connection
# config/initializers/familia.rb (Rails)
# or at the top of your script
require 'familia'
# Basic configuration
Familia.uri = 'redis://localhost:6379/0'
# Or with authentication
Familia.uri = 'redis://user:password@localhost:6379/0'3. Create Your First Model
class User < Familia::Horreum
  identifier_field :email
  field :email
  field :name
  field :created_at
end4. Basic Operations
# Create and save
user = User.create(email: 'alice@example.com', name: 'Alice', created_at: Time.now.to_i)
# Find by identifier
user = User.load('alice@example.com')
# Update and save
user.name = 'Alice Windows'
user.save
# Fast update (immediate persistence)
user.name!('Alice Smith')  # Sets and saves immediately
# Check existence
User.exists?('alice@example.com')  #=> true
# Delete
user.destroy
# Conditional save
user.save_if_not_exists  # Only saves if object doesn't exist yet5. Generated Method Patterns
Familia automatically generates methods for fields and data types:
class User < Familia::Horreum
  field :name                    # → name, name=, name!
  set :tags                      # → tags, tags=, tags?
  list :history                  # → history, history=, history?
end
# Field methods
user.name                        # Get field value
user.name = 'Alice'              # Set field value
user.name!('Alice')              # Set and save immediately
# Data type methods
user.tags                        # Get Set instance
user.tags = new_set              # Replace Set instance
user.tags?                       # Check if it's a Set typePrerequisites
- Ruby: 3.4+ (3.4+ recommended)
 - Valkey/Redis: 6.0+
 - 
Gems: 
redis(automatically installed) 
Core Concepts
Data Types
Familia provides direct mappings to Valkey/Redis native data structures:
class BlogPost < Familia::Horreum
  identifier_field :slug
  # Basic fields
  field :slug, :title, :content, :published_at
  # Valkey/Redis data types as instance variables
  string :view_count, default: '0'           # Atomic counters
  list :comments                             # Ordered, allows duplicates
  set :tags                                  # Unique values
  zset :popularity_scores                    # Scored/ranked data
  hashkey :metadata                          # Key-value pairs
  # Advanced field types
  counter :likes                             # Specialized atomic counter
end
post = BlogPost.create(slug: "hello-world", title: "Hello World")
# Work with Valkey/Redis data types naturally
post.view_count.increment                    # INCR view_count
post.comments.push("Great post!")           # LPUSH comments
post.tags.add("ruby", "programming")        # SADD tags
post.popularity_scores.add(4.5, "user123") # ZADD popularity_scores
post.metadata["author"] = "Alice"           # HSET metadata
post.likes.increment(5)                     # INCRBY likes 5Features System
Enable advanced functionality with Familia's modular feature system:
class User < Familia::Horreum
  # Enable features as needed
  feature :expiration                        # TTL management
  feature :safe_dump                         # API-safe serialization
  feature :encrypted_fields                  # Field-level encryption
  feature :relationships                     # Object relationships
  feature :object_identifier                 # Auto-generated IDs
  feature :quantization                      # Time-based data bucketing
  identifier_field :email
  field :email, :name, :created_at
  # Feature-specific functionality
  encrypted_field :api_key                   # Automatically encrypted
  safe_dump_field :email                     # Include in safe_dump
  safe_dump_field :name                      # Include in safe_dump
  default_expiration 30.days                # Auto-expire inactive users
end
user = User.create(email: "alice@example.com", api_key: "secret123")
user.api_key.class                          # => ConcealedString
user.api_key.to_s                           # => "[CONCEALED]" (safe for logs)
user.safe_dump                              # => {email: "...", name: "..."}Querying and Finding
# Primary key lookup
user = User.load("alice@example.com")
# Existence checks
User.exists?("alice@example.com")           # => true/false
# Bulk operations
users = User.multiget("alice@example.com", "bob@example.com")
# With relationships feature - indexed lookups
class User < Familia::Horreum
  feature :relationships
  field :email, :username
  class_indexed_by :username, :username_lookup
end
# O(1) indexed finding
user = User.find_by_username("alice_doe")   # Fast hash lookupUsage Examples
Creating and Saving Objects
flower = Flower.create(name: "Red Rose", token: "prose")
flower.owners.push("Alice", "Bob")
flower.tags.add("romantic")
flower.metrics.increment("views", 1)
flower.props[:color] = "red"
flower.saveRetrieving and Updating Objects
rose = Flower.load("prose")
rose.name = "Pink Rose"
rose.saveUsing Safe Dump
user = User.create(username: "prosedog", first_name: "Rose", last_name: "Dog")
user.safe_dump
# => {id: "user:prosedog", username: "prosedog", full_name: "Rose Dog"}Working with Time-based Data
metric = DailyMetric.new
metric.counter.increment  # Increments the counter for the current hourBulk Operations
Flower.multiget("prose", "tulip", "daisy")Transactional Operations
Horreum Model Transactions:
user.transaction do |conn|
  conn.set("user:#{user.id}:status", "active")
  conn.zadd("active_users", Familia.now.to_i, user.id)
endDataType Transactions (standalone or parent-owned):
# Recommended: Use DataType methods for clean, automatic key handling
user.scores.transaction do
  user.scores.add('level1', 100)
  user.scores.add('level2', 200)
end
# Standalone DataType transaction (e.g., session storage)
session_key = Familia::StringKey.new('session:abc123')
session_key.transaction do
  session_key.set(session_data)
  session_key.expire(3600)  # Atomic: both succeed or both fail
end
# Advanced: Connection available for low-level Redis commands
user.scores.transaction do |conn|
  conn.zadd(user.scores.dbkey, 100, 'level1')
  conn.hset(user.profile.dbkey, 'status', 'active')
endPipeline Operations (batch commands for performance):
# Recommended: Use DataType methods
leaderboard.pipelined do
  leaderboard.add('player1', 500)
  leaderboard.add('player2', 600)
  leaderboard.size
end
# Advanced: Raw Redis commands for fine-grained control
leaderboard.pipelined do |conn|
  conn.zadd(leaderboard.dbkey, 500, 'player1')
  conn.zadd(leaderboard.dbkey, 600, 'player2')
endAdvanced Patterns
Time-based Expiration:
class Session < Familia::Horreum
  feature :expiration
  default_expiration 24.hours
  field :user_id, :token
end
session = Session.create(user_id: "123", token: "abc123")
session.ttl                                 # Check remaining time
session.expire_in(1.hour)                  # Custom expirationEncrypted Fields:
class SecureData < Familia::Horreum
  feature :encrypted_fields
  field :name
  encrypted_field :credit_card, :ssn
end
data = SecureData.create(name: "Alice", credit_card: "4111-1111-1111-1234")
data.credit_card.reveal                     # => "4111-1111-1111-1234"
data.credit_card.to_s                       # => "[CONCEALED]"Configuration
Basic Setup
# config/initializers/familia.rb (Rails)
require 'familia'
# Basic configuration
Familia.uri = 'redis://localhost:6379/0'
# Production configuration
Familia.configure do |config|
  config.redis_uri = ENV['REDIS_URL']
  config.debug = ENV['FAMILIA_DEBUG'] == 'true'
endConnection Pooling
require 'connection_pool'
Familia.connection_provider = lambda do |uri|
  ConnectionPool.new(size: 10, timeout: 5) do
    Redis.new(url: uri)
  end.with { |conn| yield conn if block_given?; conn }
endEncryption Setup
Familia.configure do |config|
  config.encryption_keys = {
    v1: ENV['FAMILIA_ENCRYPTION_KEY_V1'],
    v2: ENV['FAMILIA_ENCRYPTION_KEY_V2']
  }
  config.current_key_version = :v2
endOrganizing Complex Models
For large applications, you can organize model complexity using custom features and the Feature Autoloading System:
Feature Organization with Autoloader
For large applications, organize features into modular files using the autoloader:
# app/models/customer.rb - Main model file
class Customer < Familia::Horreum
  module Features
    include Familia::Features::Autoloader
    # Automatically loads all .rb files from app/models/customer/features/
  end
  identifier_field :custid
  field :custid, :name, :email
  feature :safe_dump                        # Feature configuration loaded automatically
end
# app/models/customer/features/notifications.rb - Automatically loaded
module Customer::Features::Notifications
  def send_welcome_email
    NotificationService.send_template(
      email: email,
      template: 'customer_welcome',
      variables: { name: name, custid: custid }
    )
  end
end
# app/models/customer/features/safe_dump_extensions.rb - Feature-specific config
module Customer::Features::SafeDumpExtensions
  def self.included(base)
    base.safe_dump_field :custid
    base.safe_dump_field :name
    base.safe_dump_field :email
  end
endThis approach keeps complex models organized while maintaining clean, declarative style.
AI Development Assistance
This version of Familia was developed with assistance from AI tools. The following tools provided significant help with architecture design, code generation, and documentation:
- Google Gemini - Refactoring, code generation, and documentation.
 - Claude Sonnet 4, Opus 4.1 - Architecture design, code generation, and documentation
 - Claude Desktop & Claude Code (Max plan) - Interactive development sessions and debugging
 - GitHub Copilot - Code completion and refactoring assistance
 - Qodo Merge Pro - Code review and quality improvements
 
I remain responsible for all design decisions and the final code. I believe in being transparent about development tools, especially as AI becomes more integrated into our workflows as developers.
Links
Documentation
For comprehensive guides and detailed technical information:
- Overview Guide - Conceptual understanding and getting started
 - Technical Reference - Implementation details and advanced patterns
 - Migration Guides - Upgrading from previous versions
 
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
 - Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
 
PR Compliance Checks
Pull requests are automatically reviewed by Qodo Merge with compliance checks for:
- Error Handling - External API calls and database operations must have proper error handling
 - Test Coverage - New features must include tests using the Tryouts framework
 - Changelog Fragments - User-facing changes should include a changelog entry
 - Documentation - API changes must update documentation
 - Backward Compatibility - Breaking changes must be documented
 - Thread Safety - Shared state must be properly synchronized
 - Database Key Naming - Keys must follow Familia conventions
 
See docs/qodo-merge-compliance.md for details.
License
This project is licensed under the MIT License - see the LICENSE file for details.