typeregistry

package module
v0.0.0-...-7399302 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 10, 2015 License: MIT Imports: 2 Imported by: 0

README

TypeRegistry

Build Status GoDoc

TypeRegistry is a generic system to instantiate types by name. Since go cannot instantiate a type directly, we must first register any type that we would later like to instantiate. The registry handles these mechanics for you.

In addition, the registry supports marshal, unmarshal, and custom setup for getting objects in and out of storage.

Example

To use, create a registry, then register add any types to it. Make sure to add the pointer receiver if that's what you want to instantiate.

// The type you'd like to instantiate by name.
type simpleThing struct { }

// A registry.
registry := typeregistry.New()

// Register a type, this one by pointer receiver.
name := registry.Add(&simpleThing{})

// Get a *simpleThing
thing := registry.New(name)

See this example file for more detailed examples of marshal, unmarshal and custom setup.

Common Usage

A common pattern for using this library is as a global handler to marshal/unmarshal a specific type. Here, implementations of a fictional Conversation type can be registered and then pulled in and out of storage formats. The package-level wrapper functions perform the typecasting needed to keep things simple for users.

package main

import "github.com/rcarver/typeregistry"

// Conversation is implemented many different ways.
type Conversation interface {
  Talk()
}

var registry = typeregistry.New()

// Register adds a new type to the conversation registry.
func Register(c Conversation) {
	registry.Add(c)
}

// Marshal encodes the conversation, returning its registered name, and the
// encoded bytes.
func Marshal(c Conversation) (string, []byte, error) {
	return registry.Marshal(c)
}

// ConvoSetupFunc is used to setup a conversation before it's unmarshaled.
type ConvoSetupFunc func(Conversation)

// Unmarshal decodes a conversation.
func Unmarshal(name string, data []byte, setup ConvoSetupFunc) (Conversation, error) {
	o, err := registry.Unmarshal(name, data, func(o interface{}) {
		if setup != nil {
			setup(o.(Conversation))
		}
	})
	if err == nil {
		return o.(Conversation), nil
	}
	return nil, err
}

Author

Ryan Carver (ryan@ryancarver.com)

License

MIT

Documentation

Overview

Package typeregistry is a generic system to instantiate types by name. Since go cannot instantiate a type directly, we must first register any type that we would later like to instantiate. The registry handles these mechanics for you.

In addition, the registry supports marshal, unmarshal, and custom setup injection for getting objects in and out of storage.

Marshaling an object results in the registered name of the type, plus byte data if the type implements Marshaler.

Unmarshaling performs the reverse operation, first instantiating the type by name, then using Unmarshaler (if implemented) to populate the object (note that the type should probably be a pointer reciever for this to be useful). If the object requires collaborators, or data from the outside world then a function can be passed to Unmarshal that receives the object after it's instantiated and before it's unmarshaled.

Index

Examples

Constants

This section is empty.

Variables

View Source
var NoSetup = func(i interface{}) {}

NoSetup is a SetupFunc that does nothing. It is functionally equivalent to passing nil, but it's more descriptive so please do.

Functions

This section is empty.

Types

type Marshaler

type Marshaler interface {
	Marshal() ([]byte, error)
}

Marshaler is implemented by any type that can encode a copy of itself. The style of encoding doesn't matter, it will only be seen by Unmarshaler.

type SetupFunc

type SetupFunc func(interface{})

SetupFunc is passed to Unmarshal to manually manipulate the object after it's instantiated, but before it's unmarshaled. This can be used to set dependencies that are needed during unmarshal. For example, to covert a user's ID to a user object.

type TypeRegistry

type TypeRegistry map[string]reflect.Type

TypeRegistry can instantiate, marshal, and unmarshal types from string names and type-defined encodings.

func New

func New() TypeRegistry

New initializes an empty TypeRegistry.

func (TypeRegistry) Add

func (r TypeRegistry) Add(o interface{}) string

Add puts a new type in the registry. If the type cannot be registered, it panics. It returns the name that it was registered as.

func (TypeRegistry) Marshal

func (r TypeRegistry) Marshal(o interface{}) (string, []byte, error)

Marshal encodes a type. If the type implements Marshaler or its bytes are returned.

func (TypeRegistry) New

func (r TypeRegistry) New(name string) interface{}

New instantiates a type by name. If the name is unknown, it panics.

Example
package main

import (
	"fmt"

	"github.com/rcarver/typeregistry"
)

type simpleThing struct {
	Name string
}

func main() {
	registry := typeregistry.New()
	name := registry.Add(simpleThing{})
	thing := registry.New(name)
	fmt.Printf("%#v", thing)
}
Output:

typeregistry_test.simpleThing{Name:""}

func (TypeRegistry) Unmarshal

func (r TypeRegistry) Unmarshal(name string, data []byte, setup SetupFunc) (interface{}, error)

Unmarshal decodes a type by name. If the type implements Unmarshaler, the data is used to unmarshal. SetupFunc can be passed to inject any other data into the type before it is unmarshaled.

Example
package main

import (
	"encoding/json"
	"fmt"

	"github.com/rcarver/typeregistry"
)

// User is your typical user struct.
type User struct {
	ID   string
	Name string
}

// UserService is your typical service backend to retrieve users.
type UserService struct {
	users []*User
}

// FindUser returns a user by id.
func (s *UserService) FindUser(id string) *User {
	for _, u := range s.users {
		if u.ID == id {
			return u
		}
	}
	return nil
}

// userThing is a sample struct that can marshal/unmarshal itself and requires
// a service collaborator when doing so.
type userThing struct {
	UserID string
	user   *User
	svc    *UserService
}

func (t *userThing) Marshal() ([]byte, error) {
	// We store the ID when marshaling so we can look it up fresh when
	// unmarshaled.
	t.UserID = t.user.ID
	return json.Marshal(t)
}

func (t *userThing) Unmarshal(data []byte) error {
	// Unmarshal, getting the UserID.
	if err := json.Unmarshal(data, t); err != nil {
		return err
	}
	// Use the UserID and injected service to restore the user.
	t.user = t.svc.FindUser(t.UserID)
	return nil
}

func main() {
	registry := typeregistry.New()
	registry.Add(&userThing{})

	// The service world.
	var (
		ryan   = &User{"1", "Ryan"}
		svc    = &UserService{[]*User{ryan}}
		sample = &userThing{user: ryan}
	)

	// Pass between marshal and unmarshal.
	var (
		name string
		data []byte
	)

	// Marshal it.
	func() {
		var err error
		name, data, err = registry.Marshal(sample)
		if err != nil {
			panic(err)
		}
		fmt.Printf("Sample marshaled name:%s data:%s\n", name, data)
	}()

	// Unmarshal it with custom setup.
	func() {
		si, err := registry.Unmarshal(name, data, func(o interface{}) {
			if s, ok := o.(*userThing); ok {
				s.svc = svc
			}
		})
		if err != nil {
			panic(err)
		}
		sample := si.(*userThing)
		fmt.Printf("Sample unmarshaled with ID:%s, Name:%s\n", sample.UserID, sample.user.Name)
	}()
}
Output:

Sample marshaled name:*typeregistry_test.userThing data:{"UserID":"1"}
Sample unmarshaled with ID:1, Name:Ryan

type Unmarshaler

type Unmarshaler interface {
	Unmarshal([]byte) error
}

Unmarshaler is implemented by any type that can decode of a copy of itself, as returned by its Marshal method.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL