mdd

package module
v0.12.2 Latest Latest
Warning

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

Go to latest
Published: Oct 11, 2023 License: AGPL-3.0 Imports: 8 Imported by: 5

README

[qb] Model Driven Development

👁 Go Reference Go Report Card

import "git.fractalqb.de/fractalqb/mdd" 

Documentation

Overview

Package mdd provides the metamodel for [qb] Model Driven Development. [qb]MDD focuses on creating a precise data model of the problem domain. Implementation aspects should be considered later when the model is used for implementation. Dynamic aspects are not the subject of [qb]MDD. It stems from quite some years of experience with model driven software development. The roots certainly lie in the collaboration with the brilliant minds behind the work on the long-ago code generation framework XCoder. The core concepts are:

Small but useful type system:

  • Atom – Opaque type from which other types are created

  • Enum – Just a set of distinct names

  • Class – Similar to what you know from e.g. UML

The Model represents your universe – the scope you are working on.

A Package groups closely related parts of your universe. Packages enforce a DAG on package dependencies for a clean architecture. Dependencies are derived from your model.

An Association represents a set of links between objects. If classes are the players on your team, then associations are the team's line-up for the next match. They are not part of a class. Associations organize classes to play together well in your use cases.

The only generator that comes with this Go module is and remains a diagram generator in the puml package, which is currently targeted for PlantUML.

Inheritance

Without behaviour, this is the inheritance rule:

Each object of a subclass must be a valid instance of each base class i.e.,
the object restricted to the set of properties of that base class must be in
that base class.

This rule comes with consequences:

  • Redefining attributes requires covariance, i.e. the value domain of a redefining attribute in a subclass must be a subset of the redefined attribute's value domain.

Inheritance comes with (at least) one incredible ability: If you have important parts in your system that work well with a general concept, you can transparently introduce new special cases without touching those general parts - let alone breaking general parts. As the rule above shows, for pure data structures inheritance can be boiled down to set theory and algebra. If you take behaviour into account it gets far more complex. Be aware of the LSP!

Unfortunately, turning the abstract idea of inheritance into implementations requires experience, good design, and an awareness that you are minting something that should last for some time.

One final caveat, if inheritance is new to you: Inheritance is too often characterized as an "is-a" relationship. If you take the statement "7 is a number", you are already on the wrong track! Learn to distinguish inheritance and instantiation! In other words if you focus on structure only:

7 ∊ ℕ ~ Instantiation ≠ {7} ⊂ ℕ ~ Inheritance 😉

Multiple Inheritance

[qb]MDD is intended to model the terms of the subject domain, not to prescribe artefacts in programming. Here, multiple inheritance can be the most suitable means of expression for a situation. Therefore, multiple inheritance is supported. It is not encouraged to use multiple inheritance imprudently. – It is not your new shiny toy!

Whoever implements a generator for [qb]MDD is encouraged to support multiple inheritance.

With multiple inheritance, you have to explain how to handle ambiguities i.e. inheriting more than one property with the same name:

  • Ambiguities between Attributes and Association Ends are not allowed.

  • Ambiguous association ends are not allowed (yet).

  • Ambiguous attributes without conflict are accepted. Attributes are conflicting if their type or their multiplicity or both differ. (TODO: What about derived?)

  • If possible, conflicting ambiguous attributes are automatically redefined to match the inheritance rule.

  • Attribute order is determined via base class linearization. It is the concatenation of the list of all non-redefined attributes of the classes in the linearized class list.

  • Base class linearization: The class-list of classes without base classes is the class itself. For classes with base classes it's the linearization of the 1st base-class subsequently concatenated with the reduced linearization of the following bases plus the class itself. The reduced linearization of a subsequent base-class b is the linearization of b in which all elements that are already included in the linearization to be formed are removed.

Mixing in Classes

In the OOP world, classes and mixins are related concepts where mixins typically drop the substitutability requirement that applies to classes. Again, a need for mixins primarily arises when one tries to map the abstract idea of inheritance to an implementation. – Having something like mixins in [qb]MDD is a concession to practical considerations.

Anyway, if you remove the inheritance rule from [qb]MDD classes, the result – an “MDD mixin” – becomes a simple concept. Mixing classes into other classes is the same as adding all, including inherited, attributes of the mixin to the receiving class.

  • Mixed-in attributes can be overwritten – either explicitly or by subsequent mixins. Overwriting does neither check type nor multiplicity.

  • Explicitly defined attributes cannot be overwritten.

Maintainability, Mixins and Inheritance

On many projects, I've seen design decisions made with the only question in mind: "Will it work?" But the question "How much work will it be to change it?" is the more important guide in the long run, especially when it comes to inheritance vs. mixin. Not that I had a crystal ball that would allow me to see the future that it would take to make such decisions without error. But there is one question that should be asked!

First, remember that both inheritance and mixins are means of reuse. That is, things defined once in a mixin or base class will most likely be used in at least two different places. So, take a close look at your problem domain and ask yourself:

If I change this common definition, do I always want all uses of that
definition to change as well?

If your answer is "yes," you have found a structural similarity that is intentional - not by accident. These often correspond to a taxonomic hierarchy in your problem domain and can be implemented very well by inheritance. Purely structural reuse, as supported by mixins, is often motivated by a rather random structural match, in my experience. This bears the risk of causing unintended changes by changing the mixin. But neither inheritance nor mixins are a one-size-fits-all tools. Choose wisely!

Entities

Like UML, [qb]MDD supports entities in that a class can be marked to be an entity. In the context of [qb]MDD, entities can be thought of as complementary to keys. An entity can be identified as one and the same "thing" regardless of its keys, if any. I.e. even if all of its properties change, the entity remains the same. An entity cannot have Comparability == NotComparable.

Comparability, Keys and Collections

Each Type has a Comparability. It indicates whether it can be determined that two values of a type are Equal and whether it can be determined for each two values whether one is Less than the other. If neither of both is the case a type is NotComparable. If a type supports both, Equal and Less, the comparability Orders the type.

Comparability of Atom types is defined by the modeler. Enum types always support Equal and may be defined to also support Less when setting mdd.Enum.Ordered = true. For Class types things are more complicate. A class that represents an entity always supports Equal. For anything else one has to define keys for the class.

A Key can be used for determining whether and how objects of a class are compared with each other. A class can have more than one key defined. A key with an empty name is the default key of the class. A key also has a comparability that is determined from the properties of the class that are part of the key. A key that supports Equal can be defined as a global key, meaning that it uniquely identifies the objects of that class.

(TODO key-properties of class type -> class's key)

Collection properties, i.e. collections with a maximum multiplicity greater than one, can be defined as ordered or unique. For this, the comparability of the type of the property must support this. To make a collection unique the type must be Equal comparable. To make a collection ordered the type must be Less comparable.

Error Handling

[qb]MDD is intended for modelling as code. That is, it is expected to be used via the mmb package to create a model by writing Go programs. A simple error strategy is sufficient for this:

If an error occurs, one must consider the model to be broken and throw it away.

Index

Examples

Constants

View Source
const InvalidNameRunes = internal.NotInName

Variables

This section is empty.

Functions

func MultMaxCompare

func MultMaxCompare(m, n uint) int

Types

type AssocEnd

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

func (*AssocEnd) Class

func (e *AssocEnd) Class() *Class

func (*AssocEnd) Collection added in v0.10.0

func (p *AssocEnd) Collection() (Collection, *Key)

func (*AssocEnd) Counterpart

func (e *AssocEnd) Counterpart() *AssocEnd

func (*AssocEnd) Derived

func (p *AssocEnd) Derived() bool

func (*AssocEnd) FQName

func (e *AssocEnd) FQName() NamePath

func (*AssocEnd) Model added in v0.4.0

func (a *AssocEnd) Model() *Model

func (*AssocEnd) Mult

func (p *AssocEnd) Mult() Multiplicity

func (*AssocEnd) Name

func (p *AssocEnd) Name() Name

func (*AssocEnd) Owner

func (e *AssocEnd) Owner() ModelElement

func (*AssocEnd) OwningAssoc

func (e *AssocEnd) OwningAssoc() *Association

func (*AssocEnd) SetCollection added in v0.10.0

func (p *AssocEnd) SetCollection(coll Collection, key *Key) error

func (*AssocEnd) SetDerived

func (p *AssocEnd) SetDerived(flag bool) error

func (*AssocEnd) SetTag added in v0.5.0

func (e *AssocEnd) SetTag(key, tag any) error

func (*AssocEnd) String

func (p *AssocEnd) String() string

func (*AssocEnd) Type

func (p *AssocEnd) Type() Type

type Association

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

func Associate

func Associate(
	class0 *Class, role0 Name, mult0 Multiplicity,
	class1 *Class, role1 Name, mult1 Multiplicity,
	bidi bool,
	name Name,
) (*Association, error)

func (*Association) BiDi

func (a *Association) BiDi() bool

func (*Association) End

func (a *Association) End(n Name) (end *AssocEnd, ambiguous bool)

func (*Association) EndTo

func (a *Association) EndTo(c *Class) (end *AssocEnd, ambiguous bool)

func (*Association) Ends added in v0.8.0

func (a *Association) Ends() (aEnd, bEnd *AssocEnd)

func (*Association) FQName

func (a *Association) FQName() NamePath

func (*Association) IndexEnd

func (a *Association) IndexEnd(i EndIndex) *AssocEnd

func (*Association) Model

func (a *Association) Model() *Model

func (*Association) Name

func (a *Association) Name() Name

func (*Association) SetTag

func (a *Association) SetTag(key, tag any) error

func (*Association) Tag

func (t *Association) Tag(k any) any

func (*Association) Within

func (a *Association) Within() *Package

type Atom added in v0.8.0

type Atom struct {
	// contains filtered or unexported fields
}
  • Can be defined to be non-comparable, comparable or ordered → Keys.
  • Can be defined to be a restriction of another atom type → Check covariance when redefining attributes.

[mmb.AtomTypeLib] provides a collection of useful types.

func (*Atom) Compare added in v0.8.0

func (t *Atom) Compare(u Type) (bool, int)

func (*Atom) Compares added in v0.8.0

func (t *Atom) Compares() Comparability

func (*Atom) FQName added in v0.8.0

func (t *Atom) FQName() NamePath

func (*Atom) Lang added in v0.8.0

func (t *Atom) Lang(l string) any

func (*Atom) Model added in v0.8.0

func (t *Atom) Model() *Model

func (*Atom) Name added in v0.8.0

func (t *Atom) Name() Name

func (*Atom) Package added in v0.8.0

func (t *Atom) Package() *Package

func (*Atom) Restrict added in v0.8.0

func (t *Atom) Restrict(ts ...*Atom) error

func (*Atom) Restricts added in v0.8.0

func (t *Atom) Restricts(u *Atom) int

func (*Atom) SetLang added in v0.8.0

func (t *Atom) SetLang(lang string, mapped any)

func (*Atom) SetTag added in v0.8.0

func (t *Atom) SetTag(key, tag any) error

func (*Atom) String added in v0.8.0

func (t *Atom) String() string

func (*Atom) Use added in v0.8.0

func (t *Atom) Use() []Property

type Attribute

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

func (*Attribute) Collection added in v0.10.0

func (p *Attribute) Collection() (Collection, *Key)

func (*Attribute) Derived

func (p *Attribute) Derived() bool

func (*Attribute) FQName

func (a *Attribute) FQName() NamePath

func (*Attribute) Keys

func (a *Attribute) Keys() []*Key

func (*Attribute) Mixed added in v0.5.0

func (a *Attribute) Mixed() *Attribute

func (*Attribute) Model added in v0.4.0

func (a *Attribute) Model() *Model

func (*Attribute) Mult

func (p *Attribute) Mult() Multiplicity

func (*Attribute) Name

func (p *Attribute) Name() Name

func (*Attribute) Owner

func (a *Attribute) Owner() ModelElement

func (*Attribute) OwningClass

func (a *Attribute) OwningClass() *Class

func (*Attribute) Redefines added in v0.5.0

func (a *Attribute) Redefines() []*Attribute

func (*Attribute) SetCollection added in v0.10.0

func (p *Attribute) SetCollection(coll Collection, key *Key) error

func (*Attribute) SetDerived

func (a *Attribute) SetDerived(flag bool) error

func (*Attribute) SetTag added in v0.5.0

func (a *Attribute) SetTag(key, tag any) error

func (*Attribute) String

func (p *Attribute) String() string

func (*Attribute) Type

func (p *Attribute) Type() Type

type Class

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

Class represents concepts of your problem domain, not your programming language. Generators have to know how to make code from all this.

From [qb]MDD's perspective, classes do not have behavior – this is only about data's structure.

There is an edge case: Properties (Attributes or Association Ends) being derived/non-derived

func (*Class) Abstract added in v0.2.0

func (c *Class) Abstract() bool

func (*Class) AllAttributes

func (c *Class) AllAttributes() (atts []*Attribute)

AllAttributes returns a list of all attributes starting with class c and then going through the classes from c's BaseList. Redefined attributes are omitted.

func (*Class) AppendMixins added in v0.6.0

func (c *Class) AppendMixins(ms []*Class) []*Class

func (*Class) AppendOwnAttributes added in v0.5.0

func (c *Class) AppendOwnAttributes(to []*Attribute) []*Attribute

func (*Class) AppendProperties added in v0.4.0

func (c *Class) AppendProperties(to []Property) []Property

func (*Class) Associated

func (c *Class) Associated() []*AssocEnd

func (*Class) AttCompares added in v0.10.0

func (c *Class) AttCompares() Comparability

AttCompares returns the comparability of a class which is derived from the comparability of all attributes of the class.

func (*Class) Attributes

func (c *Class) Attributes() []*Attribute

func (*Class) BaseList

func (c *Class) BaseList() (ls []*Class)

BaseList returns the linearized list of unique base class from the “closest” to the most “distant” ancestors.

func (*Class) Bases

func (c *Class) Bases() []*Class

func (*Class) Compare

func (t *Class) Compare(u Type) (bool, int)

func (*Class) Compares

func (c *Class) Compares() Comparability

Compares returns the comparability the class's default key, if any.

func (*Class) Derived

func (c *Class) Derived() []*Class

func (*Class) EachProperty

func (c *Class) EachProperty(do func(Property) error) error

func (*Class) Entity

func (c *Class) Entity() bool

func (*Class) Extends added in v0.12.0

func (c *Class) Extends(t *Class) bool

func (*Class) FQName

func (t *Class) FQName() NamePath

func (*Class) HasMixedFrom added in v0.8.2

func (c *Class) HasMixedFrom(m *Class) *Attribute

func (*Class) HasMixin added in v0.6.0

func (c *Class) HasMixin(m *Class) bool

func (*Class) IsMixedInto added in v0.8.2

func (c *Class) IsMixedInto(m *Class) bool

func (*Class) Key

func (t *Class) Key(n Name) *Key

func (*Class) KeyString added in v0.11.0

func (t *Class) KeyString(s string) *Key

func (*Class) Keys

func (t *Class) Keys() []*Key

func (*Class) MixIn added in v0.5.0

func (c *Class) MixIn(m *Class, mode MixMode) error

func (*Class) MixedInto added in v0.8.2

func (c *Class) MixedInto() []*Class

func (*Class) Mixins added in v0.5.0

func (c *Class) Mixins() []*Class

func (*Class) Model

func (t *Class) Model() *Model

func (*Class) Name

func (t *Class) Name() Name

func (*Class) NewAttribute added in v0.2.0

func (c *Class) NewAttribute(name Name, typ Type, mult Multiplicity) (*Attribute, error)

func (*Class) NewKey added in v0.9.0

func (t *Class) NewKey(n Name, atts ...KeyElement) (*Key, error)

func (*Class) OwnAttributes added in v0.5.0

func (c *Class) OwnAttributes() []*Attribute

func (*Class) Package

func (t *Class) Package() *Package

func (*Class) Properties added in v0.4.0

func (c *Class) Properties() []Property

func (*Class) Property

func (c *Class) Property(name Name, inherited bool) (res Property)

func (*Class) PropertyString added in v0.12.0

func (c *Class) PropertyString(name string, inherited bool) Property

func (*Class) SetAbstract added in v0.2.0

func (c *Class) SetAbstract(flag bool) *Class

func (*Class) SetEntity added in v0.9.3

func (c *Class) SetEntity(flag bool) *Class

func (*Class) SetTag added in v0.5.0

func (c *Class) SetTag(key, tag any) error

func (*Class) String

func (t *Class) String() string

func (*Class) Use

func (t *Class) Use() []Property

type Collection added in v0.10.0

type Collection uint
const (
	Ordered Collection = (1 << iota)
	Unique
)

func (Collection) All added in v0.10.0

func (ct Collection) All(test Collection) bool

func (Collection) Any added in v0.10.0

func (ct Collection) Any(test Collection) bool

func (Collection) String added in v0.10.0

func (i Collection) String() string

type Comparability

type Comparability uint
const (
	// For each pair of values from a Equal type it is well defined if both
	// values are equal or not.
	Equal Comparability = (1 << iota)

	// For each pair of values from an Less type it is well defined if one
	// value is lesser than the other. That does not imply Equal:
	//   x ≮ y ∧ y ≮ x ⇏ x = y
	Less

	// The type supports neither Equal or Less.
	NotComparable Comparability = 0

	// The type supports Equal and Less.
	Orders = Equal | Less
)

func (Comparability) All added in v0.10.0

func (c Comparability) All(set Comparability) bool

func (Comparability) Any added in v0.10.0

func (c Comparability) Any(set Comparability) bool

func (Comparability) String

func (i Comparability) String() string

type DuplicateBaseError added in v0.9.0

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

func (DuplicateBaseError) Error added in v0.9.0

func (e DuplicateBaseError) Error() string

type EndIndex

type EndIndex int
const (
	EndA EndIndex = 0
	EndB EndIndex = 1
)

type Enum

type Enum struct {
	// In cases where the full set of values is not known until the runtime of
	// the target system, the enum type can be defined to be extensible. Often
	// such enums have an empty value set in the model. If your system relies on
	// some special, well-known enum values, those should be defined in the
	// model already.
	Extensible bool
	// If set to true, the enum is not only Equal comparable but it becomes
	// ordered. Values are then ordered by the order of definition in the model.
	Ordered bool
	// contains filtered or unexported fields
}

An enumeration is a set of distinct values that are identified by a Name and can be used to enumerate things. By definition enum's Comparability is never NotComparable.

func (*Enum) Compare

func (t *Enum) Compare(u Type) (bool, int)

func (*Enum) Compares

func (t *Enum) Compares() Comparability

func (*Enum) FQName

func (t *Enum) FQName() NamePath

func (*Enum) HasLiteral

func (t *Enum) HasLiteral(l Name) bool

func (*Enum) Literals

func (t *Enum) Literals() []Name

func (*Enum) Model

func (t *Enum) Model() *Model

func (*Enum) Name

func (t *Enum) Name() Name

func (*Enum) Package

func (t *Enum) Package() *Package

func (*Enum) SetTag added in v0.5.0

func (t *Enum) SetTag(key, tag any) error

func (*Enum) String

func (t *Enum) String() string

func (*Enum) Use

func (t *Enum) Use() []Property

type IncomparableAtom added in v0.8.0

type IncomparableAtom int
const (
	NoRestriction IncomparableAtom = 1
)

func (IncomparableAtom) String added in v0.8.0

func (i IncomparableAtom) String() string

type IncomparableClass added in v0.8.0

type IncomparableClass int
const UnrelatedClasses IncomparableClass = 1

func (IncomparableClass) String added in v0.8.0

func (i IncomparableClass) String() string

type IncomparableEnum added in v0.8.0

type IncomparableEnum int
const (
	DisjointLiteralSets IncomparableEnum = iota + 1
	ExtExceedSuperset
)

func (IncomparableEnum) String added in v0.8.0

func (i IncomparableEnum) String() string

type Key

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

func (*Key) Add

func (k *Key) Add(p Property, key *Key) error

Add adds an new attribute from the key's owning class to the key. If the attribute's type is a class one can select an attribute key.

func (*Key) Compares

func (k *Key) Compares() Comparability

func (*Key) Elem

func (k *Key) Elem(i int) (Property, *Key)

func (*Key) FQName added in v0.12.0

func (k *Key) FQName() NamePath

func (*Key) Global added in v0.10.0

func (k *Key) Global() bool

func (*Key) Name

func (k *Key) Name() Name

func (*Key) Owner added in v0.12.0

func (k *Key) Owner() *Class

func (*Key) Part

func (k *Key) Part(a Property) (int, *Key)

func (*Key) SetGlobal added in v0.10.0

func (k *Key) SetGlobal(f bool) error

func (*Key) Size

func (k *Key) Size() int

type KeyElement

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

type MixMode added in v0.6.0

type MixMode int

MixMode describes what mixing in classes does when a mixin finds an existing mixed-in attribute with the same name.

const (
	MixFails  MixMode = iota // Mixing in fails with error
	DropMix                  // New attributes are silently dropped
	Overwrite                // The new mixin overwrites the old mixin
)

type Model

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

The Model captures all Types of the modelled problem domain. A Model consists of packages that group the types in reusable units. The dependencies between the packages are checked to be acyclic. Trying to create a cycle results in errors.

The core classes from the mdd package provide everything needed to create models. However crating models with the core API can be tedious because of extensive error checking and somewhat clunky Name handling. I recommend the API of the package mmb (MDD Model Builder) for your daily work when creating models from Go code.

func NewModel

func NewModel(name Name) (*Model, error)

func (*Model) AppendPackages added in v0.4.0

func (m *Model) AppendPackages(to []*Package) []*Package

func (*Model) Associations

func (m *Model) Associations() []*Association

func (*Model) EachPackage

func (m *Model) EachPackage(do func(*Package) error) error

func (*Model) Name

func (m *Model) Name() Name

func (*Model) NewPackage

func (m *Model) NewPackage(name Name) (*Package, error)

func (*Model) Package

func (m *Model) Package(name Name) *Package

func (*Model) PackageString added in v0.6.0

func (m *Model) PackageString(name string) *Package

func (*Model) Packages

func (m *Model) Packages() []*Package

func (*Model) SetTag

func (m *Model) SetTag(key, tag any) error

func (*Model) String

func (m *Model) String() string

func (*Model) Tag

func (t *Model) Tag(k any) any

type ModelElement

type ModelElement interface {
	Model() *Model
	FQName() NamePath
	Taggable
}

type Multiplicity

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

func Many

func Many(min uint) Multiplicity

func Mult

func Mult(min, max uint) (Multiplicity, error)

func One

func One() Multiplicity

func Opt

func Opt() Multiplicity

func (Multiplicity) Collection

func (m Multiplicity) Collection() bool

func (Multiplicity) Compare

func (m Multiplicity) Compare(n Multiplicity) (bool, int)

func (*Multiplicity) Intersect added in v0.2.0

func (m *Multiplicity) Intersect(ms ...Multiplicity) bool

func (Multiplicity) Max

func (m Multiplicity) Max() uint

func (Multiplicity) Min

func (m Multiplicity) Min() uint

func (Multiplicity) String

func (m Multiplicity) String() string

type Name

type Name []string

Names in [qb]MDD are made from words. Words are strings that must not contain runes from InvalidNameRunes. A name can be mapped to a string according to some convention, e.g. camel-case string etc. Name also implements fmt.Formatter with its on verbs to allow simple mapping to such naming conventions when printing (see Name.Format).

func NewName added in v0.5.0

func NewName(n string, acceptEmpty bool) (Name, error)

NewName splits n into whitespace separated words, creates a name from the words and validates the result using the acceptEmpty parameter. Consecutive spaces are treated as one separator.

func ParseName

func ParseName(s string) (Name, error)

ParseName creates a name from the normalized string representation that is created by Name.String.

Example
fmt.Println(ParseName("<Foo bar BAZ>"))
Output:

[Foo bar BAZ] <nil>

func (Name) Camel added in v0.3.0

func (n Name) Camel(start int, keepAcronyms bool) Name

func (Name) Caps

func (n Name) Caps(keepAcronyms bool) Name

func (Name) Empty

func (n Name) Empty() bool

func (Name) Equal

func (n Name) Equal(m Name) bool

func (Name) Format added in v0.3.0

func (n Name) Format(f fmt.State, verb rune)

Supported verbs:

'v' Format as []string
's' Format using Name.String()
'n' Concatenate the unchanged words of the Name
'l' Concatenate the lower case words of the Name
'u' Concatenate the upper case words of the Name
'c' Concatenate the capitalized words of the Name

Flags:

' ' Separate words with a space rune
'-' Separate words with minus symbol (kebab case)
'+' Separate words with underscore (snake case)
'#' Keep acronyms unchanged

When a width value is used with the 'c' verb, all words with index lesser than width will be converted to lower case. All other words will be capitalized. With "%1c" one would produce camel case with first character in lower case.

Example
n := Name{"Foo", "bar"}
fmt.Println("Slice of words:", n)
fmt.Printf("Delimited string representation: %s\n", n)
fmt.Printf("Concatenated unchanged words: %n\n", n)
fmt.Printf("Lower case words: %l\n", n)
fmt.Printf("Lower case words separated with '-': %-l\n", n)
fmt.Printf("Upper case words: %u\n", n)
fmt.Printf("Upper case words separated with '_': %+u\n", n)
fmt.Printf("Capitalized words (Camel starting at 0): %c\n", n)
fmt.Printf("Capitalized separated by ' ': % c\n", n)
fmt.Printf("Camel starting at 1: %1c\n", n)
fmt.Printf("Camel starting at 1 separated by ' ': % 1c\n", n)
fmt.Printf("Camel starting at 2 separated by '_': %+2c\n", Name{"foo", "Bar", "baz"})
fmt.Printf("With unchanged acronym: %#+c\n", Name{"JWT", "Token"})
fmt.Printf("Error: %x\n", n)
Output:

Slice of words: [Foo bar]
Delimited string representation: <Foo bar>
Concatenated unchanged words: Foobar
Lower case words: foobar
Lower case words separated with '-': foo-bar
Upper case words: FOOBAR
Upper case words separated with '_': FOO_BAR
Capitalized words (Camel starting at 0): FooBar
Capitalized separated by ' ': Foo Bar
Camel starting at 1: fooBar
Camel starting at 1 separated by ' ': foo Bar
Camel starting at 2 separated by '_': foo_bar_Baz
With unchanged acronym: JWT_Token
Error: %!x(invalid verb for mdd.Name <Foo bar>)

func (Name) Join added in v0.3.0

func (n Name) Join(sep string) string

func (Name) Lower

func (n Name) Lower(keepAcronyms bool) Name

func (Name) String

func (n Name) String() string

String returns a normalised string representation consisting of the space-separated words of the name enclosed in angle brackets, e.g. <foo bar>.

func (Name) Upper

func (n Name) Upper() Name

func (Name) Validate

func (n Name) Validate(acceptEmpty bool) error

func (Name) Word added in v0.5.0

func (n Name) Word(i int) string

type NamePath

type NamePath []Name

func (NamePath) Caps

func (p NamePath) Caps(keepAcronyms bool, ns ...int) NamePath

func (NamePath) Lower

func (p NamePath) Lower(keepAcronyms bool, ns ...int) NamePath

func (NamePath) String

func (p NamePath) String() string

func (NamePath) Upper

func (p NamePath) Upper(keepAcronyms bool, ns ...int) NamePath

type Package

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

func (*Package) AddDependency

func (p *Package) AddDependency(to *Package) []*Package

AddDependency lets package p depend on to given that this will not create cyclic dependency graph. A package is considered to depend on itself anyway but adding this dependency will have no effect.

func (*Package) AppendAtoms added in v0.8.0

func (p *Package) AppendAtoms(to []*Atom) []*Atom

func (*Package) AppendClasses added in v0.4.0

func (p *Package) AppendClasses(to []*Class) []*Class

func (*Package) AppendEnums added in v0.4.0

func (p *Package) AppendEnums(to []*Enum) []*Enum

func (*Package) AppendTypes added in v0.11.1

func (p *Package) AppendTypes(to []Type) []Type

func (*Package) Atoms added in v0.8.0

func (p *Package) Atoms() []*Atom

func (*Package) Classes

func (p *Package) Classes() []*Class

func (*Package) Dependencies

func (p *Package) Dependencies() []*Package

Dependencies returns packages that p directly depends on.

func (*Package) DependencyPath added in v0.2.0

func (p *Package) DependencyPath(to *Package) []*Package

func (*Package) DependsOn

func (p *Package) DependsOn(q *Package) bool

func (*Package) EachType

func (p *Package) EachType(do func(Type) error) error

func (*Package) Enums

func (p *Package) Enums() []*Enum

func (*Package) FQName

func (p *Package) FQName() NamePath

func (*Package) Model

func (p *Package) Model() *Model

func (*Package) Name

func (p *Package) Name() Name

func (*Package) NewAtom added in v0.8.0

func (p *Package) NewAtom(name Name, cmpr Comparability) (*Atom, error)

func (*Package) NewClass

func (p *Package) NewClass(name Name, bases ...*Class) (*Class, error)

func (*Package) NewEntity

func (p *Package) NewEntity(name Name, bases ...*Class) (*Class, error)

func (*Package) NewEnum

func (p *Package) NewEnum(name Name, literals ...Name) (*Enum, error)

func (*Package) SetTag

func (p *Package) SetTag(key, tag any) error

func (*Package) String

func (p *Package) String() string

func (*Package) Supported added in v0.5.0

func (p *Package) Supported() []*Package

Supported returns packages that are directly supported by p.

func (*Package) Supports

func (p *Package) Supports(q *Package) bool

func (*Package) Tag

func (t *Package) Tag(k any) any

func (*Package) Type

func (p *Package) Type(name Name) Type

func (*Package) TypeString added in v0.5.0

func (p *Package) TypeString(name string) Type

func (*Package) Types added in v0.11.1

func (p *Package) Types() []Type

type Property

type Property interface {
	Owner() ModelElement
	Name() Name
	FQName() NamePath
	Type() Type
	Mult() Multiplicity
	Derived() bool
	SetDerived(bool) error
	Collection() (Collection, *Key)
	SetCollection(typ Collection, key *Key) error
	Taggable
	fmt.Stringer
}

type RestrictedTag added in v0.5.0

type RestrictedTag interface {
	Tags(Taggable) bool
}

A RestrictedTag will be checked to be valid before attached to a Taggable. Tag keys as well as tag values will be checked when implementing RestrictedTag.

type SpecificTag added in v0.9.1

type SpecificTag[T Taggable] struct{}

SpecificTag can be embedded to implement a RestrictedTag for T.

func (SpecificTag[T]) Tags added in v0.9.1

func (st SpecificTag[T]) Tags(e Taggable) bool

type Taggable

type Taggable interface {
	Tag(k any) any
	SetTag(key, tag any) error
	// contains filtered or unexported methods
}

Taggable is the interface for all model elements that can be tagged with arbitrary values. This is intended for use with generators - comparable to the stereotypes of UML as an extension mechanism.

type Type

type Type interface {
	Name() Name
	FQName() NamePath
	Package() *Package
	Use() []Property
	Compares() Comparability
	Compare(t Type) (comparable bool, order int)
	Taggable
	// contains filtered or unexported methods
}

Directories

Path Synopsis
Package doc implements a [qb]MDD model for the mdd metamodel itself.
Package doc implements a [qb]MDD model for the mdd metamodel itself.
examples
modular
Package modular imeplements a [qb]MDD model in a modular fashion.
Package modular imeplements a [qb]MDD model in a modular fashion.
modular/complexpkg
Package complexpkg uses a rather complex way to define the package.
Package complexpkg uses a rather complex way to define the package.
modular/model
Package model puts together the modular example model.
Package model puts together the modular example model.
modular/simplepkg
Package simplepkg simply defines a package with a few elements.
Package simplepkg simply defines a package with a few elements.
mymusic
Package mymusic implemets a model for a music database that makes use of many typical [qb]MDD features.
Package mymusic implemets a model for a music database that makes use of many typical [qb]MDD features.
mymusic/puml
Generate PlantUML class diagram for the mymusic example model.
Generate PlantUML class diagram for the mymusic example model.
mymusic/template
Generate AsciiDoc output with Go's text/template from the mymusic example model.
Generate AsciiDoc output with Go's text/template from the mymusic example model.
Package mdg is the Model Driven Generator Toolbox for [qb]MDD.
Package mdg is the Model Driven Generator Toolbox for [qb]MDD.
Package mmb – the [qb]MDD Model Builder – provides a wrapper around mdd to create models with a convenient syntax.
Package mmb – the [qb]MDD Model Builder – provides a wrapper around mdd to create models with a convenient syntax.
Package puml provides generators for PlantUML files from [qb]MDD models.
Package puml provides generators for PlantUML files from [qb]MDD models.

Jump to

Keyboard shortcuts

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