class Sepia::Storage

Overview

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

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")

Defined in:

sepia/storage.cr

Constant Summary

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 Method Summary

Instance Method Summary

Class Method Detail

def self.backend #

Returns the current storage backend.

Returns

The currently active StorageBackend instance.

Example

backend = Sepia::Storage.backend
puts backend.class # => FileStorage or InMemoryStorage

[View source]
def self.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

[View source]
def self.build_generation_path(base_path : String | Nil, generation_id : String) : String | Nil #

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


[View source]
def self.clear #

Bulk operations


[View source]
def self.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)

[View source]
def self.count(object_class : Class) : Int32 #

[View source]
def self.delete(object : Serializable | Container, cache : Bool = true, metadata = nil) #

[View source]
def self.delete(class_name : String, id : String, cache : Bool = true) #

[View source]
def self.exists?(object_class : Class, id : String) : Bool #

[View source]
def self.export_data : Hash(String, Array(Hash(String, String))) #

[View source]
def self.gc(roots : Enumerable(Sepia::Object), dry_run : Bool = false) : Hash(String, Array(String)) #

[View source]
def self.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.


[View source]
def self.import_data(data : Hash(String, Array(Hash(String, String)))) #

[View source]
def self.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

[View source]
def self.list_all(object_class : Class) : Array(String) #

Discovery API - delegates to current backend


[View source]
def self.list_all_objects : Hash(String, Array(String)) #

[View source]
def self.load(object_class : T.class, id : String, path : String | Nil = nil, cache : Bool = true) : T forall T #

[View source]
def self.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)


[View source]
def self.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}" }

[View source]
def self.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)


[View source]
def self.save(object : Container, path : String | Nil = nil, cache : Bool = true, metadata = nil, *, force_new_generation : Bool = false) #

[View source]

Instance Method Detail

def delete(object : Serializable | Container, cache : Bool = true, metadata = nil) #

[View source]
def 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

[View source]
def path : String #

Legacy path property (only works with FileStorage)


[View source]
def path=(path : String) #

[View source]
def 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

[View source]
def 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

[View source]