Save and load rich data

The concept of coder

Though the most basic data type in Ring is bytes which is very common among various storages, modern services handle more complicated data types.

Ring factory has coder layer - which provides a customizable interface to encode before saving and to decode after loading. For example, let’s say our function f returns float type but we only have bytes storage. This is a demonstration without Ring.

def f():
    return 3.1415

# straightforward way
storage = {}  # suppose this is bytes-only db
result = f()  # run function
# set
encoded = str(result).encode('utf-8')  # encoding: to bytes
storage.set('key', encoded)
# get
encoded = storage.get('key')
decoded = float(encoded.decode('utf-8'))  # decoding: to float

assert result == decoded

You see encoding and decoding steps. The pair of them is called coder in Ring.

Pre-registered coders

Ring is distributed with a few pre-registered coders which are common in modern Python world.

ring.coder.bypass_coder Default coder.
ring.coder.JsonCoder JSON Coder.
ring.coder.pickle_coder Pickle coder.
ring.coder.DataclassCoder
see:ring.coder for the module including pre-registered coders.

Create a new coder

Users can register new custom coders with aliases.

Related coder types:

Registry:

For example, the float example above can be written as a coder like below:

class FloatCoder(ring.coder.Coder):

    def encode(self, value):
        return str(value).encode('utf-8')

    def decode(self, data):
        return float(data.decode('utf-8'))


ring.coder.registry.register('float', FloatCoder())

Now FloatCoder is registered as float. Use it in a familiar way.

@ring.lru(coder='float')
def f():
    return 3.1415
note:coder parameter of factories only take one of the registered names of coders and actual ring.coder.Coder objects. On the other hands, ring.coder.Registry.register() take raw materials of ring.coder.Coder or ring.coder.CoderTuple. See ring.coder.coderize() for details.

Override a coder

Sometimes coder is not a reusable part of the code. Do not create coders for single use. Instead of it, you can redefine encode and decode function of a ring object.

@ring.lru()
def f():
    return 3.1415

@f.ring.encode
def f_encode(value):
    return str(value).encode('utf-8')

@f.ring.decode
def f_decode(value):
    return float(data.decode('utf-8'))