Fork me on GitHub

Types And Derive

Types

A description of valid reoccurring type(s) and their behavior.

For somewhat complex types, there is a flexibility of values you can use as there are shortcuts, for example.

filespecs

Filename specifications (or simply filenames), expressed in 'umatch'. In short it can be:

  • String in gruntjs's expand minimatch format (eg '**/*.coffee') and its exclusion cousin (eg '!**/DRAFT*.*')

  • RegExps that match filenames (eg /./) again with a

    [..., '!', /regexp/, ...]

    exclusion pattern.

  • A function(filename){} callback, returning true if filename is to be matched. Consistently it can have a negation/exclusion flag before it: [..., '!', function(f){ return f === 'excludeMe.js' }, ...].

    @note use a true (i.e matched) as the result preceded by '!' for exclusion, rather the common trap than of a false result for your excluded matches (cause all your non-excluded with match with true, which is probably not what you want!).

  • @todo: NOT IMPLEMENTED: An `Array<String|RegExp|Function|Array>, recursive, i.e

    [ ..., ['AllowMe*.*', '!', function(f){ return f === 'excludeMe.js' }, [.., []], ...], ...]

    @see uses 'umatch'

@example

bundle: {
  filez: [
    '**/recources/*.*'
    '!dummy.json'
    /.*\.someExtension$/i
    '!', /.*\.excludeExtension$/i
    (filename)-> filename is 'includedFile.ext'
  ]
}

depsVars

Defines one or more dependencies (i.e Modules or other Resources), that each is bound to one or more identifiers (i.e variable or property names).

Its used in many places (like injecting deps in bundle.dependencies.imports) and is often useful.

Formal depsVars type

The formal type, (i.e. where each depVars value ends up as, no matter how its declared as), is an Object like this:

{
  'dep1': ['dep1VarName1', 'dep1VarName2'],
  ...
  'underscore': ['_'],
  'Backbone': ['backbone'],
  ....
  'depN': ['depNVarName1', ...]
}

Shortcut depsVars types

The depsVars type has shortcuts:

  • Array: eg ['arrayDep1', 'arrayDep2', ..., 'arrayDepn'], with one or more deps.

  • String: eg 'soloDep', of just one dep.

  • or even deps with one identifier { 'lodash': '_', xxx: [] }

Shortcut types are converted to the formal type when deriving, using the dependenciesBindings derive - the above will end up as

  • {arrayDep1:[], arrayDep2:[], ..arrayDepn:[]}

  • {soloDep: []}

  • { 'lodash': ['_'], xxx: [] }

Binding deps and vars is required

  • when injecting dependencies, eg exporting declarativelly through bundle.dependencies.imports eg 'lodash', bind '_' as the var to access the module in the code.

  • when converting through 'combined' template. Local dependencies (like 'underscore' or 'jquery') are not part of the combined.js file. At run time, when running on the module-less Web side as a combined.js via a simple <script/>, the uRequire generated code will only know how to grab the dependency using the binding $ from the global object (i.e window).

Inferred binding idenifiers

If a dependency (key) ends up with no identifier (var name), for example { myDep:[], ...}, then the identifiers are automagically inferred from:

booleanOrFilespecs

This type controls if a key applies to all, none or some filenames/module paths. Its either:

  • boolean (true/false), so all or none files/modules get the setting.

  • A filespecs. Important @note: if the config setting (eg globalWindow, useStrict etc) is dealing with modules (usually), a module bundleRelative path is expected without the filename extension, i.e ['models/PersonModel', ...] without '.js'. If dealing with general file, you have to match filename and extension.

Unless otherwise specified, booleanOrFilespecs uses derive arraysConcatOrOverwrite.


Deriving & Behaviors

A config can can be derived from one or more parent configs. Its a similar notion to classical OO Inheritance, where a subclass overrides the values of a superclass, but better.

Ultimately all configs are derived (inherit) from MasterDefaultsConfig which holds all default parent values.

Derivation is more flexible than simple OO inheritance (or any _.extend like function) for two main reasons :

Deeper

Keys of the config are not flat - derivation goes deeply in paths of keys, i.e {a: {b1:11, b2:12}} --derive--> {a: {b1:1, b3:3}} gives {a: {b1:11, b2:12, b3:3}}, something like or _.deepExtend.

Behavior

Behavior goes longer than depth: when inheriting in OO (or an _.extend like function), all overriding child keys simply overwrite (or hide) those on parent(s).

With derive of uRequire configs, at each member/key (or keys path, since we have depth), there is a different behavior for how to derive (or blend) with the parent's values of that key.

For example when a key is supposed to hold an Array value with derive type arrayizeConcat, the resulting Array is the child's array concat-ed to parent's. Also, if the value held is NOT already an Array (as it should), then that value becomes the 1st item of an Array first, before being concatenated to parent's. In any case, even if there just one config (nort derived by any other), the resulting value is guaranteed to be an Array.

Resulting type / structure

??????

Derive Types

arrayizeConcat

Both parent and child values are considered to be of Array type (or are turned into an Array first with _B.arrayize).

Then the items on child configs are pushed after the ones they inherit (the parent's array, higher up in hierarchy).The parent is always a shallow clone of the original parent array which remains intact. In v0.6.10 the child can be a function callback.

For example consider key bundle.filez (that has the arrayizeConcat derive behavior).

  • parent config bundle: filez: ['**/*', '!DRAFT/*.*']

  • child config bundle: filez: ['!vendor/*.*]

  • derived config: bundle: filez: ['**/*', '!DRAFT/*.*', '!vendor/*.*].

type

The type for both child and parent values, are either Array<Anything> or Anything but Array (which is _B.arrayize-d first).

Function callback

The child can also be a callback function(parentArray), that receives a shallow clone of parentArray and returns the resulted array.

Example:

  • parent config bundle:{ filez: ['**/*.js', '!DRAFT/*.*']}

  • child config bundle: { filez: function(parentArray){ parentArray.unshift('**/*.coffee'); parentArray } }

  • derived config: bundle: { filez: ['**/*.coffee', '**/*.js', '!DRAFT/*.*'] }.

This way you can manipulate the inherited array as desired.

Reset Parent

To reset the inherited parent array (always in your new child destination array), use [null] as the 1st item of your child array. For example

  • parent config bundle:filez: ['**/*', '!DRAFT/*.*']

  • child config bundle:filez: [[null], 'vendorOnly/*.*]

  • derived config: bundle: filez: ['vendorOnly/*.*].

arrayizeUniqueConcat

Just like arrayizeConcat, but only === unique items are pushed to the result array.

arraysConcatOrOverwrite

If both child and parent values are already an Array, then the items on child (derived) configs are pushed after the ones they inherit (like arrayizeConcat).

Otherwise, the child value (even if its an array) overwrites the value it inherits.

For example consider key build.globalWindow (that has the arraysConcatOrOverwrite derive behavior).

  • parent config build: globalWindow: ['**/*']

  • child config build: globalWindow: true

  • blended config: build: globalWindow: true

or similarly

  • parent config build: globalWindow: true

  • child config build: globalWindow: ['**/*']

  • blended config: build: globalWindow: ['**/*']

@note reset parent works like arrayizeConcat's, so you can produce a new Array, even when deriving from an Array.

dependenciesBindings

This derivation refers to depsVars type.

Each dependency name (the key) of child configs is added to the resulted object, if not already there.

Its identifiers / variable names are then arrayizeUniqueConcat-ed onto the array.

For example with a parent value:

{
  myDep1: ['myDep1Var1', 'myDep1Var2']
}

and a child value:

{
  myDep1: ['myDep1Var1', 'myMissingDep1Var3']

  # identifier is a String, not an Array
  myDep2: 'myDep2Var'
}

the resulted derived object will be

{
  # only missing 'myMissingDep1Var3' identifier is appended to array
  myDep1: ['myDep1Var1', 'myDep1Var2', 'myMissingDep1Var3']

  # identifier is arrayized
  myDep2: ['myDep2Var']
}