class
Sepia::FileStorage
- Sepia::FileStorage
- Sepia::StorageBackend
- Reference
- Object
Overview
Filesystem-based storage backend for Sepia objects.
This is the default storage backend that stores objects on the local filesystem. Serializable objects are stored as files, while Container objects are stored as directories with nested structures.
Directory Structure
The storage creates a directory structure like:
storage_path/
├── ClassName1/
│ ├── object1_id (Serializable object file)
│ └── object2_id (Serializable object file)
└── ClassName2/
├── container1/ (Container directory)
│ ├── data.json (Primitive properties)
│ └── refs/ (Reference files/symlinks)
└── container2/ (Container directory)
Example
# Configure Sepia to use filesystem storage
Sepia::Storage.configure(:filesystem, {"path" => "./data"})
# Objects will be stored in ./data/ClassName/sepia_id
Defined in:
sepia/file_storage.crConstructors
-
.new(path : String = Dir.tempdir, watch : Bool | Hash = false)
Creates a new FileStorage instance.
Instance Method Summary
-
#clear
Clears all objects from storage.
-
#count(object_class : Class) : Int32
Returns the count of objects for a given class.
-
#delete(class_name : String, id : String)
Deletes an object by class name and ID.
-
#delete(object : Serializable | Container)
Deletes an object from the filesystem.
-
#exists?(object_class : Class, id : String) : Bool
Checks if an object with the given ID exists.
-
#export_data : Hash(String, Array(Hash(String, String)))
Exports all data as a portable hash structure.
-
#import_data(data : Hash(String, Array(Hash(String, String))))
Imports data from an exported hash structure.
-
#list_all(object_class : Class) : Array(String)
Lists all object IDs for a given class.
-
#list_all_objects : Hash(String, Array(String))
Lists all objects grouped by class name.
-
#load(object_class : Class, id : String, path : String | Nil = nil) : Object
Loads an object from the filesystem.
-
#load_with_cache(object_class : Class, id : String, path : String | Nil = nil) : Object
Loads an object with caching support.
-
#on_watcher_change(&block : Event -> )
Registers a callback to be called when external file changes are detected.
-
#path : String
Root directory path where objects are stored.
-
#path=(path : String)
Root directory path where objects are stored.
-
#save(object : Serializable, path : String | Nil = nil)
Saves a Serializable object to the filesystem.
-
#save(object : Container, path : String | Nil = nil)
Saves a Container object to the filesystem.
-
#start_watcher : Bool
Starts the file system watcher if it's not already running.
-
#stop_watcher : Bool
Stops the file system watcher if it's running.
- #watcher : Watcher | Nil
-
#watcher_enabled? : Bool
Configuration for watcher creation.
-
#watcher_running? : Bool
Checks if the file system watcher is currently running.
Instance methods inherited from class Sepia::StorageBackend
clear
clear,
count(object_class : Class) : Int32
count,
delete(class_name : String, id : String)delete(object : Serializable | Container) delete, exists?(object_class : Class, id : String) : Bool exists?, export_data : Hash(String, Array(Hash(String, String))) export_data, import_data(data : Hash(String, Array(Hash(String, String)))) import_data, list_all(object_class : Class) : Array(String) list_all, list_all_objects : Hash(String, Array(String)) list_all_objects, load(object_class : Class, id : String, path : String | Nil = nil) : Object load, save(object : Serializable, path : String | Nil = nil)
save(object : Container, path : String | Nil = nil) save
Constructor Detail
Creates a new FileStorage instance.
The #path parameter specifies the root directory where objects will be stored.
If not provided, uses the system's temporary directory.
The watch parameter enables automatic file system monitoring for external
changes. When enabled, the storage will automatically invalidate cache entries
when files are modified externally.
Parameters
- path : Root directory for object storage (default: system temp directory)
- watch : Watcher configuration (default: false - disabled)
false: Disable watchertrue: Enable watcher with default settingsHash: Custom watcher configuration options
Examples
# Use system temp directory
storage = FileStorage.new
# Use custom directory
storage = FileStorage.new("./data")
# Enable watcher with default settings
storage = FileStorage.new("./data", watch: true)
# Enable watcher with custom settings
storage = FileStorage.new("./data", watch: {
recursive: true,
latency: 0.1,
})
Instance Method Detail
Clears all objects from storage.
Removes the entire storage directory and recreates it. This permanently deletes all data - use with caution.
storage.clear # Deletes everything in the storage path
Returns the count of objects for a given class.
This is equivalent to list_all(object_class).size but may be
more efficient in some implementations.
count = storage.count(MyDocument)
puts "Found #{count} documents"
Deletes an object by class name and ID.
Alternative method to delete objects without loading them first. Requires knowing whether the class is a Container or Serializable type.
# Delete without loading the object
storage.delete("MyDocument", "doc-uuid")
storage.delete("MyBoard", "board-uuid")
Deletes an object from the filesystem.
Removes the object's file or directory. For Container objects, recursively removes the entire directory structure including all nested objects.
doc = MyDocument.load("doc-uuid")
storage.delete(doc) # Removes the file
board = Board.load("board-uuid")
storage.delete(board) # Removes the directory and all contents
Checks if an object with the given ID exists.
For Serializable objects, checks if the file exists. For Container objects, checks if the directory exists.
if storage.exists?(MyDocument, "doc-uuid")
puts "Document exists"
end
Exports all data as a portable hash structure.
Returns a hash where keys are class names and values are arrays of object data. Each object includes its ID and either its content (for Serializable objects) or a container type marker.
Useful for backing up or migrating data between storage backends.
data = storage.export_data
# data = {
# "MyDocument" => [
# {"id" => "doc1", "content" => "Hello"},
# {"id" => "doc2", "content" => "World"}
# ],
# "MyBoard" => [
# {"id" => "board1", "type" => "container"}
# ]
# }
Imports data from an exported hash structure.
Restores objects from the data structure created by #export_data.
Clears any existing data before importing.
Useful for restoring backups or migrating from another storage backend.
data = {
"MyDocument" => [
{"id" => "doc1", "content" => "Hello"},
],
}
storage.import_data(data)
Lists all object IDs for a given class.
Returns an array of all object IDs found in the class directory. The IDs are sorted alphabetically.
ids = storage.list_all(MyDocument)
ids # => ["doc-uuid1", "doc-uuid2", "doc-uuid3"]
Lists all objects grouped by class name.
Returns a hash where keys are class names and values are arrays of object IDs for that class. This provides a complete inventory of all objects in storage.
Useful for administrative purposes, data migration, or debugging.
all_objects = storage.list_all_objects
# all_objects = {
# "MyDocument" => ["doc1", "doc2"],
# "MyBoard" => ["board1"],
# "User" => ["user1", "user2", "user3"]
# }
Loads an object from the filesystem.
Deserializes an object of the specified class with the given ID.
For Serializable objects, reads the file content and uses the class's
from_sepia method. For Container objects, reconstructs the object
from its directory structure.
Raises an exception if the object is not found.
# Load a Serializable object
doc = storage.load(MyDocument, "doc-uuid")
# Load a Container object
board = storage.load(MyBoard, "board-uuid")
Loads an object with caching support.
Similar to the regular #load method but integrates with the storage
caching system for better performance when loading the same objects
multiple times.
Parameters
- object_class : The class of object to load
- id : The object's unique identifier
- path : Optional custom load path
Returns
The loaded object.
Example
# Load with caching (faster for repeated loads)
doc1 = storage.load_with_cache(MyDocument, "doc-123")
doc2 = storage.load_with_cache(MyDocument, "doc-123") # Returns cached instance
Registers a callback to be called when external file changes are detected.
This allows users to add custom logic in addition to automatic cache invalidation.
Parameters
- &block : Callback block that receives Event objects
Example
storage.on_watcher_change do |event|
puts "External change: #{event.type} #{event.object_class}:#{event.object_id}"
end
Root directory path where objects are stored.
Default is the system's temporary directory. Can be set to any absolute path.
storage = FileStorage.new
storage.path # => "/tmp"
# Custom path
storage = FileStorage.new("./my_data")
storage.path # => "./my_data"
Root directory path where objects are stored.
Default is the system's temporary directory. Can be set to any absolute path.
storage = FileStorage.new
storage.path # => "/tmp"
# Custom path
storage = FileStorage.new("./my_data")
storage.path # => "./my_data"
Saves a Serializable object to the filesystem.
Writes the object's serialized content to a file. Creates any necessary parent directories. Uses atomic write operations to prevent corruption.
The object is saved to path/class_name/sepia_id if no specific path is provided.
Atomic Writes
The file is first written to a temporary file (with .tmp extension), then atomically renamed to the final path. This prevents partial writes and ensures data integrity.
doc = MyDocument.new("Hello")
storage = FileStorage.new("./data")
storage.save(doc) # Creates ./data/MyDocument/uuid
Saves a Container object to the filesystem.
Creates a directory for the container and saves all nested objects and references. The container's primitive properties are saved to a data.json file, while nested Sepia objects are saved as files or symlinks.
board = Board.new("My Board")
storage = FileStorage.new("./data")
storage.save(board) # Creates ./data/Board/uuid/
Starts the file system watcher if it's not already running.
Returns
true if watcher was started, false if already running or not configured
Example
storage.start_watcher
puts "Watcher running: #{storage.watcher_running?}"
Stops the file system watcher if it's running.
Returns
true if watcher was stopped, false if not running
Example
storage.stop_watcher
puts "Watcher running: #{storage.watcher_running?}"
Configuration for watcher creation.
Stores the watcher configuration for later use or recreation.
storage = FileStorage.new("./data", watch: true)
storage.watcher_enabled? # => true
Checks if the file system watcher is currently running.
Returns
true if watcher is running, false otherwise
Example
storage = FileStorage.new("./data", watch: true)
puts storage.watcher_running? # => true