regula

package module
v0.6.2 Latest Latest
Warning

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

Go to latest
Published: Sep 11, 2019 License: MIT Imports: 9 Imported by: 0

README

Regula

Build Status ReadTheDocs GoDoc

Regula is an open source Business Rules Engine solution.

Please note that Regula is an experiment and that the API is currently considered unstable.

Documentation

Comprehensive documentation is viewable on Read the Docs:

https://regula.readthedocs.io/en/latest/

Documentation

Overview

Package regula provides a rules engine implementation.

Usage of this package revolves around the concept of rulesets.

A ruleset can be represented as a list of rules that can be evaluated against a set of parameters given by a caller. Each rule is evaluated in order and if one matches with the given parameters it returns a result and the evaluation stops. All the rules of a ruleset always return the same type.

rs, err := regula.NewStringRuleset(
	rule.New(
		rule.Eq(
			rule.StringParam("group"),
			rule.StringValue("admin"),
		),
		rule.StringValue("first rule matched"),
	),
	rule.New(
		rule.In(
			rule.Int64Param("score"),
			rule.Int64Value(10),
			rule.Int64Value(20),
			rule.Int64Value(30),
		),
		rule.StringValue("second rule matched"),
	),
	rule.New(
		rule.True(),
		rule.StringValue("default rule matched"),
	),
)
if err != nil {
	log.Fatal(err)
}

ret, err := rs.Eval(regula.Params{
	"group": "staff",
	"score": int64(20),
})

To query and evaluate rulesets with a set of parameters, the engine must be used. An engine takes an evaluator which is responsible of evaluating rulesets on demand and return a value, the engine then parses the value into a type safe result and return it to the caller.

While the evaluator is stateful and can hold rulesets in-memory, fetch them over the network or read them from a file, the engine is stateless and simply deleguates the evaluation to the evaluator.

engine := regula.NewEngine(evaluator)

s, res, err := engine.GetString("path/to/string/ruleset/key", regula.Params{
	"user-id": 123,
	"email": "example@provider.com",
})

i, res, err := engine.GetInt64("path/to/int/ruleset/key", regula.Params{
	"user-id": 123,
	"email": "example@provider.com",
})

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrTypeMismatch is returned when the evaluated rule doesn't return the expected result type.
	ErrTypeMismatch = errors.New("type returned by rule doesn't match")

	// ErrRulesetNotFound must be returned when no ruleset is found for a given key.
	ErrRulesetNotFound = errors.New("ruleset not found")

	// ErrRulesetIncoherentType is returned when a ruleset contains rules of different types.
	ErrRulesetIncoherentType = errors.New("types in ruleset are incoherent")
)

Functions

This section is empty.

Types

type Engine

type Engine struct {
	// contains filtered or unexported fields
}

Engine is used to evaluate a ruleset against a group of parameters. It provides a list of type safe methods to evaluate a ruleset and always returns the expected type to the caller. The engine is stateless and relies on the given evaluator to evaluate a ruleset. It is safe for concurrent use.

Example
package main

import (
	"context"
	"fmt"

	"github.com/heetch/regula/rule"

	"github.com/heetch/regula"
)

var ev regula.Evaluator

func main() {
	engine := regula.NewEngine(ev)

	str, res, err := engine.GetString(context.Background(), "/a/b/c", regula.Params{
		"product-id": "1234",
		"user-id":    "5678",
	})

	if err != nil {
		switch err {
		case regula.ErrRulesetNotFound:
			// when the ruleset doesn't exist
		case regula.ErrTypeMismatch:
			// when the ruleset returns the bad type
		case rule.ErrNoMatch:
			// when the ruleset doesn't match
		default:
			// something unexpected happened
		}
	}

	fmt.Println(str)
	fmt.Println(res.Version)
	// Output
	// some-string
	// 5b4cbdf307bb5346a6c42ac3
}
Output:

func NewEngine

func NewEngine(evaluator Evaluator) *Engine

NewEngine creates an Engine using the given evaluator.

func (*Engine) GetBool

func (e *Engine) GetBool(ctx context.Context, path string, params rule.Params, opts ...Option) (bool, *EvalResult, error)

GetBool evaluates a ruleset and returns the result as a bool.

Example
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/heetch/regula"
)

var ev regula.Evaluator

func main() {
	engine := regula.NewEngine(ev)

	b, res, err := engine.GetBool(context.Background(), "/path/to/bool/key", regula.Params{
		"product-id": "1234",
		"user-id":    "5678",
	})

	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(b)
	fmt.Println(res.Version)
	// Output
	// true
	// 5b4cbdf307bb5346a6c42ac3
}
Output:

func (*Engine) GetFloat64

func (e *Engine) GetFloat64(ctx context.Context, path string, params rule.Params, opts ...Option) (float64, *EvalResult, error)

GetFloat64 evaluates a ruleset and returns the result as a float64.

Example
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/heetch/regula"
)

var ev regula.Evaluator

func main() {
	engine := regula.NewEngine(ev)

	f, res, err := engine.GetFloat64(context.Background(), "/path/to/float64/key", regula.Params{
		"product-id": "1234",
		"user-id":    "5678",
	})

	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(f)
	fmt.Println(res.Version)
	// Output
	// 3.14
	// 5b4cbdf307bb5346a6c42ac3
}
Output:

func (*Engine) GetInt64

func (e *Engine) GetInt64(ctx context.Context, path string, params rule.Params, opts ...Option) (int64, *EvalResult, error)

GetInt64 evaluates a ruleset and returns the result as an int64.

Example
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/heetch/regula"
)

var ev regula.Evaluator

func main() {
	engine := regula.NewEngine(ev)

	i, res, err := engine.GetInt64(context.Background(), "/path/to/int64/key", regula.Params{
		"product-id": "1234",
		"user-id":    "5678",
	})

	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(i)
	fmt.Println(res.Version)
	// Output
	// 10
	// 5b4cbdf307bb5346a6c42ac3
}
Output:

func (*Engine) GetString

func (e *Engine) GetString(ctx context.Context, path string, params rule.Params, opts ...Option) (string, *EvalResult, error)

GetString evaluates a ruleset and returns the result as a string.

Example
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/heetch/regula"
)

var ev regula.Evaluator

func main() {
	engine := regula.NewEngine(ev)

	s, res, err := engine.GetString(context.Background(), "/path/to/string/key", regula.Params{
		"product-id": "1234",
		"user-id":    "5678",
	})

	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(s)
	fmt.Println(res.Version)
	// Output
	// some-string
	// 5b4cbdf307bb5346a6c42ac3
}
Output:

func (*Engine) LoadStruct

func (e *Engine) LoadStruct(ctx context.Context, to interface{}, params rule.Params) error

LoadStruct takes a pointer to struct and params and loads rulesets into fields tagged with the "ruleset" struct tag.

Example
package main

import (
	"context"
	"fmt"
	"log"
	"time"

	"github.com/heetch/regula"
)

var ev regula.Evaluator

func main() {
	type Values struct {
		A string        `ruleset:"/path/to/string/key"`
		B int64         `ruleset:"/path/to/int64/key,required"`
		C time.Duration `ruleset:"/path/to/duration/key"`
	}

	var v Values

	engine := regula.NewEngine(ev)

	err := engine.LoadStruct(context.Background(), &v, regula.Params{
		"product-id": "1234",
		"user-id":    "5678",
	})

	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(v.A)
	fmt.Println(v.B)
	fmt.Println(v.C)
}
Output:

some-string
10
3s

type EvalResult

type EvalResult struct {
	// Result of the evaluation
	Value *rule.Value
	// Version of the ruleset that generated this value
	Version string
}

EvalResult is the product of an evaluation. It contains the value generated as long as some metadata.

type Evaluator

type Evaluator interface {
	// Eval evaluates a ruleset using the given params.
	// If no ruleset is found for a given path, the implementation must return ErrRulesetNotFound.
	Eval(ctx context.Context, path string, params rule.Params) (*EvalResult, error)
	// EvalVersion evaluates a specific version of a ruleset using the given params.
	// If no ruleset is found for a given path, the implementation must return ErrRulesetNotFound.
	EvalVersion(ctx context.Context, path string, version string, params rule.Params) (*EvalResult, error)
}

An Evaluator provides methods to evaluate rulesets from any location. Long running implementations must listen to the given context for timeout and cancelation.

type Option

type Option func(cfg *engineConfig)

Option is used to customize the engine behaviour.

func Version

func Version(version string) Option

Version is an option used to describe which ruleset version the engine should return.

type Params

type Params map[string]interface{}

Params is a map based rule.Params implementation.

func (Params) EncodeValue

func (p Params) EncodeValue(key string) (string, error)

EncodeValue returns the string representation of the selected value.

func (Params) GetBool

func (p Params) GetBool(key string) (bool, error)

GetBool extracts a bool parameter corresponding to the given key.

func (Params) GetFloat64

func (p Params) GetFloat64(key string) (float64, error)

GetFloat64 extracts a float64 parameter corresponding to the given key.

func (Params) GetInt64

func (p Params) GetInt64(key string) (int64, error)

GetInt64 extracts an int64 parameter corresponding to the given key.

func (Params) GetString

func (p Params) GetString(key string) (string, error)

GetString extracts a string parameter corresponding to the given key.

func (Params) Keys

func (p Params) Keys() []string

Keys returns the list of all the keys.

type Ruleset

type Ruleset struct {
	Rules []*rule.Rule `json:"rules"`
	Type  string       `json:"type"`
}

A Ruleset is list of rules that must return the same type.

Example
package main

import (
	"fmt"
	"log"

	"github.com/heetch/regula/rule"

	"github.com/heetch/regula"
)

func main() {
	rs, err := regula.NewStringRuleset(
		rule.New(
			rule.Eq(
				rule.StringParam("group"),
				rule.StringValue("admin"),
			),
			rule.StringValue("first rule matched"),
		),
		rule.New(
			rule.In(
				rule.Int64Param("score"),
				rule.Int64Value(10),
				rule.Int64Value(20),
				rule.Int64Value(30),
			),
			rule.StringValue("second rule matched"),
		),
		rule.New(
			rule.True(),
			rule.StringValue("default rule matched"),
		),
	)
	if err != nil {
		log.Fatal(err)
	}

	ret, err := rs.Eval(regula.Params{
		"group": "staff",
		"score": int64(20),
	})
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(ret.Data)
	// Output
	// second rule matched
}
Output:

func NewBoolRuleset

func NewBoolRuleset(rules ...*rule.Rule) (*Ruleset, error)

NewBoolRuleset creates a ruleset which rules all return a bool otherwise ErrRulesetIncoherentType is returned.

func NewFloat64Ruleset

func NewFloat64Ruleset(rules ...*rule.Rule) (*Ruleset, error)

NewFloat64Ruleset creates a ruleset which rules all return an float64 otherwise ErrRulesetIncoherentType is returned.

func NewInt64Ruleset

func NewInt64Ruleset(rules ...*rule.Rule) (*Ruleset, error)

NewInt64Ruleset creates a ruleset which rules all return an int64 otherwise ErrRulesetIncoherentType is returned.

func NewStringRuleset

func NewStringRuleset(rules ...*rule.Rule) (*Ruleset, error)

NewStringRuleset creates a ruleset which rules all return a string otherwise ErrRulesetIncoherentType is returned.

func (*Ruleset) Eval

func (r *Ruleset) Eval(params rule.Params) (*rule.Value, error)

Eval evaluates every rule of the ruleset until one matches. It returns rule.ErrNoMatch if no rule matches the given context.

func (*Ruleset) Params

func (r *Ruleset) Params() []rule.Param

Params returns a list of all the parameters used in all the underlying rules.

func (*Ruleset) UnmarshalJSON

func (r *Ruleset) UnmarshalJSON(data []byte) error

UnmarshalJSON implements the json.Unmarshaler interface.

type RulesetBuffer

type RulesetBuffer struct {
	// contains filtered or unexported fields
}

RulesetBuffer can hold a group of rulesets in memory and can be used as an evaluator. It is safe for concurrent use.

func NewRulesetBuffer

func NewRulesetBuffer() *RulesetBuffer

NewRulesetBuffer creates a ready to use RulesetBuffer.

func (*RulesetBuffer) Add

func (b *RulesetBuffer) Add(path, version string, r *Ruleset)

Add adds the given ruleset version to a list for a specific path. The last added ruleset is treated as the latest version.

func (*RulesetBuffer) Eval

func (b *RulesetBuffer) Eval(ctx context.Context, path string, params rule.Params) (*EvalResult, error)

Eval evaluates the latest added ruleset or returns ErrRulesetNotFound if not found.

func (*RulesetBuffer) EvalVersion

func (b *RulesetBuffer) EvalVersion(ctx context.Context, path, version string, params rule.Params) (*EvalResult, error)

EvalVersion evaluates the selected ruleset version or returns ErrRulesetNotFound if not found.

func (*RulesetBuffer) GetVersion

func (b *RulesetBuffer) GetVersion(path, version string) (*Ruleset, error)

GetVersion returns a ruleset associated with the given path and version.

func (*RulesetBuffer) Latest

func (b *RulesetBuffer) Latest(path string) (*Ruleset, string, error)

Latest returns the latest version of a ruleset.

Directories

Path Synopsis
api
cmd
Package version contains the version of Regula.
Package version contains the version of Regula.

Jump to

Keyboard shortcuts

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