@the-oz/globs
v1.1.0
Published
ts implementation of globs framework
Downloads
72
Keywords
Readme
Globs Framework
A Typescript implementation of globs framework
This is a metamodel framework.
The goal of this framework is to replace beans by an object call a Glob. This Glob is a kind of map. The key to access a field is not a string but an instance of a Field. These Fields (for a given 'bean') are contained in an instance of a GlobType. GlobType and Field contains additional custom information.
Thanks to that we can write generic code not magic;-) which can be serialized in XMl/Json/binary and inserted in a DB. It can also be used to map object to other object using some configuration files. There is multiple way to create a GlobType.
Getting started
Build library locally
cd globframework.ts
npm run build && npm run fix && npm pack
Use Globs in your project
The tarball glob.tgz
generated previously must be moved at the root of the repo that uses it.
Then, add globs to your dependencies:
{
"dependencies": {
"globs": "file:./globs-X.X.X.tgz"
}
}
Finally, install reflect-metadata
npm install --save-dev reflect-metadata
If using with create-react-app
Globsframework uses experimental features such as decorators and metadata. As such, additional babel loaders are needed in order to use the library. In case you are using React, the easiest solution is to install a tool such as craco with the necessary loaders:
npm install --save-dev @babel/plugin-proposal-class-properties @craco/craco
Then update your package.json with the following
{
"craco": {
"babel": {
"plugins": [
"@babel/plugin-proposal-class-properties"
]
}
}
}
If you plan on using decorators in the project, then update your package.json with the following then install the missing lib
{
"craco": {
"babel": {
"plugins": [
[
"@babel/plugin-proposal-decorators",
{
"legacy": true
}
],
[
"@babel/plugin-proposal-class-properties",
{
"loose": true
}
]
]
}
}
}
npm install --save-dev @babel/plugin-proposal-decorators
Alternatively, you can use customize-cra
package along with con react-scripts-rewired
. Then, in the config-overrides.js file:
const { override, addExternalBabelPlugin } = require('customize-cra')
const customLoader = (config) => {
// do somethging
return config
}
module.exports = override(
addExternalBabelPlugin('@babel/plugin-proposal-class-properties'),
customLoader,
)
GLOSSARY
- GlobType: an entity with a name and fields. It can be instantiated in a glob
- Field: an entity with a name data type
- KeyField: any field which is considered part of the key (i.e. id/key in a relational db), it cannot change once created (a key uniquely defines an instance, changing it is equivalent to deal with another instance)
- Glob: a data instance holding values that respect its corresponding globType structure
- Annotation: an annotation defines a specific behaviour a globType or a field can have
Globs in a nutshell
Globs are used to hold data. Glob-types are the entity used to give structure to that data. Each glob-type is composed of fields which shapes the data that the glob-type instance (the glob) will hold.
Each of the glob-type fields can be itself a glob.
How to create a glob using the primitives
The following snippet shows of a glob-type with two fields is built as well as how a glob of can be instantiated from that type and its fields set to certain value
const globType: GlobType = GlobTypeBuilderFactory.create('integerString')
.addStringField('string')
.addIntegerField('integer')
.build()
const glob: Glob = globType.instantiate()
const field1: StringField = globType.getField('string').asStringField()
const field2: IntegerField = globType.getField('integer').asIntegerField()
glob.set(field1, 'hello')
glob.set(field2, 1)
How to create a glob with the glob-model generator
import { loadGlobType } from './Loader'
import { DoubleField } from './DoubleField'
@loadGlobType
class ComplexNumber {
static TYPE = globType()
static REAL = doubleField()
static COMPLEX = doubleField()
static create(real: number, complex: number) {
return this.TYPE.instantiate()
.set(this.REAL, real)
.set(this.COMPLEX, complex)
}
static show(glob: Glob) {
return `${glob.getValue(this.REAL)} + ${glob.getValue(
this.COMPLEX
)}*i`
}
}
const num = ComplexNumber.create(1, 2)
ComplexNumber.show(num) // '1 + 2*i'
Globs provide type checking
In the snippet of code above, we set the value of a string field to "hello"
. If we were to set the value to any other type, an error would be raised:
const globType: GlobType = GlobTypeBuilderFactory.create('string-glob-type')
.addStringField('string')
.build()
const glob: Glob = globType.instantiate()
const field1: StringField = globType.getField('string').asStringField()
glob.set(field1, 1) // throws an error
Globs provide a system of annotations
An annotation is a special kind of glob. We use annotation to add any sort of meta information to our system. For instance, if we are using a field called user_name
which is actually saved as the column name
on the table user
in our database, we can provide an annotation that will be able to translate the name on the fly while keeping the original structure unchanged.
Annotations are carried over by glob-types and by fields.
We access annotations through keys. The keys are used to compare globs between them. For example, java implementation of globs creates keys by hashing a glob's globType and keyFields.
KeyFields are Fields used to describe the structure of annotations' specific information. In other words a keyField describes a value which is a key itself (check).
Serialization & Deserialization
Glob
import { JsonSerializer } from './JsonSerializer'
import { JsonDeserializer } from './JsonDeserializer'
const globType: GlobType = GlobTypeBuilderFactory.create('aType')
.addStringArrayField('stringArray')
.addStringField('id')
.addDoubleField('value')
.build()
const data: MutableGlob = globType.instantiate()
const stringArrayField: StringArrayField = globType
.getField('stringArray')
.asStringArrayField()
data.set(stringArrayField, ['hello'])
const stringField = globType.getField('id').asStringField()
data.set(stringField, '1234')
const doubleField = globType.getField('value').asStringField()
data.set(doubleField, 3.141592)
const json = JsonSerializer.glob().serialize(data)
t.deepEqual(
json,
`{"_kind":"aType","stringArray":["hello"],"id":"1234","value":3.141592}`)
const deserialized = JsonDeserializer.glob(
[globType]
).deserialize(json)
t.deepEqual(deserialized, data)
GlobType
import { JsonSerializer } from './JsonSerializer'
import { JsonDeserializer } from './JsonDeserializer'
const annotation = FieldNameAnnotationBuilder.init('_id').instance
const globType: GlobType = GlobTypeBuilderFactory.create('aType')
.addStringArrayField('stringArray')
.addStringField('id', [annotation])
.addDoubleField('value')
.build()
const globTypeSerializer = JsonSerializer.globType()
const json = globTypeSerializer.serialize(globType)
t.deepEqual(
json,
'{"kind":"aType","fields":[' +
'{"name":"stringArray","type":"stringArray"},' +
'{"name":"id","type":"string","annotations":[' +
'{"_kind":"fieldNameAnnotation","name":"_id"}]},' +
'{"name":"value","type":"double"}]}'
)
const deserialized = JsonDeserializer.globType([
annotation.getGlobType()
]).deserialize(json)
t.deepEqual(deserialized, globType)
Local development
Install node modules:
npm install
Run library tests
npm run test:unit
Run tests
npm run test
Watch tests
npm run watch:test
Generate documentation:
npm run doc
Known Issues
Files Import
[] fix default exports so that we do not get TypeError: Class extends value undefined is not a constructor or null. In particular, we have this error when importing files in errors that use the default imports (import {X, Y, Z} from '../..'
) instead of import {X} from '../../X'
Next Steps
Codebase
- add missing annotations
- add annotation to run action at the end of loadGlobType
import { loadGlobType } from './Loader'
import { globType, stringField } from './Entities'
@loadGlobType()
class Shop {
TYPE = globType()
NAME = stringField()
URL = stringField()
// TODO: run the following automatically @loadAction()
logInfo() {
console.log("generated glob type")
console.log(Shop.TYPE.getName())
}
}
Publish on NPM
The library is versioned using semver and conventional commits.
To publish on NPM we use standard-version
.
In essence, all it takes to publish a major version is:
npm run npmpublish
git push --follow-tags origin master && npm publish
If building an alpha release for instance, you would use:
npm run npmpublish -- --prerelease --alpha
Documentation
- add table of content