class

Sepia::Storage

Inherits Reference < Object

Central storage management class.

The Storage class manages pluggable storage backends and provides both a modern class-based API and backward compatibility with the original singleton pattern.

⚠️ WARNING: The Storage API and backend interfaces are subject to change. The on-disk format for the filesystem backend is not stable.

Supported Backends

  • :filesystem - Default file-based storage (FileStorage)
  • :memory - In-memory storage for testing (InMemoryStorage)

Usage

# Configure storage backend
Sepia::Storage.configure(:filesystem, {"path" => "./data"})

# Or use in-memory storage
Sepia::Storage.configure(:memory)

# Class-based API (recommended)
Sepia::Storage.save(my_object)
loaded = Sepia::Storage.load(MyClass, "object-id")

# Legacy singleton API (still supported)
Sepia::Storage::INSTANCE.save(my_object)
loaded = Sepia::Storage::INSTANCE.load(MyClass, "object-id")

Constants

INSTANCE = new

Legacy singleton instance for backward compatibility.

Provides the same API as the class methods for existing code that relies on the singleton pattern.

Class methods

backend

Returns the current storage backend.

Returns

The currently active StorageBackend instance.

Example

backend = Sepia::Storage.backend
puts backend.class # => FileStorage or InMemoryStorage
Source
backend=(backend : StorageBackend)

Sets the current storage backend.

Allows switching to a different backend implementation at runtime.

Parameters

  • backend : A StorageBackend instance to use

Example

# Switch to custom backend
custom_backend = MyCustomStorage.new
Sepia::Storage.backend = custom_backend
Source
backup(objects : Array(Sepia::Object), output_path : String) : String

Creates a backup of the specified objects.

This is a convenience method that wraps the Sepia::Backup.create method and validates that the current storage backend supports backup operations.

Parameters

  • objects : Array of objects to include in the backup
  • output_path : Path where the backup tar file will be created

Returns

The path to the created backup file

Example

# Backup specific objects
documents = [doc1, doc2, doc3]
backup_path = Sepia::Storage.backup(documents, "docs_backup.tar")

# Backup a user's entire object tree
user_data = [user_object]
backup_path = Sepia::Storage.backup(user_data, "user_backup_#{Time.utc.to_unix}.tar")

Raises

  • Sepia::Backup::BackendNotSupportedError if current backend doesn't support backups
  • Sepia::Backup::BackupCreationError if backup creation fails
Source
backup(object : Sepia::Object, output_path : String) : String

Creates a backup of a single object and its references.

Convenience method for backing up one object tree.

Parameters

  • object : Root object to backup (includes all referenced objects)
  • output_path : Path where the backup tar file will be created

Returns

The path to the created backup file

Example

# Backup a project and all its documents
project = Sepia::Storage.load(Project, "project-123")
backup_path = Sepia::Storage.backup(project, "project_backup.tar")
Source
backup_all(output_path : String, progress_callback = nil) : String

Creates a backup of all objects in the current storage.

This method attempts to backup all objects currently stored in the storage backend. Be aware that this can be resource-intensive for large storage systems.

Parameters

  • output_path : Path where the backup tar file will be created
  • progress_callback : Optional callback for progress updates

Returns

The path to the created backup file

Example

# Backup everything with progress updates
backup_path = Sepia::Storage.backup_all("full_backup.tar") do |progress|
  puts "Backup progress: #{progress} objects processed"
end
Source
backup_supported?

Checks if the current storage backend supports backup operations.

Currently, only FileStorage supports backup operations as it provides access to the underlying file system structure.

Returns

true if backup is supported, false otherwise

Example

if Sepia::Storage.backup_supported?
  puts "Backup operations are available"
else
  puts "Switch to FileStorage to enable backup features"
end
Source
build_generation_path(base_path : String | Nil, generation_id : String) : String | Nil

Build a generation-specific path from a base path and generation ID.

Source
clear

Bulk operations

Source
configure(backend : Symbol, config : Hash(String, String | Bool | Hash(String, String)) = {} of String => String)

Configures storage using a named backend.

Provides a convenient way to configure common backends without instantiating them manually.

Parameters

  • backend : Symbol identifying the backend type (:filesystem or :memory)
  • config : Optional configuration hash for the backend

Configuration Options

For :filesystem backend:

  • "path": Root directory path (defaults to system temp directory)
  • "watch": Enable file system watcher (default: false)
    • true: Enable watcher with default settings
    • false: Disable watcher
    • Hash: Custom watcher configuration options

For :memory backend:

  • No configuration options available

Examples

# Configure filesystem storage with custom path
Sepia::Storage.configure(:filesystem, {"path" => "./app_data"})

# Configure filesystem storage with watcher enabled
Sepia::Storage.configure(:filesystem, {"watch" => true})

# Configure filesystem storage with custom watcher settings
Sepia::Storage.configure(:filesystem, {
  "path"  => "./app_data",
  "watch" => {
    "recursive" => true,
    "latency"   => 0.1,
  },
})

# Configure in-memory storage
Sepia::Storage.configure(:memory)
Source
count(object_class : Class) : Int32
Source
delete(object : Serializable | Container, cache : Bool = true, metadata = nil)
Source
delete(class_name : String, id : String, cache : Bool = true)
Source
exists?(object_class : Class, id : String) : Bool
Source
export_data
Source
gc(roots : Enumerable(Sepia::Object), dry_run : Bool = false) : Hash(String, Array(String))
Source
get_latest_generation(object_class : Class, base_id : String)

Get the latest generation object for a given base ID.

Returns the object with the highest generation number, or nil if no generations exist.

Source
import_data(data : Hash(String, Array(Hash(String, String))))
Source
last_event(object_class : Class, id : String) : LogEvent | Nil

Get the last event for a specific object.

Parameters

  • object_class : The class of the object
  • id : The object's unique identifier

Returns

The last event for the object, or nil if no events exist

Example

last_event = Sepia::Storage.last_event(MyDocument, "doc-123")
if last_event
  puts "Last modified: #{last_event.timestamp}"
end
Source
list_all(object_class : Class) : Array(String)

Discovery API - delegates to current backend

Source
list_all_objects
Source
load(object_class : T.class, id : String, path : String | Nil = nil, cache : Bool = true) : T forall T
Source
next_generation_number(object_class : Class, base_id : String) : Int32

Get the next generation number for an object based on existing files.

Scans the filesystem to find the highest existing generation number and returns the next one. This is independent of event logging.

Parameters

  • object_class : The class of the object
  • base_id : The base ID without generation suffix

Returns

The next generation number (1 if no generations exist)

Source
object_events(object_class : Class, id : String) : Array(LogEvent)

Get all events for a specific object.

Parameters

  • object_class : The class of the object
  • id : The object's unique identifier

Returns

Array of events for the specified object, ordered by timestamp

Example

events = Sepia::Storage.object_events(MyDocument, "doc-123")
events.each { |event| puts "#{event.timestamp}: #{event.event_type}" }
Source
save(object : Serializable, path : String | Nil = nil, cache : Bool = true, metadata = nil, *, force_new_generation : Bool = false)

Class methods with cache integration (caching is default)

Source
save(object : Container, path : String | Nil = nil, cache : Bool = true, metadata = nil, *, force_new_generation : Bool = false)
Source

Instance methods

backup(objects : Array(Sepia::Object), output_path : String) : String

Instance version of backup method for backward compatibility.

Example

Sepia::Storage::INSTANCE.backup([doc1, doc2], "backup.tar")
Source
backup(object : Sepia::Object, output_path : String) : String

Instance version of single object backup method.

Source
backup_all(output_path : String, progress_callback = nil) : String

Instance version of backup_all method.

Source
backup_supported?

Instance version of backup_supported? method.

Source
delete(object : Serializable | Container, cache : Bool = true, metadata = nil)
Source
load(object_class : T.class, id : String, path : String | Nil = nil, cache : Bool = true) : T forall T

Loads an object using the current backend.

First checks the cache for the object. If not found, loads from backend and automatically caches the result for future retrievals. Set cache: false to disable caching for this operation.

Parameters

  • object_class : The class of object to load
  • id : The object's unique identifier
  • path : Optional custom load path
  • cache : Whether to use cache (default: true)

Returns

An instance of type T loaded from storage.

Example

# Load with caching (default)
doc = Sepia::Storage.load(MyDocument, "doc-uuid")

# Load without caching
doc = Sepia::Storage.load(MyDocument, "doc-uuid", cache: false)

# Type is inferred, no casting needed
puts doc.content # doc is typed as MyDocument
Source
path

Legacy path property (only works with FileStorage)

Source
path=(path : String)
Source
save(object : Serializable, path : String | Nil = nil, cache : Bool = true, metadata = nil, *, force_new_generation : Bool = false)

Saves a Serializable object using the current backend.

Automatically caches the object for faster retrieval. Set cache: false to disable caching for this operation. Optionally logs the save operation if the object's class has event logging enabled.

Parameters

  • object : The Serializable object to save
  • path : Optional custom save path
  • cache : Whether to cache the object (default: true)
  • metadata : Optional metadata for event logging

Example

doc = MyDocument.new("Hello")
Sepia::Storage.save(doc)                                # Save with caching (default)
Sepia::Storage.save(doc, cache: false)                  # Save without caching
Sepia::Storage.save(doc, metadata: {"user" => "alice"}) # Save with event logging
Source
save(object : Container, path : String | Nil = nil, cache : Bool = true, metadata = nil, *, force_new_generation : Bool = false)

Saves a Container object using the current backend.

Automatically caches the object for faster retrieval. Set cache: false to disable caching for this operation. Optionally logs the save operation if the object's class has event logging enabled.

Parameters

  • object : The Container object to save
  • path : Optional custom save path
  • cache : Whether to cache the object (default: true)
  • metadata : Optional metadata for event logging

Example

board = Board.new("My Board")
Sepia::Storage.save(board)                                # Save with caching (default)
Sepia::Storage.save(board, cache: false)                  # Save without caching
Sepia::Storage.save(board, metadata: {"user" => "alice"}) # Save with event logging
Source