Go-Shelve is a dependencies-free Go package that provides a persistent, map-like
object called Shelf
. It lets you store and retrieve Go objects directly, with
the serialization and storage handled automatically by the Shelf
. Additionally,
you can customize the underlying key-value storage and serialization Codec to
better suit your application's needs.
This project is inspired by the shelve
module from the Python standard
library and aims to provide a similar set of functionalities.
Check the driver's directory for additional storage and Codec options.
To use this package in your Go project, you can install it using go get
:
go get github.com/lucmq/go-shelve
Here are some examples of how to use go-shelve
:
The following example illustrates the usage of a Shelf
with string keys and
values. The Shelf
type uses generics and can be instantiated with any Go type
as a value and any comparable type for the key.
package main
import (
"fmt"
"log"
"os"
"path/filepath"
"github.com/lucmq/go-shelve/shelve"
)
func main() {
// Note: In a real application be sure to replace the
// os.TempDir() with a non temporary directory and
// provide better error treatment.
path := filepath.Join(os.TempDir(), "go-shelve")
// Open the shelf with default options
shelf, err := shelve.Open[string, string](path)
if err != nil {
log.Fatal(err)
}
defer shelf.Close()
// Use the database
shelf.Put("language", "Go")
shelf.Put("module", "Go-Shelve")
// Note: Saved values will be available between restarts
value, ok, _ := shelf.Get("language")
fmt.Println(value, ok)
// Output: Go true
}
By default, a Shelf
serializes data using the Gob format and stores it using
sdb
(for "shelve-db"), a simple key-value storage created for this project.
This database should be suitable for a wide range of applications, but the
driver's directory provides additional options for
configuring a Shelf
with other databases from the Go ecosystem.
The example below shows how to customize a Shelf
to use BoltDB
for storage
together with MessagePack
for serialization:
package main
import (
"fmt"
"log"
"os"
"path/filepath"
bboltd "github.com/lucmq/go-shelve/driver/db/bbolt"
"github.com/lucmq/go-shelve/driver/encoding/msgpack"
"github.com/lucmq/go-shelve/shelve"
)
func main() {
path := filepath.Join(os.TempDir(), "bolt-example")
db, _ := bboltd.NewDefault(path, []byte("example-bucket"))
codec := msgpack.NewDefault()
// Open the shelf with custom options
shelf, err := shelve.Open[string, string](
path,
shelve.WithDatabase(db),
shelve.WithCodec(codec),
)
if err != nil {
log.Fatal(err)
}
defer shelf.Close()
// Use the database
shelf.Put("language", "Go")
shelf.Put("module", "Go-Shelve")
value, ok, _ := shelf.Get("language")
fmt.Println(value, ok)
// Output: Go true
}
An interesting use case for Shelf
is storing data in files that can be read
transparently with the JSON
format, each named by a semantically meaningful
key. This can be used to save configuration and application data as
human-readable files.
The diskv
driver in driver/db/diskv
provides a
database driver that uses a key-value store based on a file-per-record design
that would suit this purpose.
The example below provides a simple illustration of how this could be done to save data for an imaginary game:
package main
import (
"fmt"
"os"
"path/filepath"
diskvd "github.com/lucmq/go-shelve/driver/db/diskv"
"github.com/lucmq/go-shelve/shelve"
)
var StoragePath = filepath.Join(os.TempDir(), "game-test", "db")
type Player struct {
Name string
Level int
Gold int
Items []string
}
type Config struct {
Difficulty string
}
// NewShelf creates a customized Shelf using Diskv and JSON.
func NewShelf[V any](path string) (*shelve.Shelf[string, V], error) {
path = filepath.Join(StoragePath, path)
extension := "json" // Extension of the record files
db, err := diskvd.NewDefault(path, extension)
if err != nil {
return nil, err
}
return shelve.Open[string, V](
path,
shelve.WithDatabase(db),
shelve.WithCodec(shelve.JSONCodec()),
)
}
func main() {
// Open the shelf with custom options
players, _ := NewShelf[Player]("players")
config, _ := NewShelf[Config]("config")
defer players.Close()
defer config.Close()
// Create the game data
player := Player{
Name: "Frodo",
Level: 14,
Gold: 9999,
Items: []string{"Sting", "Lembas"},
}
cfg := Config{
Difficulty: "Hard",
}
// Save the data. Serialization and persistence will be
// handled automatically by the Shelf.
players.Put(player.Name, player)
config.Put("config", cfg)
// The app storage will contain readable JSON files with
// configuration and game state, that can be retrieved
// back to a Go type:
value, ok, _ := players.Get("Frodo")
fmt.Println(ok, value.Name, value.Items)
// Output: true Frodo [Sting Lembas]
}
Contributions to this package are welcome! If you find any issues or have suggestions for improvements, please feel free to open an issue or submit a pull request.
In particular, if you are interested in contributing a driver for a key-value storage or a encoding format, check this guideline and wishlist.
This project is licensed under the MIT License - see the LICENSE file for details.