@geronimus/flatware v0.1.0
Contents
Flatware
(As in place settings.) This is a simple library to help you place settings. (eg, Configurations.)
We're trying to keep things simple. Therefore we're sticking with the idea of a one-level (flat) list of values, each of which has a unique name.
flatware defines two kinds of objects - a spec (specification), and a conf (configuration).
spec
Specification
It defines the values you are expecting to get. You can use it to validate a conf.
conf
Configuration
A thin wrapper around a simple value map. But the point is that you can validate it against a spec.
Value Types
You can use these to define the values a spec will accept. For simplicity's sake, we're sticking to a very limited set of value types:
- boolean : JavaScript's boolean type. true and false are the only values.
- number : JavaScript's number type, based on the IEEE 754 Floating-Point standard, similar to a type called double in many languages. Can accurately express integer values from -9,007,199,254,740,991 to 9,007,199,254,740,991. Can express real numbers with notably less precision.
- string : JavaScript's text type.
- Date : JavaScript's Date type.
Constraints
These are further limitations on what values a spec allows.
- optionsList : You can define a list (Array) of acceptable values. The chosen value must be one of these. Applies to: ( number, sting, Date )
- upperBound : The greatest / highest / latest value you will accept. (Inclusive.) Applies to: ( number, Date )
- lowerBound : The smallest / lowest / earliest value you will accept. (Inclusive.) Applies to: ( number, Date )
- pattern : A regular expression defining a text pattern that values you accept must match. Applies to: ( string )
Define a spec
Our example defines an imaginary configuration for a video game called Rescue . (Tom Spreen's unlicensed Star Trek: The Next Generation adventure for Apple computers in the mid 1990s.)
const spec = flatware.spec.fromObject({
includeCubeShip: {
type: "boolean",
desc: "Determines whether or not the Borg ship will be included as one of the enemies.",
},
enemies: {
type: "number",
desc: "The number of enemy ships to include on the map.",
lowerBound: 3,
upperBound: 30,
optionsList: [
3, 6, 9, 12, 15, 18, 21, 24, 27, 30
]
},
skillLevel: {
type: "string",
desc: "Defines the attack strategies that enemies will use.",
optionsList: [
"The Cadet's Game",
"The Captain's Game",
"The Admiral's Game"
]
},
userName: {
type: "string",
desc: "The name we will display on the scoreboard for high-scoring games. Must be a string of between one and seven characters, without any whitespace.",
pattern: /[^\s]{1,7}/
},
gameStartTime: {
type: "Date",
desc: "The date on which the game's timer starts. Must be during the second half of the 24th century.",
lowerBound: new Date( "2350-01-01 00:00:00.000Z" ),
upperBound: new Date( "2399-12-31 23:59:59.999Z" )
}
});
You don't have to include descriptions, but they are helpful. Especially if you choose to generate a template.
Templates
Once you have a spec defined, you can use it to generate a template:
spec.getTemplate();
The template is a JSON string, (eg, You can easily save it as a text file.), Each setting will have all of its defined properties, as well as a value property, which users can set to any value, in order to define a configuration.
Here is an example of what a fragment of the template that the example spec we gave above might include:
{
"skillLevel": {
"type": "string",
"desc": "Defines the attack strategies that enemies will use.",
"optionsList": [
"The Cadet's Game",
"The Captain's Game",
"The Admiral's Game"
],
"value": null
},
...
}
You can define either a conf, or a spec, (or both), using a valid template.
eg,
let conf;
let spec;
fs.readFile( "./template.json", ( error, template ) => {
if ( error )
throw error;
else {
conf = flatware.conf.fromTemplate( template );
spec = flatware.spec.fromTemplate( template );
}
});
Configurations
You can also define a conf using a simple object:
const conf = flatware.conf.fromObject({
includeCubeShip: true,
enemies: 15,
skillLevel: "The Captain's Game",
userName: "Picard",
gameStartTime: "2366-06-18 01:00:00.000Z"
});
Or from a JSON file:
let conf;
fs.readFile( "./conf.json", ( error, confJSON ) => {
if ( error )
throw error;
else
conf = flatware.conf.fromJSON( confJSON );
});
Validation
You can use a spec to validate a conf object.
These methods give you a detailed report on the differences between your conf and your spec. But it will not throw any errors. You're free to determine how to use it.
conf.adheresTo( spec )
adheresTo requires the conf to exactly match the spec. Any missing, illegal, or extra values will give a false result.
{
result: false,
missing: [ includeCubeShip ],
illegal: { enemies: 1 },
extra: { starbases: 9 },
okay: {
skillLevel: "The Captain's Game",
gameStartTime: Tue Jun 05 2379 21:52:31 GMT (Greenwhich Mean Time)
}
}
conf.conformsWith( spec )
conformsWith requires the conf to not contradict the spec's definition of any of the settings it uses. It only reports false in the case of a type or constraint violation. Missing and extra values are allowed.
{
result: true,
missing: [ includeCubeShip ],
illegal: {},
extra: { starbases: 9 },
okay: {
enemies: 15,
skillLevel: "The Captain's Game",
gameStartTime: Tue Jun 05 2379 21:52:31 GMT (Greenwhich Mean Time)
}
}
API
There is also a full, programmatic application programming interface for defining specs and confs. We present it below.
- flatware
- flatware.conf
- flatware.parseTemplate
flatware
The parent object. The only thing you need to import. ALl fo the functionality springs from this namespace.
flatware.conf
The conf interface. (See below(### setting.desc).)
flatware.conf.new() : conf
Create a new blank conf object.
flatware.conf.fromObject( obj : Object ) : conf
Create a conf object defined by an object representation.
`obj`: Any key-value map, represented as a JavaScript object. If you want this to adhere to - or conform to - a spec, then the values should belong to ___flatware___'s legal value types. (See above.)
returns
A conf object.
flatware.conf.fromJSON( jsonText : JSONString ) : conf
Create a conf object defined by a JSON representation. This should work in the same way as flatware.conf.fromObject
without having parse the JSON representation of an object.
`jsonText`: The valid JSON string defining the configuration.
returns
A conf object.
flatware.conf.fromTemplate( template : JSONString ) : conf
Create a conf object defined by a template representation. This should work in the same way as flatware.conf.fromObject
, except that it takes template JSON representations. It ignores the spec properties, and only uses the value
property.
`template`: The valid JSON string defining the template.
returns
A conf object.
flatware.parseTemplate( template : JSONString ) : Object
Takes a valid template (see above) represented as a JSON string, and returns an object containing both the spec and the conf objects that the template defines.i (Available as spec
and conf
respectively.)
`template`: The valid JSON string defining the template.
flatware.spec
The spec interface. (See below( ### setting.desc ).)
flatware.spec.new() : conf
Create a new blank spec object.
flatware.spec.fromObject( obj : Object ) : conf
Create a spec object defined by an object representation.
`obj`: A JavaScript object where each key defines one or more settings, each of which contain the following properties:
- __type__: _Required_ One of ( `boolean`, `number`, `string`, `Date` )
- __desc__: _Optional_ The textual description of the setting's meaning.
- __optionsList__: _Optional_ You can define a list (Array) of acceptable values. The chosen value must be one of these. Applies to: ( _number_, _sting_, _Date_ )
- __upperBound__: _Optional_ The greatest / highest / latest value you will accept. (Inclusive.) Applies to: ( _number_, _Date_ )
- __lowerBound__: _Optional_ The smallest / lowest / earliest value you will accept. (Inclusive.) Applies to: ( _number_, _Date_ )
- __pattern__: _Optional_ A [regular expression](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp) defining a text pattern that values you accept must match. Applies to: ( _string_ )
returns
A spec object.
flatware.spec.fromJSON( jsonText : JSONString ) : conf
Create a spec object defined by a JSON representation. This should work in the same way as flatware.spec.fromObject
without having to parse the JSON representation of an object.
`jsonText`: The valid JSON string defining the specification.
returns
A spec object.
flatware.spec.fromTemplate( template : JSONString ) : spec
Create a spec object defined by a template representation. This should work in the same way as flatware.spec.fromObject
, except that it takes template JSON representations. It ignores the value
property and just uses the values that define a spec. (See above.)
`template`: The valid JSON string defining the template.
returns
A spec object.
conf.adheresTo( spec : spec ) : confReport
Does this configuration match the specification exactly? Takes a valid spec object and returns a confReport object detailing the matched properties and violations. (If any.) To obtain a simple boolean result, call conf.adheresTo( spec ).result
.
returns
A confReport object, with the following model:
- __result : boolean__ Whether or not the ___conf___ matches the ___spec___ exactly.
- __missing : Array[ string ]__ Lists any property names from the ___spec___ which are not implemented in the ___conf___.
- __extra : object__ Lists any properties found in the ___conf___ that are not defined in the ___spec___.
- __illegal : object__ Lists any properties from the ___conf___ that do not adhere to the type or constraints defined for the same name in the ___spec___.
- ___okay : object___ Lists all properties from the ___conf___ that match the type and constraints from the ___spec___ exactly.
Example:
{
result: false,
missing: [ includeCubeShip ],
illegal: { enemies: 1 },
extra: { starbases: 9 },
okay: {
skillLevel: "The Captain's Game",
gameStartTime: Tue Jun 05 2379 21:52:31 GMT (Greenwhich Mean Time)
}
}
conf.conformsWith( spec : spec ) : confReport
Of all of the properties this configuration defines, do the ones defined by the specification match it? Otherwise stated, this is like the method conf.adheresTo( spec : spec ) : confReport
, except that it tolerates extra properties not defined by the specification, and missing properties that the specification defines, but the configuration does not implement. Takes a valid spec object and returns a confReport object detailing the matched properties and violations. (If any.) To obtain a simple boolean result, call conf.conformsWith( spec ).result
.
returns
A confReport object, with the following model:
- __result : boolean__ Whether or not the properties in this ___conf___ that are defined by the ___spec___ match the ___spec___'s type and constraints.
- __missing : Array[ string ]__ Lists any property names from the ___spec___ which are not implemented in the ___conf___.
- __extra : object__ Lists any properties found in the ___conf___ that are not defined in the ___spec___.
- __illegal : object__ Lists any properties from the ___conf___ that do not adhere to the type or constraints defined for the same name in the ___spec___.
- ___okay : object___ Lists all properties from the ___conf___ that match the type and constraints from the ___spec___ exactly.
Example:
{
result: true,
missing: [ includeCubeShip ],
illegal: {},
extra: { starbases: 9 },
okay: {
enemies: 15,
skillLevel: "The Captain's Game",
gameStartTime: Tue Jun 05 2379 21:52:31 GMT (Greenwhich Mean Time)
}
}
conf.values.get( name : string ) : any
If name has been defined in this conf, returns the value associated with that name. Otherwise, it returns undefined.
conf.values.list() : object
Returns an object containing all of the names and values defined in this conf object.
conf.values.remove( name : string ) : void
Deletes the named property (and its value) from the conf object.
conf.values.set( name : string, value : boolean | number | string | Date ) : void
If the name does not already exist in the conf, creates value you provide associated with the name. If the name already exists, it associates the value you provide with it.
spec.asObject() : specObject
Returns the object representation of the spec.
Example:
{
includeCubeShip: {
type: "boolean",
desc: "Determines whether or not the Borg ship will be included as one of the enemies.",
},
enemies: {
type: "number",
desc: "The number of enemy ships to include on the map.",
lowerBound: 3,
upperBound: 30,
optionsList: [
3, 6, 9, 12, 15, 18, 21, 24, 27, 30
]
},
skillLevel: {
type: "string",
desc: "Defines the attack strategies that enemies will use.",
optionsList: [
"The Cadet's Game",
"The Captain's Game",
"The Admiral's Game"
]
},
userName: {
type: "string",
desc: "The name we will display on the scoreboard for high-scoring games. Must be a string of between one and seven characters, without any whitespace.",
pattern: /[^\s]{1,7}/
},
gameStartTime: {
type: "Date",
desc: "The date on which the game's timer starts. Must be during the second half of the 24th century.",
lowerBound: new Date( "2350-01-01 00:00:00.000Z" ),
upperBound: new Date( "2399-12-31 23:59:59.999Z" )
}
}
spec.getTemplate() : string
Returns the object representation of the spec, with an additional value
property for each defined name. You can use this to produce a template file, which you can use as a self-documenting config file, instantiating a conf with flatware.conf.fromTemplate( templateJSON )
, or even a spec, with flatware.spec.fromTemplate( templateJSON )
. (Or even both, using flatware.parseTemplate( templateJSON )
.)
spec.settings.define( name : string, type : string ) : setting
Creates a new setting within the spec, for the name you provide, and of the type you specifiy. The new setting has no description or constraints or yet. This method returns the setting object, so that you can begin to specify them immediately.
- name A string of one of more non-whitespace characters.
- type One of:
"boolean"
,"number"
,"string"
, or"Date"
. (Throws an error if the type is an illegal value.)
returns
A setting object. (See below( ### setting.desc ).)
spec.settings.fromObject( name : string, obj : object ) : setting
Accepts a name and an object defining (obligatorily) and (optionally) a description and a list of constraints. Returns the setting that the object defined.
- name A string, consisting of at least one non-whitespace character, which will be the name of the defined setting.
- obj An object defining the properties type (mandatory), desc, and constraints (optional).
Example:
{
type: "number",
desc: "The number of enemy ships to include on the map.",
lowerBound: 3,
upperBound: 30,
optionsList: [
3, 6, 9, 12, 15, 18, 21, 24, 27, 30
]
}
returns
A setting object. (See below( ### setting.desc ).)
spec.settings.get( settingName : string ) : setting
Returns the setting identified by the settingName you provide, if it exists. Otherwise, throws an error.
returns
A setting object. (See below( ### setting.desc ).)
spec.settings.list() : Array string
Returns an Array of the setting names defined by the spec.
spec.settings.remove( settingName : string ) : void
Deletes the setting with the name settingName from the spec. (If it exists.)
setting.asObject() : object
Get the object representation of the setting.
returns
The object representation of a setting.
Example:
{
type: "number",
desc: "The number of enemy ships to include on the map.",
lowerBound: 3,
upperBound: 30,
optionsList: [
3, 6, 9, 12, 15, 18, 21, 24, 27, 30
]
}
setting.desc
Read-write property. Gets or sets the description of this setting. Must be a string of at least three words.
setting.getConstraint( constraint : string ) : constraint
Returns the definition of the constraint you specify.
returns
A constraint object.
- optionsList ( number, string, Date ) An array of values of the settting's type.
- lowerBound ( number, Date ) The minimum acceptable value for a number or Date.
- upperBound ( number, Date ) The maximum acceptable value for a number or Date.
- pattern ( string ) A regular expression (pattern) specifying acceptable string values.
setting.listConstraints() : object
Returns an object, where the keys are the constraint names, and the values are the constraint definitions.
- optionsList ( number, string, Date ) An array of values of the settting's type.
- lowerBound ( number, Date ) The minimum acceptable value for a number or Date.
- upperBound ( number, Date ) The maximum acceptable value for a number or Date.
- pattern ( string ) A regular expression (pattern) specifying acceptable string values.
setting.name
Read-only property. The name of the setting.
setting.redefineType( newType : string ) : setting
Changes the type of an existing setting. Resets its description and constraints.
- newType (
"boolean"
|"number"
|"string"
|"Date"
) The type that this setting will now become.
returns
The new setting. Remember, any existing desc and constraint information will be erased.
setting.rename( newName : string ) : setting
Changes the name of this setting within its spec.
- newName A string consisting of a least one non-whitespace character.
returns
The spec object, as defined by its new name.
setting.type
Read-only property. The type of the property.
6 years ago