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*.*'
)RegExp
s 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 thecombined.js
file. At run time, when running on the module-less Web side as acombined.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.ewindow
).
Inferred binding idenifiers
If a dependency (key) ends up with no identifier (var name), for example { myDep:[], ...}
, then the identifiers are automagically inferred from:
the code it self, i.e when you have
define ['lodash'], (_)->
or_ = require 'lodash'
somewhere in your code, it binds'lodash'
dependency with_
identifier.or using any other relevant part of the config like
bundle.dependencies.depsVars
,bundle.dependencies._knownDepsVars
etc.
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']
}